[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"@babel/preset-env\"],\n  \"plugins\": [\n    [\n      \"@babel/plugin-proposal-object-rest-spread\",\n      {\n        \"loose\": true\n      }\n    ]\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\nindent_style = tab\ntrim_trailing_whitespace = true\n\n[*.json]\nindent_style = space\nindent_size = 2\n\n[*.yml]\nindent_style = space\nindent_size = 2\n\n[*.md]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": ".npmignore merge=ours\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# Reference - https://help.github.com/articles/about-code-owners/\n\n# Complete repo\n* @dequelabs/axe-codeowners\n\n# Localisation\nlocales/ja.json @mlca11y/mlca11y @dequelabs/axe-codeowners\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Join the axe Slack Community\n    url: https://accessibility.deque.com/axe-community\n    about: Connect with other people passionate about digital accessibility and axe tools.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/documentation.yml",
    "content": "name: Improve our documentation\ndescription: Report issues in our documentation or things we could document better.\nlabels: ['docs', 'ungroomed']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Documentation reports can be made for axe-core, [axe Extension](https://www.deque.com/axe/browser-extensions/), and [axe Linter](https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter). If you have a documentation report for one of our npm integrations (e.g. `@axe-core/webdriverjs`), please open an issue in https://github.com/dequelabs/axe-core-npm.\n  - type: dropdown\n    id: product\n    attributes:\n      label: Product\n      description: Which product is the documentation report for?\n      options:\n        - axe-core\n        - axe Extension\n        - axe Linter\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Description\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "content": "name: Request a feature\ndescription: Request an enhancement, improvement, or new rule.\nlabels: ['feat', 'ungroomed']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Feature requests can be made for axe-core, [axe Extension](https://www.deque.com/axe/browser-extensions/), and [axe Linter](https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter). If you have a feature request for one of our npm integrations (e.g. `@axe-core/webdriverjs`), please open an issue in https://github.com/dequelabs/axe-core-npm.\n  - type: dropdown\n    id: product\n    attributes:\n      label: Product\n      description: Which product is the feature for?\n      options:\n        - axe-core\n        - axe Extension\n        - axe Linter\n    validations:\n      required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Feature Description\n      description: What do you wish the product did? If the feature is about adding a new rule to axe-core, please follow our [rule template](https://github.com/dequelabs/axe-core/blob/develop/doc/rule-proposal.md).\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/possible-bug.yml",
    "content": "name: Report an issue\ndescription: Report a bug or an issue that you've encountered.\nlabels: ['ungroomed']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for reporting an issue to one of our products. Please provide all necessary information to reproduce the issue. Without adequate details, your issue may be closed without investigation.\n\n        Issues can be reported for axe-core, [axe Extension](https://www.deque.com/axe/browser-extensions/), and [axe Linter](https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter). If you're having an issue with one of our npm integrations (e.g. `@axe-core/webdriverjs`), please open an issue in https://github.com/dequelabs/axe-core-npm.\n  - type: dropdown\n    id: product\n    attributes:\n      label: Product\n      description: Which product did you encounter the issue?\n      options:\n        - axe-core\n        - axe Extension\n        - axe Linter\n    validations:\n      required: true\n  - type: input\n    id: version\n    attributes:\n      label: Product Version\n      description: For axe-core use `axe.version`. For axe Extension go to the settings screen in the extension. For axe Linter look at the installed extension list for the version.\n  - type: checkboxes\n    id: latest-axe\n    attributes:\n      label: Latest Version\n      options:\n        - label: I have tested the issue with the latest version of the product\n          required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Issue Description\n      description: Please include a description of the issue and a page or code snippet where it can be reproduced.\n      value: |\n        #### Expectation\n        Describe what you expected the product to do.\n\n        #### Actual\n        Describe what the product actually does.\n\n        #### How to Reproduce\n        Provide a code sample or link to a webpage that reproduces the issue. Without this, your issue may be closed without investigation.\n\n        #### Additional context\n        Any thing else we should know about the issue?\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.yaml",
    "content": "name: Ask a question\ndescription: General questions for us, the product, or why things are the way they are.\nlabels: ['question', 'ungroomed']\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Questions can be asked for axe-core, [axe Extension](https://www.deque.com/axe/browser-extensions/), and [axe Linter](https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter). If you have a question about one of our npm integrations (e.g. `@axe-core/webdriverjs`), please open an issue in https://github.com/dequelabs/axe-core-npm.\n  - type: dropdown\n    id: product\n    attributes:\n      label: Product\n      description: Which product is the question for?\n      options:\n        - axe-core\n        - axe Extension\n        - axe Linter\n    validations:\n      required: true\n  - type: textarea\n    id: question\n    attributes:\n      label: Question\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<< Describe the changes >>\n\nCloses:\n"
  },
  {
    "path": ".github/actions/install-deps/action.yml",
    "content": "name: 'Install Dependencies'\ndescription: 'Install OS and Project dependencies'\n\ninputs:\n  node-version:\n    description: 'Node.js version to install'\n    required: false\n  start-xvfb:\n    description: 'If provided, this is the display number to run xvfb on. Should be in `:N` format, e.g., `:99`.'\n    required: false\n  nightly:\n    description: 'If true, installs the nightly versions of browsers.'\n    required: false\noutputs:\n  chrome-path:\n    description: 'Path to the installed Chrome binary'\n    value: ${{ steps.setup-chrome.outputs.chrome-path }}\n  firefox-path:\n    description: 'Path to the installed Firefox binary'\n    value: ${{ steps.setup-firefox.outputs.firefox-path }}\n  chromedriver-path:\n    description: 'Path to the installed ChromeDriver binary'\n    value: ${{ steps.setup-chrome.outputs.chromedriver-path }}\n  chrome-version:\n    description: 'Version of the installed Chrome binary'\n    value: ${{ steps.setup-chrome.outputs.chrome-version }}\n  chromedriver-version:\n    description: 'Version of the installed ChromeDriver binary'\n    value: ${{ steps.setup-chrome.outputs.chromedriver-version }}\n  firefox-version:\n    description: 'Version of the installed Firefox binary'\n    value: ${{ steps.setup-firefox.outputs.firefox-version }}\n\nruns:\n  using: 'composite'\n  steps:\n    - name: Setup Node.js\n      uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0\n      with:\n        registry-url: 'https://registry.npmjs.org'\n        node-version: ${{ inputs.node-version }}\n        node-version-file: ${{ inputs.node-version == '' && '.nvmrc' || '' }}\n        cache: npm\n    - name: Fix Chrome Sandbox Permissions\n      shell: bash\n      run: |\n        sudo chown root:root /opt/google/chrome/chrome-sandbox\n        sudo chmod 4755 /opt/google/chrome/chrome-sandbox\n    - name: Install Xvfb\n      shell: bash\n      if: ${{ inputs.start-xvfb }}\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y xvfb x11-xserver-utils\n    - name: Install Google Chrome for Testing\n      id: setup-chrome\n      uses: browser-actions/setup-chrome@b94431e051d1c52dcbe9a7092a4f10f827795416 # v2.1.0\n      with:\n        # @see https://github.com/dequelabs/axe-core/issues/5027\n        chrome-version: ${{ inputs.nightly == 'true' && 'beta' || 145 }}\n        install-chromedriver: true\n        install-dependencies: true\n    - name: Install Firefox\n      id: setup-firefox\n      uses: browser-actions/setup-firefox@5914774dda97099441f02628f8d46411fcfbd208 # v1.7.0\n      with:\n        firefox-version: ${{ inputs.nightly == 'true' && 'latest-nightly' || 'latest' }}\n    - name: Install Project Dependencies\n      shell: bash\n      run: npm ci\n    - name: Start Xvfb\n      if: ${{ inputs.start-xvfb }}\n      env:\n        DISPLAY: ${{ inputs.start-xvfb }}\n      shell: bash\n      # This is the same resolution as what CircleCI used.\n      # Maintaining it for consistency between the environments\n      # since something may be resolution dependent.\n      run: Xvfb \"$DISPLAY\" -screen 0 1280x1024x24 &\n"
  },
  {
    "path": ".github/axe-linter.yml",
    "content": "exclude:\n  - CHANGELOG.md\n  - test/**/*\n"
  },
  {
    "path": ".github/bin/determine-version.sh",
    "content": "#!/usr/bin/env bash\n\nset -eo pipefail\n\necho \"::group::Determining new prerelease version\"\n\nNAME=$(npm pkg get name | tr -d '\"')\nLATEST_VERSION=$(npm pkg get version | tr -d '\"')\n\nSHORT_SHA=$(echo \"${GITHUB_SHA}\" | cut -c1-7)\nNEW_VERSION=\"$LATEST_VERSION-canary.${SHORT_SHA}\"\n\necho \"Latest version in package.json: $LATEST_VERSION\"\necho \"New prerelease version: $NEW_VERSION\"\n\necho \"version=$NEW_VERSION\" >> \"$GITHUB_OUTPUT\"\necho \"name=$NAME\" >> \"$GITHUB_OUTPUT\"\n\necho \"::endgroup::\"\n"
  },
  {
    "path": ".github/bin/validate-npm-deploy.sh",
    "content": "#!/usr/bin/env bash\n\nset -eo pipefail\n\nif [ -z \"$PACKAGE_NAME\" ] || [ -z \"$VERSION\" ]; then\n  echo \"::error::PACKAGE_NAME and VERSION environment variables must be set.\"\n  exit 1\nfi\n\nNPM_ROOT_PATH=$(npm root -g)\n\nnpm install -g \"${PACKAGE_NAME}@${VERSION}\" || {\n  echo \"::error::✗ Failed to install package: ${PACKAGE_NAME}@${VERSION}\"\n  exit 1\n}\n\ncd \"$NPM_ROOT_PATH\" || {\n  echo \"::error::✗ Failed to change directory to global npm root: $NPM_ROOT_PATH\"\n  exit 1\n}\n\nnode -pe \"window={}; document={}; require('${PACKAGE_NAME}');\" || {\n  echo \"::error::✗ Failed to import CommonJS module for package: ${PACKAGE_NAME}\"\n  exit 1\n}\n\ncd \"${NPM_ROOT_PATH}/${PACKAGE_NAME}\" || {\n  echo \"::error::✗ Failed to change directory to package path: ${NPM_ROOT_PATH}/${PACKAGE_NAME}\"\n  exit 1\n}\n\ntypes=$(node -pe \"require('./package.json').types\")\nif [ \"$types\" == \"undefined\" ]\nthen\n  types=$(node -pe \"require('./package.json').typings\")\nfi\nif [ \"$types\" != \"undefined\" ] && [ ! -f \"$types\" ]\nthen\n  echo \"::error::The types file is missing\"\n  exit 1;\nfi\necho \"Types file '$types' is present in the package\"\n"
  },
  {
    "path": ".github/bin/validate-package.mjs",
    "content": "#!/usr/bin/env node\n\n/**\n * @fileoverview Validates the package before publishing.\n * This script performs several checks to ensure the package\n * is correctly set up, including:\n * - Verifying the existence of files listed in `package.json`'s `files` array.\n * - Ensuring the package can be imported using both ESM `import` and CommonJS `require()`.\n * - Validating Subresource Integrity (SRI) hashes for the built files.\n *\n * The script generates a summary report compatible with\n * GitHub Actions, providing detailed feedback on each\n * validation step.\n *\n * Running this script locally has a few implications to be\n * aware of:\n * 1. It links and unlinks the package globally. So this\n * could impact other workspaces where current links are used.\n * 2. To test the step summary, set the `GITHUB_STEP_SUMMARY`\n * environment variable to a file path. If this file does not\n * exist, it will be created.\n */\n\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { createRequire } from 'node:module';\nimport { access, appendFile, readFile } from 'node:fs/promises';\nimport { execSync } from 'node:child_process';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst isDebug = process.env.DEBUG === 'true';\nconst repoRoot = resolve(import.meta.dirname, '..', '..');\n/**\n * Start the exit code at 0 for a successful run. If any checks\n * fail, we increment by 1 for each failure. When every check is done,\n * we exit with the final exit code.\n *\n * This means our exit code informs us of how many failures happened.\n *\n * For anyone unfamiliar with exit codes in shell programs,\n * an exit code of `0` means success, and any non-zero exit code\n * means failure.\n *\n * Special note as well, in theory this _could_ go above `255`,\n * causing the actual exit code to wrap around back to `0` and\n * keep counting. But, if we have that many checks in here down\n * the road then all the validation will need a major refactor.\n */\nlet exitCode = 0;\nconst missing = [];\nconst summaryFile = process.env.GITHUB_STEP_SUMMARY;\nlet summary = `# Package Validation\n\n<dl>\n  <dt>Package Name</dt>\n  <dd>${pkg.name}</dd>\n  <dt>Package Version</dt>\n  <dd>${pkg.version}</dd>\n  <dt>License</dt>\n  <dd>${pkg.license}</dd>\n</dl>\n\n`;\n\nconsole.group('Package Information');\nconsole.log('Name:', pkg.name);\nconsole.log('Version:', pkg.version);\nconsole.log('License:', pkg.license);\nconsole.groupEnd();\n\n/**\n * Checks if a file or folder exists on the filesystem.\n *\n * @param {string} path - The path to check (relative to repo root)\n * @returns {Promise<boolean>} True if the path exists, false otherwise\n */\nconst exists = async path => {\n  const absolutePath = resolve(repoRoot, path);\n  try {\n    await access(absolutePath);\n    return true;\n  } catch {\n    return false;\n  }\n};\n\n/**\n * Appends text to the GitHub Actions step summary file if it\n * exists. This is mostly useful for local testing where the\n * summary file may not be set. However if we want to set it\n * for testing, we can.\n *\n * Since we build the summary in chunks, this function\n * appends the current summary and then clears it for the\n * next section.\n *\n * @param {string} text - The text to append to the summary file\n * @returns {Promise<void>}\n */\nconst appendToSummaryFile = async text => {\n  if (summaryFile) {\n    await appendFile(summaryFile, text);\n    summary = '';\n  }\n};\n\n/**\n * Verifies that all files and folders listed in the `files`\n * array of `package.json` exist in the repository.\n */\nconst fileExistenceCheck = async () => {\n  summary += `\n\\n## File Existence Check\n\nThe following results table shows the status of files and folders\nlisted in the \\`files\\` array of \\`package.json\\`.\n\n> [!NOTE]\n> This check only validates the existence of files and folders\n> defined. It does not validate the contents. Thus a folder\n> could exist but be empty and still pass this check. Or\n> a file could exist but have incorrect syntax.\n\n| File | Status |\\n|------|--------|\n`;\n\n  console.log('Checking for existence of package files:');\n\n  for (const file of pkg.files) {\n    if (await exists(file)) {\n      console.info(`✓ ${file}`);\n      summary += `| \\`${file}\\` | ✓ Found |\\n`;\n    } else {\n      console.error(`✗ ${file}`);\n      summary += `| \\`${file}\\` | ✗ Missing |\\n`;\n      missing.push(file);\n    }\n  }\n\n  await appendToSummaryFile(summary);\n\n  if (missing.length > 0) {\n    await appendToSummaryFile(\n      `\\n**ERROR: Missing files: ${missing.join(', ')}**\\n`\n    );\n    console.error(`::error::Missing files: ${missing.join(', ')}`);\n    exitCode++;\n  }\n};\n\n/**\n * Validates that the main package file can be loaded via\n * CommonJS require. This ensures backward compatibility\n * for projects using CommonJS.\n */\nconst validateCommonJS = async () => {\n  summary += `\\n## CommonJS Compatibility Check\n\nThis check validates that the main package file can be loaded\nusing CommonJS \\`require()\\`, ensuring backward compatibility.\n\n| File | Status | Version |\\n|------|--------|--------|\n`;\n\n  const require = createRequire(import.meta.url);\n\n  console.log('Validating CommonJS compatibility:');\n\n  try {\n    const axe = require(`${pkg.name}`);\n\n    if (!axe || typeof axe !== 'object') {\n      throw new Error('Module did not export an object');\n    }\n\n    if (!axe.version) {\n      throw new Error('Missing version property');\n    }\n\n    console.info(`✓ ${pkg.name} (CommonJS)`);\n    summary += `| \\`${pkg.name}\\` | ✓ CommonJS Compatible | ${axe.version} |\\n`;\n  } catch (error) {\n    console.error(`✗ ${pkg.name} (CommonJS):`, error.message);\n    summary += `| \\`${pkg.name}\\` | ✗ CommonJS Failed | Not Found |\\n`;\n    summary += `\\n\\`\\`\\`\\n${error.message}\\n\\`\\`\\`\\n`;\n    exitCode++;\n  }\n\n  await appendToSummaryFile(summary);\n};\n\n/**\n * Validates that the package and all files listed in the\n * `files` array of `package.json` can be imported using\n * ESM `import` statements.\n */\nconst validateImportable = async () => {\n  summary += `\\n## Importable Check\n\nThis check attempts to import the package. As well as all\ndefined files in the \\`files\\` array of \\`package.json\\`.\n\n> [!NOTE]\n> This check fails anything that resolves to \\`node_modules\\`,\n> this is because \\`axe-core\\` should be linked before\n> this is called. When \\`exports\\` can be added to the\n> package definition, then we can self reference imports and\n> the link will no longer be required.\n\n| File | Status | Version |\\n|------|--------|--------|\n`;\n\n  const importTargets = [...pkg.files.map(file => `${pkg.name}/${file}`)];\n  let anyCaught = false;\n\n  console.log('Validating package files are importable:');\n\n  try {\n    const axe = await import(pkg.name);\n    console.info(`✓ ${pkg.name}`);\n\n    if (!axe.default?.version) {\n      throw new Error('Missing version property');\n    }\n\n    summary += `| \\`${pkg.name}\\` | ✓ Importable | ${axe.default.version} |\\n`;\n  } catch {\n    console.error(`✗ ${pkg.name}`);\n    summary += `| \\`${pkg.name}\\` | ✗ Not Importable | Not Found |\\n`;\n    anyCaught = true;\n  }\n\n  for (const target of importTargets) {\n    // Skip things that can't be imported directly\n    // One day we can hopefully import anything as bytes to validate.\n    // Ref: https://github.com/tc39/proposal-import-bytes\n    if (\n      target.endsWith('.txt') ||\n      target.endsWith('/') ||\n      target.endsWith('.d.ts')\n    ) {\n      continue;\n    }\n\n    // `import.meta.resolve` is used here to determine\n    // where the import would be resolved from, following\n    // any symlinks. Since this package is linked, it\n    // should never have `node_modules` in the resolved\n    // path. It *could* happen if a dev is writing code\n    // within a parent folder named as such, but that is\n    // unsafe anyways.\n    // -------------------------------------------------\n    // If this is ever setup to run in the post-deploy\n    // test, then this will cause issues as that runs\n    // from this folder specifically.\n    if (import.meta.resolve(target).includes('node_modules')) {\n      exitCode++;\n      summary += `| \\`${target}\\` | ✗ Resolves to node_modules |\\n`;\n      console.error(`✗ ${target} resolves to node_modules`);\n      continue;\n    }\n\n    try {\n      let version = '';\n      if (target.endsWith('.json')) {\n        const data = await import(target, { with: { type: 'json' } });\n        version = Object.keys(data.default).at(-1);\n      } else {\n        const axe = await import(target);\n\n        if (!axe.default?.version) {\n          throw new Error('Missing version property');\n        }\n\n        version = axe.default.version;\n      }\n      console.info(`✓ ${target}`);\n      summary += `| \\`${target}\\` | ✓ Importable | ${version} |\\n`;\n    } catch (error) {\n      console.error(`✗ ${target}`);\n      summary += `| \\`${target}\\` | ✗ Not Importable | Not Found |\\n`;\n      summary += `\\n\\`\\`\\`\\n${error.message}\\n\\`\\`\\`\\n`;\n      anyCaught = true;\n    }\n  }\n\n  if (anyCaught) {\n    exitCode++;\n  }\n\n  await appendToSummaryFile(summary);\n};\n\n/**\n * When a PR targets `master` or a `release-*` branch,\n * or these branches are pushed to, we run SRI validation.\n * Otherwise, it is skipped since the SRI hashes are only\n * updated when releasing.\n *\n * The history file is deprecated. However, until it is removed\n * we should be prudent and continue to validate it.\n */\nconst validateSriHashes = async () => {\n  const currentBranch =\n    process.env.GITHUB_REF_NAME || process.env.GITHUB_HEAD_REF || '';\n\n  if (!/^release-.+/.test(currentBranch) && currentBranch !== 'master') {\n    console.log(`Skipping SRI validation (current branch: ${currentBranch})`);\n    return;\n  }\n\n  summary += `\\n## Subresource Integrity Check\n\nThis check validates the current build against the SRI hash\nfor the version defined in \\`sri-history.json\\`.\n\n| File | Status |\n|------|--------|\n`;\n\n  const sriHistory = await import(`${pkg.name}/sri-history.json`, {\n    with: { type: 'json' }\n  });\n  const expectedSri = sriHistory.default[pkg.version];\n  // calculate the SRI hash for `axe.js` and `axe.min.js`\n  // Using `sri-toolbox` as that is what is used in the build process\n  const { generate } = await import('sri-toolbox');\n\n  const filesToCheck = [\n    {\n      name: 'axe.js',\n      path: fileURLToPath(import.meta.resolve(`${pkg.name}/axe.js`))\n    },\n    {\n      name: 'axe.min.js',\n      path: fileURLToPath(import.meta.resolve(`${pkg.name}/axe.min.js`))\n    }\n  ];\n  const mismatches = [];\n\n  for (const file of filesToCheck) {\n    const calculatedSri = generate(\n      { algorithms: ['sha256'] },\n      await readFile(file.path)\n    );\n\n    console.log(`Expected SRI for ${file.name}:`, expectedSri[file.name]);\n    console.log(`Calculated SRI for ${file.name}:`, calculatedSri);\n    if (calculatedSri !== expectedSri[file.name]) {\n      console.error(`✗ ${file.name}`);\n      summary += `| \\`${file.name}\\` | ✗ Invalid SRI |\\n`;\n      mismatches.push({\n        name: file.name,\n        expected: expectedSri[file.name],\n        calculated: calculatedSri\n      });\n      continue;\n    }\n\n    console.info(`✓ ${file.name}`);\n    summary += `| \\`${file.name}\\` | ✓ Valid SRI |\\n`;\n  }\n\n  if (mismatches.length > 0) {\n    summary += `\\n### SRI Mismatches\\n\\n`;\n\n    for (const mismatch of mismatches) {\n      summary += `**${mismatch.name}:**\\n`;\n      summary += `- Expected: \\`${mismatch.expected}\\`\\n`;\n      summary += `- Calculated: \\`${mismatch.calculated}\\`\\n\\n`;\n    }\n\n    exitCode++;\n  }\n\n  await appendToSummaryFile(summary);\n};\n\n// Start running checks that don't require linking first.\nawait fileExistenceCheck();\n\n/**\n * @type {import('child_process').ExecSyncOptionsWithBufferEncoding}\n */\nconst execOptions = {\n  cwd: repoRoot,\n  stdio: isDebug ? 'inherit' : 'pipe',\n  timeout: 200000\n};\n\nconsole.log('Creating npm link for package validation...');\n\ntry {\n  // Link the package globally, then update the package\n  // internally to use the linked version.\n  // This is needed because we don't have `exports` defined\n  // yet, so self referencing imports won't work.\n  // We also have a circular dependency on the package.\n  // That means if we try to resolve the import without\n  // linking, it will resolve the version in `node_modules`\n  // from npm.\n  execSync('npm link', execOptions);\n  execSync(`npm link ${pkg.name}`, execOptions);\n\n  // Run any checks that require the package to reference itself.\n  await validateCommonJS();\n  await validateImportable();\n  await validateSriHashes();\n} catch (error) {\n  console.error('Failed to create npm link:', error.message);\n  await appendToSummaryFile(`\n    ## Failed to create npm link\n\n    <details><summary>Click to expand error details</summary>\n\n    \\n\\`\\`\\`\\n${error.message}\\n\\`\\`\\`\\n\n\n    </details>\n\n    This failure prevented running critical validation checks.\n    Therefore the entire validation has failed.\n  `);\n  console.error(`Failed to create npm link: ${error.message}`);\n  exitCode++;\n}\n\nconsole.log('Removing npm link...');\ntry {\n  execSync(`npm unlink ${pkg.name}`, execOptions);\n  execSync('npm unlink -g', execOptions);\n} catch (error) {\n  // Not a hard failure if unlinking fails since all these\n  // checks are last. As long as they completed fine,\n  // validation is acceptable.\n  // This is more for when running locally to test if\n  // something goes wrong. As the developer's machine state\n  // is impacted and they need to know about it.\n  console.error('Failed to remove npm link:', error.message);\n}\n\nprocess.exit(exitCode);\n"
  },
  {
    "path": ".github/bin/wait-for-npm-ready.sh",
    "content": "#!/usr/bin/env bash\n\nset -eo pipefail\n\nif [ -z \"$VERSION\" ] || [ -z \"$PACKAGE_NAME\" ]; then\n  echo \"✗ ERROR: VERSION and PACKAGE_NAME environment variables must be set.\"\n  exit 1\nfi\n\nSLEEP_SECONDS=${SLEEP_SECONDS:-10}\nMAX_ATTEMPTS=${MAX_ATTEMPTS:-30}\n\necho \"::group::Waiting for ${PACKAGE_NAME}@${VERSION} to be available on npm registry...\"\n\nfor i in $(seq 1 \"$MAX_ATTEMPTS\"); do\n  echo \"Attempt $i of $MAX_ATTEMPTS...\"\n\n  if npm view \"${PACKAGE_NAME}@${VERSION}\" version > /dev/null 2>&1; then\n    PUBLISHED_VERSION=$(npm view \"${PACKAGE_NAME}@${VERSION}\" version)\n    echo \"✓ Package ${PACKAGE_NAME}@${PUBLISHED_VERSION} is now available on npm!\"\n    echo \"::endgroup::\"\n    exit 0\n  fi\n\n  if [ \"$i\" -lt \"$MAX_ATTEMPTS\" ]; then\n    echo \"Package not yet available, waiting ${SLEEP_SECONDS} seconds...\"\n    sleep \"$SLEEP_SECONDS\"\n  fi\ndone\n\necho \"✗ Timeout: Package ${PACKAGE_NAME}@${VERSION} not available after $((MAX_ATTEMPTS * SLEEP_SECONDS)) seconds\"\necho \"::endgroup::\"\n\nexit 1\n"
  },
  {
    "path": ".github/bin/wait-for-workflow-success.sh",
    "content": "#!/usr/bin/env bash\n\n# This script waits for a specified GitHub Actions workflow to complete successfully.\n# Debug mode can be enabled by setting the DEBUG environment variable to \"true\".\n# Exit codes are as follows:\n# 0 - Workflow completed successfully\n# 1 - Workflow completed with failure\n# 2 - Missing required tools on the host system\n# 3 - Missing required environment variables for configuration\n# 20 - Timeout waiting for workflow to complete\n\nset -eo pipefail\n\nif ! command -v jq &> /dev/null; then\n  echo \"::error::jq is not installed. Please install jq to use this script.\"\n  exit 2\nfi\n\nif ! command -v gh &> /dev/null; then\n  echo \"::error::GitHub CLI (gh) is not installed. Please install gh to use this script.\"\n  exit 2\nfi\n\nif [ -z \"$REPOSITORY\" ]; then\n  echo \"::error::REPOSITORY environment variable must be set.\"\n  exit 3\nfi\n\nif [ -z \"$SHA\" ]; then\n  echo \"::error::SHA environment variable must be set.\"\n  exit 3\nfi\n\nif [ -z \"$WORKFLOW_NAME\" ]; then\n  echo \"::error::WORKFLOW_NAME environment variable must be set.\"\n  exit 3\nfi\n\nif [ -z \"$BRANCH\" ]; then\n  echo \"::error::BRANCH environment variable must be set.\"\n  exit 3\nfi\n\n# When running locally for testing, this might be forgotten to get set.\n# Create a temp file just so there is something to write to that will get thrown away.\nif [ -z \"$GITHUB_STEP_SUMMARY\" ]; then\n  GITHUB_STEP_SUMMARY=$(mktemp)\nfi\n\necho \"Waiting for '$WORKFLOW_NAME' workflow to complete for commit $SHA\"\n\n# If not provided, default to 5 minutes for the job runner to time out.\nTIMEOUT_MINUTES=${TIMEOUT_MINUTES:-5}\n# Round down if given a fractional number by just removing the decimal portion.\nTIMEOUT_MINUTES=${TIMEOUT_MINUTES%.*}\nsleep_seconds=30\nmax_attempts=$(( (TIMEOUT_MINUTES * 60) / sleep_seconds ))\nattempt=0\n\n# We *could* do `status=success` as a query parameter. But then we lose visibility\n# into \"in-progress\" for debugging purposes to at least know if it found a run\n# while waiting.\n# Ref: https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository\napi_url=\"repos/$REPOSITORY/actions/runs?head_sha=$SHA&branch=$BRANCH&exclude_pull_requests=true&event=push\"\n\n# This jq filter can seem complicated. So here is the breakdown:\n# 1. `.workflow_runs` - Get the array of workflow runs from the API response\n# 2. `sort_by(.created_at) | reverse` - Sort the runs by creation date in descending order. Since the API has no guaranteed order.\n# 3. `[.[] | select(.name == \"'\"$WORKFLOW_NAME\"'\")][0]` - Filter the runs to only include those with the specified workflow name. Then take the first one (most recent)\n# 4. `{status: .status, conclusion: .conclusion}` - Extract only the status and conclusion fields. Since we know this is the most recent run, we only care about these fields later.\n# 5. `select(. != null)` - Ensure that we only get a result if there is a matching workflow run\njq_filter='.workflow_runs | sort_by(.created_at) | reverse | [.[] | select(.name == \"'\"$WORKFLOW_NAME\"'\")][0] | {status: .status, conclusion: .conclusion} | select(. != null)'\n\ncat >> \"$GITHUB_STEP_SUMMARY\" <<EOF\n# Wait for Workflow Success\n\n## Config Data\n\n### Environment Inputs\n\n* Repository: \\`$REPOSITORY\\`\n* Commit SHA: \\`$SHA\\`\n* Branch: \\`$BRANCH\\`\n* Workflow Name: \\`$WORKFLOW_NAME\\`\n* Debug Mode: \\`${DEBUG:-false}\\`\n* Timeout Minutes: \\`$TIMEOUT_MINUTES\\`\n\n### Internal Configuration\n\n* Sleep Seconds: \\`$sleep_seconds\\`\n* Max Attempts: \\`$max_attempts\\`\n\n### API Input\n\n* API URL: \\`$api_url\\`\n* JQ Filter: \\`$jq_filter\\`\n\nEOF\n\nif [ \"$DEBUG\" = \"true\" ]; then\n  log_output=$(mktemp)\nelse\n  log_output=\"/dev/null\"\nfi\n\n# Logs API errors to the summary if debug is enabled.\nfunction writeLogToSummary() {\n  if [ \"$DEBUG\" != \"true\" ]; then\n    return\n  fi\n\n  {\n    echo \"\"\n    echo \"## GH API Error Log\"\n    echo \"\"\n    if [ ! -s \"$log_output\" ]; then\n      echo \"No errors captured.\"\n    else\n      echo '```'\n      cat \"$log_output\"\n      echo '```'\n    fi\n    echo \"\"\n  } >> \"$GITHUB_STEP_SUMMARY\"\n}\n\nwhile [ \"$attempt\" -lt \"$max_attempts\" ]; do\n  # Redirect errors to /dev/null to avoid unusable data in the variable in case of failure.\n  # If we seem to be having issues in CI later, it would be valuable to setup debugging to log to $GITHUB_STEP_SUMMARY.\n  workflow_data=$(gh api \"$api_url\" --jq \"$jq_filter\" 2>\"$log_output\" || echo \"\")\n\n  if [ -z \"$workflow_data\" ]; then\n    echo \"Attempt $((attempt + 1))/$max_attempts - Workflow run not found yet\"\n  else\n    status=$(echo \"$workflow_data\" | jq -r '.status')\n    conclusion=$(echo \"$workflow_data\" | jq -r '.conclusion')\n\n    echo \"Attempt $((attempt + 1))/$max_attempts - Status: $status, Conclusion: $conclusion\"\n\n    if [ \"$status\" = \"completed\" ]; then\n      # Write the result to the summary file\n      function writeResultToSummary() {\n        cat >> \"$GITHUB_STEP_SUMMARY\" <<EOF\n## Result of workflow - $WORKFLOW_NAME\n\n* Status: \\`$status\\`\n* Conclusion: \\`$conclusion\\`\n\n### API Details\n\n* Total Attempts: \\`$((attempt + 1))\\`\n* Time Elapsed: \\`$(((attempt + 1) * sleep_seconds)) seconds\\`\n* Remaining allocated time: \\`$(((max_attempts - attempt - 1) * sleep_seconds)) seconds\\`\n\nEOF\n      }\n\n      if [ \"$conclusion\" = \"success\" ]; then\n        echo \"'$WORKFLOW_NAME' workflow completed successfully!\"\n        writeResultToSummary\n        writeLogToSummary\n        exit 0\n      else\n        echo \"::error::'$WORKFLOW_NAME' workflow completed with status: $conclusion\"\n        writeResultToSummary\n        writeLogToSummary\n        exit 1\n      fi\n    fi\n  fi\n\n  attempt=$((attempt + 1))\n  sleep \"$sleep_seconds\"\ndone\n\ncat >> \"$GITHUB_STEP_SUMMARY\" <<EOF\n## Result of workflow - $WORKFLOW_NAME\n\nThe maximum number of attempts was reached without the workflow completing.\nTherefore, the resolution could not be determined to proceed with deployment.\n\n> [!TIP]\n> Re-running this workflow with debug mode enabled will capture API error logs to help diagnose issues.\n\n> [!WARNING]\n> This can typically indicate that GitHub Action runners are experiencing delays.\n> Please check the [GitHub Status Page](https://www.githubstatus.com/) for any ongoing incidents.\n> If the status is normal, or if it already is, wait a little bit before re-running the workflow.\n\n> [!CAUTION]\n> If another commit is already deployed, then do *not* re-run this deployment workflow.\n> Re-running this would cause an older commit to be the next tag.\n> If multiple deployments are failed in a row, then re-run them sequentially as the incident is resolved.\n\nEOF\nwriteLogToSummary\n\necho \"::error::Timeout waiting for '$WORKFLOW_NAME' workflow to complete\"\nexit 20\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\n\nupdates:\n  - package-ecosystem: 'github-actions'\n    directory: '/'\n    schedule:\n      interval: 'monthly'\n    open-pull-requests-limit: 10\n    commit-message:\n      prefix: 'chore'\n    groups:\n      # Any updates not caught by the group config will get individual PRs\n      gha-low-risk:\n        update-types:\n          - 'minor'\n          - 'patch'\n    cooldown:\n      default-days: 7\n\n  - package-ecosystem: 'npm'\n    directory: '/'\n    schedule:\n      interval: 'monthly'\n    open-pull-requests-limit: 10\n    commit-message:\n      prefix: 'chore'\n    ignore:\n      # Prevent updates to ESM-only versions\n      - dependency-name: 'chalk'\n        versions: ['>=5.0.0']\n      - dependency-name: 'execa'\n        versions: ['>=6.0.0']\n      - dependency-name: 'inquirer'\n        versions: ['>=9.0.0']\n      - dependency-name: 'chai'\n        versions: ['>=5.0.0']\n      - dependency-name: 'conventional-commits-parser'\n        versions: ['>=6.0.0']\n      # Prevent Webpack error caused by v0.11+ of esbuild\n      # @see https://github.com/dequelabs/axe-core/issues/3771\n      - dependency-name: 'esbuild'\n        versions: ['>=0.11.0']\n      # Prevent colorjs.io issue caused by >v0.4.3\n      # @see https://github.com/dequelabs/axe-core/issues/4428\n      - dependency-name: 'colorjs.io'\n        versions: ['>0.4.3']\n      # Still need to support node 18 in our tests\n      - dependency-name: 'glob'\n        versions: ['>=11.0.0']\n      # Use node 4 types for backward compatibility\n      - dependency-name: '@types/node'\n        versions: ['>=5.0.0']\n      # Breaking change that we need to handle in its own pr first\n      # @see https://github.com/dequelabs/axe-core/pull/4264\n      - dependency-name: 'css-selector-parser'\n        versions: ['>=2.0.0']\n    groups:\n      # Any updates not caught by the group config will get individual PRs\n      npm-low-risk:\n        update-types:\n          - 'minor'\n          - 'patch'\n    cooldown:\n      default-days: 7\n"
  },
  {
    "path": ".github/review.yml",
    "content": "# Require that merge commits have security review performed on them.\nreview_merges: true\n\napprove_button_tooltip: No security or PR title concerns\ndecline_button_tooltip: Found security or PR title concerns\n\npending_description: Pull request not yet approved for security and a correct PR title\npending_summary: >\n  Awaiting security and PR title review. To approve this pull request, please click the \"Approve\" button above.\n  You may **not** review your own Pull Request.\n\napproved_description: Pull request has been reviewed and approved for security and a correct PR title\napproved_summary: Pull request has been approved for security and having an incorrect PR title by @${approver}.\n\ndeclined_description: Pull request contains security concerns or has an incorrect PR title\ndeclined_summary: Commit was reviewed and declined for security and/or an incorrect PR title by @${approver}.\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "# Do not rename this file. The name \"deploy.yml\" is known to\n# npm for trusted OIDC publishing.\nname: Deploy\n\non:\n  # Run on push and not `workflow_run` after tests finish.\n  # Specifically because `workflow_run` only runs from the context\n  # of the default branch, regardless of which branch triggered the tests.\n  # That means no non-default branches could deploy.\n  push:\n    branches:\n      - master\n      - develop\n\nconcurrency:\n  group: deploy/${{ github.ref_name }}\n  cancel-in-progress: false\n\npermissions:\n  contents: read\n\njobs:\n  # Since we can't run against `workflow_run`, we have to\n  # wait for for the Tests to succeed first before any\n  # processing can happen.\n  wait-for-tests:\n    name: Wait for Tests to Pass\n    if: github.repository_owner == 'dequelabs'\n    runs-on: ubuntu-24.04\n    permissions:\n      contents: read\n      actions: read\n      statuses: read\n    timeout-minutes: 15\n    steps:\n      - &checkout\n        name: Checkout repository\n        timeout-minutes: 2\n        uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0\n        with:\n          persist-credentials: false\n      - name: Wait for Tests workflow to complete\n        timeout-minutes: 13\n        env:\n          SHA: ${{ github.sha }}\n          REPOSITORY: ${{ github.repository }}\n          BRANCH: ${{ github.ref_name }}\n          WORKFLOW_NAME: Tests\n          DEBUG: ${{ runner.debug == '1' }}\n          # One minute less than the job timeout to allow for the script to do cleanup work.\n          TIMEOUT_MINUTES: 12\n          GH_TOKEN: ${{ github.token }}\n        run: ./.github/bin/wait-for-workflow-success.sh\n  deploy-next:\n    name: Deploy \"next\" to npm\n    needs: wait-for-tests\n    if: ${{ github.ref_name == 'develop' }}\n    environment:\n      name: registry.npmjs.org\n    permissions:\n      contents: read\n      id-token: write # Required for OIDC\n    runs-on: ubuntu-24.04\n    outputs:\n      version: ${{ steps.determine-version.outputs.version }}\n      packageName: ${{ steps.determine-version.outputs.name }}\n    steps:\n      - *checkout\n      - &setup-node\n        name: Setup NodeJS\n        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0\n        with:\n          registry-url: 'https://registry.npmjs.org'\n          node-version-file: .nvmrc\n          cache: npm\n      - &install-project-deps\n        name: Install Project Dependencies\n        shell: bash\n        run: npm ci\n      - &build\n        name: Build\n        run: |\n          npm run prepare\n          npm run build\n      - name: Determine prerelease version\n        id: determine-version\n        run: ./.github/bin/determine-version.sh\n      - name: Bump version\n        env:\n          NEW_VERSION: ${{ steps.determine-version.outputs.version }}\n        run: npm version \"$NEW_VERSION\" --no-git-tag-version --ignore-scripts\n      - &validate-package\n        name: Validate package is consumable\n        env:\n          # Ref: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#runner-context\n          # Linting shows this context might be invalid, but it shouldn't be per docs.\n          # Probably something missing in the schema.\n          DEBUG: ${{ runner.debug == '1' }}\n        run: node .github/bin/validate-package.mjs\n      - name: Publish \"next\" version to npm\n        run: npm publish --tag=next\n  validate-next-deploy:\n    name: Validate Next Deployment\n    needs: deploy-next\n    runs-on: ubuntu-24.04\n    steps:\n      - *checkout\n      - *setup-node\n      # In theory since this is a new job now, by the time\n      # this would kick off the package should be available.\n      # But, to be safe in case of delays in propagation,\n      # we'll implement a retry mechanism.\n      - name: Wait for package to be available on npm\n        env:\n          VERSION: ${{ needs.deploy-next.outputs.version }}\n          PACKAGE_NAME: ${{ needs.deploy-next.outputs.packageName }}\n        run: ./.github/bin/wait-for-npm-ready.sh\n      - name: Validate installation of \"next\" version\n        env:\n          PACKAGE_NAME: ${{ needs.deploy-next.outputs.packageName }}\n          VERSION: ${{ needs.deploy-next.outputs.version }}\n        run: ./.github/bin/validate-npm-deploy.sh\n  prod-hold:\n    name: Await approval to deploy to production\n    needs: wait-for-tests\n    if: ${{ github.ref_name == 'master' }}\n    environment:\n      name: production-hold\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Awaiting approval to deploy to production\n        run: echo \"Approval granted to proceed to production deployment.\"\n  prod-deploy:\n    name: Deploy stable to npm\n    needs: prod-hold\n    if: ${{ needs.prod-hold.result == 'success' }}\n    environment:\n      name: registry.npmjs.org\n    permissions:\n      contents: read\n      id-token: write # Required for OIDC\n    outputs:\n      version: ${{ steps.get-data.outputs.version }}\n      packageName: ${{ steps.get-data.outputs.name }}\n    runs-on: ubuntu-24.04\n    steps:\n      - *checkout\n      - *setup-node\n      - *install-project-deps\n      - *build\n      - *validate-package\n      - name: Publish stable version to npm\n        run: npm publish\n      - name: Get published package data\n        id: get-data\n        run: |\n          VERSION=$(npm pkg get version | tr -d '\"')\n          NAME=$(npm pkg get name | tr -d '\"')\n          echo \"version=$VERSION\" >> $GITHUB_OUTPUT\n          echo \"name=$NAME\" >> $GITHUB_OUTPUT\n  create-github-release:\n    name: Create GitHub Release\n    needs: prod-deploy\n    runs-on: ubuntu-24.04\n    permissions:\n      contents: write # Required to create releases\n    steps:\n      - *checkout\n      - name: Install Release Helper\n        run: go install gopkg.in/aktau/github-release.v0@latest\n      - name: Download Release Script\n        run: curl https://raw.githubusercontent.com/dequelabs/attest-release-scripts/develop/src/node-github-release.sh -s -o ./node-github-release.sh\n      - name: Make Release Script Executable\n        run: chmod +x ./node-github-release.sh\n      - name: Create GitHub Release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: ./node-github-release.sh\n  validate-deploy:\n    name: Validate Deployment\n    needs: prod-deploy\n    runs-on: ubuntu-24.04\n    steps:\n      - *checkout\n      - *setup-node\n      # In theory since this is a new job now, by the time\n      # this would kick off the package should be available.\n      # But, to be safe in case of delays in propagation,\n      # we'll implement a retry mechanism.\n      - name: Wait for package to be available on npm\n        env:\n          VERSION: ${{ needs.prod-deploy.outputs.version }}\n          PACKAGE_NAME: ${{ needs.prod-deploy.outputs.packageName }}\n        run: ./.github/bin/wait-for-npm-ready.sh\n      - name: Validate installation of stable version\n        env:\n          PACKAGE_NAME: ${{ needs.prod-deploy.outputs.packageName }}\n          VERSION: ${{ needs.prod-deploy.outputs.version }}\n        run: ./.github/bin/validate-npm-deploy.sh\n"
  },
  {
    "path": ".github/workflows/format.yml",
    "content": "name: Formatter\n\non:\n  pull_request:\n    branches:\n      - develop\n\njobs:\n  prettier:\n    # This conditional prevents running the job on PRs from forks; won't\n    # have permissions to commit changes, so the job would fail if it ran.\n    # PRs from forks will instead rely on failing the fmt_check job in test.yml\n    if: github.event.pull_request.head.repo.full_name == github.repository\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          ref: ${{ github.event.pull_request.head.ref }}\n      - name: Install dependencies\n        run: npm ci\n      - uses: actions/setup-node@v6\n        with:\n          node-version-file: .nvmrc\n          cache: 'npm'\n      # Workflows are not allowed to edit workflows. As result, we need to prevent Prettier from formatting them.\n      - name: Prevent workflows from being formatted\n        run: echo \".github\" >> .prettierignore && cat .prettierignore\n      - run: npm run fmt\n      # Prevent the prettierignore change from being committed.\n      - run: git checkout .prettierignore\n      - uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # tag=v5\n        with:\n          commit_message: ':robot: Automated formatting fixes'\n"
  },
  {
    "path": ".github/workflows/label-extension-linter-issues.yml",
    "content": "name: Add extension and linting labels to associated opened issues\n\non:\n  issues:\n    types: [opened]\n\njobs:\n  label_extension_linting_issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - name: Label Extension Issues\n        uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # tag=1\n        if: contains(toJson(github.event.issue.body), '### Product\\n\\naxe Extension\\n\\n')\n        with:\n          add-labels: 'extension'\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n      - name: Label Linting Issues\n        uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # tag=1\n        if: contains(toJson(github.event.issue.body), '### Product\\n\\naxe Linter\\n\\n')\n        with:\n          add-labels: 'linting'\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/nightly-tests.yml",
    "content": "name: Nightly Tests\n\non:\n  schedule:\n    # Runs every day at 2:17 AM UTC\n    # Schedules should try to be offset from common times\n    # to avoid high contention times on GitHub runners.\n    - cron: '17 2 * * *'\n  workflow_dispatch:\n\nenv:\n  CHROME_DEVEL_SANDBOX: /opt/google/chrome/chrome-sandbox\n\npermissions:\n  contents: read\n\njobs:\n  browsers:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    env:\n      DISPLAY: ':99'\n    steps:\n      - &checkout\n        name: Checkout repository\n        uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0\n        with:\n          persist-credentials: false\n      - name: Install Dependencies\n        id: install-deps\n        uses: ./.github/actions/install-deps\n        with:\n          nightly: 'true'\n          start-xvfb: ${{ env.DISPLAY }}\n      - &build\n        name: Build\n        id: build\n        run: |\n          npm run prepare\n          npm run build\n      - name: Run Firefox Nightly Browser Tests\n        env:\n          FIREFOX_NIGHTLY_BIN: ${{ steps.install-deps.outputs.firefox-path }}\n        run: npm run test -- --browsers FirefoxNightly\n      - name: Run Chrome Beta Browser Tests\n        if: ${{ !cancelled() && steps.build.conclusion == 'success' }}\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test -- --browsers Chrome\n  act:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - &install-deps\n        name: Install Deps\n        id: install-deps\n        uses: ./.github/actions/install-deps\n      - *build\n      - name: Install Latest WCAG ACT Rules\n        run: npm install w3c/wcag-act-rules#main\n      - name: Run ACT Tests\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test:act\n  aria-practices:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 7\n    steps:\n      - *checkout\n      - *install-deps\n      - *build\n      - name: Install Latest W3C Aria Practices\n        run: npm install w3c/aria-practices#main\n      - name: Run ARIA Practices Tests\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test:apg\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Create release candidate\n\non: [workflow_dispatch]\n\njobs:\n  create_release:\n    name: Create release\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-node@v6\n        with:\n          node-version-file: .nvmrc\n          cache: 'npm'\n      - name: Run release script and open PR\n        run: |\n          git config user.name \"API Team CI User\"\n          git config user.email \"aciattestteamci@deque.com\"\n\n          Branch=\"release-$(date +%Y-%m-%d)\"\n          echo \"Branch: $Branch\"\n          git checkout -b \"$Branch\"\n\n          npm ci\n          npm run release\n\n          git push origin \"$Branch\" --force\n\n          Version=$(jq -r .version ./package.json)\n          echo \"Version: $Version\"\n\n          # Get the additions to the changelog as the commit body and generate the PR url\n          ReleaseNotes=$(\n            git show \\\n              --no-color \\\n              --no-prefix \\\n              --output-indicator-new=! CHANGELOG.md | egrep '^!' | awk -F'^[!]' '{print $2}' | sed -e 's/\\n/$0A/g'\n          )\n\n          echo \"$ReleaseNotes\" >> /tmp/pr.txt\n          echo \"\" >> /tmp/pr.txt\n          echo \"This PR was opened by a robot :robot: :tada:\" >> /tmp/pr.txt\n          gh pr create --title \"chore(release): v$Version\" --body-file \"/tmp/pr.txt\" --base master\n        env:\n          GITHUB_TOKEN: ${{ secrets.PAT }}\n"
  },
  {
    "path": ".github/workflows/semantic-pr-title.yml",
    "content": "name: Semantic PR title\n\non:\n  pull_request:\n    types:\n      - opened\n      - reopened\n      - edited\n      - synchronize\n\njobs:\n  semantic-pr-title:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dequelabs/semantic-pr-title@v1\n"
  },
  {
    "path": ".github/workflows/sync-master-develop.yml",
    "content": "name: Sync master/develop branches\non:\n  push:\n    branches:\n      - master\njobs:\n  create_sync_pull_request:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dequelabs/action-sync-branches@v1\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          pr-title: 'chore: merge master into develop'\n          pr-reviewers: straker,WilcoFiers,stephenmathieson\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Tests\n\non:\n  pull_request:\n  push:\n    branches:\n      - master\n      - develop\n      - release-*\n\n# We want to group to the workflow for each branch.\n# Non-push events will be cancelled if a new one is started.\n# Push events will run sequentially. This helps ensure that\n# the `next` tag isn't out of sync.\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref_name }}\n  cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\npermissions: {}\n\nenv:\n  CHROME_DEVEL_SANDBOX: /opt/google/chrome/chrome-sandbox\n\njobs:\n  lint:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - &checkout\n        name: Checkout repository\n        uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0\n        with:\n          persist-credentials: false\n      - &setup-node\n        name: Set up Node.js\n        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0\n        with:\n          node-version-file: .nvmrc\n          cache: 'npm'\n      - &install-deps-directly\n        name: Install Dependencies\n        run: npm ci\n      - name: Run ESLint\n        run: npm run eslint\n\n  fmt_check:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - *setup-node\n      - *install-deps-directly\n      - run: npm run fmt:check\n\n  build:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - *setup-node\n      - *install-deps-directly\n      - &build\n        name: Build\n        run: |\n          npm run prepare\n          npm run build\n      - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0\n        with:\n          name: axe-core\n          path: axe.js\n          retention-days: 1\n\n  test_chrome:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    env:\n      DISPLAY: :99\n    steps:\n      - *checkout\n      - &install-deps-with-xvfb\n        name: Install Deps\n        uses: ./.github/actions/install-deps\n        id: install-deps\n        with:\n          start-xvfb: ${{ env.DISPLAY }}\n      - *build\n      - name: Run Tests Against Chrome\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test -- --browsers Chrome\n      - name: Run Chrome Integration Tests\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test:integration:chrome\n\n  test_firefox:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    env:\n      DISPLAY: :99\n    steps:\n      - *checkout\n      - *install-deps-with-xvfb\n      - *build\n      - name: Run Tests Against Firefox\n        env:\n          FIREFOX_BIN: ${{ steps.install-deps.outputs.firefox-path }}\n        run: npm run test -- --browsers Firefox\n      - name: Run Firefox Integration Tests\n        env:\n          FIREFOX_BIN: ${{ steps.install-deps.outputs.firefox-path }}\n        run: npm run test:integration:firefox\n\n  # Run examples under `doc/examples`\n  test_examples:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - &install-deps\n        name: Install Deps\n        id: install-deps\n        uses: ./.github/actions/install-deps\n      - *build\n      - name: Run Tests Against Examples\n        run: npm run test:examples\n\n  test_act:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    steps:\n      - *checkout\n      - *install-deps\n      - &restore-axe-build\n        name: Restore axe build\n        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0\n        with:\n          name: axe-core\n      - name: Run ACT Tests\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test:act\n\n  test_aria_practices:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    steps:\n      - *checkout\n      - *install-deps\n      - *restore-axe-build\n      - name: Run ARIA Practices Tests\n        env:\n          CHROME_BIN: ${{ steps.install-deps.outputs.chrome-path }}\n          CHROMEDRIVER_BIN: ${{ steps.install-deps.outputs.chromedriver-path }}\n        run: npm run test:apg\n\n  test_locales:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    steps:\n      - *checkout\n      - *install-deps\n      - *restore-axe-build\n      - name: Run Locale Tests\n        run: npm run test:locales\n\n  test_virtual_rules:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    steps:\n      - *checkout\n      - *install-deps\n      - *restore-axe-build\n      - name: Run Virtual Rules Tests\n        run: npm run test:virtual-rules\n\n  test_jsdom:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    steps:\n      - *checkout\n      - *install-deps\n      - *restore-axe-build\n      - name: Run jsdom Tests\n        run: npm run test:jsdom\n\n  build_api_docs:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - *install-deps\n      - name: Run API Docs Build\n        run: npm run api-docs\n\n  test_rule_help_version:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    if: ${{ github.ref_name == 'master' }}\n    steps:\n      - *checkout\n      - *install-deps\n      - name: Run Rule Help Version Tests\n        run: npm run test:rule-help-version\n\n  sri-validate:\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    needs: build\n    # Run on master and RC branches along with PRs targeting those branches.\n    if: ${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') || github.event.pull_request.base.ref == 'master' || startsWith(github.event.pull_request.base.ref, 'release-') }}\n    steps:\n      - *checkout\n      - *install-deps\n      - *restore-axe-build\n      - name: Validate Subresource Integrity\n        run: npm run sri-validate\n\n  test_node:\n    # The package can't be built on Node 6 anymore, but should still run there.\n    # So we need to pull in a previous build artifact.\n    needs: build\n    strategy:\n      matrix:\n        node:\n          - 6\n          - 18\n          - 20\n          - 22\n          - 24\n    runs-on: ubuntu-24.04\n    timeout-minutes: 10\n    steps:\n      - *checkout\n      - name: Set up Node.js\n        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0\n        with:\n          node-version: ${{ matrix.node }}\n      - *restore-axe-build\n      - name: Run Node.js Tests\n        run: npm run test:node\n"
  },
  {
    "path": ".github/workflows/update-generated-files.yaml",
    "content": "name: Update generated files\n\non:\n  push:\n    branches:\n      - develop\n\nenv:\n  BRANCH_NAME: sync-generated-files\n\njobs:\n  update_generated_files:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: actions/setup-node@v6\n        with:\n          node-version-file: .nvmrc\n          cache: 'npm'\n\n      - name: Build\n        run: |\n          npm ci\n          npm run build\n\n      - name: Check for changes\n        id: changes\n        run: |\n          changes=$(git status --porcelain | tr -d '\\n')\n          # see https://unix.stackexchange.com/a/509498\n          echo $changes | grep . && echo \"Changes detected\" || echo \"No changes\"\n          echo \"changes=$changes\" >> \"$GITHUB_OUTPUT\"\n\n      - name: Check branch exists\n        id: branchExists\n        if: steps.changes.outputs.changes\n        run: |\n          exists=$(git ls-remote --heads origin $BRANCH_NAME)\n          echo $exists | grep . && echo \"Branch '$BRANCH_NAME' already exists on remote\" || echo \"Branch does not exists in remote\"\n          echo \"exists=$exists\" >> \"$GITHUB_OUTPUT\"\n\n      - name: Create pull request\n        if: ${{ steps.changes.outputs.changes && !steps.branchExists.outputs.exists }}\n        run: |\n          git status\n          git config user.name github-actions\n          git config user.email github-actions@github.com\n          git add .\n          git checkout -b $BRANCH_NAME\n          git commit -m \"chore: sync generated files\"\n          git push origin $BRANCH_NAME\n          gh pr create --base develop --head $BRANCH_NAME --title \"chore: sync generated files\" --body \"\"\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Update pull request\n        if: ${{ steps.changes.outputs.changes && steps.branchExists.outputs.exists }}\n        run: |\n          git reset HEAD --hard\n          git checkout $BRANCH_NAME\n          npm run build\n          git status\n          git config user.name github-actions\n          git config user.email github-actions@github.com\n          git add .\n          git commit -m \"chore: sync generated files\"\n          git push origin $BRANCH_NAME\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# defaults\nnode_modules\nnpm-shrinkwrap.json\ndoc/examples/*/package-lock.json\n.DS_Store\n.idea\n\n# tmp - `dev` time generated assets\ntmp\n\n# test - runner generated assets\ntest/*/index.html\ntest/integration/*/index.html\n!test/integration/virtual-rules/index.html\n\n# dist\naxe.js\naxe.*.js\n\n# generated src file\nlib/core/base/metadata-function-map.js\n\n# generated jsdoc api docs\ndoc/api/*\n\n# ts generated file\ntypings/axe-core/axe-core-tests.js\n\n# doc\ndoc/rule-descriptions.*.md\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "npx grunt configure\nnpx lint-staged"
  },
  {
    "path": ".jsdoc.json",
    "content": "{\n  \"tags\": {\n    \"allowUnknownTags\": true,\n    \"dictionaries\": [\"jsdoc\"]\n  },\n  \"source\": {\n    \"include\": [\"lib\", \"README.md\"],\n    \"includePattern\": \".js$\",\n    \"excludePattern\": \"(node_modules/|doc/api)\"\n  },\n  \"opts\": {\n    \"destination\": \"./doc/api\",\n    \"encoding\": \"utf8\",\n    \"recurse\": true,\n    \"template\": \"./node_modules/clean-jsdoc-theme\"\n  },\n  \"plugins\": [\"plugins/markdown\"]\n}\n"
  },
  {
    "path": ".npmrc",
    "content": "registry=https://registry.npmjs.org"
  },
  {
    "path": ".nvmrc",
    "content": "24\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules/\ndoc/api\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"printWidth\": 80,\n  \"useTabs\": false,\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"trailingComma\": \"none\",\n  \"bracketSpacing\": true,\n  \"arrowParens\": \"avoid\"\n}\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\"deque-systems.vscode-axe-linter\"]\n}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"chrome\",\n      \"request\": \"attach\",\n      \"name\": \"Attach to Karma test:debug\",\n      \"address\": \"localhost\",\n      \"port\": 9765, // keep in sync with debugPort in karma.conf.js\n      \"webRoot\": \"${workspaceFolder}\",\n      \"sourceMaps\": true,\n      \"sourceMapPathOverrides\": {\n        \"*\": \"${webRoot}/*\",\n        \"base/*\": \"${webRoot}/*\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n\n### [4.11.1](https://github.com/dequelabs/axe-core/compare/v4.11.0...v4.11.1) (2026-01-06)\n\n### Bug Fixes\n\n- allow shadow roots in axe.run contexts ([#4952](https://github.com/dequelabs/axe-core/issues/4952)) ([d4aee16](https://github.com/dequelabs/axe-core/commit/d4aee16494f3225e9f5065f23a9e1deccb46fc9a)), closes [#4941](https://github.com/dequelabs/axe-core/issues/4941)\n- color contrast fails for oklch and oklab with none ([#4959](https://github.com/dequelabs/axe-core/issues/4959)) ([8f249fd](https://github.com/dequelabs/axe-core/commit/8f249fdcffe379466fcff8ec8ac46e37b65fdbce))\n- **color-contrast:** do not incomplete on textarea ([#4968](https://github.com/dequelabs/axe-core/issues/4968)) ([d271788](https://github.com/dequelabs/axe-core/commit/d27178866d4962e1157b1be435143d028873f545)), closes [#4947](https://github.com/dequelabs/axe-core/issues/4947)\n- **commons/color:** Match browser behavior for out-of-gamut oklch colors ([#4908](https://github.com/dequelabs/axe-core/issues/4908)) ([5036be8](https://github.com/dequelabs/axe-core/commit/5036be811e0ede4bf061ab1f970f78b7e9c7ec0c))\n- don't runs rules that select `html` on nested `html` elements ([#4969](https://github.com/dequelabs/axe-core/issues/4969)) ([1e9a5c3](https://github.com/dequelabs/axe-core/commit/1e9a5c36812ff69a75f23fed3d290497f9fba37d))\n- replaced luminance threshold constant 0.03928 with 0.04045 ([#4934](https://github.com/dequelabs/axe-core/issues/4934)) ([316967d](https://github.com/dequelabs/axe-core/commit/316967d50c554e71bcdf59ac945d1d5bb2f0684b)), closes [#4933](https://github.com/dequelabs/axe-core/issues/4933)\n- **rgaa:** adjust mapping of aria-hidden-\\* and valid-lang ([#4935](https://github.com/dequelabs/axe-core/issues/4935)) ([77571f2](https://github.com/dequelabs/axe-core/commit/77571f2103a90a5703233729c78be008395f1572))\n- **valid-lang:** update valid-langs for newer language codes ([#4966](https://github.com/dequelabs/axe-core/issues/4966)) ([c3f5446](https://github.com/dequelabs/axe-core/commit/c3f54464fd0995edc6619203b46b65d2984b218d)), closes [#4963](https://github.com/dequelabs/axe-core/issues/4963)\n\n## [4.11.0](https://github.com/dequelabs/axe-core/compare/v4.10.3...v4.11.0) (2025-10-07)\n\n### Features\n\n- add RGAA tags to rules ([#4862](https://github.com/dequelabs/axe-core/issues/4862)) ([53a925a](https://github.com/dequelabs/axe-core/commit/53a925a31bb2bf4a1584252fa7a58c0662225320))\n- **aria-prohibited-attr:** add support for fallback roles ([#4325](https://github.com/dequelabs/axe-core/issues/4325)) ([62a19a9](https://github.com/dequelabs/axe-core/commit/62a19a9f753f8c49885dafbab7a2a9468eb6571d))\n- **axe.d.ts:** add nodeSerializer typings ([#4551](https://github.com/dequelabs/axe-core/issues/4551)) ([a2f3a48](https://github.com/dequelabs/axe-core/commit/a2f3a485d5e02993c0f35762cd9d80a6ce4ced5f)), closes [#4093](https://github.com/dequelabs/axe-core/issues/4093)\n- **DqElement:** deprecate fromFrame function ([#4881](https://github.com/dequelabs/axe-core/issues/4881)) ([374c376](https://github.com/dequelabs/axe-core/commit/374c376d0b4a043e8beaa7cc9a47521314eee02c)), closes [#4093](https://github.com/dequelabs/axe-core/issues/4093)\n- **DqElement:** Truncate large `html` strings when the element has a large outerHTML string ([#4796](https://github.com/dequelabs/axe-core/issues/4796)) ([404a4fb](https://github.com/dequelabs/axe-core/commit/404a4fb24a156dc433ac9c70dbefe415d41ca980)), closes [#4544](https://github.com/dequelabs/axe-core/issues/4544)\n- **get-xpath:** return proper relative selector for id ([#4846](https://github.com/dequelabs/axe-core/issues/4846)) ([1035f9e](https://github.com/dequelabs/axe-core/commit/1035f9ef134cbfc02c91c37f881eb5759f602bf3)), closes [#4845](https://github.com/dequelabs/axe-core/issues/4845)\n- **i18n:** Add Portugal Portuguese translation ([#4725](https://github.com/dequelabs/axe-core/issues/4725)) ([5b6a65a](https://github.com/dequelabs/axe-core/commit/5b6a65a103188251568862f46020488cf7fd8a94))\n- incomplete with node on which an error occurred ([#4863](https://github.com/dequelabs/axe-core/issues/4863)) ([32ed8da](https://github.com/dequelabs/axe-core/commit/32ed8daad1111772559f6e1cf6c8171e83c0f299))\n- **locale:** Added ru locale ([#4565](https://github.com/dequelabs/axe-core/issues/4565)) ([067b01d](https://github.com/dequelabs/axe-core/commit/067b01d66db1d2c276f26743a8d13d5d60d33446))\n- **tap:** some best practice rules map to RGAA ([#4895](https://github.com/dequelabs/axe-core/issues/4895)) ([bc33f4c](https://github.com/dequelabs/axe-core/commit/bc33f4cf5d4d384118c08d8be1afc0c4fc9272ec))\n- **td-headers-attr:** report headers attribute referencing other <td> elements as unsupported ([#4589](https://github.com/dequelabs/axe-core/issues/4589)) ([ec7c6c8](https://github.com/dequelabs/axe-core/commit/ec7c6c8875970388c4fe2c73147a3dd43497161e)), closes [#3987](https://github.com/dequelabs/axe-core/issues/3987)\n\n### Bug Fixes\n\n- **aria-allowed-role:** add form to allowed roles of form element ([#4588](https://github.com/dequelabs/axe-core/issues/4588)) ([8aa47ac](https://github.com/dequelabs/axe-core/commit/8aa47ac01f9959b9d47ac24dcd2fd8c88c9279f7)), closes [/github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js#L264](https://github.com/dequelabs//github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js/issues/L264)\n- **aria-allowed-role:** Add math to allowed roles for img element ([#4658](https://github.com/dequelabs/axe-core/issues/4658)) ([95b6c18](https://github.com/dequelabs/axe-core/commit/95b6c184872cf70c7f54a96aa813a9e8bc2c066d)), closes [#4657](https://github.com/dequelabs/axe-core/issues/4657)\n- **autocomplete-valid :** Ignore readonly autocomplete field ([#4721](https://github.com/dequelabs/axe-core/issues/4721)) ([491f4ec](https://github.com/dequelabs/axe-core/commit/491f4ecdbbb79d019daa63debc03ac0efb47adf8)), closes [#4708](https://github.com/dequelabs/axe-core/issues/4708)\n- **autocomplete-valid:** treat values \"xon\" and \"xoff\" as non-WCAG-violations ([#4878](https://github.com/dequelabs/axe-core/issues/4878)) ([52bc611](https://github.com/dequelabs/axe-core/commit/52bc61162aa170a30a38246ade099ba3fc10cc2a)), closes [#4877](https://github.com/dequelabs/axe-core/issues/4877)\n- **axe.d.ts:** add typings for preload options object ([#4543](https://github.com/dequelabs/axe-core/issues/4543)) ([cfd2974](https://github.com/dequelabs/axe-core/commit/cfd297498c0e34edd5ff7e62935060bb9dda4db7))\n- **button-name,input-button-name,input-img-alt:** allow label to give accessible name ([#4607](https://github.com/dequelabs/axe-core/issues/4607)) ([a9710d7](https://github.com/dequelabs/axe-core/commit/a9710d757c6ca6ee0ce5d26be3427bab54b87a7a)), closes [#4472](https://github.com/dequelabs/axe-core/issues/4472) [#3696](https://github.com/dequelabs/axe-core/issues/3696) [#3696](https://github.com/dequelabs/axe-core/issues/3696)\n- **captions:** fix grammar in captions check incomplete message ([#4661](https://github.com/dequelabs/axe-core/issues/4661)) ([11de515](https://github.com/dequelabs/axe-core/commit/11de515858a7c10a3d7400163fc2b834715152fc))\n- **color-contrast:** do not run on elements with font-size: 0 ([#4822](https://github.com/dequelabs/axe-core/issues/4822)) ([d77c885](https://github.com/dequelabs/axe-core/commit/d77c8854c847573597eccf54c00091a4a2134cfd)), closes [#4820](https://github.com/dequelabs/axe-core/issues/4820)\n- consistently parse tabindex, following HTML 5 spec ([#4637](https://github.com/dequelabs/axe-core/issues/4637)) ([645a850](https://github.com/dequelabs/axe-core/commit/645a850f601f4f3f18cc4aaca399aad18a9fa5d2)), closes [#4632](https://github.com/dequelabs/axe-core/issues/4632)\n- **core:** measure perf for async checks ([#4609](https://github.com/dequelabs/axe-core/issues/4609)) ([7e9bacf](https://github.com/dequelabs/axe-core/commit/7e9bacf1ecb8c53404fac3eeed087e370e2a9cfa))\n- fix grammar when using \"alternative text\" in a sentence ([#4811](https://github.com/dequelabs/axe-core/issues/4811)) ([237a586](https://github.com/dequelabs/axe-core/commit/237a5861b0fb044c885b154436696279deca7a13)), closes [#4394](https://github.com/dequelabs/axe-core/issues/4394)\n- **get-ancestry:** add nth-child selector for multiple siblings of shadow root ([#4606](https://github.com/dequelabs/axe-core/issues/4606)) ([1cdd6c3](https://github.com/dequelabs/axe-core/commit/1cdd6c3e698a6a4c28604448284993c4c20ca272)), closes [#4563](https://github.com/dequelabs/axe-core/issues/4563)\n- **get-ancestry:** don't error when there is no parent ([#4617](https://github.com/dequelabs/axe-core/issues/4617)) ([a005703](https://github.com/dequelabs/axe-core/commit/a0057039072f68bd605e8bacdca64692d57f612e))\n- **locale:** fix typos in japanese (ja) locale ([#4856](https://github.com/dequelabs/axe-core/issues/4856)) ([3462cc5](https://github.com/dequelabs/axe-core/commit/3462cc57e8480334c125c38b7ecb42344b405dd4))\n- **locale:** fixed typos in german (DE) locale ([#4631](https://github.com/dequelabs/axe-core/issues/4631)) ([b7736de](https://github.com/dequelabs/axe-core/commit/b7736deae9ec14a4e81182adb53be73f3cce9894))\n- **locale:** proofread and updated de.json ([#4643](https://github.com/dequelabs/axe-core/issues/4643)) ([8060ada](https://github.com/dequelabs/axe-core/commit/8060ada737a23cdf68bb5b4c95b8c0e2cca45dad))\n- **meta-viewport:** lower impact to moderate ([#4887](https://github.com/dequelabs/axe-core/issues/4887)) ([2f32aa5](https://github.com/dequelabs/axe-core/commit/2f32aa5bada78ffcfc965ed2b64be533263c6bd5)), closes [#4714](https://github.com/dequelabs/axe-core/issues/4714)\n- **no-autoplay-audio:** don't timeout for preload=none media elements ([#4684](https://github.com/dequelabs/axe-core/issues/4684)) ([cdc871e](https://github.com/dequelabs/axe-core/commit/cdc871e68f3dbc6acbfed12d3ec63ea4da1a4065))\n- **performanceTimer:** throwing in axe catch clause ([#4852](https://github.com/dequelabs/axe-core/issues/4852)) ([a4ade04](https://github.com/dequelabs/axe-core/commit/a4ade04bc2ba93dcad8a24094fb0dc5edb6da8b2)), closes [/github.com/dequelabs/axe-core/blob/e7dae4ec48cbfef74de9f833fdcfb178c1002985/lib/core/base/rule.js#L297-L300](https://github.com/dequelabs//github.com/dequelabs/axe-core/blob/e7dae4ec48cbfef74de9f833fdcfb178c1002985/lib/core/base/rule.js/issues/L297-L300)\n- **performanceTimer:** work in frames ([#4834](https://github.com/dequelabs/axe-core/issues/4834)) ([d7dfebc](https://github.com/dequelabs/axe-core/commit/d7dfebc0271d2970c0937024ce693a771885002c))\n- **rules:** Change \"alternate text\" to \"alternative text\" ([#4582](https://github.com/dequelabs/axe-core/issues/4582)) ([b03ada3](https://github.com/dequelabs/axe-core/commit/b03ada3dd3b6490bb88e366bf807b10e1a4038a4))\n- **target-size:** do not treat focusable tabpanels as targets ([#4702](https://github.com/dequelabs/axe-core/issues/4702)) ([60d11f2](https://github.com/dequelabs/axe-core/commit/60d11f2d01b9e859e54a15ae0232b8b5d1c48d35)), closes [#4421](https://github.com/dequelabs/axe-core/issues/4421) [#4701](https://github.com/dequelabs/axe-core/issues/4701)\n- **type:** correct RuleError type ([#4893](https://github.com/dequelabs/axe-core/issues/4893)) ([d1aa8e2](https://github.com/dequelabs/axe-core/commit/d1aa8e2094031159b041a3e9cf2c796a26b3daba))\n- **types:** correct raw types ([#4903](https://github.com/dequelabs/axe-core/issues/4903)) ([3eade11](https://github.com/dequelabs/axe-core/commit/3eade110a7ac173e3537e8eb18dd0db026f13c75))\n\n### [4.10.3](https://github.com/dequelabs/axe-core/compare/v4.10.2...v4.10.3) (2025-03-04)\n\n### Bug Fixes\n\n- **aria-allowed-role:** Add math to allowed roles for img element ([#4658](https://github.com/dequelabs/axe-core/issues/4658)) ([f6dddd9](https://github.com/dequelabs/axe-core/commit/f6dddd905bb86d2073d760d4c1ff39996e59a4f4)), closes [#4657](https://github.com/dequelabs/axe-core/issues/4657)\n- **captions:** fix grammar in captions check incomplete message ([#4661](https://github.com/dequelabs/axe-core/issues/4661)) ([3ef7058](https://github.com/dequelabs/axe-core/commit/3ef7058d3a16a6898eaed718e39b34b45f9ed74f))\n- consistently parse tabindex, following HTML5 spec ([#4637](https://github.com/dequelabs/axe-core/issues/4637)) ([3b0a361](https://github.com/dequelabs/axe-core/commit/3b0a3619d5e6147d8885725cce196868dac89f7f)), closes [#4632](https://github.com/dequelabs/axe-core/issues/4632)\n- **core:** measure perf for async checks ([#4609](https://github.com/dequelabs/axe-core/issues/4609)) ([e7dc26e](https://github.com/dequelabs/axe-core/commit/e7dc26e6cbaf9089611853805e05216b6529e1f9))\n- **locale:** fixed typos in german (DE) locale ([#4631](https://github.com/dequelabs/axe-core/issues/4631)) ([0740980](https://github.com/dequelabs/axe-core/commit/07409802115c199a68f58dcaf7467e35b4867140))\n- **locale:** proofread and updated de.json ([#4643](https://github.com/dequelabs/axe-core/issues/4643)) ([910cdb2](https://github.com/dequelabs/axe-core/commit/910cdb20702ce116c781d58f021adc05095ffcbb))\n- **no-autoplay-audio:** don't timeout for preload=none media elements ([#4684](https://github.com/dequelabs/axe-core/issues/4684)) ([b7f1ad1](https://github.com/dequelabs/axe-core/commit/b7f1ad1ccf719b7149a5ef1805b405707a474e31))\n- **target-size:** do not treat focusable tabpanels as targets ([#4702](https://github.com/dequelabs/axe-core/issues/4702)) ([67d4e4f](https://github.com/dequelabs/axe-core/commit/67d4e4f7d0d0b803b66f216ff86f401649ed024f)), closes [#4421](https://github.com/dequelabs/axe-core/issues/4421) [#4701](https://github.com/dequelabs/axe-core/issues/4701)\n\n### [4.10.2](https://github.com/dequelabs/axe-core/compare/v4.10.1...v4.10.2) (2024-10-21)\n\n### Bug Fixes\n\n- **get-ancestry:** don't error when there is no parent ([#4617](https://github.com/dequelabs/axe-core/issues/4617)) ([6c07102](https://github.com/dequelabs/axe-core/commit/6c07102b1d29145b8dc5f1d96229f3d0b8b38068))\n\n### [4.10.1](https://github.com/dequelabs/axe-core/compare/v4.10.0...v4.10.1) (2024-10-16)\n\n### Bug Fixes\n\n- **aria-allowed-role:** add form to allowed roles of form element ([#4588](https://github.com/dequelabs/axe-core/issues/4588)) ([d462d67](https://github.com/dequelabs/axe-core/commit/d462d674bb7de0848ce2695f80b95d677c5016e0)), closes [/github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js#L264](https://github.com/dequelabs//github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js/issues/L264)\n- **axe.d.ts:** add typings for preload options object ([#4543](https://github.com/dequelabs/axe-core/issues/4543)) ([72e269f](https://github.com/dequelabs/axe-core/commit/72e269f1e6d6039e70e614005f04ebfd3fe5aca5))\n- **button-name,input-button-name,input-img-alt:** allow label to give accessible name ([#4607](https://github.com/dequelabs/axe-core/issues/4607)) ([364eb72](https://github.com/dequelabs/axe-core/commit/364eb72bb8f20b0ffc31be24cc96cbd349c301cb)), closes [#4472](https://github.com/dequelabs/axe-core/issues/4472) [#3696](https://github.com/dequelabs/axe-core/issues/3696) [#3696](https://github.com/dequelabs/axe-core/issues/3696)\n- **get-ancestry:** add nth-child selector for multiple siblings of shadow root ([#4606](https://github.com/dequelabs/axe-core/issues/4606)) ([bdd94a2](https://github.com/dequelabs/axe-core/commit/bdd94a227a95cd5b9f8e2a1e0fd259ddd702810c)), closes [#4563](https://github.com/dequelabs/axe-core/issues/4563)\n- **rules:** Change \"alternate text\" to \"alternative text\" ([#4582](https://github.com/dequelabs/axe-core/issues/4582)) ([31e0f61](https://github.com/dequelabs/axe-core/commit/31e0f61ca871b3df86468577c449a02c8ece12f0))\n\n## [4.10.0](https://github.com/dequelabs/axe-core/compare/v4.9.1...v4.10.0) (2024-07-29)\n\n### Features\n\n- **new-rule:** summary elements must have an accessible name ([#4511](https://github.com/dequelabs/axe-core/issues/4511)) ([0d8a99e](https://github.com/dequelabs/axe-core/commit/0d8a99eadd8d49e5d3ea0f11ad77be732148431e)), closes [#4510](https://github.com/dequelabs/axe-core/issues/4510)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** allow aria-multiline=false for element with contenteditable ([#4537](https://github.com/dequelabs/axe-core/issues/4537)) ([f019068](https://github.com/dequelabs/axe-core/commit/f0190685722495d00be644cabb1c9741d74acdea))\n- **aria-allowed-attr:** allow aria-required=false when normally not allowed ([#4532](https://github.com/dequelabs/axe-core/issues/4532)) ([2e242e1](https://github.com/dequelabs/axe-core/commit/2e242e146929902c97e181e41fa45e656cf3eb51))\n- **aria-prohibited-attr:** allow aria-label/ledby on descendants of widget ([#4541](https://github.com/dequelabs/axe-core/issues/4541)) ([07c5d91](https://github.com/dequelabs/axe-core/commit/07c5d91c658bda6bcd2743950bf70f25abd1f9ae))\n- **aria-roledescription:** keep disabled with { runOnly: 'wcag2a' } ([#4526](https://github.com/dequelabs/axe-core/issues/4526)) ([5b4cb9d](https://github.com/dequelabs/axe-core/commit/5b4cb9d7992a4c07745e64708040777de64874bd)), closes [#4523](https://github.com/dequelabs/axe-core/issues/4523)\n- **autocomplete-valid:** incomplete for invalid but safe values ([#4500](https://github.com/dequelabs/axe-core/issues/4500)) ([e31a974](https://github.com/dequelabs/axe-core/commit/e31a974de395845c08af345f9458a8091e2b1c4b)), closes [#4492](https://github.com/dequelabs/axe-core/issues/4492)\n- **build:** limit locales to valid files when using the --all-lang option ([#4486](https://github.com/dequelabs/axe-core/issues/4486)) ([d3db593](https://github.com/dequelabs/axe-core/commit/d3db593991261ad44eef1c142d8a4646edde93fa)), closes [#4485](https://github.com/dequelabs/axe-core/issues/4485)\n- Prevent errors when loading axe in Angular + Jest ([#4456](https://github.com/dequelabs/axe-core/issues/4456)) ([3ef9353](https://github.com/dequelabs/axe-core/commit/3ef93531a574c2be76a92d59599d978714cca9d0)), closes [#4400](https://github.com/dequelabs/axe-core/issues/4400)\n- Minor grammatical fixes for some rules and checks ([#4499](https://github.com/dequelabs/axe-core/issues/4499)) ([11fad59](https://github.com/dequelabs/axe-core/commit/11fad598c25eadd29f35ef6be382d907057d4537))\n- **landmark-unique:** follow spec, aside -> landmark ([#4469](https://github.com/dequelabs/axe-core/issues/4469)) ([e32f803](https://github.com/dequelabs/axe-core/commit/e32f8034246a92e4132dc04f6310e2b414d6d43f)), closes [#4460](https://github.com/dequelabs/axe-core/issues/4460)\n- **required-attr:** allow aria-valuetext on slider instead of valuenow ([#4518](https://github.com/dequelabs/axe-core/issues/4518)) ([135898b](https://github.com/dequelabs/axe-core/commit/135898b38d5eb46c42170527a0ac9add425c5c3d)), closes [#4515](https://github.com/dequelabs/axe-core/issues/4515)\n\n### [4.9.1](https://github.com/dequelabs/axe-core/compare/v4.9.0...v4.9.1) (2024-05-06)\n\n### Bug Fixes\n\n- Prevent errors when loading axe in a page with prototype.js\n- **aria-allowed-attr:** allow meter role allowed aria-\\* attributes on meter element ([#4435](https://github.com/dequelabs/axe-core/issues/4435)) ([7ac6392](https://github.com/dequelabs/axe-core/commit/7ac63921e7fab21f3359dcfc8affa7585bc9c25b))\n- **aria-allowed-role:** add gridcell, separator, slider and treeitem to allowed roles of button element ([#4398](https://github.com/dequelabs/axe-core/issues/4398)) ([4788bf8](https://github.com/dequelabs/axe-core/commit/4788bf8d6fd963d7b017dad950b122ffcea8d151))\n- **aria-roles:** correct abstract roles (types) for aria-roles([#4421](https://github.com/dequelabs/axe-core/pull/4421))\n- **aria-valid-attr-value:** aria-controls & aria-haspopup incomplete ([#4418](https://github.com/dequelabs/axe-core/pull/4418))\n- fix building axe-core translation files with region locales ([#4396](https://github.com/dequelabs/axe-core/issues/4396)) ([5c318f3](https://github.com/dequelabs/axe-core/commit/5c318f3537056be5779cb53374bc6f4785947c91)), closes [#4388](https://github.com/dequelabs/axe-core/issues/4388)\n- **invalidrole:** allow upper and mixed case role names ([#4358](https://github.com/dequelabs/axe-core/issues/4358)) ([105016c](https://github.com/dequelabs/axe-core/commit/105016cfe9d82876cfed2ff5c656a7842c5b3761)), closes [#2695](https://github.com/dequelabs/axe-core/issues/2695)\n- **isVisibleOnScreen:** account for position: absolute elements inside overflow container ([#4405](https://github.com/dequelabs/axe-core/issues/4405)) ([2940f6e](https://github.com/dequelabs/axe-core/commit/2940f6ee36ba52d8cf089be2a3c8e7c516c81dd6)), closes [#4016](https://github.com/dequelabs/axe-core/issues/4016)\n- **label-content-name-mismatch:** better dismiss and wysiwyg symbolic text characters ([#4402](https://github.com/dequelabs/axe-core/issues/4402))\n- **region:** Decorative images ignored by region rule ([#4412](https://github.com/dequelabs/axe-core/pull/4412))\n- **target-size:** ignore descendant elements in shadow dom ([#4410](https://github.com/dequelabs/axe-core/issues/4410)) ([6091367](https://github.com/dequelabs/axe-core/commit/6091367a20f70e536fc7e8d77eae4fa7232bc7c0))\n- **target-size:** pass for element that has nearby elements that are obscured ([#4422](https://github.com/dequelabs/axe-core/issues/4422)) ([3a90bb7](https://github.com/dequelabs/axe-core/commit/3a90bb70c8db087b2f03cc30a4aee756995c311c)), closes [#4387](https://github.com/dequelabs/axe-core/issues/4387)\n\n## [4.9.0](https://github.com/dequelabs/axe-core/compare/v4.8.4...v4.9.0) (2024-03-25)\n\n### Features\n\n- adding the wcag131 tag to the aria-hidden-body rule ([#4349](https://github.com/dequelabs/axe-core/issues/4349)) ([dd4c3c3](https://github.com/dequelabs/axe-core/commit/dd4c3c34a42d2b96f5495890f5c5d5e8f6ca8d32)), closes [#4315](https://github.com/dequelabs/axe-core/issues/4315)\n- **checks:** deprecate aria-busy check ([#4356](https://github.com/dequelabs/axe-core/issues/4356)) ([be0b555](https://github.com/dequelabs/axe-core/commit/be0b5558acfbeb6bbb176ac7fd7d8fdfb973b30b)), closes [#4347](https://github.com/dequelabs/axe-core/issues/4347) [#4340](https://github.com/dequelabs/axe-core/issues/4340)\n- **color:** add color channel values and luminosity, saturation, clip functions ([#4366](https://github.com/dequelabs/axe-core/issues/4366)) ([9e70199](https://github.com/dequelabs/axe-core/commit/9e7019990bbbf5182ab50c5c968143b81d216dcb)), closes [/github.com/dequelabs/axe-core/pull/4365/files#r1517706612](https://github.com/dequelabs//github.com/dequelabs/axe-core/pull/4365/files/issues/r1517706612)\n- **i18n:** add Greek Translations ([#3836](https://github.com/dequelabs/axe-core/issues/3836)) ([3ea9a48](https://github.com/dequelabs/axe-core/commit/3ea9a48cf88d02271db8b19651bff0415237b856))\n- **i18n:** Add Italian translation ([#4344](https://github.com/dequelabs/axe-core/issues/4344)) ([de1baa9](https://github.com/dequelabs/axe-core/commit/de1baa9a9f6495f695d25d61d14ed55983dded76))\n- **i18n:** Add Simplified Chinese translation ([#4379](https://github.com/dequelabs/axe-core/issues/4379)) ([bda7c8d](https://github.com/dequelabs/axe-core/commit/bda7c8d8bf5936a56c66240e1ea0373a3b769809))\n- **i18n:** Add Taiwanese Mandarin translation ([#4299](https://github.com/dequelabs/axe-core/issues/4299)) ([c5e11de](https://github.com/dequelabs/axe-core/commit/c5e11de06973392b113906c05e3a3004af4c38ae))\n\n### Bug Fixes\n\n- Add LICENSE-3RD-PARTY.txt file ([#4304](https://github.com/dequelabs/axe-core/issues/4304)) ([daa0fe6](https://github.com/dequelabs/axe-core/commit/daa0fe677d4837c9c79bad8ee6c77aff11212339))\n- add Object.values polyfill for node <=6 ([#4274](https://github.com/dequelabs/axe-core/issues/4274)) ([5eb867b](https://github.com/dequelabs/axe-core/commit/5eb867b04e174140122c62eb5c705a842a3489e1))\n- **aria-required-children:** avoid confusing aria-busy message in failures ([#4347](https://github.com/dequelabs/axe-core/issues/4347)) ([591607d](https://github.com/dequelabs/axe-core/commit/591607dd829c11e2cca5beee12c75628d1a8235e)), closes [#fail13](https://github.com/dequelabs/axe-core/issues/fail13) [#4340](https://github.com/dequelabs/axe-core/issues/4340)\n- avoid reading element-specific node properties of non-element node types ([#4317](https://github.com/dequelabs/axe-core/issues/4317)) ([b853b18](https://github.com/dequelabs/axe-core/commit/b853b18a24dd2d1c9408705b821cc11146ae1186)), closes [#4316](https://github.com/dequelabs/axe-core/issues/4316) [#4316](https://github.com/dequelabs/axe-core/issues/4316)\n- **color-contrast:** handle text that is outside `overflow: hidden` ancestor ([#4357](https://github.com/dequelabs/axe-core/issues/4357)) ([bdb7300](https://github.com/dequelabs/axe-core/commit/bdb7300c67d451d3b0169707924a0c6bc4defe40)), closes [#4253](https://github.com/dequelabs/axe-core/issues/4253)\n- **color-contrast:** support color blend modes hue, saturation, color, luminosity ([#4365](https://github.com/dequelabs/axe-core/issues/4365)) ([7ae4761](https://github.com/dequelabs/axe-core/commit/7ae476124d60eafd28d85abf48188cd85c99543a))\n- **d.ts:** RawNodesResult issues ([#4229](https://github.com/dequelabs/axe-core/issues/4229)) ([d660518](https://github.com/dequelabs/axe-core/commit/d6605181ec942bcca46e3bfe889064b3781919ca))\n- **d.ts:** RunOptions.reporter can be any string ([#4218](https://github.com/dequelabs/axe-core/issues/4218)) ([e53f5c5](https://github.com/dequelabs/axe-core/commit/e53f5c5184a0e5f75db65e7929a9da84d7ee6df6))\n- **i18n:** update Italian translations ([#4377](https://github.com/dequelabs/axe-core/issues/4377)) ([4d65d4b](https://github.com/dequelabs/axe-core/commit/4d65d4bf40f4ee2697e079451dd84a0155e8fb51))\n- **listitem:** clarify roleNotValid message ([#4374](https://github.com/dequelabs/axe-core/issues/4374)) ([0f8a9af](https://github.com/dequelabs/axe-core/commit/0f8a9af2a82d49e7d8ff3024da0e4c485ca46d38))\n- **scrollable-region-focusable:** missing wcag213 tag ([#4201](https://github.com/dequelabs/axe-core/issues/4201)) ([0080a72](https://github.com/dequelabs/axe-core/commit/0080a7255eb7f246bb7b6f53974a95b65983b83a))\n- **target-size:** always pass 10x targets (avoid perf bottleneck) ([#4376](https://github.com/dequelabs/axe-core/issues/4376)) ([be327c4](https://github.com/dequelabs/axe-core/commit/be327c422f67ac657218f711b3b799567ba3aa37))\n- **target-size:** do not crash for nodes with many overlapping widgets ([#4373](https://github.com/dequelabs/axe-core/issues/4373)) ([1dbea83](https://github.com/dequelabs/axe-core/commit/1dbea83d4749f9f71f263883869b076b0d42021f)), closes [#4359](https://github.com/dequelabs/axe-core/issues/4359) [#4359](https://github.com/dequelabs/axe-core/issues/4359) [#4360](https://github.com/dequelabs/axe-core/issues/4360)\n- **utils/get-selector:** ignore 'xmlns' attribute when generating a selector ([#4303](https://github.com/dequelabs/axe-core/issues/4303)) ([938b411](https://github.com/dequelabs/axe-core/commit/938b411bb0609b54e5c46a8e5b50c9ea4de4bdee))\n\n### [4.8.4](https://github.com/dequelabs/axe-core/compare/v4.8.3...v4.8.4) (2024-02-07)\n\n### Bug Fixes\n\n- Add LICENSE-3RD-PARTY.txt file ([#4304](https://github.com/dequelabs/axe-core/issues/4304)) ([139c553](https://github.com/dequelabs/axe-core/commit/139c5535c72e926f03bb37a9ba0b7fd6b97cba8c))\n- avoid reading element-specific node properties of non-element node types ([#4317](https://github.com/dequelabs/axe-core/issues/4317)) ([a2a6935](https://github.com/dequelabs/axe-core/commit/a2a69355ea5aafce14367cf967153f7958a8878c)), closes [#4316](https://github.com/dequelabs/axe-core/issues/4316) [#4316](https://github.com/dequelabs/axe-core/issues/4316)\n- **d.ts:** RawNodesResult issues ([#4229](https://github.com/dequelabs/axe-core/issues/4229)) ([f105266](https://github.com/dequelabs/axe-core/commit/f1052662b3b8b57d520fcbd23a3e9d4a5660a7e1))\n- **d.ts:** RunOptions.reporter can be any string ([#4218](https://github.com/dequelabs/axe-core/issues/4218)) ([80de793](https://github.com/dequelabs/axe-core/commit/80de793362bbbffde85654e874942a26df0108a8))\n- **utils/get-selector:** ignore 'xmlns' attribute when generating a selector ([#4303](https://github.com/dequelabs/axe-core/issues/4303)) ([8c68546](https://github.com/dequelabs/axe-core/commit/8c6854661f4613d0b7a6ba98bbfdc0c9ca61b4d1))\n\n### [4.8.3](https://github.com/dequelabs/axe-core/compare/v4.8.2...v4.8.3) (2023-12-18)\n\n### Bug Fixes\n\n- add Object.values polyfill for node <=6 ([#4274](https://github.com/dequelabs/axe-core/issues/4274)) ([b39b0e6](https://github.com/dequelabs/axe-core/commit/b39b0e60b68f8c1e34dc056809a04f8ccf8f24c7))\n\n### [4.8.2](https://github.com/dequelabs/axe-core/compare/v4.8.1...v4.8.2) (2023-09-18)\n\n### Bug Fixes\n\n- polyfill Object.hasOwn for node 14 ([#4152](https://github.com/dequelabs/axe-core/issues/4152)) ([c7b597b](https://github.com/dequelabs/axe-core/commit/c7b597b9ec9c74009f4ddac16d5311ee642ab352))\n\n### [4.8.1](https://github.com/dequelabs/axe-core/compare/v4.8.0...v4.8.1) (2023-09-08)\n\n### Bug Fixes\n\n- **target-size:** show closest offset in message ([#4151](https://github.com/dequelabs/axe-core/issues/4151)) ([a0882f6](https://github.com/dequelabs/axe-core/commit/a0882f64610279adce39b015c7e62bf955e04a22))\n\n## [4.8.0](https://github.com/dequelabs/axe-core/compare/v4.7.2...v4.8.0) (2023-09-06)\n\n### Consistent Rule Impact\n\nThis release changes it so that a rule never changes what impact it reports. To facilitate this while without changing the impact on certain issues, some rules have been split. The following changes were involved:\n\n- Deprecate impact on checks; use rules instead ([#4114](https://github.com/dequelabs/axe-core/issues/4114)) ([2cc5547](https://github.com/dequelabs/axe-core/commit/2cc5547634ee783701675631ee3978129707e6f0))\n- add rule aria-deprecated-role ([#4074](https://github.com/dequelabs/axe-core/issues/4074)) ([03f2771](https://github.com/dequelabs/axe-core/commit/03f2771ab43bd877b7919c29b4f5e737b5a69544))\n- add rule aria-conditional-attr ([#4094](https://github.com/dequelabs/axe-core/issues/4094)) ([d417630](https://github.com/dequelabs/axe-core/commit/d417630e89a41603426c2bb545b49057f03ed8e5))\n- **aria-input/toggle-field-name:** set impact always to serious ([#4095](https://github.com/dequelabs/axe-core/issues/4095)) ([e031d68](https://github.com/dequelabs/axe-core/commit/e031d68652229a80ba6ff7d02d29a50a846bfa5b))\n- **aria-prohibited-attr:** add rule aria-prohibited-attr ([#4088](https://github.com/dequelabs/axe-core/issues/4088)) ([7b115d3](https://github.com/dequelabs/axe-core/commit/7b115d3a9e7256ae2c0a1d7d0f9ba791a06c8599))\n- **impact:** aria-roles / aria-valid-attr-value is always \"critical\" ([#4112](https://github.com/dequelabs/axe-core/issues/4112)) ([5cc8041](https://github.com/dequelabs/axe-core/commit/5cc8041f74a6f015dcbca36ee7414767528277c2))\n- **impact:** scope-attr-valid is always \"moderate\" ([#4113](https://github.com/dequelabs/axe-core/issues/4113)) ([131f552](https://github.com/dequelabs/axe-core/commit/131f5524e8c8022ace047ac6d69d779460c85fe6))\n\n### Other Features\n\n- deprecate & disable duplicate-id / duplicate-id-active ([#4071](https://github.com/dequelabs/axe-core/issues/4071)) ([733c45e](https://github.com/dequelabs/axe-core/commit/733c45e6a40a9f8ff6e75f7db864edff0b404ca2))\n- **duplicate-id-aria:** set to review on fail and tag as wcag412 ([#4075](https://github.com/dequelabs/axe-core/issues/4075)) ([9f1a3e3](https://github.com/dequelabs/axe-core/commit/9f1a3e3cbffbe09eaf90fa254c6421fd4264cf4a))\n- add EN.301.549 tags to rules ([#4063](https://github.com/dequelabs/axe-core/issues/4063)) ([de3da89](https://github.com/dequelabs/axe-core/commit/de3da897e56179d94ef8a0dc1a667b5663c489d1))\n- **checks:** enable help-same-as-label, but remove from rules ([#4096](https://github.com/dequelabs/axe-core/issues/4096)) ([034038a](https://github.com/dequelabs/axe-core/commit/034038a625b390ed25b30fccc96e3fc1f384dbc1))\n- **new-rule:** aria-braille-equivalent finds incorrect uses of aria-braille attributes ([#4107](https://github.com/dequelabs/axe-core/issues/4107)) ([6260a2f](https://github.com/dequelabs/axe-core/commit/6260a2f25781b465960aec0b1e7781be5496c9bd))\n- **page-no-duplicate-banner/contentinfo:** deprecate options.nativeScopeFilter, take into ancestors with sectioning roles ([#4105](https://github.com/dequelabs/axe-core/issues/4105)) ([c6e07be](https://github.com/dequelabs/axe-core/commit/c6e07bec43ef1935f2afb9429e9f12a937c38f14))\n\n### Type Fixes & Improvements\n\nVarious improvements were made to the types. Potentially the most impactful of which is that the `target` and `ancestry` property now return as `UnlabelledFrameSelector` instead of as `string[]`, which is incorrect for selectors involving shadow DOM. This may create some issues during migration for any code that has been incorrectly assuming these two properties have the `string[]` type. For more details and other type changes, see the commit itself:\n\n- **d.ts:** improve axe.d.ts types ([#4081](https://github.com/dequelabs/axe-core/issues/4081)) ([7c5f991](https://github.com/dequelabs/axe-core/commit/7c5f99143a1d97e294d21e14917f4963013fc6f8)), closes [#3966](https://github.com/dequelabs/axe-core/issues/3966)\n\n### Bug Fixes\n\n- **access-name:** get name from header elements ([#4097](https://github.com/dequelabs/axe-core/issues/4097)) ([fbe99bf](https://github.com/dequelabs/axe-core/commit/fbe99bf87a3ebd7d6bc4b4eca7a58bbff28a5b23))\n- add <search> element semantics ([#4115](https://github.com/dequelabs/axe-core/issues/4115)) ([637bf6c](https://github.com/dequelabs/axe-core/commit/637bf6c58c3e62877511687d8a6046f8aee63f03))\n- **aria-allowed-attr:** pass aria-expanded on checkbox & switch ([#4110](https://github.com/dequelabs/axe-core/issues/4110)) ([fcf76e0](https://github.com/dequelabs/axe-core/commit/fcf76e04d8534dfed75caf1f2c4a74ef4faa29ae)), closes [#3339](https://github.com/dequelabs/axe-core/issues/3339)\n- **aria-allowed-role:** Add doc-glossary to allowed roles for aside element ([#4083](https://github.com/dequelabs/axe-core/issues/4083)) ([6ca38f6](https://github.com/dequelabs/axe-core/commit/6ca38f65c28e9df0c429df1018b519394e22507e))\n- **aria-allowed-role:** add meter to allowed roles for named img ([#4055](https://github.com/dequelabs/axe-core/issues/4055)) ([173f29d](https://github.com/dequelabs/axe-core/commit/173f29da9558a1fd0510609aacc9e4deebdf74b4)), closes [#4054](https://github.com/dequelabs/axe-core/issues/4054)\n- **aria-required-childen:** test visibility of grandchildren ([#4091](https://github.com/dequelabs/axe-core/issues/4091)) ([a202b69](https://github.com/dequelabs/axe-core/commit/a202b69b955b45fc10abe06059925013bede07eb))\n- **aria-text:** typo in rule description ([#4131](https://github.com/dequelabs/axe-core/issues/4131)) ([85a0e9c](https://github.com/dequelabs/axe-core/commit/85a0e9c358ae78b4ceb2093dc9891d523eaf25b2))\n- **aria-valid-attr-value:** allow empty value on aria-braille\\* & aria-valuetext ([#4109](https://github.com/dequelabs/axe-core/issues/4109)) ([c4c3e65](https://github.com/dequelabs/axe-core/commit/c4c3e658408d89b5ccd747d5fad9031c5d3a0de0))\n- avoid memory issues by doing better cleanup ([#4059](https://github.com/dequelabs/axe-core/issues/4059)) ([16c5cfa](https://github.com/dequelabs/axe-core/commit/16c5cfa66615537b2131a5a381fbed9a5336d853))\n- avoid problems from element IDs that exist on object prototype ([#4060](https://github.com/dequelabs/axe-core/issues/4060)) ([8d135dd](https://github.com/dequelabs/axe-core/commit/8d135dd58ccd72393b981464f66a01e770d9cf95))\n- **color-contrast:** correctly handle flex and position ([#4086](https://github.com/dequelabs/axe-core/issues/4086)) ([9d5f496](https://github.com/dequelabs/axe-core/commit/9d5f496c4ee7e95d113cdceab284fb6ca7be98e3))\n- **color-contrast:** get text stoke from offset shadows ([#4079](https://github.com/dequelabs/axe-core/issues/4079)) ([13acffe](https://github.com/dequelabs/axe-core/commit/13acffe540f834f5321f9c5c124b565cec92ce06))\n- **color-contrast:** ignore format unicode characters ([#4102](https://github.com/dequelabs/axe-core/issues/4102)) ([049522e](https://github.com/dequelabs/axe-core/commit/049522e3ef0676b198763e39e8c8a300c8eeb195))\n- **color-contrast:** ignore zero width characters ([#4103](https://github.com/dequelabs/axe-core/issues/4103)) ([4deb0a0](https://github.com/dequelabs/axe-core/commit/4deb0a0876d574c3d7d586b27ae07d4f5be586db))\n- **color-contrast:** process non-rgb color functions ([#4092](https://github.com/dequelabs/axe-core/issues/4092)) ([9634282](https://github.com/dequelabs/axe-core/commit/963428256d7a119c7b6188868eb9d4a4651a8949))\n- **commons/dom/createGrid:** only add the visible, non-overflow areas of an element to the grid ([#4101](https://github.com/dequelabs/axe-core/issues/4101)) ([d77f47b](https://github.com/dequelabs/axe-core/commit/d77f47b8dd346e205b6cddb4f6ce544ef5f699e4))\n- ensure reporter errors can propagate ([#4111](https://github.com/dequelabs/axe-core/issues/4111)) ([080cc1b](https://github.com/dequelabs/axe-core/commit/080cc1b5f5ed048ab435c312dec291d1b4eb4393))\n- ignore stylesheets fetching style tag in jsdom ([#4138](https://github.com/dequelabs/axe-core/issues/4138)) ([d7c16a4](https://github.com/dequelabs/axe-core/commit/d7c16a481d5a5f68c1e970040e01f125b2025378))\n- **jsdom:** allow axe.setup() without a global window ([#4116](https://github.com/dequelabs/axe-core/issues/4116)) ([33b0314](https://github.com/dequelabs/axe-core/commit/33b0314922762c0e562b613219b5cc96e3ce31f5))\n- **target-size:** correctly calculate bounding box ([#4125](https://github.com/dequelabs/axe-core/issues/4125)) ([1494b4c](https://github.com/dequelabs/axe-core/commit/1494b4c2159fbae2a937cc7c3dc1d269915ef4d4))\n- **target-size:** update to match new spacing requirements ([#4117](https://github.com/dequelabs/axe-core/issues/4117)) ([49eaa0e](https://github.com/dequelabs/axe-core/commit/49eaa0e1663724f70b2571cc7393e306bf0c7321))\n- Use correct copyright year ([#4098](https://github.com/dequelabs/axe-core/issues/4098)) ([cab6a2b](https://github.com/dequelabs/axe-core/commit/cab6a2b2f012f5963d0f4294217578c790508fcc))\n- **utils/clone:** don't try to clone elements from different window context ([#4072](https://github.com/dequelabs/axe-core/issues/4072)) ([55000d0](https://github.com/dequelabs/axe-core/commit/55000d066f018e4c3f2b9ec4eabf23eb1781dfbb))\n\n### [4.7.2](https://github.com/dequelabs/axe-core/compare/v4.7.1...v4.7.2) (2023-05-25)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** Add 'aria-required' to switch role ([#4029](https://github.com/dequelabs/axe-core/issues/4029)) ([cb51be4](https://github.com/dequelabs/axe-core/commit/cb51be4e3ed69e8e8b3725cab5ad1a4671f64c0c)), closes [#4027](https://github.com/dequelabs/axe-core/issues/4027)\n- **aria-allowed-attr:** allow aria-required on role=slider ([#4035](https://github.com/dequelabs/axe-core/issues/4035)) ([bb2bf60](https://github.com/dequelabs/axe-core/commit/bb2bf606d75409722c645a3b2e3240cbce7e97ef))\n- **aria-required-children:** set related nodes for invalid children ([#4033](https://github.com/dequelabs/axe-core/issues/4033)) ([377f72b](https://github.com/dequelabs/axe-core/commit/377f72b16a4db5272b6c056a070e977dc0589cf5))\n- **tags:** Add / correct several TTv5 tags ([#4031](https://github.com/dequelabs/axe-core/issues/4031)) ([25859dd](https://github.com/dequelabs/axe-core/commit/25859dd737e271f69e3912d69ede2a127d78caa4))\n\n### [4.7.1](https://github.com/dequelabs/axe-core/compare/v4.7.0...v4.7.1) (2023-05-15)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** no inconsistent aria-checked on HTML checkboxes ([#3895](https://github.com/dequelabs/axe-core/issues/3895)) ([704043e](https://github.com/dequelabs/axe-core/commit/704043e8a4b9359e871403c3b4fc294b9feee931))\n- **aria-allowed-attrs:** add aria-expanded to allowed attrs for menuitemcheckbox and menuitemradio ([#3994](https://github.com/dequelabs/axe-core/issues/3994)) ([0f405c6](https://github.com/dequelabs/axe-core/commit/0f405c6da55570db2d536e2a4a5464865d73e821))\n- **aria-required-children:** trigger reviewEmpty with hidden children ([#4012](https://github.com/dequelabs/axe-core/issues/4012)) ([a19b6cb](https://github.com/dequelabs/axe-core/commit/a19b6cb5252deb062f6170ab035d804742e7c1df))\n- **color-contrast:** support CSS 4 color spaces ([#4020](https://github.com/dequelabs/axe-core/issues/4020)) ([65621c3](https://github.com/dequelabs/axe-core/commit/65621c339fd42798cb3ce66bac62865e62926e8c))\n- **link-in-text-block:** set links with pseudo-content for review ([#4005](https://github.com/dequelabs/axe-core/issues/4005)) ([949f4f8](https://github.com/dequelabs/axe-core/commit/949f4f8dfccd018b88f929bd650dc8920ce4f6f0))\n\n## [4.7.0](https://github.com/dequelabs/axe-core/compare/v4.6.3...v4.7.0) (2023-04-17)\n\n### Features\n\n- **aria-roledescription:** deprecate rule ([#3948](https://github.com/dequelabs/axe-core/issues/3948)) ([1913a9e](https://github.com/dequelabs/axe-core/commit/1913a9eaf0e669927c57d56710053303cda193f8))\n- **aria-roles:** deprecate the ARIA directory role ([#3952](https://github.com/dequelabs/axe-core/issues/3952)) ([893fdd0](https://github.com/dequelabs/axe-core/commit/893fdd0901f9218d9add39c16b2e6b77227fbdcd))\n- **d.ts:** setup/teardown, reporters & metadata definitions ([#3966](https://github.com/dequelabs/axe-core/issues/3966)) ([78264ee](https://github.com/dequelabs/axe-core/commit/78264ee663d528bc3fbfc9ea7dbba180259f01af))\n- deprecate bower ([#3889](https://github.com/dequelabs/axe-core/issues/3889)) ([651d811](https://github.com/dequelabs/axe-core/commit/651d811f0f1d1dfc5ab899568eaeb83931011f34))\n- deprecate color.filteredRectStack, color.getRectStack, and dom.visuallyContains ([#3859](https://github.com/dequelabs/axe-core/issues/3859)) ([3be2bad](https://github.com/dequelabs/axe-core/commit/3be2bad2a896e72a92fe70810500fc1ef67b7027))\n- **dom.focusDisabled,dom.isVisibleForScreenreader:** support the inert attribute ([#3857](https://github.com/dequelabs/axe-core/issues/3857)) ([273c971](https://github.com/dequelabs/axe-core/commit/273c97199bd596a288378427becba672b4482678))\n- **metadata:** add Trusted Tester tag ([#3986](https://github.com/dequelabs/axe-core/issues/3986)) ([1f6a2a6](https://github.com/dequelabs/axe-core/commit/1f6a2a68ac10c770091741b328de7efb2ccc6687))\n- support the dialog element ([#3902](https://github.com/dequelabs/axe-core/issues/3902)) ([d4522cd](https://github.com/dequelabs/axe-core/commit/d4522cdd7a90018336098f9b2119e353bd5a71ee))\n\n### Bug Fixes\n\n- **aria-allowed-attrs:** allow aria-description and aria-braille\\* attrs ([#3956](https://github.com/dequelabs/axe-core/issues/3956)) ([2842395](https://github.com/dequelabs/axe-core/commit/2842395f9a8990f670e7025749ff8301b68a15ae))\n- **aria-input-field-name:** skip combobox popups ([#3886](https://github.com/dequelabs/axe-core/issues/3886)) ([3dcdd42](https://github.com/dequelabs/axe-core/commit/3dcdd42d9ce52817d0931aa4fea1ec2b1fc9d650))\n- **aria-required-children:** allow separator in menu(bar) ([#3868](https://github.com/dequelabs/axe-core/issues/3868)) ([ec9f507](https://github.com/dequelabs/axe-core/commit/ec9f50708a233acfa4f9b851618077d6637e6582))\n- **aria-required-children:** do not fail for children with aria-hidden ([#3949](https://github.com/dequelabs/axe-core/issues/3949)) ([8714d6b](https://github.com/dequelabs/axe-core/commit/8714d6ba6debec93d095f5f12385d92c55b0d4b3))\n- **aria-required-children:** list elements that are not allowed ([#3951](https://github.com/dequelabs/axe-core/issues/3951)) ([cce7586](https://github.com/dequelabs/axe-core/commit/cce75869be032006dc505a2af853886943973319))\n- **autocomplete-valid:** allow webauthn token ([#3866](https://github.com/dequelabs/axe-core/issues/3866)) ([fd3010f](https://github.com/dequelabs/axe-core/commit/fd3010ff74eb677d4a84198bb1ca314d54393cb7))\n- **color-contrast:** correcly apply opacity to foreground color ([#3973](https://github.com/dequelabs/axe-core/issues/3973)) ([d7db279](https://github.com/dequelabs/axe-core/commit/d7db279549c443c1e2f43a6b33ebee0968c64325))\n- **color-contrast:** correctly calculate contrast of flex/grid items with z-index ([#3884](https://github.com/dequelabs/axe-core/issues/3884)) ([cef96be](https://github.com/dequelabs/axe-core/commit/cef96be6740657047031c2019908822f957e6c63))\n- **color-contrast:** correctly compute background color for elements with opacity ([#3944](https://github.com/dequelabs/axe-core/issues/3944)) ([c051fe8](https://github.com/dequelabs/axe-core/commit/c051fe851fb5eaa75e6dc0205c4db5e75d80f3a4)), closes [#3932](https://github.com/dequelabs/axe-core/issues/3932)\n- **color-contrast:** correctly compute color contrast of <slot> elements ([#3847](https://github.com/dequelabs/axe-core/issues/3847)) ([4c3a00c](https://github.com/dequelabs/axe-core/commit/4c3a00c7bd6de68b2795be37113a59d804d0a313))\n- **color-contrast:** do not check contrast on elemets that are inerted ([#3894](https://github.com/dequelabs/axe-core/issues/3894)) ([da19946](https://github.com/dequelabs/axe-core/commit/da19946db610c3ab8898431645274a8a76d61a33))\n- **color-contrast:** skip ligature icons ([#3867](https://github.com/dequelabs/axe-core/issues/3867)) ([9486288](https://github.com/dequelabs/axe-core/commit/948628894e3119e7f6ad45a230fbee23ffe64ef2))\n- **create-grid:** correctly compute stack order for non-positioned stacking contexts ([#3930](https://github.com/dequelabs/axe-core/issues/3930)) ([8db2c24](https://github.com/dequelabs/axe-core/commit/8db2c2492d55a903b7903ed71f8b792e58dc2e8c)), closes [#3932](https://github.com/dequelabs/axe-core/issues/3932)\n- **css-orientation-lock:** support the css rotate property ([#3958](https://github.com/dequelabs/axe-core/issues/3958)) ([c51f8bf](https://github.com/dequelabs/axe-core/commit/c51f8bfea87b57c269e509f88d64855368a25493))\n- **focus-order-semantics:** Add ARIA role article to list of valid roles for scrollable regions ([#3927](https://github.com/dequelabs/axe-core/issues/3927)) ([f029271](https://github.com/dequelabs/axe-core/commit/f0292714b94a1483f4148f3ca7206897cfb21318))\n- **is-icon-ligature:** prevent canvas2d warning willReadFrequently ([#3931](https://github.com/dequelabs/axe-core/issues/3931)) ([b3c52bb](https://github.com/dequelabs/axe-core/commit/b3c52bbb6eccda67dabcbf4183d7c201a227a0ac))\n- **link-in-text-block:** allow links with identical colors ([#3861](https://github.com/dequelabs/axe-core/issues/3861)) ([5f90040](https://github.com/dequelabs/axe-core/commit/5f900402470f925686a0d8b41ac01731492bbd30))\n- **respondable:** work with CDP `Page.setDocumentContent` ([#3921](https://github.com/dequelabs/axe-core/issues/3921)) ([66f23e5](https://github.com/dequelabs/axe-core/commit/66f23e59b6deddd3b95035545d622d761abe5825))\n- **scrollable-region-focusable:** change impact to serious ([#3959](https://github.com/dequelabs/axe-core/issues/3959)) ([e3a5c21](https://github.com/dequelabs/axe-core/commit/e3a5c211fe007736d98a16d69995318c2c651f2d))\n- **scrollable-region-focusable:** skip native controls ([#3862](https://github.com/dequelabs/axe-core/issues/3862)) ([b0bdefa](https://github.com/dequelabs/axe-core/commit/b0bdefa34b85363e742ff04e319c014fe95f31f7))\n\n### [4.6.3](https://github.com/dequelabs/axe-core/compare/v4.6.2...v4.6.3) (2023-01-23)\n\n### Bug Fixes\n\n- **aria-required-children:** allow separator in menu(bar) ([#3868](https://github.com/dequelabs/axe-core/issues/3868)) ([46c9499](https://github.com/dequelabs/axe-core/commit/46c9499ff46b2062098c33f2037ab31bff4fb656))\n- **autocomplete-valid:** allow webauthn token ([#3866](https://github.com/dequelabs/axe-core/issues/3866)) ([a3d1b9d](https://github.com/dequelabs/axe-core/commit/a3d1b9dae840e1c6ad096895bbd3bbc19f6836f8))\n- **color-contrast:** correctly compute color contrast of <slot> elements ([#3847](https://github.com/dequelabs/axe-core/issues/3847)) ([844cea1](https://github.com/dequelabs/axe-core/commit/844cea1238ccb30cc1c4d1510f2bb3d4cfbe1706))\n- **color-contrast:** skip ligature icons ([#3867](https://github.com/dequelabs/axe-core/issues/3867)) ([7dfbd9a](https://github.com/dequelabs/axe-core/commit/7dfbd9a1b2c92a8aa289f42635ec93de9aa32d25))\n- **link-in-text-block:** allow links with identical colors ([#3861](https://github.com/dequelabs/axe-core/issues/3861)) ([6761f36](https://github.com/dequelabs/axe-core/commit/6761f36bb7c9a2f05cea75ca88c8e0f199c032df))\n- **scrollable-region-focusable:** skip native controls ([#3862](https://github.com/dequelabs/axe-core/issues/3862)) ([aaf44e9](https://github.com/dequelabs/axe-core/commit/aaf44e908337cbf81c97433f58ec05cd3b3eeded))\n\n### [4.6.2](https://github.com/dequelabs/axe-core/compare/v4.6.1...v4.6.2) (2022-12-23)\n\n### Bug Fixes\n\n- **color-contrast:** fix color-contrast check when running in an extension ([#3838](https://github.com/dequelabs/axe-core/issues/3838)) ([31a3e01](https://github.com/dequelabs/axe-core/commit/31a3e01e3df2ff4ab9ae4eebe93c644ce706a200))\n\n### [4.6.1](https://github.com/dequelabs/axe-core/compare/v4.6.0...v4.6.1) (2022-12-14)\n\n### Bug Fixes\n\n- **d.ts:** add optional include to ContextObject ([#3830](https://github.com/dequelabs/axe-core/issues/3830)) ([36ed242](https://github.com/dequelabs/axe-core/commit/36ed242ec152d6d9bd05889229c4d37ae25a80c0))\n\n## [4.6.0](https://github.com/dequelabs/axe-core/compare/v4.5.2...v4.6.0) (2022-12-12)\n\n### Features\n\n- **aria-required-attr:** require aria-controls on combobox and aria-valuenow on focusable separator ([#3786](https://github.com/dequelabs/axe-core/issues/3786)) ([5259e88](https://github.com/dequelabs/axe-core/commit/5259e8842e49d291d35aada0fefecfde3627299f))\n- **checks/label-content-name-mismatch:** deprecate occuranceThreshold option in favor of occurrenceThreshold to fix typo ([#3782](https://github.com/dequelabs/axe-core/issues/3782)) ([5026d65](https://github.com/dequelabs/axe-core/commit/5026d65b5c93ca7ad1e52881fb5379a4a75ed9a1))\n- **commons:** deprecate flattenShadowColors in favor of flattenColors ([#3792](https://github.com/dequelabs/axe-core/issues/3792)) ([af49daf](https://github.com/dequelabs/axe-core/commit/af49dafcde281443823c2d878ce4de23ee573212))\n- **context:** allow selecting shadow DOM nodes ([#3798](https://github.com/dequelabs/axe-core/issues/3798)) ([9e1e31b](https://github.com/dequelabs/axe-core/commit/9e1e31b253398cc2a3e840c7cb9c5527f4e4ba66))\n- **list,listitem:** do not allow group as allowed parent or child ([#3784](https://github.com/dequelabs/axe-core/issues/3784)) ([d1cbf6f](https://github.com/dequelabs/axe-core/commit/d1cbf6fe20a1920649566e521c0c6668efd0d470))\n- **required-attr:** require aria-checked for checkbox-like and radio-like roles ([#3785](https://github.com/dequelabs/axe-core/issues/3785)) ([563e4e9](https://github.com/dequelabs/axe-core/commit/563e4e90facc9b955c1b2395b466cd65d72bf04e))\n- **utils:** new shadowSelectAll utility ([#3796](https://github.com/dequelabs/axe-core/issues/3796)) ([5865462](https://github.com/dequelabs/axe-core/commit/586546261a9523077e1710cecf1751a5e6f172e6))\n\n### Bug Fixes\n\n- **aria-allowed-role:** allow combobox on button, checkbox and combobox on input[type=button] ([#3354](https://github.com/dequelabs/axe-core/issues/3354)) ([ac688c0](https://github.com/dequelabs/axe-core/commit/ac688c04b70d6bcdfa13ac4d7faf824d2bc4af01)), closes [#3353](https://github.com/dequelabs/axe-core/issues/3353)\n- **aria-required-children:** allow menu as child of menu ([#3820](https://github.com/dequelabs/axe-core/issues/3820)) ([a6569e5](https://github.com/dequelabs/axe-core/commit/a6569e5dcad2fd32160b78fba86c988e7cb3d323))\n- **color-contrast:** consider -webkit-text-stroke & -webkit-text-fill-color ([#3791](https://github.com/dequelabs/axe-core/issues/3791)) ([228daf1](https://github.com/dequelabs/axe-core/commit/228daf153b3c6e45933a2ca8feac765f942663f4))\n- **color-contrast:** correctly calculate background color of text nodes with different size than their container ([#3703](https://github.com/dequelabs/axe-core/issues/3703)) ([123b83c](https://github.com/dequelabs/axe-core/commit/123b83c00f0e770dd784ce72ab1fddddf4a4961e))\n- **get-role:** handle presentation role inheritance for vnodes with no parent ([#3801](https://github.com/dequelabs/axe-core/issues/3801)) ([b971caf](https://github.com/dequelabs/axe-core/commit/b971caf3eea03170c0710c5b00272fe13f65e825))\n- **html-lang-valid:** only run rule when attribute has value ([#3663](https://github.com/dequelabs/axe-core/issues/3663)) ([1a7eecb](https://github.com/dequelabs/axe-core/commit/1a7eecb72c3b1c772392b7c18feee75e0f51dbcb)), closes [#3624](https://github.com/dequelabs/axe-core/issues/3624)\n- **metadata:** Map aria-required-children to ACT rule bc4a75 ([#3790](https://github.com/dequelabs/axe-core/issues/3790)) ([a33a523](https://github.com/dequelabs/axe-core/commit/a33a523eb4dfdc62743d78aab124e74afc98a59e))\n\n### [4.5.2](https://github.com/dequelabs/axe-core/compare/v4.5.1...v4.5.2) (2022-11-14)\n\n### Bug Fixes\n\n- **aria-required-children:** allow menu and menubar to be empty ([#3770](https://github.com/dequelabs/axe-core/issues/3770)) ([d11aed8](https://github.com/dequelabs/axe-core/commit/d11aed8a04968674ff872cf832cea9252023490e))\n- **create-grid:** include elements scrolled out of view in the grid ([#3773](https://github.com/dequelabs/axe-core/issues/3773)) ([a563263](https://github.com/dequelabs/axe-core/commit/a5632631c72f52a5cf38a955052f28b1a931f07c))\n- do not warn when using webpack ([#3777](https://github.com/dequelabs/axe-core/issues/3777)) ([d6cef9a](https://github.com/dequelabs/axe-core/commit/d6cef9a83152256966b259881521c159b0cf21a8))\n- **link-in-text-block:** don't match style or script text ([#3775](https://github.com/dequelabs/axe-core/issues/3775)) ([ab877f9](https://github.com/dequelabs/axe-core/commit/ab877f9d709205c2dadffc656f82dc631b66687b))\n- prevent undefined relatedNodes from halting axe ([#3778](https://github.com/dequelabs/axe-core/issues/3778)) ([efefb18](https://github.com/dequelabs/axe-core/commit/efefb18f720590369a97c2937331f4e2e33ef6a5))\n\n### [4.5.1](https://github.com/dequelabs/axe-core/compare/v4.5.0...v4.5.1) (2022-11-01)\n\n### Bug Fixes\n\n- allow axe to run in certain configurations of jsdom ([#3755](https://github.com/dequelabs/axe-core/issues/3755)) ([b74c5d4](https://github.com/dequelabs/axe-core/commit/b74c5d41c4041554c9dd1c00dfd6387cb069d1a5))\n- prevent crash on jsdom when preloading CSSOM ([#3754](https://github.com/dequelabs/axe-core/issues/3754)) ([b1f0c6b](https://github.com/dequelabs/axe-core/commit/b1f0c6bba2debc6c6a106412da530975cd4ade24))\n\n## [4.5.0](https://github.com/dequelabs/axe-core/compare/v4.4.3...v4.5.0) (2022-10-17)\n\n### Features\n\n#### Highlights\n\n- **new rule:** Add WCAG 2.2 target-size rule (off by default) ([#3616](https://github.com/dequelabs/axe-core/issues/3616)) ([691f1b6](https://github.com/dequelabs/axe-core/commit/691f1b69775daa8d0a620c5a1b72f06f6edc72c3))\n- **new rule:** Add meta-refresh-no-exceptions (off by default, wcag2aaa) ([#3530](https://github.com/dequelabs/axe-core/issues/3530)) ([27031a8](https://github.com/dequelabs/axe-core/commit/27031a827cf273c87449971a4c6aec467e56986d))\n- **identical-links-same-purpose:** Disable by default (wcag2aaa rule) ([#3615](https://github.com/dequelabs/axe-core/issues/3615)) ([a2c2d2f](https://github.com/dequelabs/axe-core/commit/a2c2d2fba67270b16f2421984650cadc91c8f928))\n- **link-in-text-block:** Remove experimental and enable by default ([#3706](https://github.com/dequelabs/axe-core/issues/3706)) ([37b6e7a](https://github.com/dequelabs/axe-core/commit/37b6e7a56f7fae15553ce103787546c8927a6492))\n- **i18n:** Add Norwegian Bokmål locale ([#3375](https://github.com/dequelabs/axe-core/issues/3375)) ([2886d33](https://github.com/dequelabs/axe-core/commit/2886d330d9bd7193947b6f8d650492efdd7b04ec))\n- **i18n:** Add Hebrew translation ([#3438](https://github.com/dequelabs/axe-core/issues/3438)) ([b4162ed](https://github.com/dequelabs/axe-core/commit/b4162ed181d47370284e2fbbd56d4ec9b4585d69))\n\n#### Tags and Metadata Updates\n\nVarious changes of tags, for greater consistency with [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/).\n\n- **area-alt:** Remove the wcag111 tag ([#3479](https://github.com/dequelabs/axe-core/issues/3479)) ([1c3cc51](https://github.com/dequelabs/axe-core/commit/1c3cc510057398bdbe2735da87bebbc2781f8e69))\n- **aria-hidden-focus:** Remove the wcag131 tag ([#3477](https://github.com/dequelabs/axe-core/issues/3477)) ([36ace76](https://github.com/dequelabs/axe-core/commit/36ace76cd3b3706b19e59dc5168a77a5462e78b6))\n- **empty-table-headers:** Rule is now best-practice and fails instead of incompletes ([#3427](https://github.com/dequelabs/axe-core/issues/3427)) ([0a4397d](https://github.com/dequelabs/axe-core/commit/0a4397d0998a3e3035d002e5f724cce4faf0b541)), closes [#3404](https://github.com/dequelabs/axe-core/issues/3404)\n- **frame-title:** Remove wcag241 tag ([#3519](https://github.com/dequelabs/axe-core/issues/3519)) ([9c6b828](https://github.com/dequelabs/axe-core/commit/9c6b828658d0a566e7b41801979e49115732bc86))\n- **input-image-alt:** Add the wcag412 tag ([#3478](https://github.com/dequelabs/axe-core/issues/3478)) ([34272c6](https://github.com/dequelabs/axe-core/commit/34272c6469ff84b5aafd6ae575c5064931a62d80))\n- **label,select-name:** Remove the sc131 tag ([#3476](https://github.com/dequelabs/axe-core/issues/3476)) ([7caef89](https://github.com/dequelabs/axe-core/commit/7caef89cdd0eddce7550b590eda3fb0cdc9be3ac))\n- **meta-viewport:** Report as violation of wcag 1.4.4 ([#3704](https://github.com/dequelabs/axe-core/issues/3704)) ([6f7e4a9](https://github.com/dequelabs/axe-core/commit/6f7e4a9c08d0a9c8a9f74832883be9052a63684d))\n- **metadata:** Update ACT tags & actIds ([#3498](https://github.com/dequelabs/axe-core/issues/3498)) ([571722b](https://github.com/dequelabs/axe-core/commit/571722bde4c2e0b84d7abdeece91a03e7b6613c8))\n\n#### Deprecations\n\n- Deprecate Internet Explorer support ([#3644](https://github.com/dequelabs/axe-core/issues/3644)) ([11f5163](https://github.com/dequelabs/axe-core/commit/11f51632491e265136c0284cd219225627a7415f))\n- Deprecate sri-history.json file ([#3646](https://github.com/dequelabs/axe-core/issues/3646)) ([6f6a89e](https://github.com/dequelabs/axe-core/commit/6f6a89e13d56acdda5dbcc74d1f2d7ba2190f0a6))\n- Deprecate and replace dom.isVisible, utils.isHidden, and dom.isHiddenWithCss ([#3351](https://github.com/dequelabs/axe-core/issues/3351)) ([1ae2ac0](https://github.com/dequelabs/axe-core/commit/1ae2ac045d1f709d1695fa76886e13604d415229))\n- Deprecate only-dlitems-evaluate & only-listitems-evaluate methods ([#3724](https://github.com/dequelabs/axe-core/issues/3724)) ([322e9ed](https://github.com/dequelabs/axe-core/commit/322e9edff389d407021d94a7dadf9b80e1c935de))\n- Deprecate presentation-role-conflict-matchess ([#3638](https://github.com/dequelabs/axe-core/issues/3638)) ([0f02a15](https://github.com/dequelabs/axe-core/commit/0f02a1530b91dd38cbc620e358e403f5250cba4c))\n- Deprecate is-visible-matches, use is-visible-on-screen-matches ([#3679](https://github.com/dequelabs/axe-core/issues/3679)) ([738dd8f](https://github.com/dequelabs/axe-core/commit/738dd8f38f241c061773b44dba3194281749eb15))\n- Deprecate not-html-matches, use :not(html) instead ([#3540](https://github.com/dequelabs/axe-core/issues/3540)) ([e0010d9](https://github.com/dequelabs/axe-core/commit/e0010d9f2c4f80b41dcd1e1821de269dd5a63dac))\n\n#### Others\n\n- **aria-roles:** Permit fallback roles ([#3683](https://github.com/dequelabs/axe-core/issues/3683)) ([5665260](https://github.com/dequelabs/axe-core/commit/5665260320543665d9df2b8620940b553bf17a19))\n- **aria-required-children:** Allow aria-busy on children ([#3569](https://github.com/dequelabs/axe-core/issues/3569)) ([3618f50](https://github.com/dequelabs/axe-core/commit/3618f5017b698bfa7ebd5d36e37978ed8d2e77f9))\n- **aria-required-children:** Fail for children which are not listed as required ([#3597](https://github.com/dequelabs/axe-core/issues/3597)) ([b5ceabc](https://github.com/dequelabs/axe-core/commit/b5ceabc25e4a17e8f3c114acdb1c2fe0d2ba96f3))\n- **getRules:** Return actIds when set ([#3470](https://github.com/dequelabs/axe-core/issues/3470)) ([a3d5cef](https://github.com/dequelabs/axe-core/commit/a3d5cef612013ffe0db5383a4047508654444d3c))\n- **is-in-tab-order:** Add isInTabOrder to commons ([#3619](https://github.com/dequelabs/axe-core/issues/3619)) ([77afe90](https://github.com/dequelabs/axe-core/commit/77afe903484343500ebe5fe6126cf6e029607f64)), closes [#3500](https://github.com/dequelabs/axe-core/issues/3500)\n- **list/definition-list:** Only allow required owned roles ([#3707](https://github.com/dequelabs/axe-core/issues/3707)) ([a920d35](https://github.com/dequelabs/axe-core/commit/a920d35f8d8e6f3590a01d47f20a0e3321e45a6c))\n- **presentation-role-conflict:** Test img elements with empty alt ([#3717](https://github.com/dequelabs/axe-core/issues/3717)) ([ea32fa7](https://github.com/dequelabs/axe-core/commit/ea32fa794bfc72e863997dfa85a60e23a53f0cda))\n\n### Bug Fixes\n\n- **utils:** greatly improve the speed of querySelectorAll ([#3423](https://github.com/dequelabs/axe-core/issues/3423)) ([1cae5ea](https://github.com/dequelabs/axe-core/commit/1cae5eae49abe01e6f84c8ee18b5b0c2ff700492))\n- **aria-hidden-focus:** Do not fail for focus trap bumper elements ([#3667](https://github.com/dequelabs/axe-core/issues/3667)) ([46b6658](https://github.com/dequelabs/axe-core/commit/46b6658af6ee21735b59c4c00455c13dddbce761))\n- **aria-required-attr:** aria-valuenow is no longer required for spinbutton ([#3552](https://github.com/dequelabs/axe-core/issues/3552)) ([a22cf56](https://github.com/dequelabs/axe-core/commit/a22cf564be1f3e73fc21bff59ba895d34755f889))\n- **aria-required-parent:** Allow nested group and presentational roles ([#3492](https://github.com/dequelabs/axe-core/issues/3492)) ([4685270](https://github.com/dequelabs/axe-core/commit/4685270af0d1f737d38a0c06b59137c967679227))\n- **aria-valid-attr-value:** Report empty values as incomplete ([#3635](https://github.com/dequelabs/axe-core/issues/3635)) ([fff39db](https://github.com/dequelabs/axe-core/commit/fff39dba1408d2a11b15485191b843b0661e0790))\n- **avoid-inline-spacing:** Add spacing threshold ([#3533](https://github.com/dequelabs/axe-core/issues/3533)) ([92add05](https://github.com/dequelabs/axe-core/commit/92add056355cc4106caaaf523854f82afb06e191))\n- **checks/no-focusable-disable:** Don't count non-disableable elements as disabled ([#3393](https://github.com/dequelabs/axe-core/issues/3393)) ([bb8b5ca](https://github.com/dequelabs/axe-core/commit/bb8b5ca760f45755ba628ac5873350b5dc4c47d1)), closes [#2466](https://github.com/dequelabs/axe-core/issues/2466) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [#3315](https://github.com/dequelabs/axe-core/issues/3315)\n- **checks/unsupportedrole:** Support unsupported dpub and fallback roles, add role to message ([#3395](https://github.com/dequelabs/axe-core/issues/3395)) ([3c0f10f](https://github.com/dequelabs/axe-core/commit/3c0f10f979ae1e3377dd6ef0d2b445ea5ec90eb3)), closes [#2466](https://github.com/dequelabs/axe-core/issues/2466) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [#3282](https://github.com/dequelabs/axe-core/issues/3282)\n- **color-contrast-enhanced:** Avoid duplicates between color-contrast and color-contrast-enhanced ([#3714](https://github.com/dequelabs/axe-core/issues/3714)) ([2811f77](https://github.com/dequelabs/axe-core/commit/2811f778e1d0293148375bb4028d5ba0379f1b21))\n- **color-contrast:** Correctly determine color contrast for element that exactly overlaps midpoint of node ([#3565](https://github.com/dequelabs/axe-core/issues/3565)) ([90de9aa](https://github.com/dequelabs/axe-core/commit/90de9aa241d9dbbd39ccb2714194ae3a7c0cb2ce))\n- **frame-focusable-content:** don't fail for elements with negative tabindex ([#3493](https://github.com/dequelabs/axe-core/issues/3493)) ([94e75ac](https://github.com/dequelabs/axe-core/commit/94e75ac80689145297fbc2dc8298b772412c7525))\n- **frame-title-unique:** Make frame-title-unique reviewOnly and WCAG412 ([#3610](https://github.com/dequelabs/axe-core/issues/3610)) ([8401b8e](https://github.com/dequelabs/axe-core/commit/8401b8e8fbf691d8252e090b13864b826c5e8591)), closes [#3487](https://github.com/dequelabs/axe-core/issues/3487)\n- **frame-title:** ignore frames with negative tabindex ([f61f825](https://github.com/dequelabs/axe-core/commit/f61f825843ec323fbe7ebb7096bf4c7341f10feb))\n- **frame-title:** return incomplete for presentational iframe with empty title ([#3594](https://github.com/dequelabs/axe-core/issues/3594)) ([c2cfd84](https://github.com/dequelabs/axe-core/commit/c2cfd845bd1b0ff684b9cda4f7fb5f6f30fcfda7))\n- **is-hidden-for-everyone:** count elements inside closed details as hidden ([#3726](https://github.com/dequelabs/axe-core/issues/3726)) ([e65e962](https://github.com/dequelabs/axe-core/commit/e65e9627cd709f6202dd2151ba39bac5a50a8fbb))\n- **is-hidden-for-everyone:** support content-visibility: hidden ([#3690](https://github.com/dequelabs/axe-core/issues/3690)) ([95cf6e7](https://github.com/dequelabs/axe-core/commit/95cf6e7c3019358570b7dd7ea1206ce24a980b65))\n- **is-visible-on-screen:** ignore elements hidden by overflow:hidden ([#3676](https://github.com/dequelabs/axe-core/issues/3676)) ([2935950](https://github.com/dequelabs/axe-core/commit/293595066cea9a5c18d2ed7bf4de69cbfffe1ed1))\n- **label:** avoid passing labels because of an input[value] ([#3688](https://github.com/dequelabs/axe-core/issues/3688)) ([54a8116](https://github.com/dequelabs/axe-core/commit/54a8116546b056569d1551b8ea3c8b01fa7d8df5))\n- **link-in-text-block:** Update rule to match current guidance, revise tests ([#3575](https://github.com/dequelabs/axe-core/issues/3575)) ([edb88ed](https://github.com/dequelabs/axe-core/commit/edb88edf48e91df87497b471e1b101842adf9e1f))\n- **meta-refresh:** Add WCAG's 20-hour exception ([#3525](https://github.com/dequelabs/axe-core/issues/3525)) ([5beb6c3](https://github.com/dequelabs/axe-core/commit/5beb6c3c31936a8cb787709ef2bd11730916b5b2))\n- **no-autoplay-audio:** add reviewOnFail and update tests ([#3557](https://github.com/dequelabs/axe-core/issues/3557)) ([60ec997](https://github.com/dequelabs/axe-core/commit/60ec99735b2872d7f992b379900acd25918e3f86))\n- **object-alt:** ignore unloaded objects ([#3680](https://github.com/dequelabs/axe-core/issues/3680)) ([8e03e2c](https://github.com/dequelabs/axe-core/commit/8e03e2cabe628b036a48575ec84569e7e0e333ae))\n- **page-has-heading-one,landmark-one-main:** do not fail if modal is open ([#3501](https://github.com/dequelabs/axe-core/issues/3501)) ([c6af316](https://github.com/dequelabs/axe-core/commit/c6af31600a804f1643d404e439391f3b810e8da5))\n- **td-headers-attr:** ignore hidden cells with headers attr ([#3684](https://github.com/dequelabs/axe-core/issues/3684)) ([e0403f4](https://github.com/dequelabs/axe-core/commit/e0403f48ee86e883548cd1f40927a3b5edaeb974))\n- **td-headers-attr:** ignore table elements with their role changed ([#3687](https://github.com/dequelabs/axe-core/issues/3687)) ([902d07c](https://github.com/dequelabs/axe-core/commit/902d07c8d2268dc4ab0a6573909bd348ede706b1))\n- **utils.matches:** fix attribute exists selector to match empty attributes ([#3669](https://github.com/dequelabs/axe-core/issues/3669)) ([4b768ea](https://github.com/dequelabs/axe-core/commit/4b768ea2170291038be80b29f277206f21719dff))\n- **valid-lang:** ignore lang on elements with no text ([#3523](https://github.com/dequelabs/axe-core/issues/3523)) ([fd85f39](https://github.com/dequelabs/axe-core/commit/fd85f3927ef85543887ff9c0bda67e46f8e9c316))\n- **valid-lang:** run on aria-hidden text ([#3634](https://github.com/dequelabs/axe-core/issues/3634)) ([a0860bd](https://github.com/dequelabs/axe-core/commit/a0860bd7b5afaabf8f2aee2bd516b9015eb7b5cf))\n- **video-caption:** remove excludeHidden: false ([#3554](https://github.com/dequelabs/axe-core/issues/3554)) ([a3e4bbe](https://github.com/dequelabs/axe-core/commit/a3e4bbec30fd7cf0f197fdf69d0886cd475b06c1))\n\n### [4.4.3](https://github.com/dequelabs/axe-core/compare/v4.4.2...v4.4.3) (2022-07-13)\n\n### Bug Fixes\n\n- **axe.d.ts:** updates type definition for Rule to add reviewOnFail ([#3521](https://github.com/dequelabs/axe-core/issues/3521)) ([afb2478](https://github.com/dequelabs/axe-core/commit/afb247844e697475f5bf81fd7b03ed30c2c65830))\n- Continue supporting Node >=4 ([#3538](https://github.com/dequelabs/axe-core/issues/3538)) ([da7923b](https://github.com/dequelabs/axe-core/commit/da7923bd5bcaa71f52f6b4e6b8eece1ea12c1c2a)), closes [#3537](https://github.com/dequelabs/axe-core/issues/3537)\n\n### [4.4.2](https://github.com/dequelabs/axe-core/compare/v4.4.1...v4.4.2) (2022-05-12)\n\n### Bug Fixes\n\n- **aria-hidden-focusable:** report incomplete with onfocus ([#3407](https://github.com/dequelabs/axe-core/issues/3407)) ([6755e89](https://github.com/dequelabs/axe-core/commit/6755e8961389ba2fcf87c7877737482b819a0553))\n- **aria-hidden-focus:** Update rule help & description ([#3422](https://github.com/dequelabs/axe-core/issues/3422)) ([7cfb4b1](https://github.com/dequelabs/axe-core/commit/7cfb4b1c468231022838bbbe9edef69fa50de891))\n- **aria-valid-attr-value:** add note about element id being in a different shadow DOM tree ([#3421](https://github.com/dequelabs/axe-core/issues/3421)) ([24f74df](https://github.com/dequelabs/axe-core/commit/24f74df8474cfd4f602cb368563955ff42cc2870))\n- **color-contrast:** consistently return color contrast information in the data object for pseudo elements ([#3453](https://github.com/dequelabs/axe-core/issues/3453)) ([1a9d95e](https://github.com/dequelabs/axe-core/commit/1a9d95effeaca26e2d7cf7aa867e6a76f8cf50ad))\n- **deprecatedrole,color-contrast:** fix message to properly include deprecated role, improve color-contrast pass messages ([#3387](https://github.com/dequelabs/axe-core/issues/3387)) ([650e503](https://github.com/dequelabs/axe-core/commit/650e5037dc1f8908897e834ee1ab3aa8e87ac1e7))\n- **html-elms:** update role allowances for nav element ([#3402](https://github.com/dequelabs/axe-core/issues/3402)) ([8aa816a](https://github.com/dequelabs/axe-core/commit/8aa816a15fbd667ee2bf8256a984dcc8aa7c0392)), closes [#3401](https://github.com/dequelabs/axe-core/issues/3401)\n- **standards:** fix address typo in html-elms.js ([#3418](https://github.com/dequelabs/axe-core/issues/3418)) ([f235cc7](https://github.com/dequelabs/axe-core/commit/f235cc7069734096df8db434d4c1e68f11fcc88d)), closes [#3417](https://github.com/dequelabs/axe-core/issues/3417)\n\n## [4.4.1](https://github.com/dequelabs/axe-core/compare/v4.4.0...v4.4.1) (2022-02-03)\n\n### Bug Fixes\n\n- **DPUB:** deprecate endnote, rather than endnotes (plural) ([#3373](https://github.com/dequelabs/axe-core/issues/3373)) ([1ba9917](https://github.com/dequelabs/axe-core/pull/3377/commits/1ba9917801df9194092769c7c58f048884bc25fd))\n- **d.ts:** correct getFrameContexts return type ([#3370](https://github.com/dequelabs/axe-core/issues/3370)) ([a1b9611](https://github.com/dequelabs/axe-core/commit/a1b96113add29756a421e3934cb089d3734dec16))\n\n## [4.4.0](https://github.com/dequelabs/axe-core/compare/v4.3.5...v4.4.0) (2022-01-26)\n\n### Features\n\n- add new ARIA roles ([#3138](https://github.com/dequelabs/axe-core/issues/3138)) ([61be7e5](https://github.com/dequelabs/axe-core/commit/61be7e555152d89c6770679fd6fdac10038f7cd3))\n- **aria-allowed-attr:** report violation for non-global ARIA attrs on element without role ([#3342](https://github.com/dequelabs/axe-core/issues/3342)) ([fb5d990](https://github.com/dequelabs/axe-core/commit/fb5d99005cf5e989a51b276b88ad67011cc02d49))\n- **aria-allowed-attr:** report violations for non-global ARIA attributes on elements without a role ([#3102](https://github.com/dequelabs/axe-core/issues/3102)) ([87cfc0b](https://github.com/dequelabs/axe-core/commit/87cfc0b4f4d998a88a2d534438e4f2ccf9427a86))\n- **color-contrast:** add support for CSS mix-blend-mode ([#3226](https://github.com/dequelabs/axe-core/issues/3226)) ([d497f40](https://github.com/dequelabs/axe-core/commit/d497f40026ba2266e31e7e8802511eb242ef0066))\n- **commons:** deprecate shadowElementsFromPoint ([#3311](https://github.com/dequelabs/axe-core/issues/3311)) ([c3a7d16](https://github.com/dequelabs/axe-core/commit/c3a7d1648d0e319003f573f6b4cfe94a1a043808))\n- **configure:** Deprecate branding: Object, use a string instead. ([#3278](https://github.com/dequelabs/axe-core/issues/3278)) ([1f01309](https://github.com/dequelabs/axe-core/commit/1f0130993b64e2016fcc9ea17f63f8da380de513))\n- **dpub:** upgrade to DPUB 1.1 and report deprecated roles ([#3280](https://github.com/dequelabs/axe-core/issues/3280)) ([034a846](https://github.com/dequelabs/axe-core/commit/034a846cf38bff4ff5836b22c78c0f44e2cb3f6d))\n- **options:** make axe.ping configurable with pingWaitTime ([#3273](https://github.com/dequelabs/axe-core/issues/3273)) ([ce4dfaf](https://github.com/dequelabs/axe-core/commit/ce4dfaff7b98c69e15290b71f76e1c523c78ed8d))\n- **rule:** add new color-contrast-enhanced rule (WCAG AAA) ([#3235](https://github.com/dequelabs/axe-core/issues/3235)) ([bec20fc](https://github.com/dequelabs/axe-core/commit/bec20fcbf2f407ecab2fc6d0d829d23525989d48)), closes [#2934](https://github.com/dequelabs/axe-core/issues/2934)\n\n### Bug Fixes\n\n- **allowed-role:** area without href can have a button or link role ([#3275](https://github.com/dequelabs/axe-core/issues/3275)) ([bf7e60a](https://github.com/dequelabs/axe-core/commit/bf7e60aad9b00844c2b18691d463d5478b53aa2a))\n- **aria-allowed-attr:** check for invalid `aria-attributes` for `role=\"row\"` ([#3160](https://github.com/dequelabs/axe-core/issues/3160)) ([cfa900d](https://github.com/dequelabs/axe-core/commit/cfa900d57265907b638dad36ba405a5b40dbde9c))\n- **aria-allowed-attr:** revert violation for non-global ARIA attrs on element without role ([#3243](https://github.com/dequelabs/axe-core/issues/3243)) ([112b960](https://github.com/dequelabs/axe-core/commit/112b960ee95b6a6abfb38a15b7092d9847512f0f))\n- **aria-allowed-children,aria-allowed-parent:** allow group role in listbox ([#3195](https://github.com/dequelabs/axe-core/issues/3195)) ([cb01975](https://github.com/dequelabs/axe-core/commit/cb019755d9cb52b997aae340f406ac26d0cf90e5))\n- **aria-allowed-role:** allow title, aria-label and aria-labelledby on a img element with a supported role ([#3224](https://github.com/dequelabs/axe-core/issues/3224)) ([006a681](https://github.com/dequelabs/axe-core/commit/006a681395422bbd0603bab346dbdc6b38087d83))\n- **aria-allowed-role:** landmark roles banner on header and contentinfo on footer to only report on top-level rule ([#3142](https://github.com/dequelabs/axe-core/issues/3142)) ([1fd4b00](https://github.com/dequelabs/axe-core/commit/1fd4b004b2543727d4a3775f355934327765baa1))\n- **aria-allowed-roles:** allow role=radio on img with non-empty name ([#3320](https://github.com/dequelabs/axe-core/issues/3320)) ([accafdf](https://github.com/dequelabs/axe-core/commit/accafdfe170b8ad4b3706134d60808a614e40b00))\n- **aria-allowed-roles:** update role allowances for section element ([#3238](https://github.com/dequelabs/axe-core/issues/3238)) ([99676ec](https://github.com/dequelabs/axe-core/commit/99676ece547be39d71e776a5b9cae2da41c31572)), closes [#3237](https://github.com/dequelabs/axe-core/issues/3237)\n- **aria-allowed-role:** Update allowed roles based on ARIA spec updates ([#3124](https://github.com/dequelabs/axe-core/issues/3124)) ([00f6efc](https://github.com/dequelabs/axe-core/commit/00f6efcd55eb0a4c56cc3ca1acc7c79e3d22f58d))\n- **aria-allowed-role:** updates the allowed roles for the wbr element to none and presentation ([#3192](https://github.com/dequelabs/axe-core/issues/3192)) ([2f439b3](https://github.com/dequelabs/axe-core/commit/2f439b3fdb7e7fa3228e663c5313af0f08aa4327)), closes [#3177](https://github.com/dequelabs/axe-core/issues/3177)\n- **aria-prohibited-attr:** update metadata message ([#3206](https://github.com/dequelabs/axe-core/issues/3206)) ([d1a768e](https://github.com/dequelabs/axe-core/commit/d1a768eaefe6d1c95e925174bc979bc7a95ee7d9))\n- **autocomplete-valid:** Allow custom autocomplete attribute values ([#3225](https://github.com/dequelabs/axe-core/issues/3225)) ([6076ee8](https://github.com/dequelabs/axe-core/commit/6076ee8a7ba7527c9886916db1eda5d90cd26259))\n- **axe.configure:** do not remove newline characters from locale doT strings ([#3216](https://github.com/dequelabs/axe-core/issues/3216)) ([ea2ce17](https://github.com/dequelabs/axe-core/commit/ea2ce171fd7562e6b85471e72dddc84be23a4297))\n- **axe.d.ts:** allow Node for include/exclude object ([#3338](https://github.com/dequelabs/axe-core/issues/3338)) ([e699939](https://github.com/dequelabs/axe-core/commit/e699939bfd43fcc66b357d0e7329adce6f29cd6b))\n- **axe.run:** add option to increase iframe ping timeout ([#3233](https://github.com/dequelabs/axe-core/issues/3233)) ([ec937e3](https://github.com/dequelabs/axe-core/commit/ec937e3e147274cbdbba2b046a651c90623130e4))\n- check for hidden elements on `aria-errormessage` ([#3156](https://github.com/dequelabs/axe-core/issues/3156)) ([95d37dd](https://github.com/dequelabs/axe-core/commit/95d37dd794dc8552d731fabf45244b260da53d8f))\n- **color-contrast:** account for 0 width scroll regions with children ([#3172](https://github.com/dequelabs/axe-core/issues/3172)) ([5908f0d](https://github.com/dequelabs/axe-core/commit/5908f0d644c20e7091329bd8bbeb191837d27feb))\n- **color-contrast:** account for elements that do not fill entire bounding size ([#3186](https://github.com/dequelabs/axe-core/issues/3186)) ([699697b](https://github.com/dequelabs/axe-core/commit/699697bc237b6c69050e4572ba5cfdc5f338f450))\n- **color-contrast:** check bg on fg contrast for thin text-shadows ([#3350](https://github.com/dequelabs/axe-core/issues/3350)) ([d92a7e5](https://github.com/dequelabs/axe-core/commit/d92a7e527eb61e5c62a59019b024f288ebac3663))\n- **color-contrast:** correctly apply page background color ([#3207](https://github.com/dequelabs/axe-core/issues/3207)) ([fbc581d](https://github.com/dequelabs/axe-core/commit/fbc581d77e457fe092ecc2b95015e667292f1a08))\n- **color-contrast:** correctly compute color-contrast of truncated children ([#3203](https://github.com/dequelabs/axe-core/issues/3203)) ([ac7b2b5](https://github.com/dequelabs/axe-core/commit/ac7b2b5ec402e9de91c50ef39aefd5843f0d62bb))\n- **color-contrast:** correctly handle nested scroll regions ([#3212](https://github.com/dequelabs/axe-core/issues/3212)) ([22db29c](https://github.com/dequelabs/axe-core/commit/22db29ca7e9964a8447392fba45a09057a926ab9))\n- **color-contrast:** correctly work with positioned elements without z-index ([#3209](https://github.com/dequelabs/axe-core/issues/3209)) ([725a20c](https://github.com/dequelabs/axe-core/commit/725a20c91b9006e64009059f0ab9d1a0098d29df))\n- **color-contrast:** inconsistency of bgOverlap message based on scroll ([#3310](https://github.com/dequelabs/axe-core/issues/3310)) ([25eff98](https://github.com/dequelabs/axe-core/commit/25eff98e698f8dd00e5efd05a9b325a5202eae9b))\n- **color-contrast:** properly blend multiple alpha colors ([#3193](https://github.com/dequelabs/axe-core/issues/3193)) ([e930a70](https://github.com/dequelabs/axe-core/commit/e930a7081d4308549370f74e9d341badd9661584))\n- **core:** Incomplete fallback was missing, and could cause infinite loop ([#3302](https://github.com/dequelabs/axe-core/issues/3302)) ([f23d8c8](https://github.com/dequelabs/axe-core/commit/f23d8c8e305d27c8323547731b335d2900e03239))\n- **custom-elms:** Don't error on custom Element.children prop ([#3326](https://github.com/dequelabs/axe-core/issues/3326)) ([2ad92f6](https://github.com/dequelabs/axe-core/commit/2ad92f67205fd370c3ad5ba44274248c2b9fe6e5))\n- **d.ts:** Add PartialResults type ([#3126](https://github.com/dequelabs/axe-core/issues/3126)) ([544b6d5](https://github.com/dequelabs/axe-core/commit/544b6d579f3eecf8e102a53a911bbce0bd53b74f))\n- **get-selector:** do not URL encode or token escape attribute selectors ([#3215](https://github.com/dequelabs/axe-core/issues/3215)) ([6f7e183](https://github.com/dequelabs/axe-core/commit/6f7e183553206afa2ca21914bf388e019b4acfdc))\n- **getFrameContext:** option.iframe=false always returns an empty array ([#3279](https://github.com/dequelabs/axe-core/issues/3279)) ([dfa9725](https://github.com/dequelabs/axe-core/commit/dfa9725e39b8b4bca5a1856d44ff21c9894fc958))\n- greater consistency of help / description text ([#3204](https://github.com/dequelabs/axe-core/issues/3204)) ([0677565](https://github.com/dequelabs/axe-core/commit/0677565941486cf339e7267760d4e533d4a29a05))\n- **is-visible:** do not error if window.Node does not exist ([#3168](https://github.com/dequelabs/axe-core/issues/3168)) ([4046087](https://github.com/dequelabs/axe-core/commit/404608773abf7b4d069a64931adf4ac7e942b663))\n- **jsdoc:** typo Sting -> String ([d1cc205](https://github.com/dequelabs/axe-core/commit/d1cc205629cb159ca760b18ece1f1e9aea22ec3a))\n- **label-content-name-mismatch:** account for formatting elements ([#3349](https://github.com/dequelabs/axe-core/issues/3349)) ([53a6684](https://github.com/dequelabs/axe-core/commit/53a6684a6ebef004d451ff1be63bbfe4503e9447))\n- **label-title-only:** allow hidden labels ([#3183](https://github.com/dequelabs/axe-core/issues/3183)) ([cad3994](https://github.com/dequelabs/axe-core/commit/cad39949c29bc3f83863e3484feef82e89e12118))\n- **listitem:** allow as child of menu ([#3286](https://github.com/dequelabs/axe-core/issues/3286)) ([4bf7d35](https://github.com/dequelabs/axe-core/commit/4bf7d35a1f283a181205bb31f8f4c64c450772ca))\n- **nativeSelectValue:** update selected value on change ([#3154](https://github.com/dequelabs/axe-core/issues/3154)) ([1ee88cb](https://github.com/dequelabs/axe-core/commit/1ee88cb4bb557560f10eab136464c321d4dee81e))\n- **nested-interactive/aria-text:** allow \"tabindex=-1\" on elements with no role ([#3165](https://github.com/dequelabs/axe-core/issues/3165)) ([0ddc00b](https://github.com/dequelabs/axe-core/commit/0ddc00bb2d0eed457d9ce8ba5cd05606ef3bdc9e)), closes [#2466](https://github.com/dequelabs/axe-core/issues/2466) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [#2934](https://github.com/dequelabs/axe-core/issues/2934)\n- **nested-interactive:** add focusable descendants as related nodes ([#3261](https://github.com/dequelabs/axe-core/issues/3261)) ([3b2fdda](https://github.com/dequelabs/axe-core/commit/3b2fdda5ff90703dd20e9b19c4c0331a3d32cd5e))\n- **nested-interactive:** add message for negative tabindex ([#3194](https://github.com/dequelabs/axe-core/issues/3194)) ([b445291](https://github.com/dequelabs/axe-core/commit/b44529130568347816fa810c959b68f980161241)), closes [#2466](https://github.com/dequelabs/axe-core/issues/2466) [#2934](https://github.com/dequelabs/axe-core/issues/2934) [/github.com/dequelabs/axe-core/issues/3163#issuecomment-949804464](https://github.com/dequelabs//github.com/dequelabs/axe-core/issues/3163/issues/issuecomment-949804464)\n- **nested-interactive:** update negative tabindex message to include asssistive technologies ([#3262](https://github.com/dequelabs/axe-core/issues/3262)) ([b985776](https://github.com/dequelabs/axe-core/commit/b985776b6fdb2c96f40df38cf86f7241039d4f5b))\n- **p-as-heading:** `p-as-heading` rule to account for `textContent` length ([#3145](https://github.com/dequelabs/axe-core/issues/3145)) ([400a230](https://github.com/dequelabs/axe-core/commit/400a2308510246d64d37fac3db375201610cd7e7))\n- **prohibited-attr:** always report incomplete if there is text in the subtree ([#3347](https://github.com/dequelabs/axe-core/issues/3347)) ([2e27dca](https://github.com/dequelabs/axe-core/commit/2e27dca551d1aee273ad8ac055f7dfd45578dad0))\n- **region:** Allow skip menu buttons ([#3277](https://github.com/dequelabs/axe-core/issues/3277)) ([6b6f2e3](https://github.com/dequelabs/axe-core/commit/6b6f2e36b09f70633b36da1cbcf2bcab59edebcf))\n- remove optional crypto dependency (webpack compatibility) ([#3358](https://github.com/dequelabs/axe-core/issues/3358)) ([aa9d095](https://github.com/dequelabs/axe-core/commit/aa9d0957cbe1a91d7491a27cdea643f800ec7384))\n- **reporter:** Run inside isolated contexts ([#3129](https://github.com/dequelabs/axe-core/issues/3129)) ([afe6675](https://github.com/dequelabs/axe-core/commit/afe6675d2452089602dcc6c9e931987936e9a55a))\n- **respondable:** avoid crashes if the data in `window.postMessage` is `null` ([#3249](https://github.com/dequelabs/axe-core/issues/3249)) ([b37b2f6](https://github.com/dequelabs/axe-core/commit/b37b2f67ac4f2204cf63be351a70cb8a680812a3))\n- **scrollable-region-focusable:** treat overflow:clip as hidden ([#3304](https://github.com/dequelabs/axe-core/issues/3304)) ([ef45377](https://github.com/dequelabs/axe-core/commit/ef453771b252a04fb5854f7d4d5b10281889f395))\n- Separate Level AAA rules from A and best-practices ([#3191](https://github.com/dequelabs/axe-core/issues/3191)) ([828e526](https://github.com/dequelabs/axe-core/commit/828e526fd06ee596df73f4825e750aad459ca75e))\n- **skip-link:** work with absolute and relative paths ([#2875](https://github.com/dequelabs/axe-core/issues/2875)) ([ee49d3e](https://github.com/dequelabs/axe-core/commit/ee49d3e83e8c77159d22b475c7d6d801d921b114))\n- **typescript:** allow passing a NodeList to ElementContext ([#3161](https://github.com/dequelabs/axe-core/issues/3161)) ([ad4b165](https://github.com/dequelabs/axe-core/commit/ad4b165a0e019cd65f70fa5d085d83cea3e7338c))\n\n### [4.3.5](https://github.com/dequelabs/axe-core/compare/v4.3.4...v4.3.5) (2021-10-29)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** revert violation for non-global ARIA attrs on element without role ([#3243](https://github.com/dequelabs/axe-core/issues/3243)) ([e138fd6](https://github.com/dequelabs/axe-core/commit/e138fd6a00a8da6c48a74a614adc5dae8f2044e1))\n\n### [4.3.4](https://github.com/dequelabs/axe-core/compare/v4.3.3...v4.3.4) (2021-10-22)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** check for invalid `aria-attributes` for `role=\"row\"` ([#3160](https://github.com/dequelabs/axe-core/issues/3160)) ([76aa5ec](https://github.com/dequelabs/axe-core/commit/76aa5ec5ac8a311b90974f0dba9cf92594f92019))\n- **aria-allowed-children,aria-allowed-parent:** allow group role in listbox ([#3195](https://github.com/dequelabs/axe-core/issues/3195)) ([d742b29](https://github.com/dequelabs/axe-core/commit/d742b299370afa23645b1292ffc15f753113e05a))\n- **aria-allowed-role:** updates the allowed roles for the wbr element to none and presentation ([#3192](https://github.com/dequelabs/axe-core/issues/3192)) ([66db765](https://github.com/dequelabs/axe-core/commit/66db765a17b5dc0904fcafe83aeb7c3eb5d60c12)), closes [#3177](https://github.com/dequelabs/axe-core/issues/3177)\n- **aria-prohibited-attr:** update metadata message ([#3206](https://github.com/dequelabs/axe-core/issues/3206)) ([f9cf9fa](https://github.com/dequelabs/axe-core/commit/f9cf9fafe10e944be643d642cffda4fc762d0fc5))\n- **axe.configure:** do not remove newline characters from locale doT strings ([#3216](https://github.com/dequelabs/axe-core/issues/3216)) ([5925898](https://github.com/dequelabs/axe-core/commit/59258984c0e52d91342040291fcc6f05ce2f135f))\n- **axe.run:** add option to increase iframe ping timeout ([#3233](https://github.com/dequelabs/axe-core/issues/3233)) ([023f356](https://github.com/dequelabs/axe-core/commit/023f356ae752c559fb3788c84b1937bd2ba047ee))\n- **color-contrast:** account for 0 width scroll regions with children ([#3172](https://github.com/dequelabs/axe-core/issues/3172)) ([ac913a1](https://github.com/dequelabs/axe-core/commit/ac913a11ddd8fd3b242a6ee500773c17fef77011))\n- **color-contrast:** account for elements that do not fill entire bounding size ([#3186](https://github.com/dequelabs/axe-core/issues/3186)) ([84229d4](https://github.com/dequelabs/axe-core/commit/84229d41969907a62876488dc7a8f070542a9fe6))\n- **color-contrast:** correctly apply page background color ([#3207](https://github.com/dequelabs/axe-core/issues/3207)) ([0ea7e1b](https://github.com/dequelabs/axe-core/commit/0ea7e1b881bb06067e98bae8ffe814605404475f))\n- **color-contrast:** correctly compute color-contrast of truncated children ([#3203](https://github.com/dequelabs/axe-core/issues/3203)) ([4adb911](https://github.com/dequelabs/axe-core/commit/4adb9119a8a1cb457db81a40534db103c14bd2a1))\n- **color-contrast:** correctly handle nested scroll regions ([#3212](https://github.com/dequelabs/axe-core/issues/3212)) ([80d91c0](https://github.com/dequelabs/axe-core/commit/80d91c01e46072ae8d36b952dfc264ef6f13eada))\n- **color-contrast:** correctly work with positioned elements without z-index ([#3209](https://github.com/dequelabs/axe-core/issues/3209)) ([0322070](https://github.com/dequelabs/axe-core/commit/03220704c9dfa6f3af8d13800f9861e16552854b))\n- greater consistency of help / description text ([#3204](https://github.com/dequelabs/axe-core/issues/3204)) ([0be12e6](https://github.com/dequelabs/axe-core/commit/0be12e62e5c0b2f1d280a7b17380281a30ae65f0))\n- Separate Level AAA rules from A and best-practices ([#3191](https://github.com/dequelabs/axe-core/issues/3191)) ([7e6e6da](https://github.com/dequelabs/axe-core/commit/7e6e6da379eb2cb852a84c4ce088df7065740b61))\n- **color-contrast:** properly blend multiple alpha colors ([#3193](https://github.com/dequelabs/axe-core/issues/3193)) ([5aa0441](https://github.com/dequelabs/axe-core/commit/5aa0441f2b33f8e1055ac32d981df4f796f7bb88))\n- **is-visible:** do not error if window.Node does not exist ([#3168](https://github.com/dequelabs/axe-core/issues/3168)) ([cf58aea](https://github.com/dequelabs/axe-core/commit/cf58aea086a7bd590838673068d34325b4e9eef7))\n- **label-title-only:** allow hidden labels ([#3183](https://github.com/dequelabs/axe-core/issues/3183)) ([ab636ef](https://github.com/dequelabs/axe-core/commit/ab636efa743ba2cbf1194b87aa27be5aba70989b))\n- **nativeSelectValue:** update selected value on change ([#3154](https://github.com/dequelabs/axe-core/issues/3154)) ([ad584a1](https://github.com/dequelabs/axe-core/commit/ad584a10fc4b6c601c887835d7c879c77dc143d9))\n- **p-as-heading:** `p-as-heading` rule to account for `textContent` length ([#3145](https://github.com/dequelabs/axe-core/issues/3145)) ([e0d4dc6](https://github.com/dequelabs/axe-core/commit/e0d4dc63bdd391a92f833d83b50644934deb7804))\n- **typescript:** allow passing a NodeList to ElementContext ([#3161](https://github.com/dequelabs/axe-core/issues/3161)) ([5f2e517](https://github.com/dequelabs/axe-core/commit/5f2e517c0038397e12c72ef322313443c25e7907))\n- check for hidden elements on `aria-errormessage` ([#3156](https://github.com/dequelabs/axe-core/issues/3156)) ([69b2e33](https://github.com/dequelabs/axe-core/commit/69b2e33b4094512f2ccfd05393b567763bba2e11))\n\n### [4.3.3](https://github.com/dequelabs/axe-core/compare/v4.3.2...v4.3.3) (2021-08-24)\n\n### Bug Fixes\n\n- **aria-allowed-role:** Update allowed roles based on ARIA spec updates ([#3124](https://github.com/dequelabs/axe-core/issues/3124)) ([a1f637f](https://github.com/dequelabs/axe-core/commit/a1f637f3f5ebf0e483fd21865bd2191c24ccb87a))\n- **d.ts:** Add PartialResults type ([#3126](https://github.com/dequelabs/axe-core/issues/3126)) ([5cdaf01](https://github.com/dequelabs/axe-core/commit/5cdaf012a2f09834d8b7e5f3a645a40e61d47ea9))\n- **reporter:** Run inside isolated contexts ([#3129](https://github.com/dequelabs/axe-core/issues/3129)) ([98066f8](https://github.com/dequelabs/axe-core/commit/98066f8864d4ef09b4b3de12456992d3ca3207b4))\n\n### [4.3.2](https://github.com/dequelabs/axe-core/compare/v4.3.1...v4.3.2) (2021-07-27)\n\n### Bug Fixes\n\n- **aria-hidden-focusable:** disabled aria-hidden fieldset should not have focusable children ([#3056](https://github.com/dequelabs/axe-core/issues/3056)) ([0865bd7](https://github.com/dequelabs/axe-core/commit/0865bd797f60da2befc52113464bc841f2cb2a47))\n- **aria-required-attr:** only require aria-controls if aria-expanded=true ([#3089](https://github.com/dequelabs/axe-core/issues/3089)) ([63b6c7b](https://github.com/dequelabs/axe-core/commit/63b6c7b04c6837a313251f1621be6032933c8289))\n- **aria-required-parent:** Filter out group from required parent roles if group is present ([#3084](https://github.com/dequelabs/axe-core/issues/3084)) ([1cb270c](https://github.com/dequelabs/axe-core/commit/1cb270c355338238acefd21789373f10aa4cb3ec))\n- **axe.d.ts:** fix finishRun types ([#3098](https://github.com/dequelabs/axe-core/issues/3098)) ([e79c65c](https://github.com/dequelabs/axe-core/commit/e79c65cf7def687c54cc3bc249354c4eacf3e152))\n- **color-contrast:** check for size before ignoring pseudo elements ([#3097](https://github.com/dequelabs/axe-core/issues/3097)) ([e0f6c0c](https://github.com/dequelabs/axe-core/commit/e0f6c0cfb8425bc0f7548c79919ac2cbd8393e83))\n- **core:** stop mutating Context's input ([#3076](https://github.com/dequelabs/axe-core/issues/3076)) ([5dc34ee](https://github.com/dequelabs/axe-core/commit/5dc34eed3272409ae6486c76dad1394f1d557b5e))\n- **finishRun:** handle null for failed iframe results ([#3096](https://github.com/dequelabs/axe-core/issues/3096)) ([8947099](https://github.com/dequelabs/axe-core/commit/8947099ea1113dbe1890e53c56f70e974317b144))\n- **run,finishRun:** don't mutate options, set default reporter to v1 ([#3088](https://github.com/dequelabs/axe-core/issues/3088)) ([90f0b27](https://github.com/dequelabs/axe-core/commit/90f0b275a83dec38a9cae555ea1ddf9b4938b14d))\n\n### [4.3.1](https://github.com/dequelabs/axe-core/compare/v4.3.0...v4.3.1) (2021-07-13)\n\n### Bug Fixes\n\n- add sri-history.json to npm bundle ([#3078](https://github.com/dequelabs/axe-core/issues/3078)) ([b525275](https://github.com/dequelabs/axe-core/commit/b52527515302dcafe6d5a8efd48c60d2d1d21a35))\n\n## [4.3.0](https://github.com/dequelabs/axe-core/compare/v4.2.3...v4.3.0) (2021-07-09)\n\n### Features\n\n- **options:** accept a string for options.runOnly ([4392bc0](https://github.com/dequelabs/axe-core/commit/4392bc05fb208606fc589b261d3419b9625c6b6c))\n- **runPartial:** Test without frame communication ([#3006](https://github.com/dequelabs/axe-core/issues/3006)) ([42738b5](https://github.com/dequelabs/axe-core/commit/42738b5258f058a36b00533e8c5cfdc0f1bcdbed))\n- **utils:** add getFrameContexts method ([#2995](https://github.com/dequelabs/axe-core/issues/2995)) ([f478bab](https://github.com/dequelabs/axe-core/commit/f478babae453b02dec5bf4961b7a995a5964976a))\n- deprecate autocomplete appropriate check ([#2917](https://github.com/dequelabs/axe-core/issues/2917)) ([1fe1487](https://github.com/dequelabs/axe-core/commit/1fe1487758fa0f1f4b7bfc1d8c18bbe397be362d))\n- Remove deprecated phantomjs example ([#2913](https://github.com/dequelabs/axe-core/issues/2913)) ([4a01ffe](https://github.com/dequelabs/axe-core/commit/4a01ffe1adf009745ea12a71f4a888843dc779da))\n- Support multiple languages at once in builds ([#2994](https://github.com/dequelabs/axe-core/issues/2994)) ([f18a56b](https://github.com/dequelabs/axe-core/commit/f18a56b3efafdb14989b45bfff0f94e1863b3899))\n- **rule:** add ACT Rule IDs to test rule objects ([#2866](https://github.com/dequelabs/axe-core/issues/2866)) ([cc1ebf5](https://github.com/dequelabs/axe-core/commit/cc1ebf520caaf787fe73498cac0e4917d357edad)), closes [#2820](https://github.com/dequelabs/axe-core/issues/2820)\n\n### Bug Fixes\n\n- **accText:** ignore text in embedded content elements ([#3022](https://github.com/dequelabs/axe-core/issues/3022)) ([fa4f926](https://github.com/dequelabs/axe-core/commit/fa4f926c089bec2cfca882b61b74fecac504c8e0)), closes [#3017](https://github.com/dequelabs/axe-core/issues/3017)\n- **aria-allowed-attr:** allow aria-posinset and aria-setsize on row elements for treegrids ([#2952](https://github.com/dequelabs/axe-core/issues/2952)) ([24e6115](https://github.com/dequelabs/axe-core/commit/24e6115adb8834b02e1e0a535a661c31caefa588))\n- **aria-allowed-attr:** pass aria-label on some HTML elements ([#2935](https://github.com/dequelabs/axe-core/issues/2935)) ([d2584ed](https://github.com/dequelabs/axe-core/commit/d2584edfa7b439a5702f5b8d368253b9abe690fc))\n- **aria-level:** New check for aria-level > 6 as needs review ([#3061](https://github.com/dequelabs/axe-core/issues/3061)) ([73d3ae1](https://github.com/dequelabs/axe-core/commit/73d3ae101f841c086cee89346ad66afed265097f))\n- **aria-roles:** Mark as needs review if both none and presentation are used on element with no implicit role ([#2989](https://github.com/dequelabs/axe-core/issues/2989)) ([70c683c](https://github.com/dequelabs/axe-core/commit/70c683cd9844b23d62641e7063bae757a897ca38))\n- **autocomplete-appropriate:** pass for autocomplete=username and type=email ([#2896](https://github.com/dequelabs/axe-core/issues/2896)) ([43394bc](https://github.com/dequelabs/axe-core/commit/43394bcd01c631f0c129f2f584d88a3a134a92a7))\n- **color-contrast:** add special case for new sr-only technique ([#2985](https://github.com/dequelabs/axe-core/issues/2985)) ([d6a72f9](https://github.com/dequelabs/axe-core/commit/d6a72f9316b00eee683aed4dbf0a0fafe0661a2f))\n- **color-contrast:** check for pseudo elements on element itself, not just parents ([#2980](https://github.com/dequelabs/axe-core/issues/2980)) ([9b6ccd0](https://github.com/dequelabs/axe-core/commit/9b6ccd0c2948e7036ef3984810909f59d1e65f27))\n- **color-contrast-matches:** only absolutely positioned elements using clip should be not visible ([#3038](https://github.com/dequelabs/axe-core/issues/3038)) ([e93fdb1](https://github.com/dequelabs/axe-core/commit/e93fdb1f68656976d0cdfd73564f6580640be0f7))\n- **combobox:** support aria 1.2 pattern for comboboxes ([#3033](https://github.com/dequelabs/axe-core/issues/3033)) ([5ab026d](https://github.com/dequelabs/axe-core/commit/5ab026d1bc9932fdf6cd2b135df32dd80051b5c9))\n- **frame-tested:** run without respondable ([#2942](https://github.com/dequelabs/axe-core/issues/2942)) ([c046114](https://github.com/dequelabs/axe-core/commit/c046114e9740e3e130ad0cb98288eb77331fbb93))\n- **getStandards:** Read standards from utils ([#2903](https://github.com/dequelabs/axe-core/issues/2903)) ([f1a0368](https://github.com/dequelabs/axe-core/commit/f1a036890b9befe8c1a0af388fd0f215bee6d9ae))\n- **heading-order:** Prevent crash on page with iframes but no headings ([#2965](https://github.com/dequelabs/axe-core/issues/2965)) ([99e7f0c](https://github.com/dequelabs/axe-core/commit/99e7f0c2ae5758328e1299ff54a261c7e581c475))\n- **heading-order:** use aria-level on headings in addition to role=header elements ([#3028](https://github.com/dequelabs/axe-core/issues/3028)) ([caccd38](https://github.com/dequelabs/axe-core/commit/caccd381d3817cc46e824d1a76e5ea888bf519bf))\n- **page-no-duplicate:** don't count elements hidden from screenreaders as potential duplicates ([#3051](https://github.com/dequelabs/axe-core/issues/3051)) ([5e0098b](https://github.com/dequelabs/axe-core/commit/5e0098b0262614db20fc9d6689a5d5b95c153d8d))\n- **publish-metadata:** use fail message for rules with reviewOnFail:true ([#2987](https://github.com/dequelabs/axe-core/issues/2987)) ([b6dc5f6](https://github.com/dequelabs/axe-core/commit/b6dc5f654c6c4b56217eabfd093447122890a7a2))\n- **utils:** unify selecting nodes in shadow tree with shadowSelect() ([#3068](https://github.com/dequelabs/axe-core/issues/3068)) ([21681da](https://github.com/dequelabs/axe-core/commit/21681da0c8b952b5a64e17751e05686a98c6b5da))\n- JS error in @axe-core/react caused by stale reference to heading ([b7094c8](https://github.com/dequelabs/axe-core/commit/b7094c8146ec816ab3d079e57f07bf0f21ce6676))\n- Remove unnecessary files from npm package ([#3054](https://github.com/dequelabs/axe-core/issues/3054)) ([7600b7c](https://github.com/dequelabs/axe-core/commit/7600b7cd4a9bbb019497895d1134246d02af9763))\n- **meta-viewport:** test that a user-scalable number does not prevent zoom ([b3562fb](https://github.com/dequelabs/axe-core/commit/b3562fb9fffa3152014d56a6e02a05622c10b9a9))\n- **region:** contents in iframes should pass the region rule if the iframe itself is in a region ([#2949](https://github.com/dequelabs/axe-core/issues/2949)) ([43145d6](https://github.com/dequelabs/axe-core/commit/43145d6f1136c0e2c9fd9a9f3a401a68f56fe665))\n- **required-parent:** Allow *item > group > *item nesting ([#2898](https://github.com/dequelabs/axe-core/issues/2898)) ([59b4a7e](https://github.com/dequelabs/axe-core/commit/59b4a7e2d939076e7aed6308ff25d1f3460d944b))\n- **SerialVirtualNode:** properly handle empty string attributes ([#3042](https://github.com/dequelabs/axe-core/issues/3042)) ([dddbc0a](https://github.com/dequelabs/axe-core/commit/dddbc0ac20ffa0bc65223d392310d447b767efe9))\n- **sri-history:** add backported releases ([#3004](https://github.com/dequelabs/axe-core/issues/3004)) ([6eecf34](https://github.com/dequelabs/axe-core/commit/6eecf344751e03170bd70fe076584011b5a6cacb))\n- treat input with no role as textbox ([#2929](https://github.com/dequelabs/axe-core/issues/2929)) ([13d909d](https://github.com/dequelabs/axe-core/commit/13d909dd12dc40ab9ed448793aef5031e207d429))\n- **types:** make `evaluate` check optional ([#2902](https://github.com/dequelabs/axe-core/issues/2902)) ([417f572](https://github.com/dequelabs/axe-core/commit/417f5724112e46f128efd5f56da9f891bf1e938c))\n\n### [4.2.4](https://github.com/dequelabs/axe-core/compare/v4.2.3...v4.2.4) (2021-10-22)\n\n### Bug Fixes\n\n- **axe.run:** add option to increase iframe ping timeout ([#3233](https://github.com/dequelabs/axe-core/issues/3233)) ([99a848e](https://github.com/dequelabs/axe-core/commit/99a848e785793782b264aa55cd71f5a35c5677c9))\n\n### [4.2.3](https://github.com/dequelabs/axe-core/compare/v4.2.2...v4.2.3) (2021-06-22)\n\n### Bug Fixes\n\n- **accText:** ignore text in embedded content elements ([#3022](https://github.com/dequelabs/axe-core/issues/3022)) ([8fb4635](https://github.com/dequelabs/axe-core/commit/8fb4635e01c82b80ff23994edad180dd73730a98)), closes [#3017](https://github.com/dequelabs/axe-core/issues/3017)\n- **color-contrast:** add special case for new sr-only technique ([#2985](https://github.com/dequelabs/axe-core/issues/2985)) ([79cbf01](https://github.com/dequelabs/axe-core/commit/79cbf0168bdb9311db2e31043ad36c20b77e39fe))\n- **color-contrast:** check for pseudo elements on element itself, not just parents ([#2980](https://github.com/dequelabs/axe-core/issues/2980)) ([3122550](https://github.com/dequelabs/axe-core/commit/3122550b1beaeafaac62c81fb47bcd4290ba266a))\n- **frame-tested:** run without respondable ([#2942](https://github.com/dequelabs/axe-core/issues/2942)) ([a1d725d](https://github.com/dequelabs/axe-core/commit/a1d725d5cdedc4bfd6ba69bb25aa0a1213fd63cf))\n- **publish-metadata:** use fail message for rules with reviewOnFail:true ([#2987](https://github.com/dequelabs/axe-core/issues/2987)) ([00fefa9](https://github.com/dequelabs/axe-core/commit/00fefa9bfb9479279afc89b2da45e748bace77d2))\n- **sri-history:** add backported releases ([#3004](https://github.com/dequelabs/axe-core/issues/3004)) ([0332e80](https://github.com/dequelabs/axe-core/commit/0332e806524ca9ca78512423a70eb80d623795eb))\n\n### [4.2.2](https://github.com/dequelabs/axe-core/compare/v4.2.1...v4.2.2) (2021-06-03)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** allow aria-posinset and aria-setsize on row elements for treegrids ([#2952](https://github.com/dequelabs/axe-core/issues/2952)) ([3023e69](https://github.com/dequelabs/axe-core/commit/3023e697b85c13f18f2cbb46b202400d8ce6a10d))\n- **heading-order:** Prevent crash on page with iframes but no headings ([#2965](https://github.com/dequelabs/axe-core/issues/2965)) ([4b7db37](https://github.com/dequelabs/axe-core/commit/4b7db3763735891972b8a13d6622fa30a687f3cb))\n- **meta-viewport:** test that a user-scalable number does not prevent zoom ([048c5c1](https://github.com/dequelabs/axe-core/commit/048c5c18c8245a43721a12237ac5f07f5b4a856b))\n- JS error in @axe-core/react caused by stale reference to heading ([3afda4e](https://github.com/dequelabs/axe-core/commit/3afda4effc4a099632138c5874ab305baaa5934a))\n\n### [4.2.1](https://github.com/dequelabs/axe-core/compare/v4.2.0...v4.2.1) (2021-05-18)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** pass aria-label on some HTML elements ([#2935](https://github.com/dequelabs/axe-core/issues/2935)) ([695aa77](https://github.com/dequelabs/axe-core/commit/695aa7751246c0e5e786a66df7808faa7a244576))\n- treat input with no role as textbox ([#2929](https://github.com/dequelabs/axe-core/issues/2929)) ([de18030](https://github.com/dequelabs/axe-core/commit/de180307fd876cfc6a1a0bdb828818a323976c81))\n- **autocomplete-appropriate:** pass for autocomplete=username and type=email ([#2896](https://github.com/dequelabs/axe-core/issues/2896)) ([8b478c8](https://github.com/dequelabs/axe-core/commit/8b478c82b3362fc27df8a0087c779327e6a9d6d0))\n- **getStandards:** Read standards from utils ([#2903](https://github.com/dequelabs/axe-core/issues/2903)) ([52ad4c6](https://github.com/dequelabs/axe-core/commit/52ad4c69991e433d413846c4c9778fdd863aebeb))\n- **required-parent:** Allow *item > group > *item nesting ([#2898](https://github.com/dequelabs/axe-core/issues/2898)) ([3acd229](https://github.com/dequelabs/axe-core/commit/3acd229b08b806ea359e7e08e37e8721cddc5290))\n- **types:** make `evaluate` check optional ([#2902](https://github.com/dequelabs/axe-core/issues/2902)) ([75fabfe](https://github.com/dequelabs/axe-core/commit/75fabfef3adeade350902f2dd18928e44fbb7cf4))\n\n## [4.2.0](https://github.com/dequelabs/axe-core/compare/v4.1.2...v4.2.0) (2021-04-23)\n\n### Features\n\n- add axe.frameMessenger with configurable allowedOrigins ([#2880](https://github.com/dequelabs/axe-core/issues/2880)) ([b27bab3](https://github.com/dequelabs/axe-core/commit/b27bab3954f006e1257f7d70bd467991a2d9330e))\n- **aria-allowed-attr:** add ARIA 1.2 prohibited attrs check ([#2764](https://github.com/dequelabs/axe-core/issues/2764)) ([4a77e88](https://github.com/dequelabs/axe-core/commit/4a77e881302f7db2750fa36a573fc26d123f7388))\n- **empty-table-header:** new rule to flag empty table headers ([#2811](https://github.com/dequelabs/axe-core/issues/2811)) ([813ee7e](https://github.com/dequelabs/axe-core/commit/813ee7efdf77a55a1c09a3b8836007917ead91b5))\n- **frame-focusable-content:** new rule to test iframes with tabindex=-1 do not have focusable content ([#2785](https://github.com/dequelabs/axe-core/issues/2785)) ([aeb044c](https://github.com/dequelabs/axe-core/commit/aeb044c26908b44490bad160add8c3e6327ce759))\n- **locale:** missing translations for DE ([#2704](https://github.com/dequelabs/axe-core/issues/2704)) ([f312994](https://github.com/dequelabs/axe-core/commit/f312994a63bd239b42cf3177af9d43fc190b1d3c))\n- **locale:** Polish translation ([#2677](https://github.com/dequelabs/axe-core/issues/2677)) ([c46979f](https://github.com/dequelabs/axe-core/commit/c46979ff93a8c1bd5d9d73f1b4d3edcab046582b))\n- **nested-interactive:** new rule to flag nested interactive elements ([#2691](https://github.com/dequelabs/axe-core/issues/2691)) ([13a7cf1](https://github.com/dequelabs/axe-core/commit/13a7cf12ad7f3f895a4c41c50b65bf71be12ea47))\n- **role-text:** add role-text rule ([#2702](https://github.com/dequelabs/axe-core/issues/2702)) ([7c05162](https://github.com/dequelabs/axe-core/commit/7c05162a3856c0f19514c9a87ca1c15aa5485b45))\n- **setup/teardown:** add functions to setup and teardown axe-core internal data, deprecate axe.\\_tree ([#2738](https://github.com/dequelabs/axe-core/issues/2738)) ([9d19f24](https://github.com/dequelabs/axe-core/commit/9d19f24f86f2028753ce944beeda9866b0274e7b))\n- **standards:** add graphics roles ([#2761](https://github.com/dequelabs/axe-core/issues/2761)) ([22032cc](https://github.com/dequelabs/axe-core/commit/22032cc3051e74ad8c12c27039e9fb0a9f07e9d1))\n- **standards/aria-roles:** add presentational children property ([#2689](https://github.com/dequelabs/axe-core/issues/2689)) ([78c239c](https://github.com/dequelabs/axe-core/commit/78c239cd6e3960e85cf900bc0fe511e3846c1c96))\n- **utils.getRule:** add function to get rule by id ([#2724](https://github.com/dequelabs/axe-core/issues/2724)) ([9d0af53](https://github.com/dequelabs/axe-core/commit/9d0af53192450f442d073c13313ff1d865f68935))\n- **utils/matches:** support selectors level 4 :not and :is ([#2742](https://github.com/dequelabs/axe-core/issues/2742)) ([21d9b0e](https://github.com/dequelabs/axe-core/commit/21d9b0ea4348d353dc85cacfb3fcace5eac6e4ca))\n- **virtual-node:** add attrNames property which returns list of attribute names ([#2741](https://github.com/dequelabs/axe-core/issues/2741)) ([1d864b4](https://github.com/dequelabs/axe-core/commit/1d864b4600fd88b06c8b5776bf891cf9bc402b60))\n\n### Bug Fixes\n\n- **aria-allowed-attr:** error when generic elements use aria-label and aria-labelledy ([#2766](https://github.com/dequelabs/axe-core/issues/2766)) ([64379e1](https://github.com/dequelabs/axe-core/commit/64379e1732fac6ccb0f9f2abfb8c36e22cbba9ef))\n- **aria-required-children:** allow group and rowgroup roles ([#2661](https://github.com/dequelabs/axe-core/issues/2661)) ([5a264e4](https://github.com/dequelabs/axe-core/commit/5a264e48e800bb9a783b2597e723ec9529d6d798))\n- **aria-required-children:** only match for roles that require children ([#2703](https://github.com/dequelabs/axe-core/issues/2703)) ([95de169](https://github.com/dequelabs/axe-core/commit/95de1698596ab5138d553836c90c422566d0527d))\n- **aria-valid-attr-value:** pass for aria-errormessage when aria-invalid is not set or false ([#2721](https://github.com/dequelabs/axe-core/issues/2721)) ([93a765c](https://github.com/dequelabs/axe-core/commit/93a765c372b29a0764872f50f044bfbabed68207))\n- **aria-valid-attr-value:** report when aria-labelledby ref is not in DOM ([#2723](https://github.com/dequelabs/axe-core/issues/2723)) ([116eb06](https://github.com/dequelabs/axe-core/commit/116eb06101ea49a02dbd3e86d2f91ac51e8fe9a9))\n- **aria-valid-attr-value:** return false when int type attribute uses invalid values ([#2710](https://github.com/dequelabs/axe-core/issues/2710)) ([ce9917e](https://github.com/dequelabs/axe-core/commit/ce9917e9dad91f526e9f0da99dcc347ff759e3bd))\n- **bypass:** mark as needs review rather than failure ([#2818](https://github.com/dequelabs/axe-core/issues/2818)) ([bb41b3e](https://github.com/dequelabs/axe-core/commit/bb41b3e89a22a69c5cb6fbd07e808291687343e5))\n- **focus-order-semantics:** allow role=tooltip to pass ([#2871](https://github.com/dequelabs/axe-core/issues/2871)) ([dc526d8](https://github.com/dequelabs/axe-core/commit/dc526d8034e2de0c4208d2c6f695f1ba53da950b))\n- **heading-order:** handle iframe as first result ([#2876](https://github.com/dequelabs/axe-core/issues/2876)) ([33428d8](https://github.com/dequelabs/axe-core/commit/33428d86230064d70ec6b2a638f947d0cdb31968))\n- **respondable:** Avoid message duplication with messageId ([#2816](https://github.com/dequelabs/axe-core/issues/2816)) ([4bd0acf](https://github.com/dequelabs/axe-core/commit/4bd0acf379fba2ef17679cacb70776e77e5f5e79))\n- **respondable:** work on iframes in shadow DOM ([#2857](https://github.com/dequelabs/axe-core/issues/2857)) ([38cad94](https://github.com/dequelabs/axe-core/commit/38cad9451dbde52fbab0835e3c1673825c57e5b7))\n- avoid 'undefined' showing in check messages ([#2779](https://github.com/dequelabs/axe-core/issues/2779)) ([3beb0b1](https://github.com/dequelabs/axe-core/commit/3beb0b1c7a4bcf599d4c10207a429e9ec38df698))\n- properly translate checks when building axe.js using --lang ([#2848](https://github.com/dequelabs/axe-core/issues/2848)) ([76545b0](https://github.com/dequelabs/axe-core/commit/76545b09ed0a1204b1096ef1b1d11fe6d6d89e2a))\n- **aria-required-parent:** only match for roles that require parents ([#2707](https://github.com/dequelabs/axe-core/issues/2707)) ([ce8281e](https://github.com/dequelabs/axe-core/commit/ce8281e6d45c6888d238ea33c2d39f4c67e8b267))\n- **color-contrast:** account for text client rects that start outside the parent container ([#2682](https://github.com/dequelabs/axe-core/issues/2682)) ([a4e4a34](https://github.com/dequelabs/axe-core/commit/a4e4a344cea70308d0a59a04411f6da88b80b00b))\n- **color-contrast-matches:** do not pass empty string to getElementById ([#2739](https://github.com/dequelabs/axe-core/issues/2739)) ([0b0fec2](https://github.com/dequelabs/axe-core/commit/0b0fec285d7df8675c8f5ecc42519f3734d70f72))\n- **frame-title:** update rule description to be more descriptive ([#2735](https://github.com/dequelabs/axe-core/issues/2735)) ([159e25b](https://github.com/dequelabs/axe-core/commit/159e25b45eb22498e7ab20d31f353b182b2cd1d7))\n- **heading-order:** allow partial context to pass ([#2622](https://github.com/dequelabs/axe-core/issues/2622)) ([f8baee6](https://github.com/dequelabs/axe-core/commit/f8baee68fa5220d54399a347322c6c8ac4f26cb9))\n- **landmark-complementary-is-top-level:** allow aside inside main ([#2740](https://github.com/dequelabs/axe-core/issues/2740)) ([9388c96](https://github.com/dequelabs/axe-core/commit/9388c9657fdfd80b9a76961d95a2cde939413665))\n- **metadata:** consistenct use of 'must' and 'should' ([#2770](https://github.com/dequelabs/axe-core/issues/2770)) ([603b612](https://github.com/dequelabs/axe-core/commit/603b612dfeb861b5fc08cf9605fed886c1d71107))\n- **region:** allow role=alertdialog as region ([#2660](https://github.com/dequelabs/axe-core/issues/2660)) ([b928df7](https://github.com/dequelabs/axe-core/commit/b928df7f81bc1ed79946b472f5e71dea324a611c))\n- **select-name:** fix typo in accessible name help ([#2676](https://github.com/dequelabs/axe-core/issues/2676)) ([6b916b9](https://github.com/dequelabs/axe-core/commit/6b916b908f38c465e1b319b1b8f50c1a7c9df698))\n- **to-grid/get-headers:** work with rowspan=0 ([#2722](https://github.com/dequelabs/axe-core/issues/2722)) ([508190b](https://github.com/dequelabs/axe-core/commit/508190b8e20873bc98bd0efb39d37bcb1cbcd92a))\n- **types:** Add noHtml option ([#2810](https://github.com/dequelabs/axe-core/issues/2810)) ([c03c826](https://github.com/dequelabs/axe-core/commit/c03c82635c6e4228b94438d00f7ff8723303918c))\n- **utils:** fix warning thrown by Webpack ([#2843](https://github.com/dequelabs/axe-core/issues/2843)) ([0826177](https://github.com/dequelabs/axe-core/commit/08261778e039abae0be16d001923dcc87ce168a4)), closes [#2840](https://github.com/dequelabs/axe-core/issues/2840)\n- **utils:** remove attributes from source string ([#2803](https://github.com/dequelabs/axe-core/issues/2803)) ([8e8c4fa](https://github.com/dequelabs/axe-core/commit/8e8c4faddbe95f979f0c03d1d44ccd148218379b))\n- add noHtml to axe.configure ([#2789](https://github.com/dequelabs/axe-core/issues/2789)) ([5c8dec8](https://github.com/dequelabs/axe-core/commit/5c8dec83a790aba273f8f921007dc7f0a0904851))\n- do not allow postMessage with axe version of x.y.z ([#2790](https://github.com/dequelabs/axe-core/issues/2790)) ([5acda82](https://github.com/dequelabs/axe-core/commit/5acda82fbb61d07f5036169008a08e2e1be3d155))\n\n### [4.1.4](https://github.com/dequelabs/axe-core/compare/v4.1.3...v4.1.4) (2021-04-01)\n\n### Bug Fixes\n\n- **respondable:** work on iframes in shadow DOM ([#2857](https://github.com/dequelabs/axe-core/issues/2857)) ([65cbfd0](https://github.com/dequelabs/axe-core/commit/65cbfd04edbbf48ceee2ef35f500575a4ad88afc))\n- **utils:** fix warning thrown by Webpack ([#2843](https://github.com/dequelabs/axe-core/issues/2843)) ([df5d01b](https://github.com/dequelabs/axe-core/commit/df5d01b94fef43e6ca8d2fab5219f90811700405)), closes [#2840](https://github.com/dequelabs/axe-core/issues/2840)\n\n### [4.1.3](https://github.com/dequelabs/axe-core/compare/v4.1.2...v4.1.3) (2021-03-04)\n\n### Bug Fixes\n\n- **respondable:** Avoid message duplication with messageId ([#2816](https://github.com/dequelabs/axe-core/issues/2816)) ([9b6eb59](https://github.com/dequelabs/axe-core/commit/9b6eb5987da104398acaae60b7b7ee4e0b2d3c8f))\n- **types:** Add noHtml option ([#2810](https://github.com/dequelabs/axe-core/issues/2810)) ([8bc0bae](https://github.com/dequelabs/axe-core/commit/8bc0baec5c997873daf43ff5de61ea22a8e8c896))\n\n### [4.1.2](https://github.com/dequelabs/axe-core/compare/v4.1.1...v4.1.2) (2021-02-08)\n\n### Bug Fixes\n\n- add noHtml to axe.configure ([#2789](https://github.com/dequelabs/axe-core/issues/2789)) ([6e23eb0](https://github.com/dequelabs/axe-core/commit/6e23eb0839da5806da5476a5158fb6324d3ee005))\n- do not allow postMessage with axe version of x.y.z ([#2790](https://github.com/dequelabs/axe-core/issues/2790)) ([8b1671d](https://github.com/dequelabs/axe-core/commit/8b1671d46dd2238284fbfe1448ef36f8fc2048e7))\n\n### [4.1.1](https://github.com/dequelabs/axe-core/compare/v4.1.0...v4.1.1) (2020-11-19)\n\n### Bug Fixes\n\n- remove axios import ([#2653](https://github.com/dequelabs/axe-core/issues/2653)) ([2d20cde](https://github.com/dequelabs/axe-core/commit/2d20cdee819265f3c4efcb3ccb61da0a9a88981c))\n- **color-contrast:** greatly improve color-contrast-matches speed. add aria/get-accessible-ref ([#2635](https://github.com/dequelabs/axe-core/issues/2635)) ([ba174bd](https://github.com/dequelabs/axe-core/commit/ba174bd5496d7146c1baf982cb762444cda26cff))\n\n## [4.1.0](https://github.com/dequelabs/axe-core/compare/v4.0.2...v4.1.0) (2020-11-13)\n\n### Features\n\n- **new-rule:** check that treeitem role has an accessible name ([#2615](https://github.com/dequelabs/axe-core/issues/2615)) ([5e95153](https://github.com/dequelabs/axe-core/commit/5e951536a6b4f21ef4d4a849b575b5a3c1ec6b85))\n- Add aria-dialog-name ([#2609](https://github.com/dequelabs/axe-core/issues/2609)) ([b0e14b0](https://github.com/dequelabs/axe-core/commit/b0e14b0bc1df221ef379bf7ea830d3ceb030787a))\n- **aria-toggle-field-name:** add option role ([#2605](https://github.com/dequelabs/axe-core/issues/2605)) ([0af0551](https://github.com/dequelabs/axe-core/commit/0af0551e57c2b8347eeb9ce6550056c1888b5f4a))\n- **checks:** deprecate role-none and role-presentation for presentational-role ([#2503](https://github.com/dequelabs/axe-core/issues/2503)) ([cef54a0](https://github.com/dequelabs/axe-core/commit/cef54a0fd4156f7b1e6f6ba9cb36bde060a65622))\n- **get-role:** add noPresentational option ([#2549](https://github.com/dequelabs/axe-core/issues/2549)) ([4f39299](https://github.com/dequelabs/axe-core/commit/4f39299805d6e2c0fd09a1952d08c44800ca5dfc)), closes [#1792](https://github.com/dequelabs/axe-core/issues/1792)\n- **imports:** deprecate axios ([#2542](https://github.com/dequelabs/axe-core/issues/2542)) ([82d43a0](https://github.com/dequelabs/axe-core/commit/82d43a0593a8dc9733b97138f8e6f934ed25dea2))\n- **label,select-name:** allow placeholder to pass label rule, add select-name rule ([#2448](https://github.com/dequelabs/axe-core/issues/2448)) ([1315f8e](https://github.com/dequelabs/axe-core/commit/1315f8e2fb3eb6851657be2039014e3d13127210))\n- **new-rule:** ARIA links, buttons, menuitems have an accessible name ([#2571](https://github.com/dequelabs/axe-core/issues/2571)) ([9476a1f](https://github.com/dequelabs/axe-core/commit/9476a1f5170bbc85b683c6524f44f49c1835054a))\n- **new-rule:** aria-tooltip-name ([#2548](https://github.com/dequelabs/axe-core/issues/2548)) ([d00f378](https://github.com/dequelabs/axe-core/commit/d00f3781fb5c9c7c7f88e6790de3741788e8ddd9))\n- **new-rule:** check that meter role has an accessible name ([#2607](https://github.com/dequelabs/axe-core/issues/2607)) ([3ca2f04](https://github.com/dequelabs/axe-core/commit/3ca2f04b0f737914dda667c26f0193b7b312e6f6))\n- **new-rule:** check that progressbars have an accessible name ([#2555](https://github.com/dequelabs/axe-core/issues/2555)) ([dd0b44a](https://github.com/dequelabs/axe-core/commit/dd0b44ad8aeed9e4b7b45478c62c655a6cbf10ed))\n- **presentation-role-conflict:** create rule to flag elements with role conflict resolution ([#2440](https://github.com/dequelabs/axe-core/issues/2440)) ([e4edffc](https://github.com/dequelabs/axe-core/commit/e4edffc09f2305d8b4e69af266759cf32a5326f4))\n- **rule-matches:** depreacte window-is-top-matches for is-intiator-matches ([#2531](https://github.com/dequelabs/axe-core/issues/2531)) ([db2be93](https://github.com/dequelabs/axe-core/commit/db2be93d07dda488f9d5ca66d193709eb41bf8cd))\n- **standards:** add superclassRole to ariaRoles ([#2600](https://github.com/dequelabs/axe-core/issues/2600)) ([a5e9ce0](https://github.com/dequelabs/axe-core/commit/a5e9ce03befdc8f914f72a4eb1e49a95d6a2aa91))\n- **standards:** add superclassRole to dpubRoles ([#2606](https://github.com/dequelabs/axe-core/issues/2606)) ([1b66930](https://github.com/dequelabs/axe-core/commit/1b6693092d44aa10b9d4fd093baff674a17a2d30))\n- **utils:** deprecate get/set-scroll-state ([#2581](https://github.com/dequelabs/axe-core/issues/2581)) ([3c4827f](https://github.com/dequelabs/axe-core/commit/3c4827f7e3fdab4851697111bbe7cab55539bfbd))\n- **valid-langs:** deprecate validLangs, add isValidLangs, reduce file size ([#2527](https://github.com/dequelabs/axe-core/issues/2527)) ([8a699ec](https://github.com/dequelabs/axe-core/commit/8a699ecba6c77f6a705d44616f1bcefd634ff89b))\n\n### Bug Fixes\n\n- **commons/get-text-element-stack:** account for newline characters when text is larger than container ([#2631](https://github.com/dequelabs/axe-core/issues/2631)) ([3c2429b](https://github.com/dequelabs/axe-core/commit/3c2429b4032d29cdcfd3083e46e91854dccd733b))\n- Update ACT rule tags ([#2625](https://github.com/dequelabs/axe-core/issues/2625)) ([c640d4f](https://github.com/dequelabs/axe-core/commit/c640d4fc18a7625d30c6d0572e15f26c984511e4))\n- **aria-errormessage:** allow aria-live=\"polite\" on aria-errormessage target ([926b6a8](https://github.com/dequelabs/axe-core/commit/926b6a8fe7aad0444d9bc97f2e6c8564023ab58f))\n- **aria-errormessage:** allow aria-live=polite on aria-errormessage target ([#2597](https://github.com/dequelabs/axe-core/issues/2597)) ([0d5cfb1](https://github.com/dequelabs/axe-core/commit/0d5cfb15941cc23d2b5065c203e9ccdc01c57b82))\n- **aria-roles:** add group to menuitemradio context ([#2518](https://github.com/dequelabs/axe-core/issues/2518)) ([52b89f1](https://github.com/dequelabs/axe-core/commit/52b89f1ca67b2c04b8e96495cd6fe601e5c3eb54))\n- **aria-roles:** Add WAI-ARIA 1.2 roles ([#2544](https://github.com/dequelabs/axe-core/issues/2544)) ([635b084](https://github.com/dequelabs/axe-core/commit/635b084c5460a3328118ec4f5db678deb082966b)), closes [#2107](https://github.com/dequelabs/axe-core/issues/2107) [#2107](https://github.com/dequelabs/axe-core/issues/2107) [#2107](https://github.com/dequelabs/axe-core/issues/2107) [#2107](https://github.com/dequelabs/axe-core/issues/2107)\n- **attr-non-space-content-evaluate:** Split no attribute and empty attribute message ([#2495](https://github.com/dequelabs/axe-core/issues/2495)) ([5f822f4](https://github.com/dequelabs/axe-core/commit/5f822f4f32ce5597e78dd7db77b8412aea1154a5))\n- **audit:** updated axe.reset() to reset branding, application, and tagExcludes. ([#2537](https://github.com/dequelabs/axe-core/issues/2537)) ([828864b](https://github.com/dequelabs/axe-core/commit/828864bd9c32a6866e0e0dc6d7b263668a01497e))\n- **autocomplete-valid:** allow type=tel for appropriate cc types ([#2575](https://github.com/dequelabs/axe-core/issues/2575)) ([ae21713](https://github.com/dequelabs/axe-core/commit/ae2171364bee2fe1912a9da427525be4fa99a1db))\n- **checks/aria:** Mark elements missing from aria-errormessage for review ([#2550](https://github.com/dequelabs/axe-core/issues/2550)) ([8f9a035](https://github.com/dequelabs/axe-core/commit/8f9a035dca4aaa6f9fe20766d5a6f58ec1c4039c)), closes [#2460](https://github.com/dequelabs/axe-core/issues/2460)\n- **color-contrast:** allow small text shadows to serve as text outline ([#2627](https://github.com/dequelabs/axe-core/issues/2627)) ([432e1f3](https://github.com/dequelabs/axe-core/commit/432e1f367f1017d1c2c59d2242909852fecb0607))\n- **color-contrast:** mark elements with pseudo content as needs review ([#2613](https://github.com/dequelabs/axe-core/issues/2613)) ([fcdbdbc](https://github.com/dequelabs/axe-core/commit/fcdbdbc4d7324c952d08f69607870461f707c29b))\n- **color-contrast:** properly handle scrolling text ([#2619](https://github.com/dequelabs/axe-core/issues/2619)) ([984e7e2](https://github.com/dequelabs/axe-core/commit/984e7e2a96fc0cf8a1a810a17a771511225c5257))\n- **docs/rules:** add missing category tags to rules metadata ([#2569](https://github.com/dequelabs/axe-core/issues/2569)) ([285c442](https://github.com/dequelabs/axe-core/commit/285c442152d8a80dd13edb9ac064ac5030199524)), closes [#2554](https://github.com/dequelabs/axe-core/issues/2554)\n- **explicit-label:** work with multiple labels ([#2573](https://github.com/dequelabs/axe-core/issues/2573)) ([d26f106](https://github.com/dequelabs/axe-core/commit/d26f10699d099c8a4f2aacf180862890d8dbbe18))\n- **get-headers:** fix for rowspan and colspan ([#2545](https://github.com/dequelabs/axe-core/issues/2545)) ([3f02d14](https://github.com/dequelabs/axe-core/commit/3f02d14cbe3562e22762664843f8852f779be2a3))\n- **heading-order:** evaluate headings from iframes in DOM order ([#2572](https://github.com/dequelabs/axe-core/issues/2572)) ([46f6628](https://github.com/dequelabs/axe-core/commit/46f6628cc710b9aaa8b872790a0b8c42032a8134))\n- **implilcit-role:** use type property instead of attribute to resolve input role ([#2547](https://github.com/dequelabs/axe-core/issues/2547)) ([35e853d](https://github.com/dequelabs/axe-core/commit/35e853d91e1067b9156dfb6e89da0c7445baa86f)), closes [#2514](https://github.com/dequelabs/axe-core/issues/2514)\n- **link-name:** pass landmark content as link text ([#2617](https://github.com/dequelabs/axe-core/issues/2617)) ([e77992e](https://github.com/dequelabs/axe-core/commit/e77992eb1fb5e2c2365154a0f431f223e1b158ee))\n- **name-rules:** ignore when explicit roles don't require a name ([#2629](https://github.com/dequelabs/axe-core/issues/2629)) ([52fb138](https://github.com/dequelabs/axe-core/commit/52fb13872f2ff477b3a32d10d1fb5c72fde3adda))\n- **region:** allow role=dialog and svg elements outside regions ([#2586](https://github.com/dequelabs/axe-core/issues/2586)) ([fab58d4](https://github.com/dequelabs/axe-core/commit/fab58d4bf60a4d7b1c935b31384eecd0c57f92f5))\n- **region:** treat iframes as regions ([#2614](https://github.com/dequelabs/axe-core/issues/2614)) ([936db81](https://github.com/dequelabs/axe-core/commit/936db8105b7bfa1323568ab572a1e4b0428b4566))\n- **rule:** add check node to the check result object ([#2608](https://github.com/dequelabs/axe-core/issues/2608)) ([b188911](https://github.com/dequelabs/axe-core/commit/b188911613812a9aaaa95fb86606f842ee39b11a))\n- **scrollable-region-focusalbe:** do not fail for combobox pattern ([#2601](https://github.com/dequelabs/axe-core/issues/2601)) ([ac71a57](https://github.com/dequelabs/axe-core/commit/ac71a574f0285eceb11960416902169f7dce030d))\n- deprecate aria-form-field-name-matches for no-name-method-matches ([#2584](https://github.com/dequelabs/axe-core/issues/2584)) ([8be89e3](https://github.com/dequelabs/axe-core/commit/8be89e39883e5c421da06b1343c810502ac6e9a7))\n- do not allow fallback content for objects ([#2525](https://github.com/dequelabs/axe-core/issues/2525)) ([486eafe](https://github.com/dequelabs/axe-core/commit/486eafe72ca8e1b47edd53c0142aaab415b2d2d5))\n\n### [4.0.2](https://github.com/dequelabs/axe-core/compare/v4.0.1...v4.0.2) (2020-09-08)\n\n### Bug Fixes\n\n- **color-contrast:** dont error for floating element ([#2444](https://github.com/dequelabs/axe-core/issues/2444)) ([45eb746](https://github.com/dequelabs/axe-core/commit/45eb74682c8460fbd6f1de273e710b597ff01392))\n- **i18n:** proofreading of the french translations ([#2485](https://github.com/dequelabs/axe-core/issues/2485)) ([ebd0407](https://github.com/dequelabs/axe-core/commit/ebd04074f556b1927c9302e3f999e4c1cbc2de9e)), closes [#2484](https://github.com/dequelabs/axe-core/issues/2484)\n- **implicit-role:** return gridcell for td child of grid or treegrid ([#2501](https://github.com/dequelabs/axe-core/issues/2501)) ([0553d4d](https://github.com/dequelabs/axe-core/commit/0553d4d67d59127cd67420022cd7696dea167a6a))\n- **label:** pass when role none or presentation ([#2464](https://github.com/dequelabs/axe-core/issues/2464)) ([1d3c2fc](https://github.com/dequelabs/axe-core/commit/1d3c2fc28355c8476d3966d73664a2a1f4c6124d)), closes [#2458](https://github.com/dequelabs/axe-core/issues/2458)\n- **link-name:** fix regression where link was not named from title attribute ([#2492](https://github.com/dequelabs/axe-core/issues/2492)) ([b86c73b](https://github.com/dequelabs/axe-core/commit/b86c73b7dc61c2609afa6f384502f3c91f9a7f10))\n- **required-parent:** fail if intermediate role is not the required parent ([#2494](https://github.com/dequelabs/axe-core/issues/2494)) ([522865c](https://github.com/dequelabs/axe-core/commit/522865cabbe1f4815b1f3e980018e24c062c8e5e))\n- **svg-non-empty-title:** update fail message to indicate if the title element is empty or missing ([#2462](https://github.com/dequelabs/axe-core/issues/2462)) ([9598656](https://github.com/dequelabs/axe-core/commit/9598656bfac61da35f8b9f52e1fb32fcab6b484e)), closes [#2452](https://github.com/dequelabs/axe-core/issues/2452)\n- **valid-lang:** fail when lang attribute contains only whitespace ([#2504](https://github.com/dequelabs/axe-core/issues/2504)) ([8455a7f](https://github.com/dequelabs/axe-core/commit/8455a7f3a1aa83f487745306df0c07c39b6dc803))\n\n## [4.0.1](https://github.com/dequelabs/axe-core/compare/v4.0.0...v4.0.1) (2020-08-04)\n\n### Bug Fixes\n\n- **checks**: do not normalize options for custom checks ([#2435](https://github.com/dequelabs/axe-core/issues/2435)) ([83056ad](https://github.com/dequelabs/axe-core/commit/83056ada0e50dc943a5e2829c97323a744cb3b28))\n\n## [4.0.0](https://github.com/dequelabs/axe-core/compare/v3.5.5...v4.0.0) (2020-07-28)\n\n### Breaking Changes\n\nThe following rules were deprecated in axe-core 3.x, and are removed in 4.0:\n\n- aria-dpub-role-fallback\n- checkboxgroup\n- layout-table\n- radiogroup\n- video-description\n\nThe following checks were deprecated in axe-core 3.x, and are removed in 4.0:\n\n- aria/implicit-role-fallback\n- forms/fieldset\n- forms/group-labelledby\n- media/description\n- tables/has-caption\n- tables/has-summary\n- tables/has-th\n\n### Features\n\n- add layout-table-matches method ([#2400](https://github.com/dequelabs/axe-core/issues/2400)) ([d7ba70f](https://github.com/dequelabs/axe-core/commit/d7ba70fc9916cedb2e977c9dc10667985c2bb4ed))\n- **aria/get-roles-by-type:** deprecate in favor of standards/get-aria-roles-by-type ([#2362](https://github.com/dequelabs/axe-core/issues/2362)) ([c0c37ea](https://github.com/dequelabs/axe-core/commit/c0c37ea22f306cc93341fe25e173e3f65b6f924b))\n- **aria/lookupTable, aria-allowed-attr:** deprecate aria.lookupTable and passing allowed attributes to aria-allowed-attr ([#2395](https://github.com/dequelabs/axe-core/issues/2395)) ([739d1b1](https://github.com/dequelabs/axe-core/commit/739d1b11052c93c0856e6e5d217b66e613e43f11))\n- **avoid-inline-spacing:** add option for which css properties to look at ([#2244](https://github.com/dequelabs/axe-core/issues/2244)) ([93c027a](https://github.com/dequelabs/axe-core/commit/93c027ab7d081dcda401399e1cbdb5a27026c927))\n- **checks:** normalize check options to alway be an object ([#2219](https://github.com/dequelabs/axe-core/issues/2219)) ([da12da7](https://github.com/dequelabs/axe-core/commit/da12da79b1e7a16887807980e03d4e8244bed560))\n- **checks,rules:** remove deprecated checks and rules ([#2214](https://github.com/dequelabs/axe-core/issues/2214)) ([317545a](https://github.com/dequelabs/axe-core/commit/317545a0916a16e05263d1cedb6d9753e4ef6e19))\n- **closest:** VirtualNode implementation of Element.closest. Deprecate commons.dom.findUp and commons.dom.findUpVirtual ([#2139](https://github.com/dequelabs/axe-core/issues/2139)) ([493dd22](https://github.com/dequelabs/axe-core/commit/493dd2253606189a5e475c691aa8f0dc68a8aedd))\n- **color-contrast, utils:** add more options to color-contrast, add utils.deepMerge, deprecate commons.color.hasValidContrastRatio ([#2256](https://github.com/dequelabs/axe-core/issues/2256)) ([49fdb46](https://github.com/dequelabs/axe-core/commit/49fdb46cf371321760a3bdff6acc5311b4cfd158))\n- **commons/aria:** deprecate getRole({ noImplicit }) for getExplicitRole() ([#2294](https://github.com/dequelabs/axe-core/issues/2294)) ([a2873ea](https://github.com/dequelabs/axe-core/commit/a2873ea41f5b264165a2fb76679ab121985bb4f2))\n- **commons/standards:** create the commons/standards object for helper functions against the standards table ([#2358](https://github.com/dequelabs/axe-core/issues/2358)) ([6dce974](https://github.com/dequelabs/axe-core/commit/6dce974ee5f3095143818fbf5180d21762fe5de6))\n- **duplicate-img-label:** add option for parentSelector ([#2216](https://github.com/dequelabs/axe-core/issues/2216)) ([8906806](https://github.com/dequelabs/axe-core/commit/8906806fa039ba105bea745495e91031461af445))\n- **get-role:** add presentation role resolution and inheritance ([#2281](https://github.com/dequelabs/axe-core/issues/2281)) ([e207190](https://github.com/dequelabs/axe-core/commit/e2071900c462048a7455ec1f41878ab4f70b1bdd))\n- **get-role:** work with standards object ([#2367](https://github.com/dequelabs/axe-core/issues/2367)) ([1b20faf](https://github.com/dequelabs/axe-core/commit/1b20faf73417fde425dadfe1ac0a359d2179c1a1))\n- **globals:** deduce required window and document globals from context ([#2308](https://github.com/dequelabs/axe-core/issues/2308)) ([61bac69](https://github.com/dequelabs/axe-core/commit/61bac69877da884a9a24b040f898a67c6746984f))\n- **has-descendant, page-no-duplicate:** move page-has-elm and page-no-duplicate to generic check ([#2229](https://github.com/dequelabs/axe-core/issues/2229)) ([59125a0](https://github.com/dequelabs/axe-core/commit/59125a056d4b591ee20a67be12e1c66e83ec7c25))\n- **has-lang:** add option for which attributes to look at ([#2239](https://github.com/dequelabs/axe-core/issues/2239)) ([e69c46a](https://github.com/dequelabs/axe-core/commit/e69c46a49f348b8ae1fec8929e4688940b644a06))\n- **has-text-content:** add generic check has-text-content ([#2234](https://github.com/dequelabs/axe-core/issues/2234)) ([60ddc65](https://github.com/dequelabs/axe-core/commit/60ddc6577b70e27e9e7fe340d163b01360035e13))\n- **i18n:** add Basque (eu) translation ([#1964](https://github.com/dequelabs/axe-core/issues/1964)) ([176cf82](https://github.com/dequelabs/axe-core/commit/176cf824288a927e5e21dd64a7793251a31ef180))\n- **matcher:** allow regex string to be parsed as regex ([#2324](https://github.com/dequelabs/axe-core/issues/2324)) ([321b2d1](https://github.com/dequelabs/axe-core/commit/321b2d1dbfb8f3e04847674f46ada53b19313142))\n- **matches:** add explicitRole, implicitRole, and semanticRole matches functions ([#2286](https://github.com/dequelabs/axe-core/issues/2286)) ([30efbff](https://github.com/dequelabs/axe-core/commit/30efbfffc8b83ca03b4b2697e4202027826195f9))\n- **matches-definition:** add generic check matches-definition ([#2233](https://github.com/dequelabs/axe-core/issues/2233)) ([20467aa](https://github.com/dequelabs/axe-core/commit/20467aa48705d4b6b98f123031431bb3ac3ee22b))\n- **metadata-function-map:** add metadata function map to support check evaulate functions as an id string ([#2162](https://github.com/dequelabs/axe-core/issues/2162)) ([ec9b931](https://github.com/dequelabs/axe-core/commit/ec9b931c3123b3252e3c0afd5b059f0bbcfcc0d0))\n- **non-space-content:** switch all non-empty checks to new generic check ([#2215](https://github.com/dequelabs/axe-core/issues/2215)) ([7ce7b00](https://github.com/dequelabs/axe-core/commit/7ce7b00c2d302d830bd2c0efa027d0ea3b10770c))\n- **object-alt,accessible-text:** object-alt rule and accessible text to work with serial virtual nodes with children ([e8e17e4](https://github.com/dequelabs/axe-core/commit/e8e17e42c3594518ee60838749a507504a839c69))\n- **options:** add ancestry CSS selector to nodes ([#2389](https://github.com/dequelabs/axe-core/issues/2389)) ([f2cccf5](https://github.com/dequelabs/axe-core/commit/f2cccf50def5b2409f5903ce325f18475c8ed79e))\n- **region:** add option to match nodes as region ([#2249](https://github.com/dequelabs/axe-core/issues/2249)) ([b544554](https://github.com/dequelabs/axe-core/commit/b544554902b3aa0afb477e5e78140780f42e3405))\n- **required-attrs:** deprecate options to pass more required attrs ([797ee34](https://github.com/dequelabs/axe-core/commit/797ee34e6913cc5e45d55c770127e91623cb31f7))\n- **rule:** add reviewOnFail option to have rule return as needs review instead of violation ([#2235](https://github.com/dequelabs/axe-core/issues/2235)) ([bb72acd](https://github.com/dequelabs/axe-core/commit/bb72acde17e9f783ac136c30a79f14144326a7df))\n- **rule:** optional impact on rules ([#2393](https://github.com/dequelabs/axe-core/issues/2393)) ([e48c1eb](https://github.com/dequelabs/axe-core/commit/e48c1eb67db6182d7048a21f935db5a9416077ec))\n- **scope-value:** add options for valid scope values ([#2245](https://github.com/dequelabs/axe-core/issues/2245)) ([44269ec](https://github.com/dequelabs/axe-core/commit/44269ec6695645417dda03b26fcb04abebc15f5f))\n- **standards:** add ariaRoles standard ([#2328](https://github.com/dequelabs/axe-core/issues/2328)) ([70efbc0](https://github.com/dequelabs/axe-core/commit/70efbc045448d6621c270e5cbec70fe5794d4216))\n- **standards:** add dpub-roles spec ([#2332](https://github.com/dequelabs/axe-core/issues/2332)) ([7ec3185](https://github.com/dequelabs/axe-core/commit/7ec3185d55ca8fdce07c0345f5b960b39a54aba4))\n- **standards:** add get-aria-roles-supporting-name-from-content and deprecate aria/get-roles-with-name-from-content ([#2363](https://github.com/dequelabs/axe-core/issues/2363)) ([240b528](https://github.com/dequelabs/axe-core/commit/240b528a3a5e5f06e29973df5a9427315496bd28))\n- **standards:** add get-elements-by-content-type and implicit-html-roles ([#2375](https://github.com/dequelabs/axe-core/issues/2375)) ([f1e0848](https://github.com/dequelabs/axe-core/commit/f1e0848cb359f5912e2cf12ada02b06c079d7e21))\n- add \"ACT\" tag for published W3C ACT rules ([#2382](https://github.com/dequelabs/axe-core/issues/2382)) ([cf11b64](https://github.com/dequelabs/axe-core/commit/cf11b646f9b883e153ff5ec8fd848a363ea6bb24))\n- **standards:** add html-elms spec ([#2333](https://github.com/dequelabs/axe-core/issues/2333)) ([1d6a888](https://github.com/dequelabs/axe-core/commit/1d6a8885166007f8fec78ce853b8fef8262c522d))\n- **standards:** create standards object and ariaAttrs ([#2315](https://github.com/dequelabs/axe-core/issues/2315)) ([48610de](https://github.com/dequelabs/axe-core/commit/48610de74fed473f7d87e3c01f87a46d4dec406c))\n- **utils.getFlattenTree:** default to documentElement ([#2260](https://github.com/dequelabs/axe-core/issues/2260)) ([8b14ccc](https://github.com/dequelabs/axe-core/commit/8b14ccc5b1bd89892f57ff0f02de2045e7c3756f))\n- **valid-lang:** add option for which attributes to look at ([#2240](https://github.com/dequelabs/axe-core/issues/2240)) ([ffee19e](https://github.com/dequelabs/axe-core/commit/ffee19e4a769d66f9c81016dce83ad654609a9df))\n- update SC tags for `label` rule ([#2037](https://github.com/dequelabs/axe-core/issues/2037)) ([c7113fc](https://github.com/dequelabs/axe-core/commit/c7113fcc5d3f144e37d0204eebef701ffc844280))\n\n### Bug Fixes\n\n- **accessible-name-virtual:** allow subtree text to work with virtual and serial nodes ([#2346](https://github.com/dequelabs/axe-core/issues/2346)) ([67d2dca](https://github.com/dequelabs/axe-core/commit/67d2dca91af29a47e85919b049d98a5c83ec5b99))\n- **api:** correct use of rules property in axe.run ([#2278](https://github.com/dequelabs/axe-core/issues/2278)) ([1fd9e11](https://github.com/dequelabs/axe-core/commit/1fd9e116598fc2c152a30651110758043fe58ffd))\n- **aria-allowed-attr:** Add aria-orientation to radiogroup role ([#2322](https://github.com/dequelabs/axe-core/issues/2322)) ([5e1f922](https://github.com/dequelabs/axe-core/commit/5e1f92206134c81e5c05e907ab3fc1c223243b3d))\n- **aria-allowed-attr:** allow aria-activedescendant on role=application ([#2304](https://github.com/dequelabs/axe-core/issues/2304)) ([2554f5c](https://github.com/dequelabs/axe-core/commit/2554f5cdfc7bf616b52f109adedf575c7ea65f65))\n- **aria-allowed-role:** Add fieldset to allowed elements for radiogroup ([#2326](https://github.com/dequelabs/axe-core/issues/2326)) ([a5409d4](https://github.com/dequelabs/axe-core/commit/a5409d42deb69ff5982a732a7e947ce97fce9c1e))\n- **aria-allowed-role:** allow role=presentation on hr ([#2300](https://github.com/dequelabs/axe-core/issues/2300)) ([b524ea9](https://github.com/dequelabs/axe-core/commit/b524ea954fdcf8fcf09b49bdbe546335ed0e46df))\n- **aria-lablledby:** work with virtual and serial virtual nodes ([#2341](https://github.com/dequelabs/axe-core/issues/2341)) ([c1f3db7](https://github.com/dequelabs/axe-core/commit/c1f3db749f8de71635bc378c7cdf1a22c782429a))\n- **aria-toggle-field-name:** work with virtual nodes ([#2353](https://github.com/dequelabs/axe-core/issues/2353)) ([e5fb01e](https://github.com/dequelabs/axe-core/commit/e5fb01e497c63c2deca6eebd2466faea4d9afb2a))\n- **aria/allowed-attr:** work with standards object ([#2360](https://github.com/dequelabs/axe-core/issues/2360)) ([40397f5](https://github.com/dequelabs/axe-core/commit/40397f560f0bf0bcd7d80b98c4c02857cf6f2584))\n- **aria/get-role-type:** work with standards object ([#2361](https://github.com/dequelabs/axe-core/issues/2361)) ([a61e314](https://github.com/dequelabs/axe-core/commit/a61e3142ba5976bde09a9cd82cc944eab5ee284b))\n- **autocomplete:** allow all 'tel-\\*' autocomplete values on type=tel ([#2307](https://github.com/dequelabs/axe-core/issues/2307)) ([58c8175](https://github.com/dequelabs/axe-core/commit/58c8175ce4bcfd597c1d6df4c95113d2eb753951))\n- **button-name:** work with serial virtual node ([#2351](https://github.com/dequelabs/axe-core/issues/2351)) ([efa0e56](https://github.com/dequelabs/axe-core/commit/efa0e56ed8901c0d379e564cc0d4d5532b5df547))\n- **bypass:** find headings in iframes ([#2310](https://github.com/dequelabs/axe-core/issues/2310)) ([7c23dbd](https://github.com/dequelabs/axe-core/commit/7c23dbd1d04d95e3bd2b56a0bd14571f906eb3d5))\n- **color:** allow all valid CSS colors ([#2381](https://github.com/dequelabs/axe-core/issues/2381)) ([63d69ea](https://github.com/dequelabs/axe-core/commit/63d69ea1313d0c7dcdc4142c8de160b89082d9df))\n- **color-contrast:** account for text-shadow ([#2334](https://github.com/dequelabs/axe-core/issues/2334)) ([3eb6d2c](https://github.com/dequelabs/axe-core/commit/3eb6d2ccde9dc731450c90ebe497580cb98de201))\n- **color-contrast:** ignore aria-disabled labels ([#2130](https://github.com/dequelabs/axe-core/issues/2130)) ([e451b87](https://github.com/dequelabs/axe-core/commit/e451b87166b02262b5f191be1cf9fe1548047931))\n- **color-contrast:** properly handle truncated text ([#2302](https://github.com/dequelabs/axe-core/issues/2302)) ([a56190c](https://github.com/dequelabs/axe-core/commit/a56190c845cc7619a38a28685c48d8ac38108ebd))\n- **dlitem:** allow role=presentation on parent dl ([#2301](https://github.com/dequelabs/axe-core/issues/2301)) ([9857978](https://github.com/dequelabs/axe-core/commit/9857978a1f926b9eada8f560c9fbb1950fe8c648))\n- **focusable-no-name:** work with serial virtual nodes ([#2399](https://github.com/dequelabs/axe-core/issues/2399)) ([1ef3066](https://github.com/dequelabs/axe-core/commit/1ef3066476af80a7b5cb61a8fcb113f0208d5086))\n- **forms/\\*:** allow all form control value checks to work with virtual nodes ([#2343](https://github.com/dequelabs/axe-core/issues/2343)) ([8ad41af](https://github.com/dequelabs/axe-core/commit/8ad41af457125a268256f84de7623a79e905f782))\n- **header-present:** fail for headings with non-header role ([#2306](https://github.com/dequelabs/axe-core/issues/2306)) ([b8ffb39](https://github.com/dequelabs/axe-core/commit/b8ffb39785188dd230431f4f53825f1fe42ee3af))\n- **html-namespace-matches:** work with serial virtual nodes ([#2398](https://github.com/dequelabs/axe-core/issues/2398)) ([18c22fd](https://github.com/dequelabs/axe-core/commit/18c22fd379440eb681aa18cf0b624d6b4b3cbeec))\n- **implicit-roles:** add proper implicit role calculation ([#2242](https://github.com/dequelabs/axe-core/issues/2242)) ([e9dd259](https://github.com/dequelabs/axe-core/commit/e9dd2598b469fcbf936517aba33f983191301ff9))\n- **input-button-name:** work with virtual nodes ([#2352](https://github.com/dequelabs/axe-core/issues/2352)) ([63ca388](https://github.com/dequelabs/axe-core/commit/63ca3881e8352fba3b71f2f76dae23a0d70a271f))\n- **is-valid-autocomplete:** allow autocomplete=\"one-time-code\" ([#2336](https://github.com/dequelabs/axe-core/issues/2336)) ([638346f](https://github.com/dequelabs/axe-core/commit/638346fc515cfa5fda1b71f5ad31f5d4436a21eb))\n- **label:** work with virtual nodes ([#2354](https://github.com/dequelabs/axe-core/issues/2354)) ([44b033c](https://github.com/dequelabs/axe-core/commit/44b033c51619294a8d85c26aa6c703f57db90a59))\n- **page-has-h1:** allow aria-level=1 on native headings ([#2349](https://github.com/dequelabs/axe-core/issues/2349)) ([70b10b2](https://github.com/dequelabs/axe-core/commit/70b10b29c725507b0c1b6fb0d6d4146f123e5d34))\n- **rule:** allow impact to be configured ([#2426](https://github.com/dequelabs/axe-core/issues/2426)) ([f325c75](https://github.com/dequelabs/axe-core/commit/f325c755a11895cbb868de7be648461e5d81494e))\n- **rule,check:** allow function ids for matches property in rule.configure ([#2423](https://github.com/dequelabs/axe-core/issues/2423)) ([3ccb781](https://github.com/dequelabs/axe-core/commit/3ccb78128469d234db22225ea93a40c6d8fef434))\n- **run:** cleanup globals if set from context ([#2387](https://github.com/dequelabs/axe-core/issues/2387)) ([d5b6931](https://github.com/dequelabs/axe-core/commit/d5b6931cba857a5c787d912ee56bdd973e3742d4))\n- **svg-image-alt:** work with serial virtual nodes ([#2397](https://github.com/dequelabs/axe-core/issues/2397)) ([e2537ef](https://github.com/dequelabs/axe-core/commit/e2537eff1eb4b5378d63bddf00df44347d3aa09b))\n- **types:** Allow `impact` to be `null` ([#2321](https://github.com/dequelabs/axe-core/issues/2321)) ([757aacd](https://github.com/dequelabs/axe-core/commit/757aacd9716dc8a606f23807a5e8fe6a5474fbee)), closes [#2313](https://github.com/dequelabs/axe-core/issues/2313)\n- **types:** Make any tag allowed ([#2314](https://github.com/dequelabs/axe-core/issues/2314)) ([5d98a04](https://github.com/dequelabs/axe-core/commit/5d98a043d8bcda3a3fe032dd8205c9256c481631)), closes [#2312](https://github.com/dequelabs/axe-core/issues/2312)\n- **typings:** update types file ([#2425](https://github.com/dequelabs/axe-core/issues/2425)) ([0aab922](https://github.com/dequelabs/axe-core/commit/0aab922630bf72cc658dee3fb907463d718c57f1))\n- **virtual-node:** default and lowercase type property ([#2350](https://github.com/dequelabs/axe-core/issues/2350)) ([f6b3484](https://github.com/dequelabs/axe-core/commit/f6b34849fd0d869448137b4d032ca69a91fa9f66))\n- ensure correctly generated axe is required by `aria-supported` build step ([#2295](https://github.com/dequelabs/axe-core/issues/2295)) ([1414a9f](https://github.com/dequelabs/axe-core/commit/1414a9fd822700609024aa2cef782d63cfb0bce4))\n- **aria-required-attr:** pass aria-checked for elements with checked property ([#2226](https://github.com/dequelabs/axe-core/issues/2226)) ([64318a5](https://github.com/dequelabs/axe-core/commit/64318a5bce02f05c6e6a1acf739c354b84b88082))\n- **axe.d.ts:** add `element` to NodeResults ([#2211](https://github.com/dequelabs/axe-core/issues/2211)) ([2429355](https://github.com/dequelabs/axe-core/commit/242935552c1679f0c98aff8585fd39c0442d2d6e))\n- **color-contrast:** mark more punctutions for review ([#2126](https://github.com/dequelabs/axe-core/issues/2126)) ([dc98afc](https://github.com/dequelabs/axe-core/commit/dc98afc841c86e4b7b771bbb1171152a151c3e5b))\n- **duplicate-id:** list the duplicate id in message ([#2163](https://github.com/dequelabs/axe-core/issues/2163)) ([f5d4ff9](https://github.com/dequelabs/axe-core/commit/f5d4ff9e792b979c7f7c7cf8b3607f3969c25eb4))\n- **required-children:** consider overriding descendant role(s)… ([#2131](https://github.com/dequelabs/axe-core/issues/2131)) ([e1c11dd](https://github.com/dequelabs/axe-core/commit/e1c11ddf6e001eaa593a477eb7a5cb2f3c020e6d))\n- **scrollable-region-focusable:** pass for elements with contenteditable ([#2133](https://github.com/dequelabs/axe-core/issues/2133)) ([1012dfe](https://github.com/dequelabs/axe-core/commit/1012dfec29a7a69b70357211233bb350159fa83e))\n- **th-has-data-cells:** fail when only cell points to different header ([2d420c3](https://github.com/dequelabs/axe-core/commit/2d420c33981231a21d375c0aa8907e0c4c7c8932))\n- **types:** Add \"wcag21aa\" and \"wcag21a\" to our TS definition ([#2272](https://github.com/dequelabs/axe-core/issues/2272)) ([23674d4](https://github.com/dequelabs/axe-core/commit/23674d4403a7088996a758d0081b869516be4aea))\n- **types:** Add missing properties to `Spec` ([#2273](https://github.com/dequelabs/axe-core/issues/2273)) ([c39ba9f](https://github.com/dequelabs/axe-core/commit/c39ba9ffbd3a9777660da1d7e27d2471fc4158cd))\n\n### [3.5.5](https://github.com/dequelabs/axe-core/compare/v3.5.4...v3.5.5) (2020-06-16)\n\n### Bug Fixes\n\n- **aria-input-field-name:** add the missing word must to help metadata ([b0f5562](https://github.com/dequelabs/axe-core/commit/b0f5562ac2a746e8ac78f6e483e39162680f3a22))\n- **color-contrast:** fix font-weight calculation for safari ([205b587](https://github.com/dequelabs/axe-core/commit/205b58709c280f00100bf24a7b6bf15da50ed0ed))\n- **configure:** validate rules and checks properties ([8c91ead](https://github.com/dequelabs/axe-core/commit/8c91ead900fd3cab58f77af45cce54d02e734a8e))\n- **is-visible:** return false for opacity: 0 and 0 height scrollable regions ([86ada3f](https://github.com/dequelabs/axe-core/commit/86ada3f7cef5b1fbcaad37b8acfaee93566ba8bf))\n- **listitem:** do not fail for parent with role=presentation|none ([a3ddc6e](https://github.com/dequelabs/axe-core/commit/a3ddc6e1e43716b761e8bb42fba4842d88f813dc))\n- **meta-viewport:** don't throw error if viewport property doesn't have a value ([2176794](https://github.com/dequelabs/axe-core/commit/2176794f8a831c8303984039e4a07b99d368b111))\n\n### [3.5.4](https://github.com/dequelabs/axe-core/compare/v3.5.3...v3.5.4) (2020-05-22)\n\n### Bug Fixes\n\n- **get-element-stack:** properly calculate position of children of floated elements ([28a8c58](https://github.com/dequelabs/axe-core/commit/28a8c58b409461600da07eac164e5b0ca4744502))\n\n### [3.5.3](https://github.com/dequelabs/axe-core/compare/v3.5.2...v3.5.3) (2020-03-31)\n\n### Bug Fixes\n\n- **meta-viewport:** parse negative and string values for `maximum-scale` ([#2137](https://github.com/dequelabs/axe-core/issues/2137)) ([8c92472](https://github.com/dequelabs/axe-core/commit/8c92472397676d44b333cf50ad1a9413b9783c2c))\n- **respondable:** ignore reflected messages from iframes ([#2134](https://github.com/dequelabs/axe-core/issues/2134)) ([3ab9f21](https://github.com/dequelabs/axe-core/commit/3ab9f21414d8f4195ac95ffca8f0ce52258539b0))\n- **header-present:** update verbiage to use heading instead of header ([#2132](https://github.com/dequelabs/axe-core/issues/2132)) ([8890063](https://github.com/dequelabs/axe-core/commit/889006397329a35a170f4a3d24c55c3c4fd758fc))\n- **color-contrast:** mark more punctuations for review ([#2126](https://github.com/dequelabs/axe-core/issues/2126)) ([dc98afc](https://github.com/dequelabs/axe-core/commit/dc98afc841c86e4b7b771bbb1171152a151c3e5b))\n- **unicode:** detect supplementary private use area A ([#2102](https://github.com/dequelabs/axe-core/issues/2102)) ([f1739c2](https://github.com/dequelabs/axe-core/commit/f1739c21efdf5cf6fddf425d1c72b056757f3ba1))\n- **definition-list:** Mention <div> is allowed to group content in <dl> ([#2098](https://github.com/dequelabs/axe-core/issues/2098)) ([76b1a9f](https://github.com/dequelabs/axe-core/commit/76b1a9f7f244e43f63351c46d91fc8b27040ba98))\n- **td-headers-attr:** mark as needs review if headers attr is empty ([#2096](https://github.com/dequelabs/axe-core/issues/2096)) ([699b566](https://github.com/dequelabs/axe-core/commit/699b566111cfed2a4ebeade35346a49bcb3546c7))\n- **td-has-headers:** don't fail for empty headers attribute ([#2095](https://github.com/dequelabs/axe-core/issues/2095)) ([7952a37](https://github.com/dequelabs/axe-core/commit/7952a377b960c2403c8d4758a9f35ebe525549fe))\n- **th-has-data-cells:** fail when data cell points to a different header ([#2094](https://github.com/dequelabs/axe-core/issues/2094)) ([d3bd416](https://github.com/dequelabs/axe-core/commit/d3bd416a3df7324231f17ff9e1e20dc6342c7aa5))\n- **has-lang:** update message to indicate that xml:lang is not valid on HTML pages ([#2093](https://github.com/dequelabs/axe-core/issues/2093)) ([c3a7de2](https://github.com/dequelabs/axe-core/commit/c3a7de206baf77c0b25c8c095119393b1dc85f0f))\n- **page-no-duplicate-contentinfo:** do not fail when first element is inside landmark ([#2092](https://github.com/dequelabs/axe-core/issues/2092)) ([eca7e05](https://github.com/dequelabs/axe-core/commit/eca7e05cc6ff750855bcb219833967a9d7087679))\n\n### [3.5.2](https://github.com/dequelabs/axe-core/compare/v3.5.1...v3.5.2) (2020-03-06)\n\n### Bug Fixes\n\n- **aria-allowed-role:** allow role=spinbutton on input[type=tel](<[635445b](https://github.com/dequelabs/axe-core/commit/635445ba6da7bc197bcdee6fb1a9d9dc81e43941)>)\n- **color-contrast:** ignore form elements that move text outside of node using text-indent ([#2044](https://github.com/dequelabs/axe-core/issues/2044)) ([85cc6ab](https://github.com/dequelabs/axe-core/commit/85cc6abd7f919a7311c6da01a41853cbfd539508))\n- **commons:** handle node(s) contained by SVG document when de… ([#2054](https://github.com/dequelabs/axe-core/issues/2054)) ([bf4c9bf](https://github.com/dequelabs/axe-core/commit/bf4c9bff2953b1ac7f40dbb1d0bd1986c1caa673))\n- **getCheckMessage:** add API to return check message ([#2066](https://github.com/dequelabs/axe-core/issues/2066)) ([e216322](https://github.com/dequelabs/axe-core/commit/e216322183c8b064ebfd748fea785b8c2fe78b16))\n- **has-lang:** fail check when `xml:lang` is used in HTML docum… ([#2053](https://github.com/dequelabs/axe-core/issues/2053)) ([e07aaea](https://github.com/dequelabs/axe-core/commit/e07aaea86bc9460a2d9f568637b4a77d33094ed6))\n- **label-content-name-mismatch:** ignore non `widget` aria role(s) & do not use deprecated `lookupTable.rolesOfType` ([#2022](https://github.com/dequelabs/axe-core/issues/2022)) ([89bd84c](https://github.com/dequelabs/axe-core/commit/89bd84c4a9846dc31a660eda6b274329e7ac70b5))\n- **package-lock:** change @deque/doT from agora to npm ([#2058](https://github.com/dequelabs/axe-core/issues/2058)) ([c03c0e5](https://github.com/dequelabs/axe-core/commit/c03c0e5ffc3ea1b49607355fa9d6f2e2faf0ebd1))\n- **page-has-heading-one:** find screen-reader only headings ([#2065](https://github.com/dequelabs/axe-core/issues/2065)) ([f808a12](https://github.com/dequelabs/axe-core/commit/f808a12261269719534ae52c878d1700fb70053e))\n- **region:** ignore direct child text nodes of body ([#2050](https://github.com/dequelabs/axe-core/issues/2050)) ([fde31d0](https://github.com/dequelabs/axe-core/commit/fde31d03d24820f681b257c4c42b98f25b2877ec))\n- **skip-link:** identify as skip-link only if the link is offscreen ([#2079](https://github.com/dequelabs/axe-core/issues/2079)) ([241e1d0](https://github.com/dequelabs/axe-core/commit/241e1d0715398a39c2e502ddb781f5fc29987dde))\n- check `invaildrole` fails only when all roles are invalid ([#2075](https://github.com/dequelabs/axe-core/issues/2075)) ([989b317](https://github.com/dequelabs/axe-core/commit/989b31788b08b6514dd4b606162193d4dd168be9))\n- ignore empty, whitespace or undefined `role` for rule `ari… ([#2077](https://github.com/dequelabs/axe-core/issues/2077)) ([dbd3c02](https://github.com/dequelabs/axe-core/commit/dbd3c0211b05641f366dfe2fe9c2124cad9e5e3a))\n- update tags for rule `area-alt` ([#2051](https://github.com/dequelabs/axe-core/issues/2051)) ([7db231f](https://github.com/dequelabs/axe-core/commit/7db231f2ff2b70a4e8e6f7aababe0871764ece96))\n\n### [3.5.1](https://github.com/dequelabs/axe-core/compare/v3.5.0...v3.5.1) (2020-02-12)\n\n### Bug Fixes\n\n- **color-contrast:** parse font-weight value as number ([#2031](https://github.com/dequelabs/axe-core/issues/2031)) ([#2040](https://github.com/dequelabs/axe-core/issues/2040)) ([0bb2166](https://github.com/dequelabs/axe-core/commit/0bb21669f75b3adc0e3345c85680d437c57f94d8))\n- **color-contrast:** properly pass options to check ([#2033](https://github.com/dequelabs/axe-core/issues/2033)) ([26b99c0](https://github.com/dequelabs/axe-core/commit/26b99c0a511bd6fffaaca7535d732f26b39ef46e))\n- **commons:** avoid unicode regex encoding in axe.min.js ([#2024](https://github.com/dequelabs/axe-core/issues/2024)) ([ae90dc4](https://github.com/dequelabs/axe-core/commit/ae90dc47521f6047f71befcb3551686cf857208d))\n- **sri-history:** correct SRI for various versions ([#2041](https://github.com/dequelabs/axe-core/issues/2041)) ([940c017](https://github.com/dequelabs/axe-core/commit/940c01708cede33e796972ce810a815ddf492ba4))\n\n## [3.5.0](https://github.com/dequelabs/axe-core/compare/v3.4.0...v3.5.0) (2020-02-04)\n\n### Features\n\n- **aria-dpub-role-fallback:** depreacte aria-dpub-role-fallback and move into aria-allowed-role ([#1899](https://github.com/dequelabs/axe-core/issues/1899)) ([8e25c20](https://github.com/dequelabs/axe-core/commit/8e25c20a479b03820117c6cd349b51ce7f2e94ee))\n- **aria-label:** deprecate Element arg; use virtualNode ([#1922](https://github.com/dequelabs/axe-core/issues/1922)) ([d14981c](https://github.com/dequelabs/axe-core/commit/d14981c1f67ba849f9519ee826ac646d281649c7))\n- **audit:** allow runOnly option to accept an array of rules ([#1889](https://github.com/dequelabs/axe-core/issues/1889)) ([38d6a3f](https://github.com/dequelabs/axe-core/commit/38d6a3fb26c66215cca0f3df8da268b34bcb6be5))\n- **color-contrast:** greatly improve performance for very large sites ([#1943](https://github.com/dequelabs/axe-core/issues/1943)) ([9ea0065](https://github.com/dequelabs/axe-core/commit/9ea006534a423b2b7df1826a40e8214c6bb1fc48))\n- **core:** add preload configuration option for media files ([#1958](https://github.com/dequelabs/axe-core/issues/1958)) ([8a62649](https://github.com/dequelabs/axe-core/commit/8a626498dc9694a992e76855781e2ad1cbd4fe9b))\n- **core/reporters/v1:** Add failureSummary to incomplete results ([#1972](https://github.com/dequelabs/axe-core/issues/1972)) ([c88883d](https://github.com/dequelabs/axe-core/commit/c88883d00b6578593b83f3134ebf897d22a3ba61))\n- **get-element-stack:** performant api to replace document.elementsFromPoint ([#1842](https://github.com/dequelabs/axe-core/issues/1842)) ([9ae07fb](https://github.com/dequelabs/axe-core/commit/9ae07fbda36afd18a42a96d6755055006f309a4c))\n- **i18n:** add Danish (da-DK) translation ([#1876](https://github.com/dequelabs/axe-core/issues/1876)) ([fb6fc41](https://github.com/dequelabs/axe-core/commit/fb6fc414246bdaf1590d367352e4bea033822639))\n- **landmark-no-duplicate-\\*:** add rule landmark-no-duplicate-main, don't use html as element source for all duplicate rules ([#1949](https://github.com/dequelabs/axe-core/issues/1949)) ([5ec7894](https://github.com/dequelabs/axe-core/commit/5ec7894394f8348761f5fe48ad0d09b31a27d2b2))\n- **layout-table:** deprecate layout-table rule and checks ([#1885](https://github.com/dequelabs/axe-core/issues/1885)) ([d22cb30](https://github.com/dequelabs/axe-core/commit/d22cb30ad29f5d1aec089eea3544ff88f3b4f8ab))\n- **matches:** use VirtualNode and deprecate HTMLElement ([#1988](https://github.com/dequelabs/axe-core/issues/1988)) ([2600a06](https://github.com/dequelabs/axe-core/commit/2600a062d65f0c079b30d6a9f2aa3b6faeea6872))\n- **rule:** add color-contrast check for unicode characters, behind flags. ([#1969](https://github.com/dequelabs/axe-core/issues/1969)) ([0cd4037](https://github.com/dequelabs/axe-core/commit/0cd40373ff9a080ffcb128ce66f9eaf80f0d19b4)), closes [#1906](https://github.com/dequelabs/axe-core/issues/1906)\n- **rule:** identical-links-same-purpose ([#1649](https://github.com/dequelabs/axe-core/issues/1649)) ([9c73f62](https://github.com/dequelabs/axe-core/commit/9c73f62fea2be68cb555c36fc32c79f7ce2697dd))\n- **rule:** no-autoplay-audio ([#1946](https://github.com/dequelabs/axe-core/issues/1946)) ([b2373cb](https://github.com/dequelabs/axe-core/commit/b2373cb9ce73a15e79aec86af7edff62c9a4e2b3))\n- **svg-img-alt:** rule for when svg needs a title ([#1953](https://github.com/dequelabs/axe-core/issues/1953)) ([9491e09](https://github.com/dequelabs/axe-core/commit/9491e094fe82c4dba45eb253ed18bf0a0165197c))\n- deprecate the use doT.js for messages ([#1938](https://github.com/dequelabs/axe-core/issues/1938)) ([a2ddba3](https://github.com/dequelabs/axe-core/commit/a2ddba305003db0540c093f4ad9015c8854cd98d))\n\n### Bug Fixes\n\n- **color-contrast:** properly pass options to check ([#2033](https://github.com/dequelabs/axe-core/issues/2033)) ([26b99c0](https://github.com/dequelabs/axe-core/commit/26b99c0a511bd6fffaaca7535d732f26b39ef46e))\n- **commons:** avoid unicode regex encoding in axe.min.js ([#2024](https://github.com/dequelabs/axe-core/issues/2024)) ([ae90dc4](https://github.com/dequelabs/axe-core/commit/ae90dc47521f6047f71befcb3551686cf857208d))\n- add tags with dots for wcag sc ([#1849](https://github.com/dequelabs/axe-core/issues/1849)) ([2f1ab36](https://github.com/dequelabs/axe-core/commit/2f1ab36354add65fe77be3e2831278ce37c3bbab))\n- compute orientation lock from various transformation func… ([#1937](https://github.com/dequelabs/axe-core/issues/1937)) ([c987de0](https://github.com/dequelabs/axe-core/commit/c987de0ce7cfcde5a280a9d6c643879093df1bcd))\n- ignores axe.ping responses that do not contain axe=true ([26cb1fb](https://github.com/dequelabs/axe-core/commit/26cb1fbdd7127c421bb7ca60e07d72385c6e2ea1))\n- input[role=button][value='correct'] should pass ([#1897](https://github.com/dequelabs/axe-core/issues/1897)) ([4635fef](https://github.com/dequelabs/axe-core/commit/4635fef8bafd1c1cf916962693d0deee082266cf))\n- remove heading from list of widget roles ([#1882](https://github.com/dequelabs/axe-core/issues/1882)) ([a8cbf71](https://github.com/dequelabs/axe-core/commit/a8cbf717ef3aa8b0bf09ddcb17f3a95fd2d1a64a))\n- **link-name:** test role=link when there is no href ([#1921](https://github.com/dequelabs/axe-core/issues/1921)) ([6db28bc](https://github.com/dequelabs/axe-core/commit/6db28bc455b1c8937f44346aa232eacc4a1c3457))\n- remove isNaN check ([#2010](https://github.com/dequelabs/axe-core/issues/2010)) ([5359b3f](https://github.com/dequelabs/axe-core/commit/5359b3f06ac051963ee61fcef417bcc20bdec55b))\n- **aria-allowed-attr:** allow aria-details ([#1956](https://github.com/dequelabs/axe-core/issues/1956)) ([79e1c58](https://github.com/dequelabs/axe-core/commit/79e1c58cad4fdbd1409a0b545981c12f13252432))\n- **aria-allowed-role:** allow role combobox on input tel, search, url, and email ([#1850](https://github.com/dequelabs/axe-core/issues/1850)) ([ba75961](https://github.com/dequelabs/axe-core/commit/ba759618ed8068f4eb74b6cd00f002322b20eca5))\n- **aria-hidden-focus:** mark as needs review if a modal is open ([#1995](https://github.com/dequelabs/axe-core/issues/1995)) ([28a3553](https://github.com/dequelabs/axe-core/commit/28a35531b97b987e2fd1ad0beb25fbda3822fbd5))\n- **aria-required-children:** allow comboboxes with more popup roles ([#1950](https://github.com/dequelabs/axe-core/issues/1950)) ([35a24c0](https://github.com/dequelabs/axe-core/commit/35a24c034520e3c6d95514e3b9d9f2ab6ca10e06))\n- **aria-roles:** report error for fallback roles ([#1970](https://github.com/dequelabs/axe-core/issues/1970)) ([a1b7e08](https://github.com/dequelabs/axe-core/commit/a1b7e08f1f8e1c1caff228469cd891d0458680de))\n- **aria-valid-attr-value:** mark as needs review for aria-current with invalid value ([#1998](https://github.com/dequelabs/axe-core/issues/1998)) ([39b8eae](https://github.com/dequelabs/axe-core/commit/39b8eae9d3352be4f77bef41abcb8eab268a6809))\n- **axe.d.ts:** RunOnly.values should not accept a RunOnlyOption ([#1888](https://github.com/dequelabs/axe-core/issues/1888)) ([b68aa19](https://github.com/dequelabs/axe-core/commit/b68aa19500ffb57fdf370707d7614384ac239ad6))\n- **build:** add lang query parameter to helpUrl when builing with a locale ([#1909](https://github.com/dequelabs/axe-core/issues/1909)) ([8c5f9ef](https://github.com/dequelabs/axe-core/commit/8c5f9efd6449f82e410eb7c6a68cfb5304fc66ae))\n- **color-contrast:** improve speed and accuracy of code blocks with syntax highlighting ([#2003](https://github.com/dequelabs/axe-core/issues/2003)) ([1b6ab42](https://github.com/dequelabs/axe-core/commit/1b6ab42f72b1ea0d2ed223c6fd63b9b1e54cfa9b))\n- **color-contrast:** mark as needs review for text that contains only non-BMP characters ([#2005](https://github.com/dequelabs/axe-core/issues/2005)) ([e559be0](https://github.com/dequelabs/axe-core/commit/e559be041e90951c734de4e7ad70d7299f590cf6))\n- **color-contrast:** support IE extension context ([#2008](https://github.com/dequelabs/axe-core/issues/2008)) ([62e31ea](https://github.com/dequelabs/axe-core/commit/62e31ea5034871f572f8bb47dba2596fb1b13063))\n- **color-contrast:** take into account parent opacity for foreground color ([#1902](https://github.com/dequelabs/axe-core/issues/1902)) ([8719700](https://github.com/dequelabs/axe-core/commit/87197005d046cc8c845764ff9107683938864c65))\n- **getElementStack:** do not add hidden elements to the stack ([#1991](https://github.com/dequelabs/axe-core/issues/1991)) ([759d88d](https://github.com/dequelabs/axe-core/commit/759d88d08af059755d908794038770cc57448252))\n- **is-focusable:** use tabindex attribute instead of property ([#1912](https://github.com/dequelabs/axe-core/issues/1912)) ([042a148](https://github.com/dequelabs/axe-core/commit/042a1487df76489483330274933d06fd27b842e0))\n- **is-icon-ligature:** ignore whitespace characters ([#1908](https://github.com/dequelabs/axe-core/issues/1908)) ([7d2b2a6](https://github.com/dequelabs/axe-core/commit/7d2b2a6fca992e27bab36ed8ab64b7ba3385d7e5))\n- **is-ligature-icon:** rename canvas to canvasContext ([#1880](https://github.com/dequelabs/axe-core/issues/1880)) ([de9885d](https://github.com/dequelabs/axe-core/commit/de9885d5708d6928fa2eb2816351879307a31a5b))\n- **isFocusable:** return true for summary element and false for details element with summary child ([#1957](https://github.com/dequelabs/axe-core/issues/1957)) ([34ec2d7](https://github.com/dequelabs/axe-core/commit/34ec2d7326786347f8704786e886fc8d13dd3f9b))\n- **listitem:** clarify that li elements must be contained in a list or role=list ([#1894](https://github.com/dequelabs/axe-core/issues/1894)) ([6d8cfee](https://github.com/dequelabs/axe-core/commit/6d8cfee91f0f0b5dcacba7ffb0b3d6505862e6b7))\n- **locales:** fix incompeteMessageFallback to be a string rather than an object ([#1853](https://github.com/dequelabs/axe-core/issues/1853)) ([88677a9](https://github.com/dequelabs/axe-core/commit/88677a93d0ffe32d7305984314a37e623fb51153))\n- **meta-viewport:** mark as a best-practice rule instead of wcag failure ([#1960](https://github.com/dequelabs/axe-core/issues/1960)) ([766f962](https://github.com/dequelabs/axe-core/commit/766f96210d05d35cab0139839db4f009b1139ce1))\n- **only-listitem:** add message about invalid role on li elements ([#1954](https://github.com/dequelabs/axe-core/issues/1954)) ([c3049ab](https://github.com/dequelabs/axe-core/commit/c3049abaccff72412ec3d58fab9b386fe8a2ae5a))\n- **page-has-main:** do not find hidden elements ([#2001](https://github.com/dequelabs/axe-core/issues/2001)) ([6429e60](https://github.com/dequelabs/axe-core/commit/6429e608f082db76b4cc445679b61a6e0ab8f034))\n- **page-no-duplicate-main:** do not fail for duplicate hidden elements ([#2000](https://github.com/dequelabs/axe-core/issues/2000)) ([414dfb1](https://github.com/dequelabs/axe-core/commit/414dfb1c9a4ade645ce60e8918e8143fe58b3eb6))\n- **preload:** reject promise `axe.utils.preload` when XHR fails ([#2009](https://github.com/dequelabs/axe-core/issues/2009)) ([b406b1f](https://github.com/dequelabs/axe-core/commit/b406b1fd09d72f9193d5b4011fa6f24bd33e3576))\n- **region:** allow content in roles with implicit aria-live ([#2002](https://github.com/dequelabs/axe-core/issues/2002)) ([a8d829e](https://github.com/dequelabs/axe-core/commit/a8d829e081dabb62a5247e8956adbf7a2ef000a2))\n- **region:** return outermost regionless node instead of html ([#1980](https://github.com/dequelabs/axe-core/issues/1980)) ([8d77be2](https://github.com/dequelabs/axe-core/commit/8d77be206e11537e8b509d593707c98143181bfa))\n- **region-rule:** allow live regions with explicit roles ([#1999](https://github.com/dequelabs/axe-core/issues/1999)) ([b49bd95](https://github.com/dequelabs/axe-core/commit/b49bd9547b7d8ac392ebf958356f90aca7e48a38))\n- **run:** throw error if axe.run is called after a run has started but not completed ([#1914](https://github.com/dequelabs/axe-core/issues/1914)) ([3252a02](https://github.com/dequelabs/axe-core/commit/3252a020ffd372e9583d39c989affd3d3b22957b))\n- **server-side-image-map:** return as needs review rather than failure ([#1898](https://github.com/dequelabs/axe-core/issues/1898)) ([d544856](https://github.com/dequelabs/axe-core/commit/d5448567b23de8289443c9c314b34b3140f68c30))\n- **tabindex:** don't error when tabindex property is overridden ([#1910](https://github.com/dequelabs/axe-core/issues/1910)) ([6b82a4c](https://github.com/dequelabs/axe-core/commit/6b82a4c513a1d5be78dcc54ad90a90768613b918))\n- **td-has-headers:** greatly improve performance of td-has-headers rule ([#1887](https://github.com/dequelabs/axe-core/issues/1887)) ([a550309](https://github.com/dequelabs/axe-core/commit/a550309255b025cb1e63710af2142f2c02d79657))\n- removes flaky test in axe Pro api check ([b2bdcd1](https://github.com/dequelabs/axe-core/commit/b2bdcd13e7623d3111f7035f2e951ff6b330132c))\n- **typings:** add proper return value to getRule ([#1900](https://github.com/dequelabs/axe-core/issues/1900)) ([4d907f8](https://github.com/dequelabs/axe-core/commit/4d907f86b0152122f92cceae0b242e09aff0f49a))\n- **unicode:** stop parsing escaped unicode strings ([#1997](https://github.com/dequelabs/axe-core/issues/1997)) ([7447d03](https://github.com/dequelabs/axe-core/commit/7447d03bcfd118897d8114fded1ea8a42f3da08f))\n\n## [3.4.2](https://github.com/dequelabs/axe-core/compare/v3.4.1...v3.4.2) (2020-02-04)\n\n### Bug Fixes\n\n- **color-contrast:** support IE extension context ([#2008](https://github.com/dequelabs/axe-core/issues/2008)) ([cd651a0](https://github.com/dequelabs/axe-core/commit/cd651a0713fa2f4b307cc7fc2be033f8636b40d2))\n- **unicode:** stop parsing escaped unicode strings ([#1997](https://github.com/dequelabs/axe-core/issues/1997)) ([523a31c](https://github.com/dequelabs/axe-core/commit/523a31c19fefd330e0b4f4c45f51d400c6f66164))\n\n### [3.4.1](https://github.com/dequelabs/axe-core/compare/v3.4.0...v3.4.1) (2019-12-11)\n\n### Bug Fixes\n\n- ignores axe.ping responses that do not contain axe=true ([7d8aa42](https://github.com/dequelabs/axe-core/commit/7d8aa42))\n- input[role=button][value='correct'] should pass ([#1897](https://github.com/dequelabs/axe-core/issues/1897)) ([3aba02c](https://github.com/dequelabs/axe-core/commit/3aba02c))\n- remove heading from list of widget roles ([#1882](https://github.com/dequelabs/axe-core/issues/1882)) ([beb458f](https://github.com/dequelabs/axe-core/commit/beb458f))\n- **listitem:** clarify that li elements must be contained in a list or role=list ([#1894](https://github.com/dequelabs/axe-core/issues/1894)) ([8fa0964](https://github.com/dequelabs/axe-core/commit/8fa0964))\n- removes flaky test in axe Pro api check ([f2b3b54](https://github.com/dequelabs/axe-core/commit/f2b3b54))\n- **aria-allowed-role:** allow role combobox on input tel, search, url, and email ([#1850](https://github.com/dequelabs/axe-core/issues/1850)) ([75d3c8b](https://github.com/dequelabs/axe-core/commit/75d3c8b))\n- **axe.d.ts:** RunOnly.values should not accept a RunOnlyOption ([#1888](https://github.com/dequelabs/axe-core/issues/1888)) ([94b1466](https://github.com/dequelabs/axe-core/commit/94b1466))\n- **build:** add lang query parameter to helpUrl when builing with a locale ([#1909](https://github.com/dequelabs/axe-core/issues/1909)) ([a2f0247](https://github.com/dequelabs/axe-core/commit/a2f0247))\n- **color-contrast:** take into account parent opacity for foreground color ([#1902](https://github.com/dequelabs/axe-core/issues/1902)) ([639c41b](https://github.com/dequelabs/axe-core/commit/639c41b))\n- **is-focusable:** use tabindex attribute instead of property ([#1912](https://github.com/dequelabs/axe-core/issues/1912)) ([aa5314d](https://github.com/dequelabs/axe-core/commit/aa5314d))\n- **is-icon-ligature:** ignore whitespace characters ([#1908](https://github.com/dequelabs/axe-core/issues/1908)) ([77fc838](https://github.com/dequelabs/axe-core/commit/77fc838))\n- **is-ligature-icon:** rename canvas to canvasContext ([#1880](https://github.com/dequelabs/axe-core/issues/1880)) ([604ba4f](https://github.com/dequelabs/axe-core/commit/604ba4f))\n- **link-name:** test role=link when there is no href ([#1921](https://github.com/dequelabs/axe-core/issues/1921)) ([e839c57](https://github.com/dequelabs/axe-core/commit/e839c57))\n- **locales:** fix incompeteMessageFallback to be a string rather than an object ([#1853](https://github.com/dequelabs/axe-core/issues/1853)) ([02a1d8a](https://github.com/dequelabs/axe-core/commit/02a1d8a))\n- **run:** throw error if axe.run is called after a run has started but not completed ([#1914](https://github.com/dequelabs/axe-core/issues/1914)) ([290d125](https://github.com/dequelabs/axe-core/commit/290d125))\n- **server-side-image-map:** return as needs review rather than failure ([#1898](https://github.com/dequelabs/axe-core/issues/1898)) ([ce73af9](https://github.com/dequelabs/axe-core/commit/ce73af9))\n- **tabindex:** don't error when tabindex property is overridden ([#1910](https://github.com/dequelabs/axe-core/issues/1910)) ([e6875ee](https://github.com/dequelabs/axe-core/commit/e6875ee))\n- **td-has-headers:** greatly improve performance of td-has-headers rule ([#1887](https://github.com/dequelabs/axe-core/issues/1887)) ([a588cad](https://github.com/dequelabs/axe-core/commit/a588cad))\n- **typings:** add proper return value to getRule ([#1900](https://github.com/dequelabs/axe-core/issues/1900)) ([0d7c3d2](https://github.com/dequelabs/axe-core/commit/0d7c3d2))\n\n## [3.4.0](https://github.com/dequelabs/axe-core/compare/v3.3.2...v3.4.0) (2019-10-18)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** allow 'aria-readonly' on listbox ([#1825](https://github.com/dequelabs/axe-core/issues/1825)) ([15f5d2d](https://github.com/dequelabs/axe-core/commit/15f5d2d))\n- **aria-form-field-name-matches:** don't test combobox elements when they have a child input ([#1742](https://github.com/dequelabs/axe-core/issues/1742)) ([f0be6dc](https://github.com/dequelabs/axe-core/commit/f0be6dc))\n- **aria-required-children:** allow combobox to own a searchbox ([#1708](https://github.com/dequelabs/axe-core/issues/1708)) ([42158ac](https://github.com/dequelabs/axe-core/commit/42158ac))\n- **aria-required-children:** allow reviewEmpty nodes to have empty children ([#1791](https://github.com/dequelabs/axe-core/issues/1791)) ([a5d727c](https://github.com/dequelabs/axe-core/commit/a5d727c))\n- **aria-valid-attr-value:** fix incomplete translation message ([#1739](https://github.com/dequelabs/axe-core/issues/1739)) ([a0f3eef](https://github.com/dequelabs/axe-core/commit/a0f3eef))\n- **color-contrast:** ignore nodes that don't contain text ([#1837](https://github.com/dequelabs/axe-core/issues/1837)) ([223a4bc](https://github.com/dequelabs/axe-core/commit/223a4bc))\n- **color-contrast-matches:** don't check aria-disabled explicit label element ([#1741](https://github.com/dequelabs/axe-core/issues/1741)) ([5bb566f](https://github.com/dequelabs/axe-core/commit/5bb566f))\n- **configure:** use translations for failure summaries ([#1808](https://github.com/dequelabs/axe-core/issues/1808)) ([5e53d7b](https://github.com/dequelabs/axe-core/commit/5e53d7b))\n- **flatten-tree:** do not call deprecated getDistributedNodes ([#1729](https://github.com/dequelabs/axe-core/issues/1729)) ([46a5d11](https://github.com/dequelabs/axe-core/commit/46a5d11))\n- **form-field-multiple-label:** incomplete rather than fail for multiple labels ([#1798](https://github.com/dequelabs/axe-core/issues/1798)) ([0bdaa2b](https://github.com/dequelabs/axe-core/commit/0bdaa2b))\n- **get-background-color:** scroll element into view horizontally ([#1845](https://github.com/dequelabs/axe-core/issues/1845)) ([50df70a](https://github.com/dequelabs/axe-core/commit/50df70a))\n- **get-selector:** don't throw error for disconnected fragment ([#1802](https://github.com/dequelabs/axe-core/issues/1802)) ([bb6591b](https://github.com/dequelabs/axe-core/commit/bb6591b))\n- **label-content-name-mismatch:** ignore ligature fonts ([#1829](https://github.com/dequelabs/axe-core/issues/1829)) ([683e005](https://github.com/dequelabs/axe-core/commit/683e005))\n- allows all roles on img tag with no alt attribute ([929085a](https://github.com/dequelabs/axe-core/commit/929085a))\n- replace incorrect tag for meta-refresh rule ([#1844](https://github.com/dequelabs/axe-core/issues/1844)) ([754d56b](https://github.com/dequelabs/axe-core/commit/754d56b))\n- update tags to wcag21aa for rule avoid-inline-spacing ([#1757](https://github.com/dequelabs/axe-core/issues/1757)) ([973d48f](https://github.com/dequelabs/axe-core/commit/973d48f))\n- **label-content-name-mismatch:** ignore private space unicode ([#1822](https://github.com/dequelabs/axe-core/issues/1822)) ([b634c34](https://github.com/dequelabs/axe-core/commit/b634c34))\n- **scrollable-region-focus:** ignore scrollable regions without content ([#1788](https://github.com/dequelabs/axe-core/issues/1788)) ([b36754e](https://github.com/dequelabs/axe-core/commit/b36754e))\n- **utils:** Fix error in IE when getting scroll state on page with SVG elements. Closes [#525](https://github.com/dequelabs/axe-core/issues/525) ([#1820](https://github.com/dequelabs/axe-core/issues/1820)) ([9a32f6f](https://github.com/dequelabs/axe-core/commit/9a32f6f))\n\n### Features\n\n- **audit:** add lang query paramenter to help urls ([#1794](https://github.com/dequelabs/axe-core/issues/1794)) ([2d1c8a6](https://github.com/dequelabs/axe-core/commit/2d1c8a6))\n- **configure:** accept ver property as axeVersion fallback ([#1812](https://github.com/dequelabs/axe-core/issues/1812)) ([4ebcde8](https://github.com/dequelabs/axe-core/commit/4ebcde8))\n- **configure:** add axeVersion property that checks compatibility of axe.version ([#1793](https://github.com/dequelabs/axe-core/issues/1793)) ([18fb8c8](https://github.com/dequelabs/axe-core/commit/18fb8c8))\n- **fieldset, group-labelledby:** deprecate fieldset and group-labelledby checks ([#1740](https://github.com/dequelabs/axe-core/issues/1740)) ([ec9b762](https://github.com/dequelabs/axe-core/commit/ec9b762))\n- **i18n:** add spanish translation ([#1758](https://github.com/dequelabs/axe-core/issues/1758)) ([e9f821d](https://github.com/dequelabs/axe-core/commit/e9f821d)), closes [#1757](https://github.com/dequelabs/axe-core/issues/1757)\n- Add axe.utils.assert method ([2f10d24](https://github.com/dequelabs/axe-core/commit/2f10d24))\n- **is-visible:** add support for clip-path techniques ([#1706](https://github.com/dequelabs/axe-core/issues/1706)) ([8ab262a](https://github.com/dequelabs/axe-core/commit/8ab262a))\n- **radiogroup, checkboxgroup:** deprecate radiogroup and checkboxgroup rules ([#1640](https://github.com/dequelabs/axe-core/issues/1640)) ([6d30880](https://github.com/dequelabs/axe-core/commit/6d30880))\n- Add SerialVirtualNode class ([82d01b0](https://github.com/dequelabs/axe-core/commit/82d01b0))\n- pt_BR localization ([#1756](https://github.com/dequelabs/axe-core/issues/1756)) ([330e2ec](https://github.com/dequelabs/axe-core/commit/330e2ec))\n- **rule:** aria-roledescription ([#1745](https://github.com/dequelabs/axe-core/issues/1745)) ([16682fd](https://github.com/dequelabs/axe-core/commit/16682fd))\n- **runVirtualNode:** Allow serialised nodes [experimental](<[512d51b](https://github.com/dequelabs/axe-core/commit/512d51b)>)\n- **video-description:** deprecate video-description rule ([#1737](https://github.com/dequelabs/axe-core/issues/1737)) ([e91c25f](https://github.com/dequelabs/axe-core/commit/e91c25f))\n\n## [3.3.3](https://github.com/dequelabs/axe-core/compare/v3.3.2...v3.3.3) (2020-02-04)\n\n### Bug Fixes\n\n- **color-contrast:** support IE extension context ([#2008](https://github.com/dequelabs/axe-core/issues/2008)) ([cd651a0](https://github.com/dequelabs/axe-core/commit/cd651a0713fa2f4b307cc7fc2be033f8636b40d2))\n- **unicode:** stop parsing escaped unicode strings ([#1997](https://github.com/dequelabs/axe-core/issues/1997)) ([523a31c](https://github.com/dequelabs/axe-core/commit/523a31c19fefd330e0b4f4c45f51d400c6f66164))\n\n### [3.3.2](https://github.com/dequelabs/axe-core/compare/v3.3.1...v3.3.2) (2019-08-12)\n\n### Bug Fixes\n\n- **aria-form-field-name-matches:** don't test combobox elements when they have a child input ([#1742](https://github.com/dequelabs/axe-core/issues/1742)) ([5ac278f](https://github.com/dequelabs/axe-core/commit/5ac278f))\n- **aria-valid-attr-value:** fix incomplete translation message ([#1739](https://github.com/dequelabs/axe-core/issues/1739)) ([6909604](https://github.com/dequelabs/axe-core/commit/6909604))\n- **flatten-tree:** do not call deprecated getDistributedNodes ([#1729](https://github.com/dequelabs/axe-core/issues/1729)) ([48ddf9d](https://github.com/dequelabs/axe-core/commit/48ddf9d))\n- update tags to wcag21aa for rule avoid-inline-spacing ([#1757](https://github.com/dequelabs/axe-core/issues/1757)) ([f534a6b](https://github.com/dequelabs/axe-core/commit/f534a6b))\n\n## [3.3.1](https://github.com/dequelabs/axe-core/compare/v3.3.0...v3.3.1) (2019-07-23)\n\n### Bug Fixes\n\n- **accessible-text-virtual:** sanitize attributes on start node ([#1694](https://github.com/dequelabs/axe-core/issues/1694)) ([c0bb08c](https://github.com/dequelabs/axe-core/commit/c0bb08c))\n- **axe.d.ts:** add optional xpath property to NodeResult ([#1642](https://github.com/dequelabs/axe-core/issues/1642)) ([287ef9c](https://github.com/dequelabs/axe-core/commit/287ef9c))\n- **get-background-color:** No longer calculate color from non-opaque overlapping element ([#1644](https://github.com/dequelabs/axe-core/issues/1644)) ([d45f81e](https://github.com/dequelabs/axe-core/commit/d45f81e))\n- **get-background-color:** No longer calculate color from non-opaque overlapping elm ([f1c2310](https://github.com/dequelabs/axe-core/commit/f1c2310))\n- **get-background-color:** process tbody, thead, and tfoot when getting background color ([#1653](https://github.com/dequelabs/axe-core/issues/1653)) ([e114bfd](https://github.com/dequelabs/axe-core/commit/e114bfd))\n- enable running axe-core in strict CSPs ([#1707](https://github.com/dequelabs/axe-core/issues/1707)) ([cc5bd59](https://github.com/dequelabs/axe-core/commit/cc5bd59))\n- **image-redundant-alt:** check for parent before calculating text ([#1705](https://github.com/dequelabs/axe-core/issues/1705)) ([1adbd02](https://github.com/dequelabs/axe-core/commit/1adbd02))\n- **th-has-data-cells:** empty cells will now pass ([#1659](https://github.com/dequelabs/axe-core/issues/1659)) ([c7489ab](https://github.com/dequelabs/axe-core/commit/c7489ab))\n- **types:** add new properties to RunOptions ([#1697](https://github.com/dequelabs/axe-core/issues/1697)) ([02aa36c](https://github.com/dequelabs/axe-core/commit/02aa36c)), closes [#1696](https://github.com/dequelabs/axe-core/issues/1696)\n- **valid-attr-value:** allow aria-describedby to return needs review ([#1671](https://github.com/dequelabs/axe-core/issues/1671)) ([2390925](https://github.com/dequelabs/axe-core/commit/2390925))\n- **color-contrast:** add px unit to error messages ([#1634](https://github.com/dequelabs/axe-core/issues/1634)) ([1712e46](https://github.com/dequelabs/axe-core/commit/1712e46))\n\n### Tests\n\n- **examples:** do not fail tests if deque.com has violations ([#1686](https://github.com/dequelabs/axe-core/issues/1686)) ([2118360](https://github.com/dequelabs/axe-core/commit/2118360))\n- ensure locale files are valid ([#1673](https://github.com/dequelabs/axe-core/issues/1673)) ([588f9b5](https://github.com/dequelabs/axe-core/commit/588f9b5))\n- require new release rule help docs to be active before creating release ([#1700](https://github.com/dequelabs/axe-core/issues/1700)) ([e9f9c18](https://github.com/dequelabs/axe-core/commit/e9f9c18))\n\n## [3.3.0](https://github.com/dequelabs/axe-core/compare/v3.2.2...v3.3.0) (2019-07-08)\n\n### Bug Fixes\n\n- add SC 131 to rule aria hidden focus ([#1474](https://github.com/dequelabs/axe-core/issues/1474)) ([8da9a0d](https://github.com/dequelabs/axe-core/commit/8da9a0d))\n- Add treegrid as allowed parent to rowgroup ([#1435](https://github.com/dequelabs/axe-core/issues/1435)) ([94e9893](https://github.com/dequelabs/axe-core/commit/94e9893)), closes [#1386](https://github.com/dequelabs/axe-core/issues/1386)\n- arguments for gather function in build template ([#1605](https://github.com/dequelabs/axe-core/issues/1605)) ([753ecf4](https://github.com/dequelabs/axe-core/commit/753ecf4))\n- check if property exists in cache of flattenedTree ([#1536](https://github.com/dequelabs/axe-core/issues/1536)) ([51c2e19](https://github.com/dequelabs/axe-core/commit/51c2e19))\n- **aria-valid-attr-value:** allow aria-owns to pass when element is not in the DOM ([#1526](https://github.com/dequelabs/axe-core/issues/1526)) ([f835ed8](https://github.com/dequelabs/axe-core/commit/f835ed8))\n- deprecate na reporter ([#1625](https://github.com/dequelabs/axe-core/issues/1625)) ([3e807f0](https://github.com/dequelabs/axe-core/commit/3e807f0))\n- Exclude iframe for html-has-lang rule (Issue 1424) ([#1430](https://github.com/dequelabs/axe-core/issues/1430)) ([98b5ed2](https://github.com/dequelabs/axe-core/commit/98b5ed2))\n- Include body as part of background color checks when element does not intersect ([#1520](https://github.com/dequelabs/axe-core/issues/1520)) ([55820cf](https://github.com/dequelabs/axe-core/commit/55820cf))\n- make nullable impact properties have nullable types to match API docs ([#1477](https://github.com/dequelabs/axe-core/issues/1477)) ([efaed91](https://github.com/dequelabs/axe-core/commit/efaed91))\n- Only load Promise polyfill if window.Promise is missing [[#1468](https://github.com/dequelabs/axe-core/issues/1468)](<[#1470](https://github.com/dequelabs/axe-core/issues/1470)>) ([1d70306](https://github.com/dequelabs/axe-core/commit/1d70306))\n- Prevent error when using `<select name=\"attributes\">` [[#1397](https://github.com/dequelabs/axe-core/issues/1397)](<[#1432](https://github.com/dequelabs/axe-core/issues/1432)>) ([b477e0d](https://github.com/dequelabs/axe-core/commit/b477e0d))\n- remove unnecessary comments ([6ed71c9](https://github.com/dequelabs/axe-core/commit/6ed71c9))\n- role=radio should not require aria-checked ([#1448](https://github.com/dequelabs/axe-core/issues/1448)) ([0643cbd](https://github.com/dequelabs/axe-core/commit/0643cbd))\n- Scroll state had top and left properties flipped ([#1469](https://github.com/dequelabs/axe-core/issues/1469)) ([2ba83d3](https://github.com/dequelabs/axe-core/commit/2ba83d3))\n- **skip-link,region:** Allow multiple skiplinks at page top ([#1496](https://github.com/dequelabs/axe-core/issues/1496)) ([642c8f1](https://github.com/dequelabs/axe-core/commit/642c8f1))\n- use execa instead of child_process.spawn ([461ce83](https://github.com/dequelabs/axe-core/commit/461ce83))\n- **aria-allowed-attr:** allow figure role ([#1558](https://github.com/dequelabs/axe-core/issues/1558)) ([a4b5240](https://github.com/dequelabs/axe-core/commit/a4b5240))\n- **aria-required-attr:** allow aria-valuenow to pass on elements with value ([#1579](https://github.com/dequelabs/axe-core/issues/1579)) ([3893e04](https://github.com/dequelabs/axe-core/commit/3893e04))\n- **aria-required-attr:** don't require aria-valuemin/max ([#1529](https://github.com/dequelabs/axe-core/issues/1529)) ([80ae444](https://github.com/dequelabs/axe-core/commit/80ae444))\n- **aria-valid-attr-value:** allow aria-controls to pass when element is not in the DOM ([a7842e5](https://github.com/dequelabs/axe-core/commit/a7842e5))\n- **flatten-tree:** do not call deprecated getDistributedNodes ([#1577](https://github.com/dequelabs/axe-core/issues/1577)) ([93d59f4](https://github.com/dequelabs/axe-core/commit/93d59f4))\n- **image-redundant-alt:** prevent redundant issues of image tree ([#1616](https://github.com/dequelabs/axe-core/issues/1616)) ([af81897](https://github.com/dequelabs/axe-core/commit/af81897))\n- **isSkipLink:** cache first page link ([#1525](https://github.com/dequelabs/axe-core/issues/1525)) ([6a1bcba](https://github.com/dequelabs/axe-core/commit/6a1bcba))\n- **matches-selector:** don't call matches function if none exist on the element ([#1613](https://github.com/dequelabs/axe-core/issues/1613)) ([7581592](https://github.com/dequelabs/axe-core/commit/7581592))\n- **multiple-label:** considers explicit labels in the same shadow tree ([#1584](https://github.com/dequelabs/axe-core/issues/1584)) ([b9a324a](https://github.com/dequelabs/axe-core/commit/b9a324a))\n- **multiple-label:** no longer raises issue when aria-labelledby overrides how AT views multiple labels ([#1538](https://github.com/dequelabs/axe-core/issues/1538)) ([fbae36b](https://github.com/dequelabs/axe-core/commit/fbae36b))\n- **prettier:** ignore generated api doc files ([#1522](https://github.com/dequelabs/axe-core/issues/1522)) ([c118da0](https://github.com/dequelabs/axe-core/commit/c118da0))\n- **raw-reporter:** do not output `DqElement`s ([#1513](https://github.com/dequelabs/axe-core/issues/1513)) ([3babcb6](https://github.com/dequelabs/axe-core/commit/3babcb6))\n- **scrollable-region-focusable:** exclude overflow:hidden as not scrollable ([#1599](https://github.com/dequelabs/axe-core/issues/1599)) ([940de07](https://github.com/dequelabs/axe-core/commit/940de07))\n- **utils:** make cache global instead of only setup in axe.run ([#1535](https://github.com/dequelabs/axe-core/issues/1535)) ([91a04c5](https://github.com/dequelabs/axe-core/commit/91a04c5))\n- **virtual-node:** fix hasClass to work with svg elements ([#1603](https://github.com/dequelabs/axe-core/issues/1603)) ([9d83662](https://github.com/dequelabs/axe-core/commit/9d83662))\n\n### Features\n\n- **autocomplete-matches:** use virtualNode only lookups ([#1604](https://github.com/dequelabs/axe-core/issues/1604)) ([b32d4fe](https://github.com/dequelabs/axe-core/commit/b32d4fe))\n- **autocomplete-valid:** allow autocomplete-valid to be run entirely off of a virtual node ([#1591](https://github.com/dequelabs/axe-core/issues/1591)) ([b3e0873](https://github.com/dequelabs/axe-core/commit/b3e0873))\n- **qsa, flatten-tree:** abstract Node and Element apis in virtual tree ([#1562](https://github.com/dequelabs/axe-core/issues/1562)) ([2f2e590](https://github.com/dequelabs/axe-core/commit/2f2e590))\n- **reporter:** adds the rawEnv reporter which wraps raw and env data ([#1556](https://github.com/dequelabs/axe-core/issues/1556)) ([ed15ed3](https://github.com/dequelabs/axe-core/commit/ed15ed3))\n- **role-img-alt:** Split rule for role=img with no accessible name ([#1586](https://github.com/dequelabs/axe-core/issues/1586)) ([2416ed3](https://github.com/dequelabs/axe-core/commit/2416ed3))\n- **rule:** add additional elements to check for incomplete with required children ([#1547](https://github.com/dequelabs/axe-core/issues/1547)) ([3726901](https://github.com/dequelabs/axe-core/commit/3726901))\n- **rule:** add more perf timing metrics to rules ([#1472](https://github.com/dequelabs/axe-core/issues/1472)) ([98646e5](https://github.com/dequelabs/axe-core/commit/98646e5))\n- **rule,check:** add new apis to run a rule synchronously ([#1467](https://github.com/dequelabs/axe-core/issues/1467)) ([84094a1](https://github.com/dequelabs/axe-core/commit/84094a1))\n- add AbstractVirtualNode for linting ([#1627](https://github.com/dequelabs/axe-core/issues/1627)) ([a072ed2](https://github.com/dequelabs/axe-core/commit/a072ed2))\n- **rule:** Adding landmark-is-unique rule ([#1394](https://github.com/dequelabs/axe-core/issues/1394)) ([0088e94](https://github.com/dequelabs/axe-core/commit/0088e94))\n- **rule:** Inline text spacing must be adjustable with custom stylesheets ([#1446](https://github.com/dequelabs/axe-core/issues/1446)) ([430b07f](https://github.com/dequelabs/axe-core/commit/430b07f))\n- Improve perf of axe.run [WWD-1821](<[#1503](https://github.com/dequelabs/axe-core/issues/1503)>) ([a84431a](https://github.com/dequelabs/axe-core/commit/a84431a))\n- **rule:** New aria-input-field-label rule ([#1610](https://github.com/dequelabs/axe-core/issues/1610)) ([73d5273](https://github.com/dequelabs/axe-core/commit/73d5273))\n- **rule:** New aria-toggle-field-label rule ([#1450](https://github.com/dequelabs/axe-core/issues/1450)) ([69a9c3b](https://github.com/dequelabs/axe-core/commit/69a9c3b))\n- **rule:** Scrollable region focusable ([#1396](https://github.com/dequelabs/axe-core/issues/1396)) ([861371a](https://github.com/dequelabs/axe-core/commit/861371a))\n- **rules:** split button name rule into button only and button input rules ([#1615](https://github.com/dequelabs/axe-core/issues/1615)) ([ce20fbf](https://github.com/dequelabs/axe-core/commit/ce20fbf))\n- **run-virtual-rule:** new api to run rules using only virtual nodes ([#1594](https://github.com/dequelabs/axe-core/issues/1594)) ([4e12217](https://github.com/dequelabs/axe-core/commit/4e12217))\n- **utils:** add support for complex CSS selectors ([#1494](https://github.com/dequelabs/axe-core/issues/1494)) ([a9f9ee5](https://github.com/dequelabs/axe-core/commit/a9f9ee5)), closes [#1493](https://github.com/dequelabs/axe-core/issues/1493)\n- **utils:** Update CSSOM for nested [@import](https://github.com/import) computation ([#1339](https://github.com/dequelabs/axe-core/issues/1339)) ([a4e177b](https://github.com/dequelabs/axe-core/commit/a4e177b))\n\n### Tests\n\n- createHTMLDocument needs mandatory title when invoked in IE ([#1442](https://github.com/dequelabs/axe-core/issues/1442)) ([8542773](https://github.com/dequelabs/axe-core/commit/8542773))\n- disable es6 syntax usage within tests directory ([#1569](https://github.com/dequelabs/axe-core/issues/1569)) ([9b13508](https://github.com/dequelabs/axe-core/commit/9b13508))\n- fix flakey test ([#1573](https://github.com/dequelabs/axe-core/issues/1573)) ([fb38ce7](https://github.com/dequelabs/axe-core/commit/fb38ce7))\n- Fix invalid test html ([#1502](https://github.com/dequelabs/axe-core/issues/1502)) ([8d85082](https://github.com/dequelabs/axe-core/commit/8d85082))\n- fix test for Safari ([#1557](https://github.com/dequelabs/axe-core/issues/1557)) ([1bac69e](https://github.com/dequelabs/axe-core/commit/1bac69e))\n- fix tests failing in IE11 ([#1570](https://github.com/dequelabs/axe-core/issues/1570)) ([2102eca](https://github.com/dequelabs/axe-core/commit/2102eca))\n- only run IE11 tests in appveyor ([#1571](https://github.com/dequelabs/axe-core/issues/1571)) ([35261ef](https://github.com/dequelabs/axe-core/commit/35261ef))\n- watch integration html and json files ([#1598](https://github.com/dequelabs/axe-core/issues/1598)) ([3de0b05](https://github.com/dequelabs/axe-core/commit/3de0b05))\n\n## [3.2.3](https://github.com/dequelabs/axe-core/compare/v3.2.2...v3.2.3) (2020-02-04)\n\n### Bug Fixes\n\n- **color-contrast:** support IE extension context ([#2008](https://github.com/dequelabs/axe-core/issues/2008)) ([cd651a0](https://github.com/dequelabs/axe-core/commit/cd651a0713fa2f4b307cc7fc2be033f8636b40d2))\n- **unicode:** stop parsing escaped unicode strings ([#1997](https://github.com/dequelabs/axe-core/issues/1997)) ([523a31c](https://github.com/dequelabs/axe-core/commit/523a31c19fefd330e0b4f4c45f51d400c6f66164))\n\n## [3.2.2](https://github.com/dequelabs/axe-core/compare/v3.2.0...v3.2.2) (2019-03-07)\n\n### Bug Fixes\n\n- Avoid \"screen is not defined\" error [[#1404](https://github.com/dequelabs/axe-core/issues/1404)](<[#1415](https://github.com/dequelabs/axe-core/issues/1415)>) ([c9653a5](https://github.com/dequelabs/axe-core/commit/c9653a5))\n\n## [3.2.1](https://github.com/dequelabs/axe-core/compare/v3.2.0...v3.2.1) (2019-03-06)\n\n### Bug Fixes\n\n- Avoid require conflict with Cypress [[#1405](https://github.com/dequelabs/axe-core/issues/1405)](<[#1406](https://github.com/dequelabs/axe-core/issues/1406)>) ([30aa570](https://github.com/dequelabs/axe-core/commit/30aa570))\n\n# [3.2.0](https://github.com/dequelabs/axe-core/compare/v3.0.3...v3.2.0) (2019-03-04)\n\n### Bug Fixes\n\n- **aria-allowed-role:** Allow iframe role=none ([a4fa44d](https://github.com/dequelabs/axe-core/commit/a4fa44d))\n- **color-contrast:** Prevent crash on large inline elments [#1306](https://github.com/dequelabs/axe-core/issues/1306) ([#1341](https://github.com/dequelabs/axe-core/issues/1341)) ([e1bcafc](https://github.com/dequelabs/axe-core/commit/e1bcafc))\n- **commons:** Allow any node in aria.getRole ([#1258](https://github.com/dequelabs/axe-core/issues/1258)) ([26fa49a](https://github.com/dequelabs/axe-core/commit/26fa49a)), closes [#1163](https://github.com/dequelabs/axe-core/issues/1163)\n- **i18n:** Fix and add Japanese translation ([#1368](https://github.com/dequelabs/axe-core/issues/1368)) ([74fa0a4](https://github.com/dequelabs/axe-core/commit/74fa0a4), [#1291](https://github.com/dequelabs/axe-core/issues/1291)) ([f450176](https://github.com/dequelabs/axe-core/commit/f450176), [#1332](https://github.com/dequelabs/axe-core/issues/1332)) ([0a03c8f](https://github.com/dequelabs/axe-core/commit/0a03c8f), [#1107](https://github.com/dequelabs/axe-core/issues/1107)) ([8138e55](https://github.com/dequelabs/axe-core/commit/8138e55))\n- **messages:** Change messages from \"page\" to \"document\" where appropriate ([#1156](https://github.com/dequelabs/axe-core/issues/1156)) ([49dff2b](https://github.com/dequelabs/axe-core/commit/49dff2b)), closes [#983](https://github.com/dequelabs/axe-core/issues/983) [#983](https://github.com/dequelabs/axe-core/issues/983)\n- **rule:** Frame-title applies to wcag242 ([#1312](https://github.com/dequelabs/axe-core/issues/1312)) ([9225ae0](https://github.com/dequelabs/axe-core/commit/9225ae0))\n- **rule:** Prevent th-has-data-cells from crashing on empty rows ([#1285](https://github.com/dequelabs/axe-core/issues/1285)) ([88017be](https://github.com/dequelabs/axe-core/commit/88017be))\n- **typedefs:** Do not require brand and application ([#1264](https://github.com/dequelabs/axe-core/issues/1264)) ([59465dc](https://github.com/dequelabs/axe-core/commit/59465dc)), closes [/github.com/dequelabs/axe-webdriverjs/blob/v2.0.1/lib/axe-injector.js#L28](https://github.com//github.com/dequelabs/axe-webdriverjs/blob/v2.0.1/lib/axe-injector.js/issues/L28)\n- Add Banner comment in generated axe files ([#1112](https://github.com/dequelabs/axe-core/issues/1112)) ([e4788bf](https://github.com/dequelabs/axe-core/commit/e4788bf))\n- Allow div groups for dlitem rule ([#1284](https://github.com/dequelabs/axe-core/issues/1284)) ([d76cd36](https://github.com/dequelabs/axe-core/commit/d76cd36))\n- Allow role presentation and none on object-alt rule ([#1224](https://github.com/dequelabs/axe-core/issues/1224)) ([d475a17](https://github.com/dequelabs/axe-core/commit/d475a17))\n- Avoid IE problems by using nodeName instead of tagName ([#1219](https://github.com/dequelabs/axe-core/issues/1219)) ([cf86ff5](https://github.com/dequelabs/axe-core/commit/cf86ff5))\n- Better unsupported attribute support for aria-roledescription ([#1382](https://github.com/dequelabs/axe-core/issues/1382)) ([93f721e](https://github.com/dequelabs/axe-core/commit/93f721e)), closes [#1216](https://github.com/dequelabs/axe-core/issues/1216)\n- Consider element's accessible names when labels are hidden ([#1187](https://github.com/dequelabs/axe-core/issues/1187)) ([b91b624](https://github.com/dequelabs/axe-core/commit/b91b624)), closes [#1176](https://github.com/dequelabs/axe-core/issues/1176)\n- Correct autocomplete appropriate to handle state terms ([#1121](https://github.com/dequelabs/axe-core/issues/1121)) ([35a4d11](https://github.com/dequelabs/axe-core/commit/35a4d11))\n- Correct autocomplete street address ([#1217](https://github.com/dequelabs/axe-core/issues/1217)) ([27fce9d](https://github.com/dequelabs/axe-core/commit/27fce9d))\n- Correct autocomplete-appropriate node type resolution ([#1318](https://github.com/dequelabs/axe-core/issues/1318)) ([2fc3eeb](https://github.com/dequelabs/axe-core/commit/2fc3eeb))\n- CSSOM generation for shadowRoot in Safari ([#1113](https://github.com/dequelabs/axe-core/issues/1113)) ([a51ae03](https://github.com/dequelabs/axe-core/commit/a51ae03))\n- Don't flag invalid roles in unsupportedrole ([#1328](https://github.com/dequelabs/axe-core/issues/1328)) ([2dfcbaa](https://github.com/dequelabs/axe-core/commit/2dfcbaa))\n- Escape href attribute when generating a CSS selector [[#1137](https://github.com/dequelabs/axe-core/issues/1137)](<[#1366](https://github.com/dequelabs/axe-core/issues/1366)>) ([0c2f42d](https://github.com/dequelabs/axe-core/commit/0c2f42d))\n- Flag hidden elms with disallowed role(s) for review ([#1225](https://github.com/dequelabs/axe-core/issues/1225)) ([bdff141](https://github.com/dequelabs/axe-core/commit/bdff141))\n- Handle noscript and template in dom.isVisible ([#1257](https://github.com/dequelabs/axe-core/issues/1257)) ([e67fc65](https://github.com/dequelabs/axe-core/commit/e67fc65))\n- html-lang-valid should consider xml:lang ([#1152](https://github.com/dequelabs/axe-core/issues/1152)) ([4279c72](https://github.com/dequelabs/axe-core/commit/4279c72))\n- Ignore invalid and allow redundant role in aria-allowed-role ([#1118](https://github.com/dequelabs/axe-core/issues/1118)) ([a0f9b31](https://github.com/dequelabs/axe-core/commit/a0f9b31))\n- Introduce dom.isHiddenWithCSS for use in dom.isFocusable ([#1211](https://github.com/dequelabs/axe-core/issues/1211)) ([2cff417](https://github.com/dequelabs/axe-core/commit/2cff417))\n- Log instead of error on unknown tag ([#1290](https://github.com/dequelabs/axe-core/issues/1290)) ([f82d773](https://github.com/dequelabs/axe-core/commit/f82d773))\n- Prevent TypeErrors in color-contrast checks ([#1320](https://github.com/dequelabs/axe-core/issues/1320)) ([a34165c](https://github.com/dequelabs/axe-core/commit/a34165c)), closes [#1306](https://github.com/dequelabs/axe-core/issues/1306) [#1259](https://github.com/dequelabs/axe-core/issues/1259)\n- Respect preload set to false ([#1298](https://github.com/dequelabs/axe-core/issues/1298)) ([e847d38](https://github.com/dequelabs/axe-core/commit/e847d38))\n- Set preload:true as default ([#1281](https://github.com/dequelabs/axe-core/issues/1281)) ([c9731c8](https://github.com/dequelabs/axe-core/commit/c9731c8))\n- Support skiplinks starting with \"/#\" ([#1286](https://github.com/dequelabs/axe-core/issues/1286)) ([f93c0c9](https://github.com/dequelabs/axe-core/commit/f93c0c9))\n- Update SC to wcag134 for CSS Orientation Rule ([d3f90df](https://github.com/dequelabs/axe-core/commit/d3f90df))\n\n### Features\n\n- **commons:** Add matches methods ([#1270](https://github.com/dequelabs/axe-core/issues/1270)) ([986c97a](https://github.com/dequelabs/axe-core/commit/986c97a)), closes [#1163](https://github.com/dequelabs/axe-core/issues/1163)\n- **image-alt:** require alt text or empty strings ([#1260](https://github.com/dequelabs/axe-core/issues/1260)) ([e24cea9](https://github.com/dequelabs/axe-core/commit/e24cea9))\n- **new-rule:** New aria-hidden-focus rule ([#1166](https://github.com/dequelabs/axe-core/issues/1166)) ([4489965](https://github.com/dequelabs/axe-core/commit/4489965))\n- **new-rule:** Separate form-field-multiple-label from label rule ([#1226](https://github.com/dequelabs/axe-core/issues/1226)) ([0e0063c](https://github.com/dequelabs/axe-core/commit/0e0063c))\n- **new-rule:** Label and Name from Content mismatch WCAG21 (Issue [#1149](https://github.com/dequelabs/axe-core/issues/1149)) ([#1335](https://github.com/dequelabs/axe-core/issues/1335)) ([a4255da](https://github.com/dequelabs/axe-core/commit/a4255da))\n- **rule:** Require unique aria labels in checkboxgroup & radiogroup ([#1316](https://github.com/dequelabs/axe-core/issues/1316)) ([c9b310d](https://github.com/dequelabs/axe-core/commit/c9b310d))\n- Add a reference to the `node` a rule failed on ([#1321](https://github.com/dequelabs/axe-core/issues/1321)) ([68741de](https://github.com/dequelabs/axe-core/commit/68741de)), closes [#1317](https://github.com/dequelabs/axe-core/issues/1317)\n- Add allowEmpty option for aria-valid-attr-value ([#1154](https://github.com/dequelabs/axe-core/issues/1154)) ([89d18d0](https://github.com/dequelabs/axe-core/commit/89d18d0)), closes [#994](https://github.com/dequelabs/axe-core/issues/994)\n- Add environment details to results ([#1353](https://github.com/dequelabs/axe-core/issues/1353)) ([e795f7d](https://github.com/dequelabs/axe-core/commit/e795f7d))\n- ARIA supported checks ([#1254](https://github.com/dequelabs/axe-core/issues/1254)) ([51a18a8](https://github.com/dequelabs/axe-core/commit/51a18a8)), closes [#918](https://github.com/dequelabs/axe-core/issues/918)\n- New rule landmark-complementary-is-top-level ([#1239](https://github.com/dequelabs/axe-core/issues/1239)) ([328ca2c](https://github.com/dequelabs/axe-core/commit/328ca2c))\n- Pass context argument to rule matches ([#1370](https://github.com/dequelabs/axe-core/issues/1370)) ([b374669](https://github.com/dequelabs/axe-core/commit/b374669))\n- Rebuild the accessible text algorithm ([#1163](https://github.com/dequelabs/axe-core/issues/1163)) ([5f420e5](https://github.com/dequelabs/axe-core/commit/5f420e5))\n- Tag review-items rule as 'best-practice' ([#1344](https://github.com/dequelabs/axe-core/issues/1344)) ([05f37de](https://github.com/dequelabs/axe-core/commit/05f37de))\n\n### Performance Improvements\n\n- Defer rules rather than checks ([#1308](https://github.com/dequelabs/axe-core/issues/1308)) ([80c1c74](https://github.com/dequelabs/axe-core/commit/80c1c74)), closes [#1172](https://github.com/dequelabs/axe-core/issues/1172)\n- Speed up getNodeFromTree ([#1302](https://github.com/dequelabs/axe-core/issues/1302)) ([5f834ed](https://github.com/dequelabs/axe-core/commit/5f834ed))\n- Update performanceTimer end mark for rules ([#1303](https://github.com/dequelabs/axe-core/issues/1303)) ([a28674e](https://github.com/dequelabs/axe-core/commit/a28674e)), closes [#701](https://github.com/dequelabs/axe-core/issues/701) [#1172](https://github.com/dequelabs/axe-core/issues/1172)\n\n### Deprecate\n\n- Deprecate axe.commons.utils namespace ([#1330](https://github.com/dequelabs/axe-core/issues/1330)) ([df93272](https://github.com/dequelabs/axe-core/commit/df93272))\n\n<a name=\"3.1.2\"></a>\n\n## [3.1.2](https://github.com/dequelabs/axe-core/compare/v3.0.3...v3.1.2) (2018-09-07)\n\n### Bug Fixes\n\n- **i18n:** Update Japanese locale ([#1107](https://github.com/dequelabs/axe-core/issues/1107)) ([8138e55](https://github.com/dequelabs/axe-core/commit/8138e55))\n- autocomplete appropriate to handle state terms ([#1121](https://github.com/dequelabs/axe-core/issues/1121)) ([35a4d11](https://github.com/dequelabs/axe-core/commit/35a4d11))\n- banner comment in generated axe files ([#1112](https://github.com/dequelabs/axe-core/issues/1112)) ([e4788bf](https://github.com/dequelabs/axe-core/commit/e4788bf))\n- ignore invalid and allow redundant role in aria-allowed-role ([#1118](https://github.com/dequelabs/axe-core/issues/1118)) ([a0f9b31](https://github.com/dequelabs/axe-core/commit/a0f9b31))\n\n<a name=\"3.1.1\"></a>\n\n## [3.1.1](https://github.com/dequelabs/axe-core/compare/v3.0.3...v3.1.1) (2018-08-28)\n\n### Bug Fixes\n\n- Fix broken 3.1.0 release script ([c3b2a52](https://github.com/dequelabs/axe-core/commit/c3b2a52))\n\n<a name=\"3.1.0\"></a>\n\n# [3.1.0](https://github.com/dequelabs/axe-core/compare/v3.0.3...v3.1.0) (2018-08-28)\n\n### Bug Fixes\n\n- **rule fix:** Allow fallback labels when input has id ([#951](https://github.com/dequelabs/axe-core/issues/951)) ([54fa569](https://github.com/dequelabs/axe-core/commit/54fa569))\n- **rule fix:** Updating aria 1.1 allowed attributes ([#964](https://github.com/dequelabs/axe-core/issues/964)) ([c3249c1](https://github.com/dequelabs/axe-core/commit/c3249c1))\n- **rule fix:** Allow aria-errormessage with fallback ([17608b6](https://github.com/dequelabs/axe-core/commit/17608b6))\n- **rule fix:** Ignore abstracts in determining element roles ([1af6088](https://github.com/dequelabs/axe-core/commit/1af6088))\n- **rule fix:** Add combobox and radio required properties ([e80af7a](https://github.com/dequelabs/axe-core/commit/e80af7a))\n- **rule fix:** Allow all ARIA idref(s) to be empty ([1498696](https://github.com/dequelabs/axe-core/commit/1498696))\n- **rule fix:** Allow divs as groups in dl ([#1076](https://github.com/dequelabs/axe-core/issues/1076)) ([f4f6df6](https://github.com/dequelabs/axe-core/commit/f4f6df6)), closes [#262](https://github.com/dequelabs/axe-core/issues/262)\n- **rule fix:** Allow live-region and dialog in `region` rule ([#1073](https://github.com/dequelabs/axe-core/issues/1073)) ([fb6438b](https://github.com/dequelabs/axe-core/commit/fb6438b))\n- **rule fix:** Allow only-dlitem / only-listitem to have any hidden content ([#1098](https://github.com/dequelabs/axe-core/issues/1098)) ([6034aae](https://github.com/dequelabs/axe-core/commit/6034aae)), closes [#1021](https://github.com/dequelabs/axe-core/issues/1021)\n- **rule fix:** Do not flag font icons in color-contrast rule ([#1095](https://github.com/dequelabs/axe-core/issues/1095)) ([b6ac084](https://github.com/dequelabs/axe-core/commit/b6ac084)), closes [#1068](https://github.com/dequelabs/axe-core/issues/1068)\n- **rule fix:** Do not require media captions / descriptions ([#1075](https://github.com/dequelabs/axe-core/issues/1075)) ([289f623](https://github.com/dequelabs/axe-core/commit/289f623)), closes [#816](https://github.com/dequelabs/axe-core/issues/816)\n- **rule fix:** improve messaging for hidden labels ([ae07b8e](https://github.com/dequelabs/axe-core/commit/ae07b8e))\n- **rule fix:** Ignore abstracts in determining element roles ([e3b1e1d](https://github.com/dequelabs/axe-core/commit/e3b1e1d))\n- **rule fix:** Correctly handle role attribute on lists and listitems. ([#949](https://github.com/dequelabs/axe-core/issues/949)) ([3a8729b](https://github.com/dequelabs/axe-core/commit/3a8729b))\n- **core:** allow returning a Promise in jsdom from axe.run ([#943](https://github.com/dequelabs/axe-core/issues/943)) ([3858a1f](https://github.com/dequelabs/axe-core/commit/3858a1f))\n- **typescript:** Correct `Check.evaluate` and `Check.after` type definitions ([#976](https://github.com/dequelabs/axe-core/issues/976)) ([db3ed40](https://github.com/dequelabs/axe-core/commit/db3ed40)), closes [#974](https://github.com/dequelabs/axe-core/issues/974)\n- **typescript:** Typescript interface updates ([#973](https://github.com/dequelabs/axe-core/issues/973)) ([f8c9905](https://github.com/dequelabs/axe-core/commit/f8c9905)), closes [#972](https://github.com/dequelabs/axe-core/issues/972)\n- **messages:** Clearer fail message for aria-labelledby ([956281b](https://github.com/dequelabs/axe-core/commit/956281b))\n- **messages:** Consistent landmark rule/check descriptions ([#1003](https://github.com/dequelabs/axe-core/issues/1003)) ([d792970](https://github.com/dequelabs/axe-core/commit/d792970))\n- **messages:** Show attribute in message ([#1061](https://github.com/dequelabs/axe-core/issues/1061)) ([9ff5d54](https://github.com/dequelabs/axe-core/commit/9ff5d54))\n- Check data is an array of IDs ([d64bc5f](https://github.com/dequelabs/axe-core/commit/d64bc5f))\n- Don't crash with slot elements without shadowDOM ([#977](https://github.com/dequelabs/axe-core/issues/977)) ([cc044af](https://github.com/dequelabs/axe-core/commit/cc044af))\n- Generate jsdoc(umentation) ([9f9b15b](https://github.com/dequelabs/axe-core/commit/9f9b15b))\n- Stop `indexOf` override from crashing Axe ([#968](https://github.com/dequelabs/axe-core/issues/968)) ([e3329ce](https://github.com/dequelabs/axe-core/commit/e3329ce))\n- Use `node.matches*` in place of `prototype.matches*` to prevent IE crash ([#956](https://github.com/dequelabs/axe-core/issues/956)) ([ebdb590](https://github.com/dequelabs/axe-core/commit/ebdb590))\n\n### Features\n\n- **new rule:** Added new html-xml-lang-mismatch rule ([#999](https://github.com/dequelabs/axe-core/issues/999)) ([7452a51](https://github.com/dequelabs/axe-core/commit/7452a51))\n- **new rule:** aria-allowed-role ([#945](https://github.com/dequelabs/axe-core/issues/945)) ([c270a46](https://github.com/dequelabs/axe-core/commit/c270a46))\n- **new rule:** css-orientation-lock (wcag21) ([#1081](https://github.com/dequelabs/axe-core/issues/1081)) ([4ae4ea0](https://github.com/dequelabs/axe-core/commit/4ae4ea0))\n- **new rule:** Add WCAG 2.1 autocomplete-valid rule ([e6189ce](https://github.com/dequelabs/axe-core/commit/e6189ce))\n- **rule fix:** Flag unsupported roles ([#1064](https://github.com/dequelabs/axe-core/issues/1064)) ([5515ee6](https://github.com/dequelabs/axe-core/commit/5515ee6))\n- **rule fix:** Remove non-existing \"text\" role ([#1069](https://github.com/dequelabs/axe-core/issues/1069)) ([67ec1f5](https://github.com/dequelabs/axe-core/commit/67ec1f5))\n- **rule fix:** Break up duplicate-id rule for ARIA+labels and active elements ([2ecfea7](https://github.com/dequelabs/axe-core/commit/2ecfea7))\n- **rule fix:** Tag aria rules as WCAG 2, SC4.1.2 issues ([e7816c0](https://github.com/dequelabs/axe-core/commit/e7816c0))\n- **core:** Allow rules to access CSSOM assets ([#958](https://github.com/dequelabs/axe-core/issues/958)) ([5d6c1fa](https://github.com/dequelabs/axe-core/commit/5d6c1fa))\n- **core:** Add `doT` template engine ([#1024](https://github.com/dequelabs/axe-core/issues/1024)) ([f6f08d4](https://github.com/dequelabs/axe-core/commit/f6f08d4))\n- **commons:** Add aria.getRole method ([1d2a0e4](https://github.com/dequelabs/axe-core/commit/1d2a0e4))\n- **commons:** Add text.isValidAutocomplete method ([8d44fe4](https://github.com/dequelabs/axe-core/commit/8d44fe4))\n- **i18n:** Add runtime localization support ([#1036](https://github.com/dequelabs/axe-core/issues/1036)) ([7d4b70f](https://github.com/dequelabs/axe-core/commit/7d4b70f))\n- **i18n:** Update FR (french) translation file for 3.1 release ([#1089](https://github.com/dequelabs/axe-core/issues/1089)) ([4a5cad0](https://github.com/dequelabs/axe-core/commit/4a5cad0))\n- **i18n:** Update ja (Japanese) locale for 3.1 release ([#1101](https://github.com/dequelabs/axe-core/issues/1101)) ([ef6cd86](https://github.com/dequelabs/axe-core/commit/ef6cd86))\n- **deprecate:** Deprecate audio-caption rule ([#1071](https://github.com/dequelabs/axe-core/issues/1071)) ([3b05fee](https://github.com/dequelabs/axe-core/commit/3b05fee))\n\n<a name=\"3.0.3\"></a>\n\n## [3.0.3](https://github.com/dequelabs/axe-core/compare/v3.0.2...v3.0.3) (2018-06-04)\n\n### Bug Fixes\n\n- Resolve markdown lint errors. ([efdad94](https://github.com/dequelabs/axe-core/commit/efdad94)) ([aa90155](https://github.com/dequelabs/axe-core/commit/aa90155))\n- Don't require all ARIA IDREFS to exist ([#921](https://github.com/dequelabs/axe-core/issues/921)) ([130efed](https://github.com/dequelabs/axe-core/commit/130efed))\n- generate unsupported aria roles and attributes. ([7315662](https://github.com/dequelabs/axe-core/commit/7315662))\n- Make empty role=lisbox elements as incomplete ([#927](https://github.com/dequelabs/axe-core/issues/927)) ([87e979f](https://github.com/dequelabs/axe-core/commit/87e979f))\n- Prevent axe-core crashing on “-“ as a class name ([#884](https://github.com/dequelabs/axe-core/issues/884)) ([9c4d84e](https://github.com/dequelabs/axe-core/commit/9c4d84e))\n- Right trim URLs before outputting them in getSelector ([#924](https://github.com/dequelabs/axe-core/issues/924)) ([4775a23](https://github.com/dequelabs/axe-core/commit/4775a23)), closes [#788](https://github.com/dequelabs/axe-core/issues/788)\n- td-has-heading to ignore td with its role changed ([#928](https://github.com/dequelabs/axe-core/issues/928)) ([d68af4c](https://github.com/dequelabs/axe-core/commit/d68af4c))\n- Update tags for frame-title rule ([#935](https://github.com/dequelabs/axe-core/issues/935)) ([6436bbf](https://github.com/dequelabs/axe-core/commit/6436bbf))\n- **core:** Define 'axe-core' as an AMD module ([#859](https://github.com/dequelabs/axe-core/issues/859)) ([7b46f63](https://github.com/dequelabs/axe-core/commit/7b46f63)), closes [#849](https://github.com/dequelabs/axe-core/issues/849) [#856](https://github.com/dequelabs/axe-core/issues/856) [#861](https://github.com/dequelabs/axe-core/issues/861) [#847](https://github.com/dequelabs/axe-core/issues/847) [#844](https://github.com/dequelabs/axe-core/issues/844) [#871](https://github.com/dequelabs/axe-core/issues/871) [#849](https://github.com/dequelabs/axe-core/issues/849) [#849](https://github.com/dequelabs/axe-core/issues/849)\n- Update tags to accesskey & link-name rules ([#922](https://github.com/dequelabs/axe-core/issues/922)) ([a8e801c](https://github.com/dequelabs/axe-core/commit/a8e801c))\n- **rule:** Layout-table does not match presentation / none roles ([#828](https://github.com/dequelabs/axe-core/issues/828)) ([5651ecc](https://github.com/dequelabs/axe-core/commit/5651ecc))\n- **rule:** restore labelledBy on unlabeled select ([b7bdf66](https://github.com/dequelabs/axe-core/commit/b7bdf66))\n- **rules:** Allow focusable role=application elements ([b5de450](https://github.com/dequelabs/axe-core/commit/b5de450))\n- **typescript:** Update ElementContext type ([#822](https://github.com/dequelabs/axe-core/issues/822)) ([eb09248](https://github.com/dequelabs/axe-core/commit/eb09248))\n\n### Features\n\n- generate ARIA supported documentation for roles and attributes. ([6f095dd](https://github.com/dequelabs/axe-core/commit/6f095dd))\n- generated impacts as a part of rule descriptions ([#898](https://github.com/dequelabs/axe-core/issues/898)) ([6265608](https://github.com/dequelabs/axe-core/commit/6265608))\n\n<a name=\"3.0.2\"></a>\n\n## [3.0.2](https://github.com/dequelabs/axe-core/compare/v3.0.0-beta.2...v3.0.2) (2018-04-24)\n\n### Bug Fixes\n\n- **rule:** Allow empty aria-labelledby values ([#829](https://github.com/dequelabs/axe-core/issues/829)) ([d280c5f](https://github.com/dequelabs/axe-core/commit/d280c5f))\n- Prevent color rules from crashing Chrome 66+ [#856](https://github.com/dequelabs/axe-core/issues/856) ([#861](https://github.com/dequelabs/axe-core/issues/861)) ([147b665](https://github.com/dequelabs/axe-core/commit/147b665))\n- **respondable:** Identify the current axe instance by its application name when it exists ([affd75c](https://github.com/dequelabs/axe-core/commit/affd75c))\n- **respondable:** Use the hard-coded axe.application name as default ([ab4a49f](https://github.com/dequelabs/axe-core/commit/ab4a49f))\n- **rule:** Ignore hashbang URLs for skiplinks ([#827](https://github.com/dequelabs/axe-core/issues/827)) ([e1f0c57](https://github.com/dequelabs/axe-core/commit/e1f0c57))\n- **rule:** Tag video-caption only as SC 1.2.2 ([87818e7](https://github.com/dequelabs/axe-core/commit/87818e7))\n\n<a name=\"3.0.1\"></a>\n\n## [3.0.1](https://github.com/dequelabs/axe-core/compare/v3.0.0...v3.0.1) (2018-04-03)\n\n### Bug Fixes\n\n- allow mixed casing of caption/summary ([ca091ac](https://github.com/dequelabs/axe-core/commit/ca091ac))\n- Allow title on button-name ([#794](https://github.com/dequelabs/axe-core/issues/794)) ([e31fe9a](https://github.com/dequelabs/axe-core/commit/e31fe9a))\n- **sri:** Fix incorrect sri-history entries ([#789](https://github.com/dequelabs/axe-core/issues/789)) ([f5f55f3](https://github.com/dequelabs/axe-core/commit/f5f55f3))\n\n<a name=\"3.0.0\"></a>\n\n# [3.0.0](https://github.com/dequelabs/axe-core/compare/v3.0.0-beta.2...v3.0.0) (2018-03-19)\n\n### Bug Fixes\n\n- Allow exclusion of Shadow DOM content ([cc66eb2](https://github.com/dequelabs/axe-core/commit/cc66eb2))\n- Avoid flatTree memory leak ([a902e80](https://github.com/dequelabs/axe-core/commit/a902e80))\n- **main-is-top-level:** Rename check to landmark-is-top-level for greater reuse ([b405af1](https://github.com/dequelabs/axe-core/commit/b405af1))\n- Avoid timing issue with axe cleanup method ([24ea6a7](https://github.com/dequelabs/axe-core/commit/24ea6a7))\n- correct misnamed check ([1e709e0](https://github.com/dequelabs/axe-core/commit/1e709e0))\n- Correct runOnly object for TypeScript definition ([571e984](https://github.com/dequelabs/axe-core/commit/571e984))\n- **has-at-least-one-main:** Rename check to page-has-main, for reusability ([9a9c283](https://github.com/dequelabs/axe-core/commit/9a9c283))\n- **has-no-more-than-one-main:** Rename check to page-no-duplicate for better reuse ([e75324b](https://github.com/dequelabs/axe-core/commit/e75324b))\n- **region:** Ignore forms without accessible name as landmarks ([8ad2718](https://github.com/dequelabs/axe-core/commit/8ad2718))\n- **rule:** skip-link rule doesn't decode URI encoded href's ([818b5cd](https://github.com/dequelabs/axe-core/commit/818b5cd))\n- Ensure all rules have a category tag ([d61e67d](https://github.com/dequelabs/axe-core/commit/d61e67d))\n- make getSelector work with URIs that cannot be shortened ([a113555](https://github.com/dequelabs/axe-core/commit/a113555))\n\n### Features\n\n- Make aria-level required with role=heading [#740](https://github.com/dequelabs/axe-core/issues/740) ([64b743f](https://github.com/dequelabs/axe-core/commit/64b743f))\n- **aria:** allow DPUB ARIA roles ([70b48f6](https://github.com/dequelabs/axe-core/commit/70b48f6))\n- **frame-tested:** Use this new rule to test if all frames are available, instead of axe.log ([83cd17d](https://github.com/dequelabs/axe-core/commit/83cd17d))\n- **landmark-contentinfo-is-top-level:** add rule ensuring top level contentinfo ([5692e7d](https://github.com/dequelabs/axe-core/commit/5692e7d))\n- **landmark-no-more-than-one-banner:** add rule ensuring no more than one banner ([6617800](https://github.com/dequelabs/axe-core/commit/6617800))\n- **landmark-no-more-than-one-contentinfo:** add rule ensuring no more than one contentinfo ([82217ef](https://github.com/dequelabs/axe-core/commit/82217ef))\n- **page-has-heading-one:** Added new best-practice rule ([cb8f261](https://github.com/dequelabs/axe-core/commit/cb8f261))\n- **rules:** add new rule aria-dpub-role-fallback ([9470c02](https://github.com/dequelabs/axe-core/commit/9470c02))\n- Make options.runOnly more forgiving about plurality ([fa81f9d](https://github.com/dequelabs/axe-core/commit/fa81f9d))\n- Translated all 3.0 rules to Japanese ([3862e7e](https://github.com/dequelabs/axe-core/commit/3862e7e))\n\n### BREAKING CHANGES\n\n- Incorrect use of runOnly now throws errors\n- **main-is-top-level:** The check main-is-top-level is no longer available\n- **has-at-least-one-main:** Original has-at-least-one-main check is no longer available\n\n<a name=\"3.0.0-beta.3\"></a>\n\n# [3.0.0-beta.3](https://github.com/dequelabs/axe-core/compare/v3.0.0-beta.2...v3.0.0-beta.3) (2018-03-08)\n\n### Bug Fixes\n\n- Correct runOnly object for TypeScript definition ([571e984](https://github.com/dequelabs/axe-core/commit/571e984))\n- **has-at-least-one-main:** Rename check to page-has-main, for reusability ([9a9c283](https://github.com/dequelabs/axe-core/commit/9a9c283))\n- **has-no-more-than-one-main:** Rename check to page-no-duplicate for better reuse ([e75324b](https://github.com/dequelabs/axe-core/commit/e75324b))\n- **main-is-top-level:** Rename check to landmark-is-top-level for greater reuse ([b405af1](https://github.com/dequelabs/axe-core/commit/b405af1))\n- **region:** Ignore forms without accessible name as landmarks ([8ad2718](https://github.com/dequelabs/axe-core/commit/8ad2718))\n\n### Features\n\n- **aria-required-attr:** Make aria-level required with role=heading [#740](https://github.com/dequelabs/axe-core/issues/740) ([64b743f](https://github.com/dequelabs/axe-core/commit/64b743f))\n- **aria:** allow DPUB ARIA roles ([70b48f6](https://github.com/dequelabs/axe-core/commit/70b48f6))\n- **frame-tested:** Use this new rule to test if all frames are available, instead of axe.log ([83cd17d](https://github.com/dequelabs/axe-core/commit/83cd17d))\n- **landmark-contentinfo-is-top-level:** Add rule ensuring top level contentinfo ([5692e7d](https://github.com/dequelabs/axe-core/commit/5692e7d))\n- **landmark-no-more-than-one-banner:** Add rule ensuring no more than one banner ([6617800](https://github.com/dequelabs/axe-core/commit/6617800))\n- **landmark-no-more-than-one-contentinfo:** Add rule ensuring no more than one contentinfo ([82217ef](https://github.com/dequelabs/axe-core/commit/82217ef))\n- **page-has-heading-one:** Added new best-practice rule ([cb8f261](https://github.com/dequelabs/axe-core/commit/cb8f261))\n- **rules:** Add new rule aria-dpub-role-fallback ([9470c02](https://github.com/dequelabs/axe-core/commit/9470c02))\n\n### BREAKING CHANGES\n\n- **main-is-top-level:** The check main-is-top-level is no longer available\n- **has-at-least-one-main:** Original has-at-least-one-main check is no longer available\n\n<a name=\"3.0.0-beta.2\"></a>\n\n# [3.0.0-beta.2](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.9...v3.0.0-beta.2) (2018-03-01)\n\n### Bug Fixes\n\n- **perf:** Improve getSelector performance ([737c81c](https://github.com/dequelabs/axe-core/commit/737c81c))\n- Delete Selenium example, use grunt-axe-selenium instead ([063e701](https://github.com/dequelabs/axe-core/commit/063e701))\n\n### Features\n\n- Update Jasmin example ([72d49d5](https://github.com/dequelabs/axe-core/commit/72d49d5))\n- Update Jest+React example ([5b35322](https://github.com/dequelabs/axe-core/commit/5b35322))\n- Update Mocha example ([cf70f9f](https://github.com/dequelabs/axe-core/commit/cf70f9f))\n- Update PhantomJS example ([641b5e6](https://github.com/dequelabs/axe-core/commit/641b5e6))\n- Update QUnit example ([028ae51](https://github.com/dequelabs/axe-core/commit/028ae51))\n\n# Change Log\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n\n<a name=\"3.0.0-beta.1\"></a>\n\n# [3.0.0-beta.1](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.9...v3.0.0-beta.1) (2018-02-19)\n\n### Bug Fixes\n\n- **audio-caption:** Change tag wcag122 to wcag121 [#686](https://github.com/dequelabs/axe-core/issues/686) ([#721](https://github.com/dequelabs/axe-core/issues/721)) ([9c7b9f1](https://github.com/dequelabs/axe-core/commit/9c7b9f1))\n- **empty-heading:** Skip headings with the role changed [#645](https://github.com/dequelabs/axe-core/issues/645) ([#722](https://github.com/dequelabs/axe-core/issues/722)) ([80ef961](https://github.com/dequelabs/axe-core/commit/80ef961))\n- **image-alt:** Exclude svg[role=\"img\"] elements ([#683](https://github.com/dequelabs/axe-core/issues/683)) ([0fe74d8](https://github.com/dequelabs/axe-core/commit/0fe74d8))\n- **label:** Prevent label rule from crashing on input without type [#678](https://github.com/dequelabs/axe-core/issues/678) ([#730](https://github.com/dequelabs/axe-core/issues/730)) ([4498680](https://github.com/dequelabs/axe-core/commit/4498680))\n- **perf:** improve select performance fixes [#702](https://github.com/dequelabs/axe-core/issues/702) ([3274919](https://github.com/dequelabs/axe-core/commit/3274919))\n- **perf:** memoize axe.utils.select ([c9cd122](https://github.com/dequelabs/axe-core/commit/c9cd122))\n- **perf:** normalize all selectors for better cache utilization ([189c165](https://github.com/dequelabs/axe-core/commit/189c165))\n- **perf:** remove need for node sorting from select completely ([7677a6a](https://github.com/dequelabs/axe-core/commit/7677a6a))\n- **performance:** significantly improve the performance of the dom.findUp utility fixes [#696](https://github.com/dequelabs/axe-core/issues/696) ([9197e03](https://github.com/dequelabs/axe-core/commit/9197e03))\n- add shadow dom coverage to all checks ([0e48413](https://github.com/dequelabs/axe-core/commit/0e48413)), closes [#690](https://github.com/dequelabs/axe-core/issues/690)\n- bug-705: cleanupPlugins() should not throw exception when no arguments are provided ([#709](https://github.com/dequelabs/axe-core/issues/709)) ([fb1d2f7](https://github.com/dequelabs/axe-core/commit/fb1d2f7))\n- Remove axe.a11yCheck() ([88d039f](https://github.com/dequelabs/axe-core/commit/88d039f))\n- use virtual methods where applicable ([6ddc4e5](https://github.com/dequelabs/axe-core/commit/6ddc4e5))\n- **type-checking:** Improve typescript axe.run call signature ([#707](https://github.com/dequelabs/axe-core/issues/707)) ([de45ee3](https://github.com/dequelabs/axe-core/commit/de45ee3))\n\n### Performance Improvements\n\n- Add performance metrics in Rule.runChecks ([#701](https://github.com/dequelabs/axe-core/issues/701)) ([27fdc2f](https://github.com/dequelabs/axe-core/commit/27fdc2f))\n\n<a name=\"3.0.0-alpha.9\"></a>\n\n# [3.0.0-alpha.9](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.8...v3.0.0-alpha.9) (2018-01-18)\n\n### Bug Fixes\n\n- **aria-errormessage:** adds support for aria-errormessage ([#517](https://github.com/dequelabs/axe-core/issues/517)) ([c96f58c](https://github.com/dequelabs/axe-core/commit/c96f58c))\n- **check:** fix bug with async checks ([441ca95](https://github.com/dequelabs/axe-core/commit/441ca95))\n- **color-contrast:** allow disabled label children ([db26bc9](https://github.com/dequelabs/axe-core/commit/db26bc9))\n- **color-contrast:** incl. elements w/ line breaks ([a464918](https://github.com/dequelabs/axe-core/commit/a464918))\n- **commons/aria:** allow aria-required on checkbox role ([3e21c6e](https://github.com/dequelabs/axe-core/commit/3e21c6e))\n- adjust color algorithm for inline elements ([7f8491e](https://github.com/dequelabs/axe-core/commit/7f8491e))\n- Allow checkbox and radio groups with hidden aria-labelledby targets [#188](https://github.com/dequelabs/axe-core/issues/188) ([6149bde](https://github.com/dequelabs/axe-core/commit/6149bde))\n- Ensure overloaded Array.prototype won't crash axe ([ea57ef2](https://github.com/dequelabs/axe-core/commit/ea57ef2))\n- handle contrast of multiline inline el's ([f9d565f](https://github.com/dequelabs/axe-core/commit/f9d565f))\n- Polyfills are made non-enumerable [#648](https://github.com/dequelabs/axe-core/issues/648). ([#649](https://github.com/dequelabs/axe-core/issues/649)) ([48d8703](https://github.com/dequelabs/axe-core/commit/48d8703))\n- Remove href-no-hash rule ([342cb6e](https://github.com/dequelabs/axe-core/commit/342cb6e))\n- Return inapplicable results ([#473](https://github.com/dequelabs/axe-core/issues/473)). ([c9caeff](https://github.com/dequelabs/axe-core/commit/c9caeff))\n- Several of fixes for IE and Edge ([#577](https://github.com/dequelabs/axe-core/issues/577)) ([63e1272](https://github.com/dequelabs/axe-core/commit/63e1272))\n- skip-link rule now checks if a target exists ([f7f9cf3](https://github.com/dequelabs/axe-core/commit/f7f9cf3))\n- **commons/aria:** allow aria-required on radio role ([58b76a6](https://github.com/dequelabs/axe-core/commit/58b76a6))\n- **commons/dom:** fix isFocusable functions by checking screenreader ([#658](https://github.com/dequelabs/axe-core/issues/658)) ([c665d0b](https://github.com/dequelabs/axe-core/commit/c665d0b)), closes [#647](https://github.com/dequelabs/axe-core/issues/647)\n- **core/utils/querySelectorAll:** Ensure that elements do not get tested twice ([#666](https://github.com/dequelabs/axe-core/issues/666)) ([a76a454](https://github.com/dequelabs/axe-core/commit/a76a454))\n- **required-children:** add combobox > listbox exception ([#559](https://github.com/dequelabs/axe-core/issues/559)) ([8d0991f](https://github.com/dequelabs/axe-core/commit/8d0991f))\n- **rules/region:** Treat `<section>` as a landmark if it has an accessible name [#640](https://github.com/dequelabs/axe-core/issues/640) ([#642](https://github.com/dequelabs/axe-core/issues/642)) ([c11b442](https://github.com/dequelabs/axe-core/commit/c11b442))\n\n### Features\n\n- Add rule, landmark-main-is-top-level ([#462](https://github.com/dequelabs/axe-core/issues/462)) ([63040bd](https://github.com/dequelabs/axe-core/commit/63040bd))\n- **collect-results-from-frames:** add frameWaitTime option ([#661](https://github.com/dequelabs/axe-core/issues/661)) ([8016ad1](https://github.com/dequelabs/axe-core/commit/8016ad1))\n- **landmark-one-main:** add rule ensuring one main landmark in document ([#498](https://github.com/dequelabs/axe-core/issues/498)) ([dfc6069](https://github.com/dequelabs/axe-core/commit/dfc6069))\n- **reporter:** return one result of each type instead of zero when resultTypes is used ([#604](https://github.com/dequelabs/axe-core/issues/604)) ([216a83b](https://github.com/dequelabs/axe-core/commit/216a83b)), closes [#603](https://github.com/dequelabs/axe-core/issues/603)\n- **rule:** Flag div/p/spans/headings in focus order ([ce5f3dc](https://github.com/dequelabs/axe-core/commit/ce5f3dc)), closes [#632](https://github.com/dequelabs/axe-core/issues/632)\n- allow options in aria-allowed-attr, aria-required-attr ([#673](https://github.com/dequelabs/axe-core/issues/673)) ([61ac303](https://github.com/dequelabs/axe-core/commit/61ac303))\n- Enable all rules by default ([18263eb](https://github.com/dequelabs/axe-core/commit/18263eb))\n- Improve generated selectors for namespaced elements in XHTML ([#582](https://github.com/dequelabs/axe-core/issues/582)) ([9e3ca45](https://github.com/dequelabs/axe-core/commit/9e3ca45)), closes [#563](https://github.com/dequelabs/axe-core/issues/563)\n\n### BREAKING CHANGES\n\n- Skip-link rule no longer requires skip lins with a focusable target.\n\n<a name=\"3.0.0-alpha.8\"></a>\n\n# [3.0.0-alpha.8](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.7...v3.0.0-alpha.8) (2017-10-20)\n\n### Bug Fixes\n\n- **aria-allowed-attr:** align rowcount, colcount, and colindex with 1.1 spec ([#555](https://github.com/dequelabs/axe-core/issues/555)) ([10efa88](https://github.com/dequelabs/axe-core/commit/10efa88))\n- **required-children:** add combobox > listbox exception ([#559](https://github.com/dequelabs/axe-core/issues/559)) ([8d0991f](https://github.com/dequelabs/axe-core/commit/8d0991f))\n- Added message about expected contrast ratio ([#381](https://github.com/dequelabs/axe-core/issues/381)) ([#562](https://github.com/dequelabs/axe-core/issues/562)) ([9e30d64](https://github.com/dequelabs/axe-core/commit/9e30d64))\n\n<a name=\"3.0.0-alpha.6\"></a>\n\n# [3.0.0-alpha.6](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.3...v3.0.0-alpha.6) (2017-09-27)\n\n### Bug Fixes\n\n- **aria:** adding support for aria-expanded in menuitem ([#521](https://github.com/dequelabs/axe-core/issues/521)) ([b30b451](https://github.com/dequelabs/axe-core/commit/b30b451))\n- **color-contrast:** Include `THEAD` and `TBODY` in contrast checks ([#514](https://github.com/dequelabs/axe-core/issues/514)) ([f98f8bd](https://github.com/dequelabs/axe-core/commit/f98f8bd))\n- Match prerelease versions for helpUrl ([#546](https://github.com/dequelabs/axe-core/issues/546)) ([5300577](https://github.com/dequelabs/axe-core/commit/5300577))\n- **helpUrl:** Properly parse x.0 versions ([#550](https://github.com/dequelabs/axe-core/issues/550)) ([a51c5ce](https://github.com/dequelabs/axe-core/commit/a51c5ce))\n- **postinstall:** use node, more conditionals ([#520](https://github.com/dequelabs/axe-core/issues/520)) ([f5b5299](https://github.com/dequelabs/axe-core/commit/f5b5299))\n- **responsible:** Restrict error construction to known errors ([0128a7e](https://github.com/dequelabs/axe-core/commit/0128a7e))\n\n<a name=\"3.0.0-alpha.5\"></a>\n\n# [3.0.0-alpha.5](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.4...v3.0.0-alpha.5) (2017-09-25)\n\n### Bug Fixes\n\n- **aria:** adding support for aria-expanded in menuitem ([#521](https://github.com/dequelabs/axe-core/issues/521)) ([b30b451](https://github.com/dequelabs/axe-core/commit/b30b451))\n- **postinstall:** use node, more conditionals ([#520](https://github.com/dequelabs/axe-core/issues/520)) ([f5b5299](https://github.com/dequelabs/axe-core/commit/f5b5299))\n- Match prerelease versions for helpUrl ([#546](https://github.com/dequelabs/axe-core/issues/546)) ([5300577](https://github.com/dequelabs/axe-core/commit/5300577))\n\n<a name=\"3.0.0-alpha.4\"></a>\n\n## [3.0.0-alpha.4](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.3...v3.0.0-alpha.4) (2017-09-08)\n\n### Bug fixes:\n\n- fix(color-contrast): Include `THEAD` and `TBODY` in contrast checks (#514) ([f98f8bd](https://github.com/dequelabs/axe-core/commit/f98f8bdacc551579c259aefd88bef41ed8157b68))\n- fix(responsible): Restrict error construction to known errors (#513) ([0128a7e](https://github.com/dequelabs/axe-core/commit/0128a7ea47847b9fa04dbf98327f4bc1760c5e11))\n\n### Features:\n\n- docs: Document how to propose axe-core rules (#507) ([cabd329](https://github.com/dequelabs/axe-core/commit/cabd3297afbbfe9dbcc41a168b5529ba52f408ba))\n\n<a name=\"3.0.0-alpha.3\"></a>\n\n## [3.0.0-alpha.3](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.2...v3.0.0-alpha.3) (2017-09-06)\n\n### Bug fixes:\n\n- Additional ARIA 1.1. support and tests (#509) ([9b4d2ee](https://github.com/dequelabs/axe-core/commit/9b4d2eea4fcb2c48bab71e442da3a588b3893853))\n- fix: RestoreScroll was running out of sync (#508) ([ff3df2d](https://github.com/dequelabs/axe-core/commit/ff3df2d9b2c01c1ca0d12c1fcaf136528287fb6d))\n\n<a name=\"3.0.0-alpha.2\"></a>\n\n## [3.0.0-alpha.2](https://github.com/dequelabs/axe-core/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) (2017-09-01)\n\n### Bug Fixes\n\n- copy precommit hook as file, not a link ([16f2f76](https://github.com/dequelabs/axe-core/commit/16f2f76))\n- expand tr support for color contrast ([5a77c2f](https://github.com/dequelabs/axe-core/commit/5a77c2f))\n- Ignore shadowRoots on elements that don't allow them ([7f66ee8](https://github.com/dequelabs/axe-core/commit/7f66ee8))\n- only run postinstall if .git exists ([1107783](https://github.com/dequelabs/axe-core/commit/1107783))\n- try telling circle to skip .git/hooks ([674408f](https://github.com/dequelabs/axe-core/commit/674408f))\n- Use frame query that supports shadow dom ([#492](https://github.com/dequelabs/axe-core/issues/492)) ([94008ff](https://github.com/dequelabs/axe-core/commit/94008ff))\n\n### Features\n\n- feat: Add sri-history file and update process ([25ddb47](https://github.com/dequelabs/axe-core/commit/25ddb47ec4eec565da330558ee061fd6e34a7c24))\n- feat: add standard-version ([e1e067d](https://github.com/dequelabs/axe-core/commit/e1e067d8f4445042360b2bef957037d5cdd0b7db))\n\n<a name=\"3.0.0-alpha.1\"></a>\n\n## [3.0.0-alpha.1](https://github.com/dequelabs/axe-core/compare/v2.3.1...v3.0.0-alpha.1) (2017-08-16)\n\n### Bug Fixes\n\n- add copyright banner back in to axe.js ([2aac29a](https://github.com/dequelabs/axe-core/commit/2aac29a))\n- Adjust if formatting ([2211d78](https://github.com/dequelabs/axe-core/commit/2211d78))\n- Align impact levels with Deque Way ([28f4477](https://github.com/dequelabs/axe-core/commit/28f4477))\n- Allow <track> to have no kind attribute ([f996d0f](https://github.com/dequelabs/axe-core/commit/f996d0f))\n- complete shadow support for color matches ([f0fe551](https://github.com/dequelabs/axe-core/commit/f0fe551))\n- **aria:** Allow implicit attribute values ([b949749](https://github.com/dequelabs/axe-core/commit/b949749))\n- Set relatedNodes on color/link-in-block rules ([#407](https://github.com/dequelabs/axe-core/issues/407)) ([7fde0fe](https://github.com/dequelabs/axe-core/commit/7fde0fe))\n- **aria:** Treegrid should own rows, not treeitems ([645d1fa](https://github.com/dequelabs/axe-core/commit/645d1fa))\n- Correct flattened tree algorithm to include the shadow host ([#405](https://github.com/dequelabs/axe-core/issues/405)) ([70985b0](https://github.com/dequelabs/axe-core/commit/70985b0))\n- Ensure all tests pass in Chrome ([0b0240f](https://github.com/dequelabs/axe-core/commit/0b0240f))\n- ensure document is fetched from correct node ([b28597c](https://github.com/dequelabs/axe-core/commit/b28597c))\n- Exclude `any` checks from output if one passed ([#466](https://github.com/dequelabs/axe-core/issues/466)) ([2dd3d68](https://github.com/dequelabs/axe-core/commit/2dd3d68))\n- get tests all passing ([#457](https://github.com/dequelabs/axe-core/issues/457)) ([4874327](https://github.com/dequelabs/axe-core/commit/4874327))\n- get virtualNode with getNodeFromTree ([9bf2870](https://github.com/dequelabs/axe-core/commit/9bf2870))\n- getComposedParent should not return slot nodes ([#438](https://github.com/dequelabs/axe-core/issues/438)) ([0478cbd](https://github.com/dequelabs/axe-core/commit/0478cbd))\n- Have table rules use shadow DOM ([453be1b](https://github.com/dequelabs/axe-core/commit/453be1b))\n- help-same-as-label for shadow DOM ([dbbc544](https://github.com/dequelabs/axe-core/commit/dbbc544))\n- incomplete results should have impact ([fcc51eb](https://github.com/dequelabs/axe-core/commit/fcc51eb))\n- Let findUp work on shadow root children ([0252218](https://github.com/dequelabs/axe-core/commit/0252218))\n- Let findUp work on shadow root children ([#447](https://github.com/dequelabs/axe-core/issues/447)) ([0f98481](https://github.com/dequelabs/axe-core/commit/0f98481))\n- Minimise scrolling in getBackgroundColor ([f4551bb](https://github.com/dequelabs/axe-core/commit/f4551bb))\n- Pass all tests that use accessibleText ([7ea8d6b](https://github.com/dequelabs/axe-core/commit/7ea8d6b))\n- **checks/aria/required-children:** add exception for native input combobox missing textbox ([81ee2e4](https://github.com/dequelabs/axe-core/commit/81ee2e4))\n- use virtualNode in duplicate-img-label ([82e51bc](https://github.com/dequelabs/axe-core/commit/82e51bc))\n- **is-in-text-block:** Add Shadow DOM support ([a125f79](https://github.com/dequelabs/axe-core/commit/a125f79))\n- pass virtualNode to Rule.run ([4534e86](https://github.com/dequelabs/axe-core/commit/4534e86))\n- Properly output error stack ([56f1867](https://github.com/dequelabs/axe-core/commit/56f1867))\n- Remove log statements ([6a6bd73](https://github.com/dequelabs/axe-core/commit/6a6bd73))\n- Solve a few tests ([02daad1](https://github.com/dequelabs/axe-core/commit/02daad1))\n- Use getAttribute(id) over .id ([#374](https://github.com/dequelabs/axe-core/issues/374)) ([353b53f](https://github.com/dequelabs/axe-core/commit/353b53f))\n- Use testUtils in table tests ([364d5cd](https://github.com/dequelabs/axe-core/commit/364d5cd))\n- use virtualNode in title-only check ([5fb06e3](https://github.com/dequelabs/axe-core/commit/5fb06e3))\n- whitespace in hidden-content test ([99e8b73](https://github.com/dequelabs/axe-core/commit/99e8b73))\n\n### Features\n\n- Add aria-orientation to additional roles ([bb07c2d](https://github.com/dequelabs/axe-core/commit/bb07c2d))\n- add check testUtils ([6f53279](https://github.com/dequelabs/axe-core/commit/6f53279))\n- Add dom.getComposedParent function ([aac57c0](https://github.com/dequelabs/axe-core/commit/aac57c0))\n- Add Japanese localisation ([5f8c9c8](https://github.com/dequelabs/axe-core/commit/5f8c9c8))\n- Add new ARIA 1.1 values for haspopup ([9f7da56](https://github.com/dequelabs/axe-core/commit/9f7da56))\n- Add option restoreScroll ([d55f3cd](https://github.com/dequelabs/axe-core/commit/d55f3cd))\n- add S.C. 2.4.4 to link-name rule. Fixes [#369](https://github.com/dequelabs/axe-core/issues/369) ([70728e6](https://github.com/dequelabs/axe-core/commit/70728e6))\n- add SD support to color-contrast-matches ([b595b42](https://github.com/dequelabs/axe-core/commit/b595b42))\n- Add shadow DOM support to list checks ([#439](https://github.com/dequelabs/axe-core/issues/439)) ([d92c1a1](https://github.com/dequelabs/axe-core/commit/d92c1a1))\n- Add shadow DOM to duplicate-img-label check ([#443](https://github.com/dequelabs/axe-core/issues/443)) ([2c0b075](https://github.com/dequelabs/axe-core/commit/2c0b075))\n- Add shadow DOM to landmark check ([98f6023](https://github.com/dequelabs/axe-core/commit/98f6023))\n- add shadow support to aria-required-children ([f729e25](https://github.com/dequelabs/axe-core/commit/f729e25))\n- add shadow support to group-labelledby ([e2a9642](https://github.com/dequelabs/axe-core/commit/e2a9642))\n- Add sri-history file and update process ([#476](https://github.com/dequelabs/axe-core/issues/476)) ([25ddb47](https://github.com/dequelabs/axe-core/commit/25ddb47))\n- Allow hidden-content to work through shadow DOM bounds ([789d62e](https://github.com/dequelabs/axe-core/commit/789d62e))\n- fieldset check shadow DOM ([da148d3](https://github.com/dequelabs/axe-core/commit/da148d3))\n- Make explicit check consider shadow DOM ([#442](https://github.com/dequelabs/axe-core/issues/442)) ([9ddfc0f](https://github.com/dequelabs/axe-core/commit/9ddfc0f))\n- Make region check work with shadow DOM ([ecd222f](https://github.com/dequelabs/axe-core/commit/ecd222f))\n- Run text.accessibleText() on virtual elements ([#420](https://github.com/dequelabs/axe-core/issues/420)) ([414fcbe](https://github.com/dequelabs/axe-core/commit/414fcbe))\n- ShadowDOM support for media checks ([0f21574](https://github.com/dequelabs/axe-core/commit/0f21574))\n- **aria:** Support progressive ARIA 1.1 attributes / roles ([#468](https://github.com/dequelabs/axe-core/issues/468)) ([ebb2a5d](https://github.com/dequelabs/axe-core/commit/ebb2a5d))\n- **aria-required-parent:** add Shadow DOM support ([6ed29f0](https://github.com/dequelabs/axe-core/commit/6ed29f0))\n- **duplicate-id:** Add shadow DOM support ([439bc71](https://github.com/dequelabs/axe-core/commit/439bc71))\n- **link-in-text-block:** Add shadow DOM support ([46a2cca](https://github.com/dequelabs/axe-core/commit/46a2cca))\n- **shadow DOM:** Create commons virtual methods, for backward compatibility ([86a4c25](https://github.com/dequelabs/axe-core/commit/86a4c25))\n\n<a name=\"2.4.2\"></a>\n\n## [2.4.2](https://github.com/dequelabs/axe-core/compare/v2.4.1...v2.4.2) (2017-09-25)\n\n### Bug Fixes\n\n- **aria:** adding support for aria-expanded in menuitem ([#521](https://github.com/dequelabs/axe-core/issues/521)) ([b4c42fe](https://github.com/dequelabs/axe-core/commit/b4c42fe))\n- Match prerelease versions for helpUrl ([#546](https://github.com/dequelabs/axe-core/issues/546)) ([c166708](https://github.com/dequelabs/axe-core/commit/c166708))\n- fix(node4): use var to declare variables ([#541](https://github.com/dequelabs/axe-core/issues/541)) ([c72badb](https://github.com/dequelabs/axe-core/commit/c72badbd55ef0b56b97f0c64a4eb544e31b4b3f1))\n\n<a name=\"2.4.1\"></a>\n\n## [2.4.1](https://github.com/dequelabs/axe-core/compare/v2.4.0...v2.4.1) (2017-09-12)\n\n### Bug Fixes\n\n- fix(postinstall): use node, more conditionals ([#520](https://github.com/dequelabs/axe-core/issues/520)) ([98fac8a](https://github.com/dequelabs/axe-core/commit/98fac8a))\n\n<a name=\"2.4.0\"></a>\n\n## [2.4.0](https://github.com/dequelabs/axe-core/compare/v2.3.1-alpha.2...v2.4.0) (2017-09-08)\n\n### Bug fixes:\n\n- fix(color-contrast): Include `THEAD` and `TBODY` in contrast checks (#514) ([f98f8bd](https://github.com/dequelabs/axe-core/commit/f98f8bdacc551579c259aefd88bef41ed8157b68))\n- fix(responsible): Restrict error construction to known errors (#513) ([0128a7e](https://github.com/dequelabs/axe-core/commit/0128a7ea47847b9fa04dbf98327f4bc1760c5e11))\n\n### Features:\n\n- docs: Document how to propose axe-core rules (#507) ([cabd329](https://github.com/dequelabs/axe-core/commit/cabd3297afbbfe9dbcc41a168b5529ba52f408ba))\n\n<a name=\"2.4.0-alpha.2\"></a>\n\n## [2.4.0-alpha.2](https://github.com/dequelabs/axe-core/compare/v2.4.0-alpha.1...v2.4.0-alpha.2) (2017-09-06)\n\n### Bug fixes:\n\n- test(aria): aria-haspopup, aria-modal for 1.1\n- style(test/aria): add line breaks for readability\n- test(aria): add missing roles and properties\n- fix: RestoreScroll was running out of sync (#508)\n\n### Features:\n\n- feat(ARIA 1.1): Allow row-index, setsize and posinset on more roles\n\n<a name=\"2.4.0-alpha.1\"></a>\n\n## [2.4.0-alpha.1](https://github.com/dequelabs/axe-core/compare/v2.3.0...v2.4.0-alpha.1) (2017-08-31)\n\n### Bug fixes:\n\n- chore: add help text for testconfig Grunt task\n- fix: Properly output error stack\n- doc: Add 'on mobile' to viewport rule\n- fix: Ensure all tests pass in Chrome\n- fix: Minimise scrolling in getBackgroundColor\n- chore: Minor build chores for whitespace and lockfile\n- fix: Align impact levels with Deque Way\n- fix: Set relatedNodes on color/link-in-block rules\n- fix: incomplete results should have impact\n- fix(aria): Allow implicit attribute values\n- chore: ignore growl in retire\n- fix: Use getAttribute(id) over .id\n- fix: Exclude `any` checks from output if one passed\n- fix(aria): Treegrid should own rows, not treeitems\n- fix(aria): add exception for native input combobox missing textbox. Fixes #160\n- test: fix aria/required-children for jshint\n- feat: allow link text from single-cell layout table\n- fix: expand tr support for color contrast\n- chore: ignore node_modules in examples when linting\n\n### Features:\n\n- feat: Add option `restoreScroll`\n- doc: add more info on testing hidden regions\n- feat: add S.C. 2.4.4 to link-name rule. Fixes #369\n- feat: Add Japanese localisation\n- doc: Add instructions on debugging on CircleCI\n- test: add unit tests for button-has-visible-text\n- chore: add descriptions to Grunt tasks\n- feat(aria): Support progressive ARIA 1.1 attributes / roles\n- feat: Add new ARIA 1.1 values for haspopup\n- feat: Add aria-orientation to additional roles\n\n<a name=\"2.3.1\"></a>\n\n## [2.3.1](https://github.com/dequelabs/axe-core/compare/v2.3.0...v2.3.1) (2017-06-15)\n\n### Bug fixes:\n\n- Improvements to hidden-content rule\n- Deduplicated langs in valid-lang options\n\n<a name=\"2.3.0\"></a>\n\n## [2.3.0](https://github.com/dequelabs/axe-core/compare/v2.2.3...v2.3.0) (2017-06-14)\n\n### Bug fixes:\n\n- Overhaul of selectors API\n- New experimental rule for hidden-content\n- New rule for flagging aria-hidden=\"true\" on document.body\n- Color-contrast rule impact is now serious\n- Color-contrast fixes for implicit labels and TR elements\n- Color-contrast puts 1:1 ratio elements into Needs Review/incomplete\n- List category mappings in docs\n- Update axe.source to work with Firefox webdriver\n\n<a name=\"2.2.3\"></a>\n\n## [2.2.3](https://github.com/dequelabs/axe-core/compare/v2.2.2...v2.2.3) (2017-06-01)\n\n### Bug fixes:\n\n- Removed the disable property from link-in-text-block\n\n<a name=\"2.2.2\"></a>\n\n## [2.2.2](https://github.com/dequelabs/axe-core/compare/2.2.1...v2.2.2) (2017-05-25)\n\n### Bug fixes\n\n- Stabilize incompleteData API for backwards compatibility\n- Change impact of duplicate-id rule to moderate\n\n<a name=\"2.2.2\"></a>\n\n## [2.2.1](https://github.com/dequelabs/axe-core/compare/2.2.0...2.2.1) (2017-05-19)\n\n### Bug fixes\n\n- Remove nodes from the color contrast incompleteData API to avoid circular references\n\n<a name=\"2.2.0\"></a>\n\n## 2.2.0 (2017-04-24)\n\n### Changes\n\n- Add configuration options for iframes: false, selectors: false, and elementRef: true\n- Improve color-contrast rule for disabled elements\n- Add webdriver task for testing mobile viewports\n- Improve audio/video captioning rules\n- Improve th-has-data-cells rule\n- Expose incomplete reasons for color contrast rule as part of Needs Review\n- Implement rule groupings as tags\n- Allow building of axe in multiple languages\n- Empty-heading rule has impact: moderate\n\n<a name=\"2.1.8\"></a>\n\n## 2.1.8 (2017-05-21)\n\n### Changes\n\n- Move from Snyk to Retire.js\n- Make CI run test-fast task instead of parallel\n- Add documentation on writing integration tests and rules\n- Allow a larger list of languages for HTML-valid-lang rule\n- Add support for [role=img] in image-alt rule\n- Fix bug with innerHeight in get-background-color\n- Improve dom.is-offscreen function\n- Integrate optional performance timer\n- Empty include defaults to document\n\n<a name=\"2.1.7\"></a>\n\n## 2.1.7 (2016-12-13)\n\n### Changes\n\n- Add promise-based axe.run API method in favor of axe.a11yCheck\n- Move TypeScript definition to root of project\n- Add Inapplicable and Can't Tell results\n- New rule: frame-title-unique\n- Improvements to table rules: td-has-header, th-has-data-cells\n- Color contrast rule performance improvements using polyfilled elementsFromPoint\n- Add better support for implicit roles\n- DQElement supports xPath\n\n<a name=\"2.0.7\"></a>\n\n## 2.0.7 (2016-09-28)\n\n### Changes\n\n- Add TypeScript definition v1\n\n<a name=\"2.0.5\"></a>\n\n## 2.0.5 (2016-04-20)\n\n### Changes\n\n- Support for UMD pattern\n- Adds 508 tagging for table rules\n- Fixes race condition for iframes\n- Exclude actual nodes from array checking\n\n<a name=\"2.0.5\"></a>\n\n## 2.0.5 (2016-04-13)\n\n### Changes\n\n- Improvements to messaging for extensions\n\n<a name=\"2.0.3\"></a>\n\n## 2.0.3 (2016-04-12)\n\n### Changes\n\n- Security improvements\n- Build includes Babel/ES6\n- Improvements to table rules\n- axe can be loaded in Node\n\n<a name=\"2.0.0\"></a>\n\n## 2.0.0 (2016-03-01)\n\n### Changes\n\n- Adds support for AMD modules\n- Fixes incompatibility with Webpack\n- Improvements to rules and checks\n- Help urls no longer hard-coded\n- Improved error handling\n\n<a name=\"1.1.1\"></a>\n\n## 1.1.1 (2015-09-04)\n\n### Changes\n\n- Adds Travis hooks\n- Adds Sauce Labs\n- Encodes HTML in descriptions\n- Updates messages and help URLs\n\n<a name=\"1.0.1\"></a>\n\n## 1.0.1 (2015-06-10)\n\n### Changes\n\n- Initial public release\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# axe-core — Claude Code Context\n\n**Last updated:** 2026-03-19\n\n## 0. Fundamental Standards\n\n- **Formatting:** Run `npm run fmt` (Prettier) and `npm run eslint` before every commit. No exceptions.\n- **Zero-Exception Testing:** 100% coverage goal. Run `npm test` before completion. `feat`/`fix` and other behavior-changing PRs require unit + integration tests where applicable; see `doc/code-submission-guidelines.md` for exceptions (e.g., some `chore`/docs changes).\n- **Import Rule:** Directory-level import restrictions are strictly enforced. See `eslint.config.js`.\n- **Commits:** Angular commit convention is mandatory. PRs with non-conforming commits will be rejected. See `doc/code-submission-guidelines.md`.\n- **Issues:** All unresolved issues tracked in [GitHub Issues](https://github.com/dequelabs/axe-core/issues).\n\n## 1. Technical Guidelines\n\n### Code Structure\n\n- **Return Early:** Keep the happy path left-aligned. Handle errors/edge cases first with early returns — never nest when you can exit.\n- **Exports:** Default export at the top of the file, immediately after imports.\n- **JSDoc:** Add JSDoc/DocBlock comments where appropriate, especially for exported APIs and complex logic. Document parameters and return types. See `doc/code-submission-guidelines.md` (source of truth) and `doc/developer-guide.md` for additional guidance.\n- **Naming:** Files and rule/check IDs in kebab-case. Functions in camelCase. Booleans prefixed `is`/`has`/`should`. Constants in `UPPER_SNAKE_CASE`.\n- **Variables:** Declare at point of use, not at the top of the function.\n\n### Virtual Nodes vs. HTMLElement\n\n- **Prefer Virtual Nodes** for attribute access and property reads (`virtualNode.attr()`, `virtualNode.props`).\n- **Use real DOM** only when you need DOM APIs (e.g., `getBoundingClientRect`, `getRootNode`).\n- **`core/utils/` functions** should default to real DOM inputs/outputs, except utilities that are explicitly documented as operating on `VirtualNode` (for example, tree or selector helpers). Avoid introducing new `VirtualNode`-dependent utilities unless there is a clear performance or API benefit.\n- **Conversion:** Use `getNodeFromTree()` from `core/utils` when you receive an ambiguous input (such as a `VirtualNode`, selector, or mixed type) and need to resolve it to a real DOM `Node`.\n\n### Import Restrictions (Hard Rules)\n\n- `standards/` → nothing (pure data, no imports).\n- `core/utils/` → other `core/utils`, `core/base`, `standards` via **direct file paths only** (no index).\n- `core/imports/` → node modules **only** (the only place npm imports are allowed).\n- `commons/` → other `commons` (direct paths), `core/utils` (index OK).\n- `checks/` and `rules/` → any directory (index OK).\n- **Never** import `commons` from `core/utils`. This is the most commonly rejected violation.\n\n### Checks & Rules\n\n- **Check evaluate functions:** Return `true` (pass), `false` (fail), or `undefined` (incomplete). Use `this.data()` to pass values to message templates and to provide incomplete-result detail.\n- **Rule JSON:** Use `all`, `any`, `none` check arrays. `selector` + optional `matches` to scope candidates. Valid `impact` values: `\"minor\"`, `\"moderate\"`, `\"serious\"`, `\"critical\"`.\n- **Standards data:** Never hardcode ARIA/HTML lists in checks. Query from `standards/` via `commons/standards` functions.\n- **Messages:** All user-facing strings live in check/rule JSON `metadata.messages`. Use `${data.property}` templates. Support singular/plural variants. Update `locales/_template.json` whenever messages change (auto-synced on `npm run build`).\n\n### High-Risk Areas (Extra Scrutiny Required)\n\n- **Color contrast:** Handles all CSS color formats, opacity, blend modes, stacking contexts, text-shadow. Return `undefined` when background cannot be determined. See `doc/developer-guide.md`.\n- **ARIA validation:** Must stay current with spec. Query roles/attrs from `standards/aria-roles.js` and `standards/aria-attrs.js`. Handle implicit vs. explicit roles. See `doc/rule-development.md`.\n- **Hidden elements:** Use `isVisibleToScreenReaders()`, not CSS visibility alone. Account for `aria-hidden=\"true\"` and Shadow DOM boundaries.\n- **i18n:** Update `locales/_template.json` on every message change and commit the generated file alongside source.\n\n## 2. Testing\n\n- **Structure:** Mirror `lib/` exactly under `test/`. File `lib/commons/text/sanitize.js` → `test/commons/text/sanitize.js`.\n- **Checks:** Use `axe.testUtils.MockCheckContext()`. Only reset `checkContext` in `afterEach` — `fixture` and `axe._tree` are auto-cleared.\n- **Integration tests:** All rule changes require an HTML + JSON pair. Use `test/integration/rules/<rule-name>/` for mocha-hosted tests or `test/integration/full/<rule-name>/` when the rule requires a full HTML page. JSON selectors must use axe array format (`[\"#id\"]`; iframes: `[\"iframe\", \"#id\"]`). Also update or create virtual-rules tests where appropriate.\n- **Shadow DOM:** Every relevant check/rule must include an open Shadow DOM test case using `queryShadowFixture`.\n- **Logging:** Do not commit `console.log` statements.\n\n## 3. Build & Commits\n\n- **Bundles (`axe.js`, `axe.min.js`)** are auto-generated for releases/publishing and are not committed to the repo (they are gitignored).\n- **Locales template (`locales/_template.json`)** is auto-generated. When message strings change, regenerate this file and commit it in the same commit as the source changes — never in a separate commit.\n- **One change per PR.** Do not mix refactoring with feature work.\n- **Commit format:** `<type>(<scope>): <subject>` — imperative, lowercase, no period, ≤100 chars total. Body explains motivation. Footer: `Closes issue #123` or full URL. See `doc/code-submission-guidelines.md` for the full type list.\n\n**Example:**\n\n```\nfix(aria-valid-attr-value): handle multiple aria-errormessage IDs\n\nWhen aria-errormessage contains multiple space-separated IDs, verify\nall IDs exist in aria-describedby instead of matching the full string.\n\nCloses issue #4957\n```\n\n## 4. Documentation & API Changes\n\n- **New rules:** Update `CHANGELOG.md`. `doc/rule-descriptions.md` is auto-generated by `npm run build`.\n- **API changes:** Update `doc/API.md` and `axe.d.ts` TypeScript definitions.\n- **Breaking changes:** Avoid breaking changes — prefer supporting both old and new formats simultaneously. If unavoidable, add `BREAKING CHANGE: description` to commit footer, include migration guide in `CHANGELOG.md`, and tag deprecated code with `@deprecated` JSDoc.\n\n## 5. Examples (Copy-Paste Reference)\n\n- **Code patterns:** `doc/examples/code-patterns.md` — return early, default export, imports, JSDoc, Virtual Node usage\n- **Test patterns:** `doc/examples/test-patterns.md` — unit tests, check tests, Shadow DOM tests, integration test HTML+JSON\n- **Rule & check templates:** `doc/examples/rule-check-templates.md` — JSON templates for rules and checks, evaluate function pattern\n- **PR review patterns:** `doc/examples/pr-review-patterns.md` — common reviewer feedback, anti-patterns, what reviewers look for\n\n## 6. Reference Docs & Help\n\n- **Contributing guide:** `CONTRIBUTING.md`\n- **Import rules detail:** `eslint.config.js`\n- **Code submission standards:** `doc/code-submission-guidelines.md`\n- **Developer guide:** `doc/developer-guide.md`\n- **Rule development:** `doc/rule-development.md`\n- **API reference:** `doc/API.md`\n- **Pull Request Checklist:** `doc/pull-request-checklist.md`\n- **Slack:** [axe-community](https://accessibility.deque.com/axe-community)\n- **Issues:** [GitHub Issues](https://github.com/dequelabs/axe-core/issues)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Contributor License Agreement\n\nIn order to contribute, you must accept the [contributor license agreement](https://cla-assistant.io/dequelabs/axe-core) (CLA). Acceptance of this agreement will be checked automatically and pull requests without a CLA cannot be merged.\n\n## Contribution Guidelines\n\nSubmitting code to the project? Please review and follow our\n[Git commit and pull request guidelines](doc/code-submission-guidelines.md).\n\n### Code Quality\n\nAlthough we do not have official code style guidelines, we can and will request you to make changes if we feel the changes are warranted. You can take clues from the existing code base to see what we consider to be reasonable code quality. Please be prepared to make changes that we ask of you even if you might not agree with the request(s).\n\nPlease respect the coding style of the files you are changing and adhere to that.\n\nThe files in this project are formatted by [Prettier](https://prettier.io/) and [ESLint](https://eslint.org/). Both are run when code is committed. Additionally, you can run ESLint manually:\n\n```console\nnpm run eslint\n```\n\n### When to use HTMLElement vs Virtual Node\n\nAxe-core uses an internal copy of the HTML page for most things internal to axe-core (called the Virtual Tree). Each HTML element on the page has an equivalent [Virtual Node](./lib/core/base/virtual-node) element, which allows us to cache or normalize information about the HTML element without mutating the actual DOM node.\n\nTypically we use the Virtual Node when possible, but understand that's not always possible (such as accessing DOM only APIs like `getRootNode` or `getBoundingClientRect`). Furthermore, any function within the [utils directory](./lib/core/utils/) should not use Virtual Nodes. The reason for this is that using Virtual Nodes requires that axe first be [setup](./lib/core/public/setup.js) and create the Virtual Tree. For the most part, util functions are functions that can be run when no Virtual Tree exists.\n\n### Directory Structure\n\nAxe-core\n\n- `standards` - Data objects of the HTML, WCAG, and ARIA standards and specs.\n- `core/base` - Defines many of the internal object structures for axe-core (rules, checks, virtual node, etc.).\n- `core/imports` - Polyfills or imports from node modules.\n- `core/public` - Functions for how axe is run or configured.\n- `core/reporters` - The different reporters that configure what data is reported from a run.\n- `core/utils` - Utility functions that can be run without needing to set up the Virtual Tree.\n- `commons/aria` - Functions that are used to validate ARIA spec or implement ARIA calculations (role, value, etc.).\n- `commons/color` - Functions that are used to calculate the foreground or background color of an element.\n- `commons/dom` - Functions that access information about the page, DOM, nodes, or its visual state.\n- `commons/forms` - Functions for helping with forms and their associated inputs.\n- `commons/matches` - Functions used to match a virtual node against a special matcher object.\n- `commons/math` - Math functions mainly used for target size and position calculations.\n- `commons/standards` - Functions for querying information from `standards`.\n- `commons/tables` - Functions for helping with data tables.\n- `commons/text` - Functions for calculating the accessible name of an element and handling strings or text related attributes.\n- `rules` - JSON metadata files for each axe-core rule as well as their associated matches functions.\n- `checks` - JSON metadata files for each axe-core check as well as their associated evaluate functions.\n\n### Import Requirements\n\nWhich functions can be imported and how they can be imported depends on where you are trying to import them from. The following is a list of directories and their import restrictions:\n\n- `standards` - Shouldn't use imports as they are just hard coded data objects.\n- `core/utils` - Can import other `core/utils`, `core`, `core/base`, or `standards` functions by direct file path (no import from index files).\n- `core/public` - Can import other `core/public` by direct file path, or any import allowed by `core/utils` by direct file path.\n- `core/imports` - The only files allowed to import from node modules, but shouldn't import from any other directory.\n- `core/reporters` Can import from `core/utils` by import from index files.\n- `commons` - Can import other `commons` by direct file path, or any import allowed by `core/utils` by import from index files.\n- `checks` and `rules` - Can import from any directory by import from index files.\n\n### Shadow DOM\n\nFor any proposed changes to rules, checks, commons, or other APIs to be accepted in axe-core, your code must support open Shadow DOM. See [API.md](./doc/API.md) and the [developer guide](./doc/developer-guide.md) for documentation on the available methods and test utilities. You can also look at existing tests for examples using our APIs.\n\n### Testing\n\nWe expect all code to be 100% covered by tests. We don't have or want code coverage metrics but we will review tests and suggest changes when we think the test(s) do(es) not adequately exercise the code/code changes.\n\nTests should be added to the `test` directory using the same file path and name of the source file the test is for. For example, the source file `lib/commons/text/sanitize.js` should have a test file at `test/commons/text/sanitize.js`.\n\nAxe uses Karma / Mocha / Chai as its testing framework.\n\n### Documentation and Comments\n\nFunctions should contain a preceding comment block with [jsdoc](http://usejsdoc.org/) style documentation of the function. For example:\n\n```js\n/**\n * Runs the Audit; which in turn should call `run` on each rule.\n * @async\n * @param  {Context}   context The scope definition/context for analysis (include/exclude)\n * @param  {Object}    options Options object to pass into rules and/or disable rules or checks\n * @param  {Function} fn       Callback function to fire when audit is complete\n */\n```\n\nClasses should contain a jsdoc comment block for each attribute. For example:\n\n```js\n/**\n * Constructor for the result of checks\n * @param {Object} check CheckResult specification\n */\nfunction CheckResult(check) {\n  /**\n   * ID of the check.  Unique in the context of a rule.\n   * @type {String}\n   */\n  this.id = check.id;\n\n  /**\n   * Any data passed by Check (by calling `this.data()`)\n   * @type {Mixed}\n   */\n  this.data = null;\n\n  /**\n   * Any node that is related to the Check, specified by calling `this.relatedNodes([HTMLElement...])` inside the Check\n   * @type {Array}\n   */\n  this.relatedNodes = [];\n\n  /**\n   * The return value of the Check's evaluate function\n   * @type {Mixed}\n   */\n  this.result = null;\n}\n```\n\n## Setting up your environment\n\nIn order to get going, fork and clone the repository. Then, if you do not have [Node.js](https://nodejs.org/download/) installed, install it!\n\nOnce the basic infrastructure is installed, from the repository root, do the following:\n\n```console\nnpm install\n```\n\nThen build the package:\n\n```console\nnpm run build\n```\n\n## Developing and testing\n\nIn order to run axe tests, `axe.js` must be built using `npm run build`. To run the unit tests:\n\n```console\nnpm test\n```\n\nTo continually watch changes to the axe source files and re-build on changes, use:\n\n```console\nnpm run develop\n```\n\nThis will also rerun any tests that have been changed, and any changes to the axe source files will trigger a rerun of that files tests.\n\nTo run axe integration tests:\n\n```console\nnpm run test:integration\n```\n\nLastly, there are a few other tests that get run during the continuous integration process:\n\n```console\n# run the tests from `doc/examples/*` using the current local build of `axe.js`\nnpm run test:examples\n\n# run the tests from `test/node`\nnpm run test:node\n```\n\n### Running and debugging specific unit tests\n\nIf you want to run a specific set of unit tests instead of all the unit tests, you can use one of the following commands:\n\n```console\n# run just the tests from `test/core`\nnpm run test:unit:core\n\n# run just the tests from `test/commons`\nnpm run test:unit:commons\n\n# run just the tests from `test/rule-matches`\nnpm run test:unit:rule-matches\n\n# run just the tests from `test/checks`\nnpm run test:unit:checks\n\n# run just the tests from `test/integration/rules`\nnpm run test:unit:integration\n\n# run just the tests from `test/integration/api`\nnpm run test:unit:api\n\n# run just the tests from `test/integration/virtual-rules`\nnpm run test:unit:virtual-rules\n```\n\nIf you need to debug the unit tests in a browser, you can run:\n\n```console\nnpm run test:debug\n```\n\nThis will start the Karma server and open up the Chrome browser. Click the `Debug` button to start debugging the tests. You can either use that browser's debugger or attach an external debugger on port 9765; [a VS Code launch profile](./.vscode/launch.json) is provided. You can also navigate to the listed URL in your browser of choice to debug tests using that browser.\n\nBecause the amount of tests is so large, it's recommended to debug only a specific set of unit tests rather than the whole test suite. You can use the `testDirs` argument when using the debug command and pass a specific test directory. The test directory names are the same as those used for `test:unit:*`:\n\n```console\n# accepts a single directory or a comma-separated list of directories\nnpm run test:debug -- testDirs=core,commons\n```\n\n## Using axe with TypeScript\n\n### Axe Development\n\nThe TypeScript definition file for axe-core is distributed with this module and can be found in [axe.d.ts](./axe.d.ts). It currently supports TypeScript 2.0+.\n\nYou can run TypeScript definition tests using the following command:\n\n```console\nnpm run test:tsc\n```\n\n## Including axe's type definition in tests\n\nInstalling axe to run accessibility tests in your TypeScript project should be as simple as importing the module:\n\n```js\nimport * as axe from 'axe-core';\n\ndescribe('Module', () => {\n  it('should have no accessibility violations', done => {\n    axe.run(compiledFixture).then(results => {\n      expect(results.violations.length).toBe(0);\n      done();\n    }, done);\n  });\n});\n```\n\n## Debugging tests that only fail on CircleCI\n\nFirst install an X-Windows client on your machine. XQuartz is a good one.\n\nThen follow the [instructions here to connect the X-Windows on CircleCI to XQuartz](https://circleci.com/docs/1.0/browser-debugging/#x11-forwarding-over-ssh)\n\nStart the build using the \"Retry the build with SSH enabled\" option in the CircleCI interface\n\nCopy the SSH command and add the -X flag to it for example\n\n```console\nssh -X -p 64605 ubuntu@13.58.157.61\n```\n\nWhen you login, set up the environment and start the chrome browser\n\n```console\nexport DISPLAY=localhost:10.0\n/opt/google/chrome/chrome\n```\n\n### .Xauthority does not exist\n\nEdit the ~/.Xauthority file and just save it with the following commands\n\n```console\nvi ~/.Xauthority\n:wq\n```\n\n### Starting the web server\n\nLog into a second ssh terminal (without -X) and execute the following commands\n\n```console\ncd axe-core\ngrunt connect watch\n```\n\nLoad your test file URL in the Chrome browser opened in XQuartz\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "var execSync = require('child_process').execSync;\n\n/*eslint\ncamelcase: [\"error\", {\"properties\": \"never\"}]\n*/\nmodule.exports = function (grunt) {\n  'use strict';\n\n  grunt.loadNpmTasks('grunt-babel');\n  grunt.loadNpmTasks('grunt-contrib-clean');\n  grunt.loadNpmTasks('grunt-contrib-concat');\n  grunt.loadNpmTasks('grunt-contrib-uglify');\n  grunt.loadNpmTasks('grunt-contrib-watch');\n  grunt.loadNpmTasks('grunt-bytesize');\n  grunt.loadTasks('build/tasks');\n\n  var langs;\n  if (grunt.option('lang')) {\n    langs = (grunt.option('lang') || '').split(/[,;]/g).map(function (lang) {\n      lang = lang.trim();\n      return lang !== 'en' ? '.' + lang : '';\n    });\n  } else if (grunt.option('all-lang')) {\n    var localeFiles = require('fs').readdirSync('./locales');\n    langs = localeFiles\n      .filter(function (file) {\n        return !file.startsWith('_') && file.endsWith('.json');\n      })\n      .map(function (file) {\n        return '.' + file.replace('.json', '');\n      });\n    langs.unshift(''); // Add default\n  } else {\n    langs = [''];\n  }\n\n  // run tests only for affected files instead of all tests\n  grunt.event.on('watch', function (action, filepath) {\n    grunt.config.set('watch.file', filepath);\n  });\n\n  process.env.NODE_NO_HTTP2 = 1; // to hide node warning - (node:18740) ExperimentalWarning: The http2 module is an experimental API.\n\n  grunt.initConfig({\n    pkg: grunt.file.readJSON('package.json'),\n    clean: {\n      core: ['dist', 'tmp/core', 'tmp/rules.js', 'axe.js', 'axe.*.js'],\n      tests: ['tmp/integration-tests.js']\n    },\n    babel: {\n      options: {\n        compact: false\n      },\n      core: {\n        files: [\n          {\n            expand: true,\n            cwd: 'lib/core',\n            src: ['index.js'],\n            dest: 'tmp/core'\n          }\n        ]\n      },\n      misc: {\n        files: [\n          {\n            expand: true,\n            cwd: 'tmp',\n            src: ['**/*.js'],\n            dest: 'tmp'\n          }\n        ]\n      }\n    },\n    'update-help': {\n      options: {\n        version: '<%=pkg.version%>'\n      },\n      rules: {\n        src: ['lib/rules/**/*.json']\n      }\n    },\n    concat: {\n      engine: {\n        options: {\n          process: true\n        },\n        coreFiles: ['tmp/core/index.js', 'tmp/core/**/*.js'],\n        files: langs.map(function (lang, i) {\n          return {\n            src: [\n              'lib/intro.stub',\n              '<%= concat.engine.coreFiles %>',\n              // include rules / checks / commons\n              '<%= configure.rules.files[' + i + '].dest.auto %>',\n              'lib/outro.stub'\n            ],\n            dest: 'axe' + lang + '.js'\n          };\n        })\n      }\n    },\n    esbuild: {\n      core: {\n        files: [\n          {\n            expand: true,\n            cwd: 'lib/core',\n            src: ['core.js'],\n            dest: 'tmp/core'\n          }\n        ]\n      }\n    },\n    'metadata-function-map': {\n      core: {\n        files: [\n          {\n            expand: true,\n            src: [\n              'lib/checks/**/*-{evaluate,after}.js',\n              'lib/rules/**/*-matches.js'\n            ],\n            dest: 'lib/core/base/metadata-function-map.js'\n          }\n        ]\n      }\n    },\n    'aria-supported': {\n      data: {\n        entry: 'lib/commons/aria/index.js',\n        destFile: 'doc/aria-supported.md',\n        options: {\n          langs: langs\n        },\n        listType: 'unsupported' // Possible values for listType: 'supported', 'unsupported', 'all'\n      }\n    },\n    configure: {\n      rules: {\n        tmp: 'tmp/rules.js',\n        options: {\n          tags: grunt.option('tags')\n        },\n        files: langs.map(function (lang) {\n          return {\n            src: [''],\n            dest: {\n              auto: 'tmp/rules' + lang + '.js',\n              descriptions: 'doc/rule-descriptions' + lang + '.md'\n            }\n          };\n        })\n      }\n    },\n    'add-locale': {\n      template: {\n        options: {\n          lang: 'xyz'\n        },\n        src: ['tmp/core/core.js'],\n        dest: './locales/_template.json'\n      },\n      newLang: {\n        options: {\n          lang: grunt.option('lang')\n        },\n        src: ['tmp/core/core.js'],\n        dest: './locales/' + (grunt.option('lang') || 'new-locale') + '.json'\n      }\n    },\n    langs: {\n      generate: {\n        check: 'lib/commons/utils/valid-langs'\n      }\n    },\n    validate: {\n      check: {\n        options: {\n          type: 'check'\n        },\n        src: 'lib/checks/**/*.json'\n      },\n      rule: {\n        options: {\n          type: 'rule'\n        },\n        src: 'lib/rules/**/*.json'\n      }\n    },\n    uglify: {\n      beautify: {\n        files: langs.map(function (lang, i) {\n          return {\n            src: ['<%= concat.engine.files[' + i + '].dest %>'],\n            dest: '<%= concat.engine.files[' + i + '].dest %>'\n          };\n        }),\n        options: {\n          mangle: false,\n          compress: false,\n          beautify: {\n            beautify: true,\n            ascii_only: true,\n            indent_level: 2,\n            braces: true,\n            quote_style: 1\n          },\n          output: {\n            comments: /^\\/*! axe/\n          }\n        }\n      },\n      minify: {\n        files: langs.map(function (lang, i) {\n          return {\n            src: ['<%= concat.engine.files[' + i + '].dest %>'],\n            dest: './axe' + lang + '.min.js'\n          };\n        }),\n        options: {\n          output: {\n            comments: /^\\/*! axe/\n          },\n          mangle: {\n            reserved: ['commons', 'utils', 'axe', 'window', 'document']\n          }\n        }\n      }\n    },\n    test: {\n      data: {\n        testFile: '<%= watch.file %>'\n      }\n    },\n    watch: {\n      axe: {\n        options: { spawn: false },\n        files: ['lib/**/*', 'Gruntfile.js'],\n        tasks: ['build', 'prettier', 'notify', 'test']\n      },\n      tests: {\n        options: { spawn: false },\n        files: ['test/**/*'],\n        tasks: ['test']\n      }\n    },\n    notify: {\n      data: {\n        title: 'Axe-core',\n        message: 'Build complete',\n        sound: 'Pop',\n        timeout: 2\n      }\n    },\n    bytesize: {\n      all: {\n        src: langs.map(function (lang) {\n          return ['./axe' + lang + '.js', './axe' + lang + '.min.js'];\n        })\n      }\n    }\n  });\n\n  grunt.registerTask('prettier', '', function () {\n    const results = execSync('npm run postbuild');\n    grunt.log.writeln(results);\n  });\n\n  grunt.registerTask('translate', [\n    'validate',\n    'esbuild',\n    'add-locale:newLang'\n  ]);\n  grunt.registerTask('build', [\n    'clean:core',\n    'validate',\n    'metadata-function-map',\n    'esbuild',\n    'configure',\n    'babel',\n    'concat:engine',\n    'uglify',\n    'aria-supported',\n    'add-locale:template',\n    'prettier',\n    'bytesize'\n  ]);\n  grunt.registerTask('default', ['build']);\n  grunt.registerTask('dev', ['watch']);\n};\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n     means each individual or legal entity that creates, contributes to the\n     creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n     means the combination of the Contributions of others (if any) used by a\n     Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n     means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n     means Source Code Form to which the initial Contributor has attached the\n     notice in Exhibit A, the Executable Form of such Source Code Form, and\n     Modifications of such Source Code Form, in each case including portions\n     thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n     means\n\n     a. that the initial Contributor has attached the notice described in\n        Exhibit B to the Covered Software; or\n\n     b. that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the terms of\n        a Secondary License.\n\n1.6. \"Executable Form\"\n\n     means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n     means a work that combines Covered Software with other material, in a\n     separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n     means this document.\n\n1.9. \"Licensable\"\n\n     means having the right to grant, to the maximum extent possible, whether\n     at the time of the initial grant or subsequently, any and all of the\n     rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n     means any of the following:\n\n     a. any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered Software; or\n\n     b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n      means any patent claim(s), including without limitation, method,\n      process, and apparatus claims, in any patent Licensable by such\n      Contributor that would be infringed, but for the grant of the License,\n      by the making, using, selling, offering for sale, having made, import,\n      or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n      means either the GNU General Public License, Version 2.0, the GNU Lesser\n      General Public License, Version 2.1, the GNU Affero General Public\n      License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n      means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n      means an individual or a legal entity exercising rights under this\n      License. For legal entities, \"You\" includes any entity that controls, is\n      controlled by, or is under common control with You. For purposes of this\n      definition, \"control\" means (a) the power, direct or indirect, to cause\n      the direction or management of such entity, whether by contract or\n      otherwise, or (b) ownership of more than fifty percent (50%) of the\n      outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n     Each Contributor hereby grants You a world-wide, royalty-free,\n     non-exclusive license:\n\n     a. under intellectual property rights (other than patent or trademark)\n        Licensable by such Contributor to use, reproduce, make available,\n        modify, display, perform, distribute, and otherwise exploit its\n        Contributions, either on an unmodified basis, with Modifications, or\n        as part of a Larger Work; and\n\n     b. under Patent Claims of such Contributor to make, use, sell, offer for\n        sale, have made, import, and otherwise transfer either its\n        Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n     The licenses granted in Section 2.1 with respect to any Contribution\n     become effective for each Contribution on the date the Contributor first\n     distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n     The licenses granted in this Section 2 are the only rights granted under\n     this License. No additional rights or licenses will be implied from the\n     distribution or licensing of Covered Software under this License.\n     Notwithstanding Section 2.1(b) above, no patent license is granted by a\n     Contributor:\n\n     a. for any code that a Contributor has removed from Covered Software; or\n\n     b. for infringements caused by: (i) Your and any other third party's\n        modifications of Covered Software, or (ii) the combination of its\n        Contributions with other software (except as part of its Contributor\n        Version); or\n\n     c. under Patent Claims infringed by Covered Software in the absence of\n        its Contributions.\n\n     This License does not grant any rights in the trademarks, service marks,\n     or logos of any Contributor (except as may be necessary to comply with\n     the notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n     No Contributor makes additional grants as a result of Your choice to\n     distribute the Covered Software under a subsequent version of this\n     License (see Section 10.2) or under the terms of a Secondary License (if\n     permitted under the terms of Section 3.3).\n\n2.5. Representation\n\n     Each Contributor represents that the Contributor believes its\n     Contributions are its original creation(s) or it has sufficient rights to\n     grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n     This License is not intended to limit any rights You have under\n     applicable copyright doctrines of fair use, fair dealing, or other\n     equivalents.\n\n2.7. Conditions\n\n     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n     Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n     All distribution of Covered Software in Source Code Form, including any\n     Modifications that You create or to which You contribute, must be under\n     the terms of this License. You must inform recipients that the Source\n     Code Form of the Covered Software is governed by the terms of this\n     License, and how they can obtain a copy of this License. You may not\n     attempt to alter or restrict the recipients' rights in the Source Code\n     Form.\n\n3.2. Distribution of Executable Form\n\n     If You distribute Covered Software in Executable Form then:\n\n     a. such Covered Software must also be made available in Source Code Form,\n        as described in Section 3.1, and You must inform recipients of the\n        Executable Form how they can obtain a copy of such Source Code Form by\n        reasonable means in a timely manner, at a charge no more than the cost\n        of distribution to the recipient; and\n\n     b. You may distribute such Executable Form under the terms of this\n        License, or sublicense it under different terms, provided that the\n        license for the Executable Form does not attempt to limit or alter the\n        recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n     You may create and distribute a Larger Work under terms of Your choice,\n     provided that You also comply with the requirements of this License for\n     the Covered Software. If the Larger Work is a combination of Covered\n     Software with a work governed by one or more Secondary Licenses, and the\n     Covered Software is not Incompatible With Secondary Licenses, this\n     License permits You to additionally distribute such Covered Software\n     under the terms of such Secondary License(s), so that the recipient of\n     the Larger Work may, at their option, further distribute the Covered\n     Software under the terms of either this License or such Secondary\n     License(s).\n\n3.4. Notices\n\n     You may not remove or alter the substance of any license notices\n     (including copyright notices, patent notices, disclaimers of warranty, or\n     limitations of liability) contained within the Source Code Form of the\n     Covered Software, except that You may alter any license notices to the\n     extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n     You may choose to offer, and to charge a fee for, warranty, support,\n     indemnity or liability obligations to one or more recipients of Covered\n     Software. However, You may do so only on Your own behalf, and not on\n     behalf of any Contributor. You must make it absolutely clear that any\n     such warranty, support, indemnity, or liability obligation is offered by\n     You alone, and You hereby agree to indemnify every Contributor for any\n     liability incurred by such Contributor as a result of warranty, support,\n     indemnity or liability terms You offer. You may include additional\n     disclaimers of warranty and limitations of liability specific to any\n     jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n   If it is impossible for You to comply with any of the terms of this License\n   with respect to some or all of the Covered Software due to statute,\n   judicial order, or regulation then You must: (a) comply with the terms of\n   this License to the maximum extent possible; and (b) describe the\n   limitations and the code they affect. Such description must be placed in a\n   text file included with all distributions of the Covered Software under\n   this License. Except to the extent prohibited by statute or regulation,\n   such description must be sufficiently detailed for a recipient of ordinary\n   skill to be able to understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n     fail to comply with any of its terms. However, if You become compliant,\n     then the rights granted under this License from a particular Contributor\n     are reinstated (a) provisionally, unless and until such Contributor\n     explicitly and finally terminates Your grants, and (b) on an ongoing\n     basis, if such Contributor fails to notify You of the non-compliance by\n     some reasonable means prior to 60 days after You have come back into\n     compliance. Moreover, Your grants from a particular Contributor are\n     reinstated on an ongoing basis if such Contributor notifies You of the\n     non-compliance by some reasonable means, this is the first time You have\n     received notice of non-compliance with this License from such\n     Contributor, and You become compliant prior to 30 days after Your receipt\n     of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n     infringement claim (excluding declaratory judgment actions,\n     counter-claims, and cross-claims) alleging that a Contributor Version\n     directly or indirectly infringes any patent, then the rights granted to\n     You by any and all Contributors for the Covered Software under Section\n     2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n     license agreements (excluding distributors and resellers) which have been\n     validly granted by You or Your distributors under this License prior to\n     termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n   Covered Software is provided under this License on an \"as is\" basis,\n   without warranty of any kind, either expressed, implied, or statutory,\n   including, without limitation, warranties that the Covered Software is free\n   of defects, merchantable, fit for a particular purpose or non-infringing.\n   The entire risk as to the quality and performance of the Covered Software\n   is with You. Should any Covered Software prove defective in any respect,\n   You (not any Contributor) assume the cost of any necessary servicing,\n   repair, or correction. This disclaimer of warranty constitutes an essential\n   part of this License. No use of  any Covered Software is authorized under\n   this License except under this disclaimer.\n\n7. Limitation of Liability\n\n   Under no circumstances and under no legal theory, whether tort (including\n   negligence), contract, or otherwise, shall any Contributor, or anyone who\n   distributes Covered Software as permitted above, be liable to You for any\n   direct, indirect, special, incidental, or consequential damages of any\n   character including, without limitation, damages for lost profits, loss of\n   goodwill, work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses, even if such party shall have been\n   informed of the possibility of such damages. This limitation of liability\n   shall not apply to liability for death or personal injury resulting from\n   such party's negligence to the extent applicable law prohibits such\n   limitation. Some jurisdictions do not allow the exclusion or limitation of\n   incidental or consequential damages, so this exclusion and limitation may\n   not apply to You.\n\n8. Litigation\n\n   Any litigation relating to this License may be brought only in the courts\n   of a jurisdiction where the defendant maintains its principal place of\n   business and such litigation shall be governed by laws of that\n   jurisdiction, without reference to its conflict-of-law provisions. Nothing\n   in this Section shall prevent a party's ability to bring cross-claims or\n   counter-claims.\n\n9. Miscellaneous\n\n   This License represents the complete agreement concerning the subject\n   matter hereof. If any provision of this License is held to be\n   unenforceable, such provision shall be reformed only to the extent\n   necessary to make it enforceable. Any law or regulation which provides that\n   the language of a contract shall be construed against the drafter shall not\n   be used to construe this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n      Mozilla Foundation is the license steward. Except as provided in Section\n      10.3, no one other than the license steward has the right to modify or\n      publish new versions of this License. Each version will be given a\n      distinguishing version number.\n\n10.2. Effect of New Versions\n\n      You may distribute the Covered Software under the terms of the version\n      of the License under which You originally received the Covered Software,\n      or under the terms of any subsequent version published by the license\n      steward.\n\n10.3. Modified Versions\n\n      If you create software not governed by this License, and you want to\n      create a new license for such software, you may create and use a\n      modified version of this License if you rename the license and remove\n      any references to the name of the license steward (except to note that\n      such modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\n      Licenses If You choose to distribute Source Code Form that is\n      Incompatible With Secondary Licenses under the terms of this version of\n      the License, the notice described in Exhibit B of this License must be\n      attached.\n\nExhibit A - Source Code Form License Notice\n\n      This Source Code Form is subject to the\n      terms of the Mozilla Public License, v.\n      2.0. If a copy of the MPL was not\n      distributed with this file, You can\n      obtain one at\n      http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a\nnotice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n      This Source Code Form is \"Incompatible\n      With Secondary Licenses\", as defined by\n      the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "LICENSE-3RD-PARTY.txt",
    "content": "-----------------------------------------------------------------------------\n                              MIT License\n  Applies to: \n  - colorjs.io; Copyright (c) 2021 Lea Verou, Chris Lilley\n  - core-js-pure; Copyright (c) 2014-2023 Denis Pushkarev\n  - css-selector-parser; Copyright (c) 2013 Dulin Marat\n  - doT.js; Copyright (c) 2011 Laura Doktorova\n      Software includes portions from jQote2 Copyright (c) 2010 aefxx,\n      http://aefxx.com/ licensed under the MIT license.\n  - emoji-regex; Copyright (c) Mathias Bynens <https://mathiasbynens.be/>\n  - es6-iterator; Copyright (c) 2013-2017 Mariusz Nowak (www.medikoo.com)\n  - es6-promise;\n      Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors\n  - event-emitter; Copyright (C) 2012-2015 Mariusz Nowak (www.medikoo.com)\n  - is-promise; Copyright (c) 2014 Forbes Lindesay\n  - lru-queue; Copyright (C) 2014 Mariusz Nowak (www.medikoo.com)\n  - typedarray;\n      Copyright (c) 2010, Linden Research, Inc.\n      Copyright (c) 2012, Joshua Bell\n  - weakmap-polyfill; Copyright (c) 2015-2021 polygonplanet\n-----------------------------------------------------------------------------\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n\n-----------------------------------------------------------------------------\n                              ISC License\n  Applies to: \n  - d; Copyright (c) 2013-2019, Mariusz Nowak, @medikoo, medikoo.com\n  - es5-ext; Copyright (c) 2011-2022, Mariusz Nowak, @medikoo, medikoo.com\n  - es6-symbol; Copyright (c) 2013-2019, Mariusz Nowak, @medikoo, medikoo.com\n  - es6-weak-map; Copyright (c) 2013-2018, Mariusz Nowak, @medikoo, medikoo.com\n  - ext; Copyright (c) 2011-2022, Mariusz Nowak, @medikoo, medikoo.com\n  - memoizee; Copyright (c) 2012-2018, Mariusz Nowak, @medikoo, medikoo.com\n  - next-tick; Copyright (c) 2012-2020, Mariusz Nowak, @medikoo, medikoo.com\n  - timers-ext; Copyright (c) 2013-2018, Mariusz Nowak, @medikoo, medikoo.com\n  - type; Copyright (c) 2019, Mariusz Nowak, @medikoo, medikoo.com\n-----------------------------------------------------------------------------\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\nOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# axe-core\n\n[![License](https://img.shields.io/npm/l/axe-core.svg?color=c41)](LICENSE)\n[![Version](https://img.shields.io/npm/v/axe-core.svg)](https://www.npmjs.com/package/axe-core)\n[![NPM downloads](https://img.shields.io/npm/dw/axe-core.svg?color=080)![](https://img.shields.io/npm/dy/axe-core.svg?color=080&label=)](https://npm-stat.com/charts.html?package=axe-core&from=2017-01-01)\n[![Commits](https://img.shields.io/github/commit-activity/y/dequelabs/axe-core.svg)](https://github.com/dequelabs/axe-core/commits/develop)\n[![GitHub contributors](https://img.shields.io/github/contributors/dequelabs/axe-core.svg?color=080)](https://github.com/dequelabs/axe-core/graphs/contributors)\n[![Join our Slack chat](https://img.shields.io/badge/slack-chat-purple.svg?logo=slack)](https://accessibility.deque.com/axe-community)\n[![Package Quality](https://npm.packagequality.com/shield/axe-core.svg)](https://packagequality.com/#?package=axe-core)\n\nAxe is an accessibility testing engine for websites and other HTML-based user interfaces. It's fast, secure, lightweight, and was built to seamlessly integrate with any existing test environment so you can automate accessibility testing alongside your regular functional testing.\n\n[Sign up for axe news](https://hubs.ly/H0fsN0b0) to get the latest on axe features, future releases, and events.\n\n## The Accessibility Rules\n\nAxe-core has different types of rules, for WCAG 2.0, 2.1, 2.2 on level A, AA and AAA as well as a number of best practices that help you identify common accessibility practices like ensuring every page has an `h1` heading, and to help you avoid \"gotchas\" in ARIA like where an ARIA attribute you used will get ignored. The complete list of rules, grouped WCAG level and best practice, can be found in [doc/rule-descriptions.md](./doc/rule-descriptions.md).\n\nWith axe-core, you can find **on average 57% of WCAG issues automatically**. Additionally, axe-core will return elements as \"incomplete\" where axe-core could not be certain, and manual review is needed.\n\nTo catch bugs earlier in the development cycle we recommend using the [axe-linter vscode extension](https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter). To improve test coverage even further we recommend the [intelligent guided tests](https://www.youtube.com/watch?v=AtsX0dPCG_4&feature=youtu.be&ab_channel=DequeSystems) in the [axe Extension](https://www.deque.com/axe/browser-extensions/).\n\n## Getting started\n\nFirst download the package:\n\n```console\nnpm install axe-core --save-dev\n```\n\nNow include the javascript file in each of your iframes in your fixtures or test systems:\n\n```html\n<script src=\"node_modules/axe-core/axe.min.js\"></script>\n```\n\nNow insert calls at each point in your tests where a new piece of UI becomes visible or exposed:\n\n```js\naxe\n  .run()\n  .then(results => {\n    if (results.violations.length) {\n      throw new Error('Accessibility issues found');\n    }\n  })\n  .catch(err => {\n    console.error('Something bad happened:', err.message);\n  });\n```\n\n## Philosophy\n\nThe web can only become an accessible, inclusive space if developers are empowered to take responsibility for accessibility testing and accessible coding practices.\n\nAutomated accessibility testing is a huge timesaver, it doesn't require special expertise, and it allows teams to focus expert resources on the accessibility issues that really need them. Unfortunately, most accessibility tools are meant to be run on sites and applications that have reached the end of the development process and often don't give clear or consistent results, causing frustration and delays just when you thought your product was ready to ship.\n\nAxe was built to reflect how web development actually works. It works with all modern browsers, tools, and testing environments a dev team might use. With axe, accessibility testing can be performed as part of your unit testing, integration testing, browser testing, and any other functional testing your team already performs on a day-to-day basis. Building accessibility testing into the early development process saves time, resources, and all kinds of frustration.\n\n## About axe - our Manifesto\n\n- Axe is open source.\n- It returns zero false positives (bugs notwithstanding).\n- It's designed to work on all modern browsers and with whatever tools, frameworks, libraries and environments you use today.\n- It's actively supported by [Deque Systems](https://www.deque.com), a major accessibility vendor.\n- It integrates with your existing functional/acceptance automated tests.\n- It automatically determines which rules to run based on the evaluation context.\n- Axe supports in-memory fixtures, static fixtures, integration tests, and iframes of infinite depth.\n- Axe is highly configurable.\n\n## Supported Browsers\n\nThe [axe-core API](doc/API.md) fully supports the following browsers:\n\n- Microsoft Edge v40 and above\n- Google Chrome v42 and above\n- Mozilla Firefox v38 and above\n- Apple Safari v7 and above\n- Internet Explorer v11 (DEPRECATED)\n\nSupport means that we will fix bugs and attempt to test each browser regularly. Only Chrome and Firefox are currently tested on every pull request.\n\nThere is limited support for JSDOM. We will attempt to make all rules compatible with JSDOM but where this is not possible, we recommend turning those rules off. Currently the `color-contrast` rule is known not to work with JSDOM.\n\nWe can only support environments where features are either natively supported or polyfilled correctly. We do not support the deprecated v0 Shadow DOM implementation.\n\n## Contents of the API Package\n\nThe [axe-core API](doc/API.md) package consists of:\n\n- `axe.js` - the JavaScript file that should be included in your web site under test (API)\n- `axe.min.js` - a minified version of the above file\n\n## Localization\n\nAxe can be built using your local language. To do so, a localization file must be added to the `./locales` directory. This file must be named in the following manner: `<langcode>.json`. To build axe using this locale, instead of the default, run axe with the `--lang` flag, like so:\n\n`grunt build --lang=nl`\n\nor equivalently:\n\n`npm run build -- --lang=nl`\n\nThis will create a new build for axe, called `axe.<lang>.js` and `axe.<lang>.min.js`. If you want to build all localized versions, simply pass in `--all-lang` instead. If you want to build multiple localized versions (but not all of them), you can pass in a comma-separated list of languages to the `--lang` flag, like `--lang=nl,ja`.\n\nTo create a new translation for axe, start by running `grunt translate --lang=<langcode>`. This will create a json file in the `./locales` directory, with the default English text in it for you to translate. Alternatively, you could copy `./locales/_template.json`. We welcome any localization for axe-core. For details on how to contribute, see the Contributing section below. For details on the message syntax, see [Check Message Template](/doc/check-message-template.md).\n\nTo update an existing translation file, re-run `grunt translate --lang=<langcode>`. This will add new messages used in English and remove messages which were not used in English.\n\nAdditionally, locale can be applied at runtime by passing a `locale` object to `axe.configure()`. The locale object must be of the same shape as existing locales in the `./locales` directory. For example:\n\n```js\naxe.configure({\n  locale: {\n    lang: 'de',\n    rules: {\n      accesskeys: {\n        help: 'Der Wert des accesskey-Attributes muss einzigartig sein.'\n      }\n      // ...\n    },\n    checks: {\n      abstractrole: {\n        fail: 'Abstrakte ARIA-Rollen dürfen nicht direkt verwendet werden.'\n      },\n      'aria-errormessage': {\n        // Note: doT (https://github.com/olado/dot) templates are supported here.\n        fail: 'Der Wert der aria-errormessage ${data.values}` muss eine Technik verwenden, um die Message anzukündigen (z. B., aria-live, aria-describedby, role=alert, etc.).'\n      }\n      // ...\n    }\n  }\n});\n```\n\n### Supported Locales\n\nAxe-core supports the following locales. Do note that since locales are contributed by our community, they are not guaranteed to include all translations needed in a release.\n\n- Basque\n- Chinese (Simplified)\n- Chinese (Traditional)\n- Danish\n- Dutch\n- French\n- German\n- Greek\n- Hebrew\n- Italian\n- Japanese\n- Korean\n- Norwegian (Bokmål)\n- Polish\n- Portuguese (Brazilian)\n- Spanish\n\n## Updates & Security\n\nAxe-core has a new minor release every 3 to 5 months, which usually introduces new rules and features. We recommend scheduling time to upgrade to these versions. Security updates will be made available for minor version lines up to **18 months old**.\n\n- See [release and support](doc/release-and-support.md) for details on the frequency of releases, long-term support and recommendations on upgrading axe-core.\n- See [backward compatibility](doc/backwards-compatibility-doc.md) for details on the types of changes different releases may introduce.\n\n## Deque Trademarks Policy\n\nDEQUE, DEQUELABS, AXE®, and AXE-CORE® are trademarks of Deque Systems, Inc. Use of the Deque trademarks must be in accordance with [Deque's trademark policy](https://www.deque.com/legal/trademarks/).\n\n## Supported ARIA Roles and Attributes.\n\nRefer [axe-core ARIA support](./doc/aria-supported.md) for a complete list of ARIA supported roles and attributes by axe.\n\n## Contributing\n\nRead the [Proposing Axe-core Rules guide](./doc/rule-proposal.md)\n\nRead the [documentation on the architecture](./doc/developer-guide.md)\n\nRead the [documentation on contributing](CONTRIBUTING.md)\n\n## Projects using axe-core\n\n[List of projects using axe-core](doc/projects.md)\n\n## Acknowledgements\n\nThanks to Marat Dulin for his [css-selector-parser](https://www.npmjs.com/package/css-selector-parser) implementation which is included for shadow DOM support. Another thank you to the [Slick Parser](https://github.com/mootools/slick/blob/master/Source/Slick.Parser.js) implementers for their contribution, we have used some of their algorithms in our shadow DOM support code. Thanks to Lea Verou and Chris Lilley for their [colorjs.io](https://colorjs.io/) library which we have used for converting between color formats.\n\n## Licenses\n\nAxe-core is distributed under the [Mozilla Public License, version 2.0](LICENSE). It comes bundled with several dependencies which are distributed under their own terms. (See [LICENSE-3RD-PARTY.txt](LICENSE-3RD-PARTY.txt))\n"
  },
  {
    "path": "SECURITY.md",
    "content": "## Security\n\nDeque takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organization [Dequelabs](https://github.com/dequelabs).\n\nIf you believe you have found a security vulnerability in any Deque-owned repository that meets [Deque's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, email them to Deque Security at [security@deque.com](mailto:security@deque.com).\n\nYou should receive a response within 24 business hours. If for some reason you do not, please follow up via email to ensure we received your original message.\n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n- Full paths of source file(s) related to the manifestation of the issue\n- The location of the affected source code (tag/branch/commit or direct URL)\n- Any special configuration required to reproduce the issue (e.g. OS, browser, settings, options etc.)\n- Step-by-step instructions to reproduce the issue\n- Proof-of-concept or exploit code (if possible)\n- Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nWe currently do not have a bug bounty program.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n"
  },
  {
    "path": "axe-linter.yml",
    "content": "# See: https://marketplace.visualstudio.com/items?itemName=deque-systems.vscode-axe-linter&ssr=false#overview\nexclude: test/**/*\n"
  },
  {
    "path": "axe.d.ts",
    "content": "// Type definitions for axe-core\n// Project: https://github.com/dequelabs/axe-core\n\ndeclare namespace axe {\n  type ImpactValue = 'minor' | 'moderate' | 'serious' | 'critical' | null;\n\n  type TagValue = string;\n\n  type ReporterVersion = 'v1' | 'v2' | 'raw' | 'rawEnv' | 'no-passes';\n\n  type RunOnlyType = 'rule' | 'rules' | 'tag' | 'tags';\n\n  type resultGroups = 'inapplicable' | 'passes' | 'incomplete' | 'violations';\n\n  type AriaAttrsType =\n    | 'boolean'\n    | 'nmtoken'\n    | 'mntokens'\n    | 'idref'\n    | 'idrefs'\n    | 'string'\n    | 'decimal'\n    | 'int';\n\n  type AriaRolesType = 'abstract' | 'widget' | 'structure' | 'landmark';\n\n  type DpubRolesType =\n    | 'section'\n    | 'landmark'\n    | 'link'\n    | 'listitem'\n    | 'img'\n    | 'navigation'\n    | 'note'\n    | 'separator'\n    | 'none'\n    | 'sectionhead';\n\n  type HtmlContentTypes =\n    | 'flow'\n    | 'sectioning'\n    | 'heading'\n    | 'phrasing'\n    | 'embedded'\n    | 'interactive';\n\n  // Array of length 2 or greater\n  type MultiArray<T> = [T, T, ...T[]];\n\n  // Selectors within a frame\n  type BaseSelector = string;\n\n  type ShadowDomSelector = MultiArray<BaseSelector>;\n  type CrossTreeSelector = BaseSelector | ShadowDomSelector;\n  type LabelledShadowDomSelector = { fromShadowDom: ShadowDomSelector };\n\n  // Cross-frame selectors\n  type FramesSelector = Array<CrossTreeSelector | LabelledShadowDomSelector>;\n  type UnlabelledFrameSelector = CrossTreeSelector[];\n  type LabelledFramesSelector = { fromFrames: MultiArray<FramesSelector[0]> };\n  /**\n   * @deprecated Use UnlabelledFrameSelector instead\n   */\n  type CrossFrameSelector = UnlabelledFrameSelector;\n\n  // Context options\n  type Selector =\n    | Node\n    | BaseSelector\n    | LabelledShadowDomSelector\n    | LabelledFramesSelector;\n  type SelectorList = Array<Selector | FramesSelector> | NodeList;\n  type ContextProp = Selector | SelectorList;\n  type ContextObject =\n    | {\n        include: ContextProp;\n        exclude?: ContextProp;\n      }\n    | {\n        exclude: ContextProp;\n        include?: ContextProp;\n      };\n  type ContextSpec = ContextProp | ContextObject;\n  /** Synonym to ContextSpec */\n  type ElementContext = ContextSpec;\n\n  type SerialSelector =\n    | BaseSelector\n    | LabelledShadowDomSelector\n    | LabelledFramesSelector;\n  type SerialFrameSelector = SerialSelector | FramesSelector;\n  type SerialSelectorList = Array<SerialFrameSelector>;\n\n  type SerialContextObject =\n    | {\n        include: SerialSelector | SerialSelectorList;\n        exclude?: SerialSelector | SerialSelectorList;\n      }\n    | {\n        exclude: SerialSelector | SerialSelectorList;\n        include?: SerialSelector | SerialSelectorList;\n      };\n\n  interface FrameContextObject {\n    include: UnlabelledFrameSelector[];\n    exclude: UnlabelledFrameSelector[];\n  }\n\n  type RunCallback<T = AxeResults> = (error: Error, results: T) => void;\n\n  interface TestEngine {\n    name: string;\n    version: string;\n  }\n  interface TestRunner {\n    name: string;\n  }\n  interface TestEnvironment {\n    userAgent: string;\n    windowWidth: number;\n    windowHeight: number;\n    orientationAngle?: number;\n    orientationType?: string;\n  }\n  interface RunOnly {\n    type: RunOnlyType;\n    values: TagValue[] | string[];\n  }\n  interface RuleObject {\n    [key: string]: {\n      enabled: boolean;\n    };\n  }\n  interface RunOptions {\n    runOnly?: RunOnly | TagValue[] | string[] | string;\n    rules?: RuleObject;\n    reporter?: ReporterVersion | string;\n    resultTypes?: resultGroups[];\n    selectors?: boolean;\n    ancestry?: boolean;\n    xpath?: boolean;\n    absolutePaths?: boolean;\n    iframes?: boolean;\n    elementRef?: boolean;\n    frameWaitTime?: number;\n    preload?: boolean | PreloadOptions;\n    performanceTimer?: boolean;\n    pingWaitTime?: number;\n  }\n  interface NormalizedRunOptions extends RunOptions {\n    runOnly?: RunOnly;\n  }\n  interface PreloadOptions {\n    assets: string[];\n    timeout?: number;\n  }\n  interface AxeResults extends EnvironmentData {\n    toolOptions: RunOptions;\n    passes: Result[];\n    violations: Result[];\n    incomplete: IncompleteResult[];\n    inapplicable: Result[];\n  }\n  interface Result {\n    description: string;\n    help: string;\n    helpUrl: string;\n    id: string;\n    impact?: ImpactValue;\n    tags: TagValue[];\n    nodes: NodeResult[];\n  }\n  interface IncompleteResult extends Result {\n    error?: Omit<RuleError, 'errorNode'>;\n  }\n  interface NodeResult {\n    html: string;\n    impact?: ImpactValue;\n    target: UnlabelledFrameSelector;\n    xpath?: string[];\n    ancestry?: UnlabelledFrameSelector;\n    any: CheckResult[];\n    all: CheckResult[];\n    none: CheckResult[];\n    failureSummary?: string;\n    element?: HTMLElement;\n  }\n  interface CheckResult {\n    id: string;\n    impact: string;\n    message: string;\n    data: any;\n    relatedNodes?: RelatedNode[];\n  }\n  interface RelatedNode {\n    html: string;\n    target: UnlabelledFrameSelector;\n    xpath?: string[];\n    ancestry?: UnlabelledFrameSelector;\n    element?: HTMLElement;\n  }\n  interface RuleLocale {\n    [key: string]: {\n      description: string;\n      help: string;\n    };\n  }\n  interface CheckMessages {\n    pass: string | { [key: string]: string };\n    fail: string | { [key: string]: string };\n    incomplete?: string | { [key: string]: string };\n  }\n  interface RuleError {\n    name: string;\n    message: string;\n    stack: string;\n    ruleId?: string;\n    method?: string;\n    cause?: SerialError;\n    errorNode?: SerialDqElement;\n  }\n  interface SerialError {\n    message: string;\n    stack: string;\n    name: string;\n    cause?: SerialError;\n  }\n  interface CheckLocale {\n    [key: string]: CheckMessages;\n  }\n  interface Locale {\n    lang?: string;\n    rules?: RuleLocale;\n    checks?: CheckLocale;\n  }\n  interface AriaAttrs {\n    type: AriaAttrsType;\n    values?: string[];\n    allowEmpty?: boolean;\n    global?: boolean;\n    unsupported?: boolean;\n  }\n  interface AriaRoles {\n    type: AriaRolesType | DpubRolesType;\n    requiredContext?: string[];\n    requiredOwned?: string[];\n    requiredAttrs?: string[];\n    allowedAttrs?: string[];\n    nameFromContent?: boolean;\n    unsupported?: boolean;\n  }\n  interface HtmlElmsVariant {\n    contentTypes?: HtmlContentTypes[];\n    allowedRoles: boolean | string[];\n    noAriaAttrs?: boolean;\n    shadowRoot?: boolean;\n    implicitAttrs?: { [key: string]: string };\n    namingMethods?: string[];\n  }\n  interface HtmlElms extends HtmlElmsVariant {\n    variant?: { [key: string]: HtmlElmsVariant };\n  }\n  interface Standards {\n    ariaAttrs?: { [key: string]: AriaAttrs };\n    ariaRoles?: { [key: string]: AriaRoles };\n    htmlElms?: { [key: string]: HtmlElms };\n    cssColors?: { [key: string]: number[] };\n  }\n  interface Spec {\n    branding?: string | Branding;\n    reporter?: ReporterVersion | string | AxeReporter;\n    checks?: Check[];\n    rules?: Rule[];\n    standards?: Standards;\n    locale?: Locale;\n    disableOtherRules?: boolean;\n    axeVersion?: string;\n    noHtml?: boolean;\n    allowedOrigins?: string[];\n    // Deprecated - do not use.\n    ver?: string;\n  }\n  /**\n   * @deprecated Use branding: string instead to set the application key in help URLs\n   */\n  interface Branding {\n    brand?: string;\n    application?: string;\n  }\n  interface CheckHelper {\n    async: () => (result: boolean | undefined | Error) => void;\n    data: (data: unknown) => void;\n    relatedNodes: (nodes: Element[]) => void;\n  }\n  interface AfterResult {\n    id: string;\n    data?: unknown;\n    relatedNodes: SerialDqElement[];\n    result: boolean | undefined;\n    node: SerialDqElement;\n  }\n  interface Check {\n    id: string;\n    evaluate?:\n      | string\n      | ((\n          this: CheckHelper,\n          node: Element,\n          options: unknown,\n          virtualNode: VirtualNode\n        ) => boolean | undefined | void);\n    after?:\n      | string\n      | ((results: AfterResult[], options: unknown) => AfterResult[]);\n    options?: any;\n    matches?: string;\n    enabled?: boolean;\n    metadata?: {\n      impact?: ImpactValue;\n      messages?: CheckMessages;\n    };\n  }\n  interface Rule {\n    id: string;\n    selector?: string;\n    impact?: ImpactValue;\n    excludeHidden?: boolean;\n    enabled?: boolean;\n    pageLevel?: boolean;\n    any?: string[];\n    all?: string[];\n    none?: string[];\n    tags?: string[];\n    matches?: string | ((node: Element, virtualNode: VirtualNode) => boolean);\n    reviewOnFail?: boolean;\n    actIds?: string[];\n    metadata?: Omit<RuleMetadata, 'ruleId' | 'tags' | 'actIds'>;\n  }\n  interface AxePlugin {\n    id: string;\n    run(...args: any[]): any;\n    commands: {\n      id: string;\n      callback(...args: any[]): void;\n    }[];\n    cleanup?(callback: Function): void;\n  }\n  interface RuleMetadata {\n    ruleId: string;\n    description: string;\n    help: string;\n    helpUrl: string;\n    tags: string[];\n    actIds?: string[];\n  }\n  interface SerialDqElement {\n    source: string;\n    nodeIndexes: number[];\n    selector: UnlabelledFrameSelector;\n    xpath: string[];\n    ancestry: UnlabelledFrameSelector;\n  }\n  interface DqElement extends SerialDqElement {\n    element: Element;\n    toJSON(): SerialDqElement;\n  }\n  interface DqElementConstructor {\n    new (elm: Element, options?: { absolutePaths?: boolean }): DqElement;\n    mergeSpecs(\n      childSpec: SerialDqElement,\n      parentSpec: SerialDqElement\n    ): SerialDqElement;\n  }\n  interface PartialRuleResult {\n    id: string;\n    result: 'inapplicable';\n    pageLevel: boolean;\n    impact: null;\n    nodes: Array<Record<string, unknown>>;\n  }\n  interface PartialResult {\n    frames: SerialDqElement[];\n    results: PartialRuleResult[];\n    environmentData?: EnvironmentData;\n  }\n  type PartialResults = Array<PartialResult | null>;\n  interface FrameContext {\n    frameSelector: CrossTreeSelector;\n    frameContext: FrameContextObject;\n  }\n\n  interface RawCheckResult extends Omit<\n    CheckResult,\n    'relatedNodes' | 'impact'\n  > {\n    relatedNodes?: Array<SerialDqElement | DqElement>;\n    impact?: ImpactValue;\n  }\n\n  interface RawNodeResult<T extends 'passed' | 'failed' | 'cantTell'> {\n    node: SerialDqElement | DqElement;\n    any: RawCheckResult[];\n    all: RawCheckResult[];\n    none: RawCheckResult[];\n    impact: ImpactValue | undefined;\n    result: T;\n  }\n\n  interface RawResult extends Omit<Result, 'nodes'> {\n    inapplicable: Array<never>;\n    passes: RawNodeResult<'passed'>[];\n    incomplete: RawNodeResult<'cantTell'>[];\n    violations: RawNodeResult<'failed'>[];\n    pageLevel: boolean;\n    result: 'failed' | 'passed' | 'incomplete' | 'inapplicable';\n  }\n\n  type AxeReporter<T = unknown> = (\n    rawResults: RawResult[],\n    option: RunOptions,\n    resolve: (report: T) => void,\n    reject: (error: Error) => void\n  ) => void;\n\n  interface VirtualNode {\n    actualNode?: Node;\n    shadowId?: string;\n    children?: VirtualNode[];\n    parent?: VirtualNode;\n    attr(attr: string): string | null;\n    hasAttr(attr: string): boolean;\n    props: { [key: string]: unknown };\n    boundingClientRect: DOMRect;\n  }\n\n  type GridCell = VirtualNode[];\n\n  interface Grid {\n    container: VirtualNode | null;\n    cells: unknown; // opaque implementation detail\n    boundaries?: DOMRect;\n    toGridIndex(num: number): number;\n    getCellFromPoint(point: { x: number; y: number }): GridCell;\n    loopGridPosition(\n      gridPosition: DOMRect,\n      callback: (gridCell: GridCell, pos: { row: number; col: number }) => void\n    ): void;\n    getGridPositionOfRect(\n      rect: { top: number; right: number; bottom: number; left: number },\n      margin?: number\n    ): DOMRect;\n  }\n\n  interface CustomNodeSerializer<T = SerialDqElement> {\n    toSpec: (dqElm: DqElement) => T;\n    mergeSpecs: (nodeSpec: T, parentFrameSpec: T) => T;\n  }\n\n  interface NodeSerializer {\n    update: <T>(serializer: CustomNodeSerializer<T>) => void;\n    toSpec: (node: Element | VirtualNode) => SerialDqElement;\n    dqElmToSpec: (\n      dqElm: DqElement | SerialDqElement,\n      options?: RunOptions\n    ) => SerialDqElement;\n    mergeSpecs: (\n      nodeSpec: SerialDqElement,\n      parentFrameSpec: SerialDqElement\n    ) => SerialDqElement;\n  }\n\n  interface Utils {\n    getElementSource: (\n      element: Node | null | undefined,\n      options?: { maxLength?: number; attrLimit?: number }\n    ) => string;\n    getFrameContexts: (\n      context?: ElementContext,\n      options?: RunOptions\n    ) => FrameContext[];\n    shadowSelect: (selector: CrossTreeSelector) => Element | null;\n    shadowSelectAll: (selector: CrossTreeSelector) => Element[];\n    getStandards(): Required<Standards>;\n    isContextSpec: (context: unknown) => context is ContextSpec;\n    isContextObject: (context: unknown) => context is ContextObject;\n    isContextProp: (context: unknown) => context is ContextProp;\n    isLabelledFramesSelector: (\n      selector: unknown\n    ) => selector is LabelledFramesSelector;\n    isLabelledShadowDomSelector: (\n      selector: unknown\n    ) => selector is LabelledShadowDomSelector;\n    RuleError: new (options: {\n      error: Error;\n      ruleId?: string;\n      method?: string;\n      errorNode?: SerialDqElement;\n    }) => RuleError;\n    serializeError: (error: Error) => SerialError;\n    DqElement: DqElementConstructor;\n    uuid: (\n      options?: { random?: Uint8Array | Array<number> },\n      buf?: Uint8Array | Array<number>,\n      offset?: number\n    ) => string | Uint8Array | Array<number>;\n    nodeSerializer: NodeSerializer;\n    normalizeRunOptions: (options?: RunOptions) => NormalizedRunOptions;\n  }\n\n  interface Aria {\n    getRoleType: (role: string | Element | VirtualNode | null) => string | null;\n  }\n\n  interface Dom {\n    isFocusable: (node: Element | VirtualNode) => boolean;\n    isNativelyFocusable: (node: Element | VirtualNode) => boolean;\n    getNodeGrid: (node: Node | VirtualNode) => Grid;\n  }\n\n  type AccessibleTextOptions = {\n    inControlContext?: boolean;\n    inLabelledByContext?: boolean;\n  };\n\n  interface Text {\n    accessibleText: (\n      element: Element,\n      options?: AccessibleTextOptions\n    ) => string;\n  }\n\n  interface Commons {\n    aria: Aria;\n    dom: Dom;\n    text: Text;\n  }\n\n  interface EnvironmentData {\n    testEngine: TestEngine;\n    testRunner: TestRunner;\n    testEnvironment: TestEnvironment;\n    url: string;\n    timestamp: string;\n  }\n\n  let version: string;\n  let plugins: any;\n  let utils: Utils;\n  let commons: Commons;\n\n  /**\n   * Source string to use as an injected script in Selenium\n   */\n  let source: string;\n\n  /**\n   * Object for axe Results\n   */\n  var AxeResults: AxeResults;\n\n  /**\n   * Runs a number of rules against the provided HTML page and returns the resulting issue list\n   *\n   * @param   {ElementContext} context  Optional The `Context` specification object @see Context\n   * @param   {RunOptions}     options  Optional Options passed into rules or checks, temporarily modifying them.\n   * @param   {RunCallback}    callback Optional The function to invoke when analysis is complete.\n   * @returns {Promise<AxeResults>|void} If the callback was not defined, axe will return a Promise.\n   */\n  function run<T = AxeResults>(context?: ElementContext): Promise<T>;\n  function run<T = AxeResults>(options: RunOptions): Promise<T>;\n  function run<T = AxeResults>(\n    callback: (error: Error, results: T) => void\n  ): void;\n  function run<T = AxeResults>(\n    context: ElementContext,\n    callback: RunCallback<T>\n  ): void;\n  function run<T = AxeResults>(\n    options: RunOptions,\n    callback: RunCallback<T>\n  ): void;\n  function run<T = AxeResults>(\n    context: ElementContext,\n    options: RunOptions\n  ): Promise<T>;\n  function run<T = AxeResults>(\n    context: ElementContext,\n    options: RunOptions,\n    callback: RunCallback<T>\n  ): void;\n\n  /**\n   * Method for configuring the data format used by axe. Helpful for adding new\n   * rules, which must be registered with the library to execute.\n   * @param  {Spec}       Spec Object with valid `branding`, `reporter`, `checks` and `rules` data\n   */\n  function configure(spec: Spec): void;\n\n  /**\n   * Run axe in the current window only\n   * @param   {ElementContext} context  Optional The `Context` specification object @see Context\n   * @param   {RunOptions}     options  Optional Options passed into rules or checks, temporarily modifying them.\n   * @returns {Promise<PartialResult>}  Partial result, for use in axe.finishRun.\n   */\n  function runPartial(\n    context: ElementContext,\n    options: RunOptions\n  ): Promise<PartialResult>;\n\n  /**\n   * Create a report from axe.runPartial results\n   * @param   {PartialResult[]}     partialResults  Results from axe.runPartial, calls in different frames on the page.\n   * @param   {RunOptions}     options  Optional Options passed into rules or checks, temporarily modifying them.\n   */\n  function finishRun(\n    partialResults: PartialResults,\n    options: RunOptions\n  ): Promise<AxeResults>;\n\n  /**\n   * Searches and returns rules that contain a tag in the list of tags.\n   * @param  {Array}  tags  Optional array of tags\n   * @return {Array}  Array of rules\n   */\n  function getRules(tags?: string[]): RuleMetadata[];\n\n  /**\n   * Restores the default axe configuration\n   */\n  function reset(): void;\n\n  /**\n   * Function to register a plugin configuration in document and its subframes\n   * @param  {Object}    plugin    A plugin configuration object\n   */\n  function registerPlugin(plugin: AxePlugin): void;\n\n  /**\n   * Function to clean up plugin configuration in document and its subframes\n   */\n  function cleanup(): void;\n\n  /**\n   * Set up alternative frame communication\n   */\n  function frameMessenger(frameMessenger: FrameMessenger): void;\n\n  /**\n   * Setup axe-core so axe.common functions can work properly.\n   */\n  function setup(node?: Element | Document): VirtualNode;\n\n  /**\n   * Clean up axe-core tree and caches. `axe.run` will call this function at the end of the run so there's no need to call it yourself afterwards.\n   */\n  function teardown(): void;\n\n  /**\n   * Check if a reporter is registered\n   */\n  function hasReporter(reporterName: string): boolean;\n\n  /**\n   * Get a reporter based the name it is registered with\n   */\n  function getReporter<T>(reporterName: string): AxeReporter<T>;\n\n  /**\n   * Register a new reporter, optionally setting it as the default\n   */\n  function addReporter<T>(\n    reporterName: string,\n    reporter: AxeReporter<T>,\n    isDefault?: boolean\n  ): void;\n\n  // axe.frameMessenger\n  type FrameMessenger = {\n    open: (topicHandler: TopicHandler) => Close | void;\n    post: (\n      frameWindow: Window,\n      data: TopicData,\n      replyHandler: ReplyHandler\n    ) => boolean | void;\n  };\n  type Close = Function;\n  type TopicHandler = (data: TopicData, responder: Responder) => void;\n  type ReplyHandler = (\n    message: any | Error,\n    keepalive: boolean,\n    responder: Responder\n  ) => void;\n  type Responder = (\n    message: any | Error,\n    keepalive?: boolean,\n    replyHandler?: ReplyHandler\n  ) => void;\n  type TopicData = { topic: string } & ReplyData;\n  type ReplyData = { channelId: string; message: any; keepalive: boolean };\n}\n\nexport = axe;\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"axe-core\",\n  \"version\": \"4.11.1\",\n  \"deprecated\": true,\n  \"contributors\": [\n    {\n      \"name\": \"David Sturley\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Dylan Barrell\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Wilco Fiers\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Dian Fay\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Marcy Sutton\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    }\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/dequelabs/axe-core.git\"\n  },\n  \"main\": \"axe.js\",\n  \"typings\": \"axe.d.ts\",\n  \"license\": \"MPL-2.0\",\n  \"ignore\": [\"**/.*\", \"node_modules\", \"test\", \"build\", \"typings\"],\n  \"devDependencies\": {}\n}\n"
  },
  {
    "path": "build/build-manual.js",
    "content": "/*eslint-env node */\n'use strict';\n\nvar path = require('path');\nvar templates = require('./templates');\n\nmodule.exports = function build(grunt, options, commons, callback) {\n  options.getFiles = options.hasOwnProperty('getFiles')\n    ? options.getFiles\n    : true;\n\n  function parseObject(src) {\n    var files = grunt.file.expand(src);\n    return files.map(function (file) {\n      var json = grunt.file.readJSON(file);\n      var dirname = path.dirname(file);\n      Object.keys(templates).forEach(function (templateName) {\n        if (json[templateName] && json[templateName].endsWith('.js')) {\n          json[templateName] = path.resolve(dirname, json[templateName]);\n          if (options.getFiles) {\n            json[templateName] = getSource(json[templateName], templateName);\n          }\n        }\n      });\n      return json;\n    });\n  }\n\n  function getSource(file, type) {\n    return grunt.template.process(templates[type], {\n      data: {\n        source: grunt.file.read(file)\n      }\n    });\n  }\n\n  callback({\n    rules: parseObject(options.rules),\n    checks: parseObject(options.checks),\n    misc: parseObject(options.misc)\n  });\n};\n"
  },
  {
    "path": "build/check-node-version.js",
    "content": "#! /usr/bin/env node\n\nconst fs = require('fs');\nconst path = require('path');\n\nconst PATH_TO_NVMRC = path.join(__dirname, '..', '.nvmrc');\nconst nvmrc = fs.readFileSync(PATH_TO_NVMRC, 'utf8');\nconst minimumVersionMajor = parseInt(nvmrc.trim(), 10);\n\nconst currentVersion = process.version.replace('v', '');\nconst currentVersionMajor = parseInt(currentVersion.split('.')[0]);\n\nconst usesMinimumVersion = currentVersionMajor >= minimumVersionMajor;\n\nif (usesMinimumVersion) {\n  process.exit();\n}\n\nconsole.error(\n  '' +\n    'Error: You are using Node.js version ' +\n    currentVersion +\n    ', but you need ' +\n    'Node.js version ' +\n    minimumVersionMajor +\n    ' or higher to build and/or test axe-core.' +\n    '\\n\\n' +\n    'Install Node.js version ' +\n    minimumVersionMajor +\n    ' or higher and try again.' +\n    '\\n\\n' +\n    'You can use nvm (https://github.com/creationix/nvm) to update your Node.js version ' +\n    ''\n);\n\nprocess.exit(1);\n"
  },
  {
    "path": "build/cherry-pick.js",
    "content": "#!/usr/bin/env node\n\nconst { execSync } = require('child_process');\nconst conventionalCommitsParser = require('conventional-commits-parser');\nconst chalk = require('chalk');\nconst { version } = require('../package.json');\n\nconst releaseType = process.argv[2];\nlet ignoreCommits = process.argv[3];\n\nif (!releaseType) {\n  console.error(\n    'Must specify a release type:\\n$ node build/cherry-pick.js [<releaseType> | minor | patch] [<ignoreCommits>]'\n  );\n  process.exit(1);\n}\n\n// doing a major release should just be merging develop into master,\n// no cherry-picking required\nif (!['minor', 'patch'].includes(releaseType)) {\n  console.error('Release type not supported:', releaseType);\n  process.exit(1);\n}\n\nif (ignoreCommits) {\n  // use the short hash if a long hash is provided\n  ignoreCommits = ignoreCommits.split(',').map(hash => hash.substring(0, 8));\n  console.log(chalk.yellow('Ignoring commits:'), ignoreCommits.join(','), '\\n');\n} else {\n  ignoreCommits = [];\n}\n\n// don't run on master or develop branch\nconst currentBranch = execSync('git branch --show-current').toString().trim();\nif (['develop', 'master'].includes(currentBranch)) {\n  console.error(\n    `Please run the script on a release branch and not the ${currentBranch} branch`\n  );\n  process.exit(1);\n}\n\n// find the version we should start pulling commits from\n// e.g. if we are currently on version 3.4.5 and need to do a minor release we pull commits starting from 3.4.0\nconst [major, minor] = version.split('.').map(Number);\n\nlet targetVersion = releaseType === 'patch' ? version : `${major}.${minor}.0`;\n\n// get all commits from a branch\nfunction getCommits(branch) {\n  // all commits are too large for execSync buffer size so we'll just get since the last 3 years\n  const date = new Date(new Date().setFullYear(new Date().getFullYear() - 3));\n  const stdout = execSync(\n    `git log ${branch || ''} --abbrev-commit --since=${date.getFullYear()}`\n  ).toString();\n  const allCommits = stdout\n    .split(/commit (?=[\\w\\d]{8}[\\n\\r])/)\n    .filter(commit => !!commit);\n\n  // parse commits\n  const commits = [];\n  for (let i = 0; i < allCommits.length; i++) {\n    const commit = allCommits[i];\n\n    const hash = commit.substring(0, 8);\n    const msg = commit.substring(commit.indexOf('\\n\\n')).trim();\n\n    const { type, scope, subject, merge, notes } =\n      conventionalCommitsParser.sync(msg, {\n        // parse merge commits\n        mergePattern: /^Merge pull request #(\\d+) from (.*)$/,\n        mergeCorrespondence: ['id', 'source'],\n\n        // allow comma in scope\n        headerPattern: /^(\\w*)(?:\\(([\\w\\$\\.\\-\\*, ]*)\\))?\\: (.*)$/\n      });\n\n    const isBreakingChange = notes.some(\n      note => note.title === 'BREAKING CHANGE'\n    );\n    const isFeat = type === 'feat';\n\n    const commitType = {\n      minor: !isBreakingChange,\n      patch: !isFeat && !isBreakingChange\n    };\n\n    // only get commits since the target version\n    // example commit message generated by running the release script:\n    // chore(release): 3.4.5\n    if (scope === 'release' && subject === targetVersion) {\n      break;\n    }\n\n    // filter merge commits and any types that don't match the release type\n    if (\n      !merge &&\n      subject &&\n      !subject.startsWith('merge branch') &&\n      scope !== 'release' &&\n      commitType[releaseType] &&\n      !ignoreCommits.includes(hash)\n    ) {\n      // add in reverse order (order commited)\n      commits.unshift({ hash, msg, type, scope, subject });\n    }\n  }\n\n  return commits;\n}\n\n// only cherry-pick commits that have not been added to the current branch already\nconst currentCommits = getCommits();\nconst commitsToCherryPick = getCommits('develop').filter(commit => {\n  return !currentCommits.find(\n    currentCommit => currentCommit.subject === commit.subject\n  );\n});\n\nif (!commitsToCherryPick.length) {\n  console.log(chalk.yellow('No commits to cherry-pick'));\n  process.exit(1);\n}\n\n// cherry-pick all commits and accept whatever is in develop to avoid merge conflicts\ncommitsToCherryPick.forEach(({ hash, type, scope, subject }) => {\n  console.log(\n    `${chalk.yellow('Cherry-picking')} ${hash} ${type}${\n      scope ? `(${scope})` : ''\n    }: ${subject}`\n  );\n\n  try {\n    execSync(`git cherry-pick ${hash} -X theirs`);\n  } catch {\n    console.error(\n      chalk.red.bold('\\nAborting cherry-pick and reseting to master')\n    );\n    console.error(\n      '\\nCannot auto-resolve cherry-pick commit. This can be caused by the commit already being applied or a file being edited in the cherry-pick branch that does not yet exist (the commit it depends on was not cherry-picked). Please review the commits being cherry-picked and either manually resolve or ignore the commit.'\n    );\n    console.error(\n      `$ node build/cherry-pick.js ${releaseType} [commitSHA1,commitSHA2,...]`\n    );\n\n    execSync('git cherry-pick --abort; git reset --hard origin/master');\n    process.exit(1);\n  }\n});\n"
  },
  {
    "path": "build/configure.js",
    "content": "/*eslint-env node */\n/*eslint max-len: off */\n'use strict';\n\nvar clone = require('clone');\nvar doT = require('@deque/dot');\nvar templates = require('./templates');\nvar buildManual = require('./build-manual');\nvar { encode } = require('html-entities');\nvar packageJSON = require('../package.json');\nvar doTRegex = /\\{\\{.+?\\}\\}/g;\n\nvar axeVersion = packageJSON.version.substring(\n  0,\n  packageJSON.version.lastIndexOf('.')\n);\n\nvar descriptionTableHeader =\n  '| Rule ID | Description | Impact | Tags | Issue Type | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/) |\\n| :------- | :------- | :------- | :------- | :------- | :------- |\\n';\n\n// prevent striping newline characters from strings (e.g. failure\n// summaries). must be synced with lib/core/imports/index.js\ndoT.templateSettings.strip = false;\n\nfunction getLocale(grunt, options) {\n  var localeFile;\n  if (options.locale) {\n    localeFile = './locales/' + options.locale + '.json';\n  }\n\n  if (localeFile) {\n    return grunt.file.readJSON(localeFile);\n  }\n}\n\nfunction makeHeaderLink(title) {\n  return title.replace(/ /g, '-').replace(/[\\.&]/g, '').toLowerCase();\n}\n\nfunction buildRules(grunt, options, commons, callback) {\n  var axeImpact = Object.freeze(['minor', 'moderate', 'serious', 'critical']); // TODO: require('../axe') does not work if grunt configure is moved after uglify, npm test breaks with undefined. Complicated grunt concurrency issue.\n  var locale = getLocale(grunt, options);\n  options.getFiles = false;\n  buildManual(grunt, options, commons, function (build) {\n    var metadata = {\n      rules: {},\n      checks: {}\n    };\n    var descriptions = {\n      wcag20: {\n        title: 'WCAG 2.0 Level A & AA Rules',\n        rules: []\n      },\n      wcag21: {\n        title: 'WCAG 2.1 Level A & AA Rules',\n        rules: []\n      },\n      wcag22: {\n        title: 'WCAG 2.2 Level A & AA Rules',\n        intro:\n          'These rules are disabled by default, until WCAG 2.2 is more widely adopted and required.',\n        rules: []\n      },\n      bestPractice: {\n        title: 'Best Practices Rules',\n        intro:\n          'Rules that do not necessarily conform to WCAG success criterion but are industry accepted practices that improve the user experience.',\n        rules: []\n      },\n      wcag2aaa: {\n        title: 'WCAG 2.x level AAA rules',\n        intro:\n          'Rules that check for conformance to WCAG AAA success criteria that can be fully automated. These are disabled by default in axe-core.',\n        rules: []\n      },\n      experimental: {\n        title: 'Experimental Rules',\n        intro:\n          'Rules we are still testing and developing. They are disabled by default in axe-core, but are enabled for the axe browser extensions.',\n        rules: []\n      },\n      deprecated: {\n        title: 'Deprecated Rules',\n        intro:\n          'Deprecated rules are disabled by default and will be removed in the next major release.',\n        rules: []\n      }\n    };\n\n    var TOC = Object.keys(descriptions)\n      .map(key => {\n        return `- [${descriptions[key].title}](#${makeHeaderLink(\n          descriptions[key].title\n        )})`;\n      })\n      .join('\\n');\n\n    var tags = options.tags ? options.tags.split(/\\s*,\\s*/) : [];\n    var rules = build.rules;\n    var checks = build.checks;\n\n    // Translate checks before parsing them so that translations\n    // get applied to the metadata object\n    if (locale && locale.checks) {\n      checks.forEach(function (check) {\n        if (locale.checks[check.id] && check.metadata) {\n          check.metadata.messages = locale.checks[check.id];\n        }\n      });\n    }\n\n    parseChecks(checks);\n\n    function parseMetaData(source, propType) {\n      var data = source.metadata;\n      var id = source.id || source.type;\n      if (id && locale && locale[propType] && propType !== 'checks') {\n        data = locale[propType][id] || data;\n      }\n      var result = clone(data) || {};\n\n      if (result.messages) {\n        Object.keys(result.messages).forEach(function (key) {\n          // only convert to templated function for strings\n          // objects handled later in publish-metadata.js\n          if (\n            typeof result.messages[key] !== 'object' &&\n            doTRegex.test(result.messages[key])\n          ) {\n            result.messages[key] = doT\n              .template(result.messages[key])\n              .toString();\n          }\n        });\n      }\n      //TODO this is actually failureSummaries, property name should better reflect that\n      if (result.failureMessage && doTRegex.test(result.failureMessage)) {\n        result.failureMessage = doT.template(result.failureMessage).toString();\n      }\n      return result;\n    }\n\n    function createFailureSummaryObject(summaries) {\n      var result = {};\n      summaries.forEach(function (summary) {\n        if (summary.type) {\n          result[summary.type] = parseMetaData(summary, 'failureSummaries');\n        }\n      });\n      return result;\n    }\n\n    function getIncompleteMsg(summaries) {\n      var summary = summaries.find(function (element) {\n        return typeof element.incompleteFallbackMessage === 'string';\n      });\n      return summary ? summary.incompleteFallbackMessage : '';\n    }\n\n    function replaceFunctions(string) {\n      return string\n        .replace(\n          /\"(evaluate|after|gather|matches|source|commons)\":\\s*(\"[^\"]+?.js\")/g,\n          function (m, p1, p2) {\n            return m.replace(p2, getSource(p2.replace(/^\"|\"$/g, ''), p1));\n          }\n        )\n        .replace(\n          /\"(function anonymous\\([\\s\\S]+?\\) {)([\\s\\S]+?)(})\"/g,\n          function (m) {\n            return JSON.parse(m);\n          }\n        )\n        .replace(/\"(\\(function \\(\\) {)([\\s\\S]+?)(}\\)\\(\\))\"/g, function (m) {\n          return JSON.parse(m);\n        });\n    }\n\n    function getSource(file, type) {\n      return grunt.template.process(templates[type], {\n        data: {\n          source: grunt.file.read(file)\n        }\n      });\n    }\n\n    function findCheck(checkCollection, id) {\n      return checkCollection.filter(function (check) {\n        if (check.id === id) {\n          return true;\n        }\n      })[0];\n    }\n\n    function blacklist(k, v) {\n      if (options.blacklist.indexOf(k) !== -1) {\n        return undefined;\n      }\n      return v;\n    }\n\n    function parseChecks(collection) {\n      return collection.map(function (check) {\n        var c = {};\n        var id = typeof check === 'string' ? check : check.id;\n        var definition = clone(findCheck(checks, id));\n        if (!definition) {\n          grunt.log.error('check ' + id + ' not found');\n        }\n        c.options = check.options || definition.options;\n        c.id = id;\n\n        if (definition.metadata && !metadata.checks[id]) {\n          metadata.checks[id] = parseMetaData(definition, 'checks');\n        }\n\n        return c.options === undefined ? id : c;\n      });\n    }\n\n    function traverseChecks(checkCollection, predicate, startValue) {\n      return checkCollection.reduce(function (out, check) {\n        var id = typeof check === 'string' ? check : check.id;\n        var definition = clone(findCheck(checks, id));\n        if (!definition) {\n          grunt.log.error('check ' + id + ' not found');\n        }\n        return predicate(definition, out);\n      }, startValue);\n    }\n\n    function parseImpactForRule(rule) {\n      function capitalize(s) {\n        return s.charAt(0).toUpperCase() + s.slice(1);\n      }\n      if (rule.impact) {\n        return capitalize(rule.impact);\n      }\n\n      function getUniqueArr(arr) {\n        return arr.filter(function (value, index, self) {\n          return self.indexOf(value) === index;\n        });\n      }\n\n      function getImpactScores(definition, out) {\n        if (definition && definition.metadata && definition.metadata.impact) {\n          var impactScore = axeImpact.indexOf(definition.metadata.impact);\n          out.push(impactScore);\n        }\n        return out;\n      }\n\n      function getScore(checkCollection, onlyHighestScore) {\n        var scores = traverseChecks(checkCollection, getImpactScores, []);\n        if (scores && scores.length) {\n          return onlyHighestScore\n            ? [Math.max.apply(null, scores)]\n            : getUniqueArr(scores);\n        } else {\n          return [];\n        }\n      }\n\n      var highestImpactForRuleTypeAny = getScore(rule.any, true);\n      var allUniqueImpactsForRuleTypeAll = getScore(rule.all, false);\n      var allUniqueImpactsForRuleTypeNone = getScore(rule.none, false);\n      var cumulativeImpacts = highestImpactForRuleTypeAny\n        .concat(allUniqueImpactsForRuleTypeAll)\n        .concat(allUniqueImpactsForRuleTypeNone);\n      var cumulativeScores = getUniqueArr(cumulativeImpacts).sort(); //order lowest to highest\n\n      return cumulativeScores.reduce(function (out, cV) {\n        return out.length\n          ? out + ', ' + capitalize(axeImpact[cV])\n          : capitalize(axeImpact[cV]);\n      }, '');\n    }\n\n    function parseFailureForRule(rule) {\n      function hasFailure(definition, out) {\n        if (\n          !rule.reviewOnFail &&\n          definition &&\n          definition.metadata &&\n          definition.metadata.impact\n        ) {\n          out = out || !!definition.metadata.messages.fail;\n        }\n        return out;\n      }\n\n      return (\n        traverseChecks(rule.any, hasFailure, false) ||\n        traverseChecks(rule.all, hasFailure, false) ||\n        traverseChecks(rule.none, hasFailure, false)\n      );\n    }\n\n    function parseIncompleteForRule(rule) {\n      function hasIncomplete(definition, out) {\n        if (definition && definition.metadata && definition.metadata.impact) {\n          out =\n            out ||\n            !!definition.metadata.messages.incomplete ||\n            rule.reviewOnFail;\n        }\n        return out;\n      }\n\n      return (\n        traverseChecks(rule.any, hasIncomplete, false) ||\n        traverseChecks(rule.all, hasIncomplete, false) ||\n        traverseChecks(rule.none, hasIncomplete, false)\n      );\n    }\n\n    function createActLinksForRule(rule) {\n      var actIds = rule.actIds || [];\n      var actLinks = [];\n      actIds.forEach(id =>\n        actLinks.push(`[${id}](https://act-rules.github.io/rules/${id})`)\n      );\n      return actLinks.join(', ');\n    }\n\n    rules.map(function (rule) {\n      var impact = parseImpactForRule(rule);\n      var canFail = parseFailureForRule(rule);\n      var canIncomplete = parseIncompleteForRule(rule);\n\n      rule.any = parseChecks(rule.any);\n      rule.all = parseChecks(rule.all);\n      rule.none = parseChecks(rule.none);\n      if (rule.metadata && !metadata.rules[rule.id]) {\n        metadata.rules[rule.id] = parseMetaData(rule, 'rules'); // Translate rules\n      }\n\n      var result;\n      if (rule.tags.includes('deprecated')) {\n        result = descriptions.deprecated.rules;\n      } else if (rule.tags.includes('experimental')) {\n        result = descriptions.experimental.rules;\n      } else if (rule.tags.find(tag => tag.includes('aaa'))) {\n        result = descriptions.wcag2aaa.rules;\n      } else if (rule.tags.includes('best-practice')) {\n        result = descriptions.bestPractice.rules;\n      } else if (rule.tags.find(tag => tag.startsWith('wcag2a'))) {\n        result = descriptions.wcag20.rules;\n      } else if (rule.tags.find(tag => tag.startsWith('wcag21a'))) {\n        result = descriptions.wcag21.rules;\n      } else {\n        result = descriptions.wcag22.rules;\n      }\n\n      var issueType = [];\n      if (canFail) {\n        issueType.push('failure');\n      }\n      if (canIncomplete) {\n        issueType.push('needs&nbsp;review');\n      }\n\n      var actLinks = createActLinksForRule(rule);\n\n      result.push([\n        `[${rule.id}](https://dequeuniversity.com/rules/axe/${axeVersion}/${rule.id}?application=RuleDescription)`,\n        encode(rule.metadata.description),\n        impact,\n        rule.tags.join(', '),\n        issueType.join(', '),\n        actLinks\n      ]);\n      if (tags.length) {\n        rule.enabled = !!rule.tags.filter(function (t) {\n          return tags.indexOf(t) !== -1;\n        }).length;\n      }\n      return rule;\n    });\n\n    var ruleTables = Object.keys(descriptions)\n      .map(key => {\n        var description = descriptions[key];\n\n        return `\n## ${description.title}\n\n${description.intro ? description.intro : ''}\n\n${\n  description.rules.length\n    ? descriptionTableHeader\n    : '_There are no matching rules_'\n}${description.rules\n          .map(function (row) {\n            return '| ' + row.join(' | ') + ' |';\n          })\n          .join('\\n')}`;\n      })\n      .join('\\n\\n');\n\n    var descriptions = `\n<!--- This file is automatically generated using build/configure.js --->\n\n# Rule Descriptions\n\n## Table of Contents\n${TOC}\n${ruleTables}`;\n\n    // Translate failureSummaries\n    metadata.failureSummaries = createFailureSummaryObject(build.misc);\n    metadata.incompleteFallbackMessage = getIncompleteMsg(build.misc);\n\n    callback({\n      auto: replaceFunctions(\n        JSON.stringify(\n          {\n            lang: options.locale || 'en',\n            data: metadata,\n            rules: rules,\n            checks: checks\n          },\n          blacklist\n        )\n      ),\n      manual: replaceFunctions(\n        JSON.stringify(\n          {\n            data: metadata,\n            rules: rules,\n            checks: checks\n          },\n          blacklist\n        )\n      ),\n      descriptions\n    });\n  });\n}\n\nmodule.exports = buildRules;\n"
  },
  {
    "path": "build/rule-generator/directories.js",
    "content": "const path = require('path');\n\nconst directories = {\n  axePath: path.join(__dirname, '..', '..', 'axe.js'),\n  rules: path.join(__dirname, '..', '..', 'lib', 'rules'),\n  checks: path.join(__dirname, '..', '..', 'lib', 'checks'),\n  testRuleMatches: path.join(__dirname, '..', '..', 'test', 'rule-matches'),\n  testChecksUnit: path.join(__dirname, '..', '..', 'test', 'checks'),\n  testChecksIntegration: path.join(\n    __dirname,\n    '..',\n    '..',\n    'test',\n    'integration',\n    'rules'\n  )\n};\n\nmodule.exports = directories;\n"
  },
  {
    "path": "build/rule-generator/get-answers.js",
    "content": "const questions = require('./questions');\nconst inquirer = require('inquirer');\n\n/**\n * Get CHECK details as entered by the user for the prompted questions\n * @method getChecks\n * @param {Array} checks recursively constructed list of CHECKS for the RULE\n * @returns {Array}\n */\nconst getChecks = async (checks = []) => {\n  const checkDetails = await inquirer.prompt([\n    questions.getCheckName,\n    questions.getCheckCategory,\n    questions.getCheckType,\n    questions.getIsAnotherCheck\n  ]);\n  checks.push(checkDetails);\n  if (checkDetails.getIsAnotherCheck) {\n    await getChecks(checks);\n  }\n  return checks;\n};\n\n/**\n * Seek answers by prompting a serious of questions to the user for rule generation\n * @method getAnswers\n * @returns {Object} answers\n */\nconst getAnswers = async () => {\n  // answers for RULE meta data\n  const { getRuleName, getIsRuleMatches } = await inquirer.prompt([\n    questions.getRuleName,\n    questions.getIsRuleMatches\n  ]);\n\n  // answers for CHECK meta data\n  let ruleChecks = [];\n  const { getIsCheck } = await inquirer.prompt([questions.getIsCheck]);\n  if (getIsCheck) {\n    ruleChecks = await getChecks();\n  }\n\n  // answers if TEST files to be created\n  const { getIsUnitTestAssets, getIsIntegrationTestAssets } =\n    await inquirer.prompt([\n      questions.getIsUnitTestAssets,\n      questions.getIsIntegrationTestAssets\n    ]);\n\n  return {\n    ruleName: getRuleName.toLowerCase(),\n    ruleHasMatches: getIsRuleMatches,\n    ruleChecks,\n    ruleHasUnitTestAssets: getIsUnitTestAssets,\n    ruleHasIntegrationTestAssets: getIsIntegrationTestAssets\n  };\n};\n\nmodule.exports = getAnswers;\n"
  },
  {
    "path": "build/rule-generator/get-files-metadata.js",
    "content": "const directories = require('./directories');\nconst outdent = require('outdent');\n\n/**\n * Helper to convert a given string to camel case (split by hyphens if any)\n * @param {String} str given string to be camel cased\n */\nconst camelCase = str => {\n  return str.replace(/-([a-z])/g, g => {\n    return g[1].toUpperCase();\n  });\n};\n\n/**\n * Get meta data for the file to be created as RULE Specification\n * @method getRuleSpecFileMeta\n * @param {String} ruleName given name for the RULE\n * @param {Boolean} ruleHasMatches does the RULE need a matches file\n * @param {Array} ruleChecks list of checks encompassing the RULE\n * @returns {Object} meta data of file\n */\nconst getRuleSpecFileMeta = (ruleName, ruleHasMatches, ruleChecks) => {\n  return {\n    name: `${ruleName}.json`,\n    content: JSON.stringify(\n      {\n        id: ruleName,\n        impact: '',\n        selector: '*',\n        ...(ruleHasMatches && {\n          matches: `${ruleName}-matches`\n        }),\n        tags: [],\n        metadata: {\n          description: '',\n          help: ''\n        },\n        preload: false,\n        all: ruleChecks\n          .filter(c => c.getCheckType === 'all')\n          .map(c => c.getCheckName.toLowerCase()),\n        any: ruleChecks\n          .filter(c => c.getCheckType === 'any')\n          .map(c => c.getCheckName.toLowerCase()),\n        none: ruleChecks\n          .filter(c => c.getCheckType === 'none')\n          .map(c => c.getCheckName.toLowerCase())\n      },\n      undefined,\n      2\n    ),\n    dir: directories.rules\n  };\n};\n\n/**\n * Get meta data of files to be created as RULE matches\n * @method getRuleMatchesFileMeta\n * @param {String} ruleName given name for the RULE\n * @param {Boolean} ruleHasMatches does the rule need a matches file\n * @param {Boolean} ruleHasUnitTestAssets does the rule need unit test assets\n * @returns {Array<Object>} meta data of files\n */\nconst getRuleMatchesFileMeta = (\n  ruleName,\n  ruleHasMatches,\n  ruleHasUnitTestAssets\n) => {\n  let files = [];\n\n  if (ruleHasMatches) {\n    const fnName = `${camelCase(ruleName)}Matches`;\n    const ruleMatchesJs = {\n      name: `${ruleName}-matches.js`,\n      content: outdent`\n        // TODO: Filter node(s)\t\n\n        export default function ${fnName}(node, virtualNode) {\n          return true;\n        }\n      `,\n      dir: directories.rules\n    };\n    files.push(ruleMatchesJs);\n  }\n\n  if (ruleHasUnitTestAssets) {\n    const ruleMatchesTestJs = {\n      name: `${ruleName}-matches.js`,\n      content: outdent`\n        describe('${ruleName}-matches', () => {\n          const rule = axe.utils.getRule('${ruleName}');\n          const { queryFixture } = axe.testUtils;\n        \n          // TODO: Replace with real tests\n          it('returns false for paragraphs', () => {\n            const vNode = queryFixture('<p id=\"target\">Hello world</p>');\n            assert.isFalse(rule.matches(vNode.actualNode, vNode));\n          });\n        });\n      `,\n      dir: directories.testRuleMatches\n    };\n    files.push(ruleMatchesTestJs);\n  }\n\n  return files;\n};\n\n/**\n * Get meta data for CHECK Spec\n * @method getCheckSpecFileMeta\n * @param {String} name given name for the CHECK\n * @param {String} dir path for the file to be created\n * @returns {Object} meta data of file\n */\nconst getCheckSpecFileMeta = (name, dir) => {\n  return {\n    name: `${name}.json`,\n    content: JSON.stringify(\n      {\n        id: `${name}`,\n        evaluate: `${name}-evaluate`,\n        metadata: {\n          messages: {\n            pass: '',\n            fail: '',\n            incomplete: ''\n          }\n        }\n      },\n      undefined,\n      2\n    ),\n    dir\n  };\n};\n\n/**\n * Get meta data for CHECK JS\n * @method getCheckJsFileMeta\n * @param {String} name given name for the CHECK\n * @param {String} dir path for the file to be created\n * @returns {Object} meta data of file\n */\nconst getCheckJsFileMeta = (name, dir) => {\n  const fnName = `${camelCase(name)}Evaluate`;\n  return {\n    name: `${name}-evaluate.js`,\n    content: outdent`\n      // TODO: Logic for check\n      export default function ${fnName}(node, options, virtualNode) {\n        return true\n      }\n    `,\n    dir\n  };\n};\n\n/**\n * Get meta data for CHECK test assets\n * @method getCheckTestJsFileMeta\n * @param {String} name given name for the CHECK\n * @param {String} dir path for the file to be created\n * @returns {Object} meta data of file\n */\nconst getCheckTestJsFileMeta = (name, dir) => {\n  return {\n    name: `${name}.js`,\n    content: outdent`\n      describe('${name} tests', () => {\n        const { checkSetup, getCheckEvaluate } = axe.testUtils;\n        const checkContext = axe.testUtils.MockCheckContext();\n        const checkEvaluate = getCheckEvaluate('${name}');\n\n        afterEach(() => {\n          checkContext.reset();\n        });\n\n        // TODO: Replace this with real tests for this check\n        it('returns false when img has no alt', () => {\n          const params = checkSetup('<img id=\"target\" />');\n          assert.isFalse(checkEvaluate.apply(checkContext, params));\n          assert.deepEqual(checkContext._data, { messageKey: 'missing-alt' });\n        });\n      });\n    `,\n    dir\n  };\n};\n\n/**\n * Get meta data for CHECK files\n * @method getChecksFileMeta\n * @param {Array} ruleChecks list of CHECK's for the RULE\n * @param {Boolean} ruleHasUnitTestAssets does the rule need unit test assets\n * @returns {Array<Object>} meta data of files\n */\nconst getChecksFileMeta = (ruleChecks, ruleHasUnitTestAssets) => {\n  const checkFiles = ruleChecks\n    .map(c => {\n      const cName = c.getCheckName.toLowerCase();\n      const outDir = `${directories.checks}/${c.getCheckCategory}`;\n      const outTestDir = `${directories.testChecksUnit}/${c.getCheckCategory}`;\n      const checkJson = getCheckSpecFileMeta(cName, outDir);\n      const checkJs = getCheckJsFileMeta(cName, outDir);\n      const checkTestJs = getCheckTestJsFileMeta(cName, outTestDir);\n      let files = [checkJson, checkJs];\n      if (ruleHasUnitTestAssets) {\n        files = files.concat(checkTestJs);\n      }\n      return files;\n    })\n    .reduce((out, item) => {\n      // flatten array of array's\n      out = out.concat(item);\n      return out;\n    }, []);\n\n  return checkFiles;\n};\n\n/**\n * Get meta data for integration file assets\n * @method getIntegrationTestAssets\n * @param {String} ruleName given name for the RULE\n * @param {*} ruleHasIntegrationTestAssets does the rule have integration test assets\n * @returns {Array<Object>} meta data of files\n */\nconst getIntegrationTestAssets = (ruleName, ruleHasIntegrationTestAssets) => {\n  let files = [];\n\n  if (ruleHasIntegrationTestAssets) {\n    const htmlFile = {\n      name: `${ruleName}.html`,\n      content: `<!-- HTML Snippets-->`,\n      dir: `${directories.testChecksIntegration}/${ruleName}`\n    };\n    files.push(htmlFile);\n\n    const jsonFile = {\n      name: `${ruleName}.json`,\n      content: JSON.stringify(\n        {\n          description: `${ruleName} tests`,\n          rule: `${ruleName}`,\n          violations: [],\n          passes: []\n        },\n        undefined,\n        2\n      ),\n      dir: `${directories.testChecksIntegration}/${ruleName}`\n    };\n    files.push(jsonFile);\n  }\n  return files;\n};\n\n/**\n * Get list of files to be created as metadata\n * @method getFilesMetaData\n * @param {Object} answers user entered answers for questions prompted by the generator\n * @returns {Array<Object>} meta data of files\n */\nconst getFilesMetaData = answers => {\n  const {\n    ruleName,\n    ruleHasMatches,\n    ruleChecks,\n    ruleHasUnitTestAssets,\n    ruleHasIntegrationTestAssets\n  } = answers;\n\n  // get rule spec file\n  const ruleSpecFile = getRuleSpecFileMeta(\n    ruleName,\n    ruleHasMatches,\n    ruleChecks\n  );\n  // get rule matches and test assets\n  const ruleMatchesFiles = getRuleMatchesFileMeta(\n    ruleName,\n    ruleHasMatches,\n    ruleHasUnitTestAssets\n  );\n  // get checks and test assets\n  const checkFiles = getChecksFileMeta(ruleChecks, ruleHasUnitTestAssets);\n  // integration test assets\n  const integrationFiles = getIntegrationTestAssets(\n    ruleName,\n    ruleHasIntegrationTestAssets\n  );\n\n  return [\n    ruleSpecFile,\n    ...ruleMatchesFiles,\n    ...checkFiles,\n    ...integrationFiles\n  ];\n};\n\nmodule.exports = getFilesMetaData;\n"
  },
  {
    "path": "build/rule-generator/questions.js",
    "content": "const fs = require('fs');\nconst { glob } = require('glob');\nconst directories = require('./directories');\n\n/**\n * Check validity of a given string to be non-empty alphabets or dashes.\n * @method isValidName\n * @param {String} input string value\n * @returns {Boolean}\n */\nconst isValidName = input => {\n  return input && input.length > 0 && input.search(/^[a-zA-Z-]+$/) !== -1;\n};\n\n/**\n * Validate if a given string is valid RULE name\n * - Checks for alphabets, dashes and undefined\n * - Checks for overlap with existing RULE files and RULE Ids.\n * @method validateGetRuleName\n * @param {String} input string value\n * @returns {Boolean | Error}\n */\nconst validateGetRuleName = async input => {\n  const ruleName = input.toLowerCase();\n  // 1) check if valid name\n  if (!isValidName(ruleName)) {\n    throw new Error(\n      'RULE name should not be empty and can only contain alphabets and dashes.'\n    );\n  }\n  // 2) ensure no rule filename overlaps\n  if (fs.existsSync(`${directories.rules}/${ruleName}.json`)) {\n    throw new Error(`RULE name conflicts with an existing rule's filename.`);\n  }\n  // 3) ensure no rule id overlaps\n  const ruleSpecs = await glob(`${directories.rules}/**/*.json`, {\n    posix: true,\n    absolute: true\n  });\n  const axeRulesIds = ruleSpecs.reduce((out, specPath) => {\n    const spec = require(specPath);\n    out.push(spec.id);\n    return out;\n  }, []);\n  if (axeRulesIds.includes(ruleName)) {\n    throw new Error('Rule ID already exists.');\n  }\n  return true;\n};\n\n/**\n * Validate if a given string is valid CHECK name\n * - Checks for alphabets, dashes and undefined\n * - Checks for overlap with existing CHECK files and CHECK Ids.\n * @method validateGetCheckName\n * @param {String} input string value\n * @returns {Boolean | Error}\n */\nconst validateGetCheckName = async input => {\n  const checkName = input.toLowerCase();\n  // 1) check if valid name\n  if (!isValidName(checkName)) {\n    throw new Error(\n      'CHECK name should not be empty and can contain alphabets and dashes.'\n    );\n  }\n  // 2) ensure no check filename overlaps\n  const checkSpecs = await glob(`${directories.checks}/**/*.json`, {\n    posix: true,\n    absolute: true\n  });\n  // cannot use `fs.existsSync` here, as we do not know which category of checks to look under\n  const axeChecksFileNames = checkSpecs.map(\n    f => f.replace('.json', '').split('/').reverse()[0]\n  );\n  if (axeChecksFileNames.includes(checkName)) {\n    throw new Error('CHECK name conflicts with an existing filename.');\n  }\n  // 3) ensure no check id overlaps\n  const ruleSpecs = await glob(`${directories.rules}/**/*.json`, {\n    posix: true,\n    absolute: true\n  });\n  const axe = require(directories.axePath);\n  const axeChecksIds = ruleSpecs.reduce((out, specPath) => {\n    const spec = require(specPath);\n    const checkIds = []\n      .concat(spec.any || [])\n      .concat(spec.all || [])\n      .concat(spec.none || []);\n    return axe.utils.uniqueArray(out, checkIds);\n  }, []);\n  if (axeChecksIds.includes(checkName)) {\n    throw new Error('CHECK ID already exists.');\n  }\n  return true;\n};\n\n// list of questions to be prompted to the user\nconst questions = {\n  getRuleName: {\n    name: 'getRuleName',\n    type: 'input',\n    message: 'What is the name of the RULE? (Eg: aria-valid):',\n    validate: validateGetRuleName\n  },\n  getIsRuleMatches: {\n    name: 'getIsRuleMatches',\n    type: 'confirm',\n    message: 'Does the RULE need a MATCHES file to be created?:'\n  },\n  getIsCheck: {\n    name: 'getIsCheck',\n    type: 'confirm',\n    message: 'Would you like to create a CHECK for the RULE?:'\n  },\n  getIsAnotherCheck: {\n    name: 'getIsAnotherCheck',\n    type: 'confirm',\n    message: 'Would you like to create another CHECK for the RULE?:'\n  },\n  getCheckName: {\n    name: 'getCheckName',\n    type: 'input',\n    message: 'Enter name of CHECK for the RULE? (Eg: aria-label):',\n    validate: validateGetCheckName\n  },\n  getCheckCategory: {\n    name: 'getCheckCategory',\n    type: 'list',\n    message: 'Choose category for the CHECK?:',\n    choices: [\n      'aria',\n      'color',\n      'forms',\n      'keyboard',\n      'label',\n      'language',\n      'lists',\n      'media',\n      'mobile',\n      'navigation',\n      'parsing',\n      'shared',\n      'tables',\n      'visibility'\n    ]\n  },\n  getCheckType: {\n    name: 'getCheckType',\n    type: 'list',\n    message: 'Choose type for the CHECK?:',\n    choices: ['all', 'any', 'none']\n  },\n  getIsUnitTestAssets: {\n    name: 'getIsUnitTestAssets',\n    type: 'confirm',\n    message: 'Would you like to create UNIT test files?'\n  },\n  getIsIntegrationTestAssets: {\n    name: 'getIsIntegrationTestAssets',\n    type: 'confirm',\n    message: 'Would you like to create INTEGRATION test files?'\n  }\n};\n\nmodule.exports = questions;\n"
  },
  {
    "path": "build/rule-generator.js",
    "content": "/* global Promise */\n\nconst fs = require('fs');\nconst chalk = require('chalk');\nconst execa = require('execa');\n\nconst { CI } = process.env;\n\nconst createFile = require('./shared/create-file');\nconst directories = require('./rule-generator/directories');\nconst getAnswers = require('./rule-generator/get-answers');\nconst getFilesMetaData = require('./rule-generator/get-files-metadata');\n\n// prevent malicious execution from CI\nif (CI) {\n  throw new Error('Cannot run Rule Generation CLI in CI.');\n}\n\n// execute\nrun();\n\nasync function run() {\n  // ensure axe exists, if not build axe, then start the generator\n  const axeExists = fs.existsSync(directories.axePath);\n  if (!axeExists) {\n    console.log(\n      chalk.red.bold(\n        `Axe does not exist. Triggering build using - 'npm run build'. Rule generation will continue after build.`\n      )\n    );\n    await execa('npm run build', { shell: true });\n  }\n\n  // rule-generator banner\n  console.log(chalk.hex('#0077c8')('Axe Rule Generator'));\n\n  // get answers (ask questions)\n  const answers = await getAnswers();\n\n  // get metadata of files to be created\n  const files = getFilesMetaData(answers);\n\n  // create the files\n  if (!files || !files.length) {\n    console.log(chalk.red.bold(`No files to generate.`));\n  }\n\n  try {\n    const result = await Promise.all(\n      files.map(async meta => {\n        const path = `${meta.dir}/${meta.name}`;\n        const content = meta.content + '\\n';\n        await createFile(path, content);\n        return path;\n      })\n    );\n    console.log(\n      chalk.green.bold(\n        '\\n' + 'Successfully generated RULE and respective files: '\n      )\n    );\n    console.log(chalk.green.bold(''));\n    console.log(chalk.green.bold(result.join('\\r\\n')));\n  } catch (err) {\n    console.log(\n      chalk.green.bold(`Error generating RULE. Please try again.`, err)\n    );\n  }\n}\n"
  },
  {
    "path": "build/shared/create-file.js",
    "content": "const { promises: fs } = require('fs');\nconst { dirname } = require('path');\n\n/**\n * Create file with given contents at specified location\n * @method createFile\n * @param {String} path file path, inclusive of file name\n * @param {String} content contents of the file\n * @returns {Promise}\n */\nconst createFile = (path, content) =>\n  fs\n    .mkdir(dirname(path), { recursive: true })\n    .then(() => fs.writeFile(path, content));\n\nmodule.exports = createFile;\n"
  },
  {
    "path": "build/shared/format.js",
    "content": "const prettier = require('prettier');\nconst prettierConfig = require('../../package.json').prettier;\n\n/**\n * Format the given `content` string, optionally using `filename` for error messages.\n *\n * @param {String} content\n * @param {String} [filename]\n */\n\nmodule.exports = (content, filename) =>\n  prettier.format(content, { ...prettierConfig, filepath: filename });\n"
  },
  {
    "path": "build/sri-update.js",
    "content": "/**\n * SRI Update will update the sri-history.json file\n * In the project root. The sri-history.json file contains\n * SRI hashes of each released version. This can be used\n * to validate that something is a known axe-core source\n * file.\n *\n * When running `npm run release`, this script will execute and\n * update sri-history.json with the SRIs of axe{.*}.js.\n * @deprecated\n */\nvar path = require('path');\nvar fs = require('fs');\nvar sriToolbox = require('sri-toolbox');\n\n// Check if we should be validating or updating\nvar validate = process.argv.some(function (arg) {\n  return arg === '--validate';\n});\n\nvar root = path.join(__dirname, '..');\nvar axeVersion = require('../package.json').version;\nvar axeHistory = require('../sri-history.json');\n\nif (typeof axeHistory[axeVersion] !== 'object') {\n  axeHistory[axeVersion] = {};\n}\nvar versionSRIs = axeHistory[axeVersion];\n\n// List all axe files (including minified and localized axe files)\nvar axeFiles = fs.readdirSync(root).filter(function (file) {\n  return file.match(/^axe(\\.[a-z.-]+)?\\.js$/);\n});\n\naxeFiles.forEach(function (axeFile) {\n  var axeSource = fs.readFileSync(path.join(root, axeFile), 'utf-8');\n  var axeIntegrity = sriToolbox.generate({ algorithms: ['sha256'] }, axeSource);\n\n  if (!validate) {\n    // Update SRI\n    versionSRIs[axeFile] = axeIntegrity;\n\n    // Test if the SRI shouldn't be changed\n  } else if (versionSRIs[axeFile] && versionSRIs[axeFile] !== axeIntegrity) {\n    console.log(axeFile, 'did not match the SRI in sri-history.json');\n    process.exitCode = 1;\n  }\n});\n\nif (!validate) {\n  fs.writeFileSync(\n    path.join(root, './sri-history.json'),\n    JSON.stringify(axeHistory, null, '\\t'),\n    'utf-8'\n  );\n  console.log('Updated sri-history.json ');\n} else if (process.exitCode === 1) {\n  console.log(\n    '\\nMake sure the package version and sri-history.json is updated ' +\n      'before publishing to NPM.\\n'\n  );\n}\n"
  },
  {
    "path": "build/tasks/add-locale.js",
    "content": "/*eslint-env node */\n'use strict';\nvar clone = require('clone');\nvar buildManual = require('../build-manual');\n\nmodule.exports = function (grunt) {\n  function mergeMessages(newMessages, oldMessages) {\n    Object.keys(newMessages).forEach(function (key) {\n      if (!oldMessages.hasOwnProperty(key)) {\n        return;\n      }\n\n      var newValue = newMessages[key];\n      var oldValue = oldMessages[key];\n\n      if (typeof newValue === 'object') {\n        // the message format might be changed, ignore old message\n        if (typeof oldValue !== 'object') {\n          return;\n        }\n\n        newMessages[key] = mergeMessages(clone(newValue), oldValue);\n      } else {\n        newMessages[key] = clone(oldValue);\n      }\n    });\n\n    return newMessages;\n  }\n\n  grunt.registerMultiTask(\n    'add-locale',\n    'Task for localizing messages in rules and checks',\n    function () {\n      var options = this.options({\n        rules: ['lib/rules/**/*.json'],\n        checks: ['lib/checks/**/*.json'],\n        misc: ['lib/misc/**/*.json'],\n        blacklist: ['metadata'],\n        tags: '',\n        lang: 'xxx'\n      });\n\n      this.files.forEach(function (file) {\n        var commons = file.src[0];\n\n        buildManual(grunt, options, commons, function (result) {\n          var locale = {\n            lang: options.lang,\n            rules: result.rules.reduce(function (out, rule) {\n              out[rule.id] = rule.metadata;\n              return out;\n            }, {}),\n            checks: result.checks.reduce(function (out, check) {\n              if (check.metadata) {\n                out[check.id] = check.metadata.messages;\n              }\n              return out;\n            }, {}),\n            failureSummaries: result.misc.reduce(function (out, misc) {\n              out[misc.type] = misc.metadata;\n              return out;\n            }, {}),\n            incompleteFallbackMessage: result.misc.reduce(function (out, misc) {\n              return misc.incompleteFallbackMessage\n                ? misc.incompleteFallbackMessage\n                : out;\n            }, '')\n          };\n\n          // update locale file if exists\n          var localeFile = './locales/' + options.lang + '.json';\n          if (grunt.file.exists(localeFile)) {\n            var oldMessages = grunt.file.readJSON(localeFile);\n\n            // mergeMessages mutates out\n            mergeMessages(locale, oldMessages);\n          }\n\n          grunt.file.write(file.dest, JSON.stringify(locale, null, '  '));\n          console.log('created file at', file.dest);\n        });\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/aria-supported.js",
    "content": "/*eslint-env node */\n'use strict';\n\nconst { roles, aria: props } = require('aria-query');\nconst format = require('../shared/format');\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'aria-supported',\n    'Task for generating a diff of supported aria roles and properties.',\n    function () {\n      /**\n       * NOTE:\n       * `axe` has to be dynamically required at this stage,\n       * as `axe` does not exist until grunt task `build:uglify` is complete,\n       * hence cannot be required at the top of the file.\n       */\n      const done = this.async();\n      const { langs } = this.options();\n      const fileNameSuffix = langs && langs.length > 0 ? `${langs[0]}` : '';\n      const axe = require(`../../axe${fileNameSuffix}`);\n      const listType = this.data.listType.toLowerCase();\n      const headings = {\n        main:\n          `# ARIA Roles and Attributes ${\n            listType === 'all' ? 'available' : listType\n          } in axe-core.\\n\\n` +\n          'It can be difficult to know which features of web technologies are accessible across ' +\n          'different platforms, and with different screen readers and other assistive technologies. ' +\n          'Axe-core does some of this work for you, by raising issues when accessibility features are ' +\n          'used that are known to cause problems.\\n\\n' +\n          'This page contains a list of ARIA 1.1 features that axe-core raises as unsupported. ' +\n          'For more information, read [We’ve got your back with “Accessibility Supported” in axe]' +\n          '(https://www.deque.com/blog/weve-got-your-back-with-accessibility-supported-in-axe/).\\n\\n' +\n          'For a detailed description about how accessibility support is decided, see [How we make ' +\n          'decisions on rules](accessibility-supported.md).',\n        attributesMdTableHeader: ['aria-attribute', 'axe-core support']\n      };\n\n      const { ariaRoles, ariaAttrs } = axe.utils.getStandards();\n      const { diff: rolesTable, notes: rolesFootnotes } = getDiff(\n        roles,\n        ariaRoles,\n        listType\n      );\n\n      const ariaQueryAriaAttributes = getAriaQueryAttributes();\n      const { diff: attributesTable, notes: attributesFootnotes } = getDiff(\n        ariaQueryAriaAttributes,\n        ariaAttrs,\n        listType\n      );\n      const formatMarkdownTableRow = columnValues =>\n        `| ${columnValues.join(' | ')} |`;\n      const attributesTableWithHeader = [\n        headings.attributesMdTableHeader,\n        ['---', '---'],\n        ...attributesTable\n      ];\n      const attributesTableMarkdown = attributesTableWithHeader\n        .map(formatMarkdownTableRow)\n        .join('\\n');\n\n      const footnotes = [...rolesFootnotes, ...attributesFootnotes].map(\n        (footnote, index) => `[^${index + 1}]: ${footnote}`\n      );\n\n      const content = `${headings.main}\\n\\n## Attributes\\n\\n${attributesTableMarkdown}\\n\\n${footnotes}`;\n\n      const destFile = this.data.destFile;\n      // Format the content so Prettier doesn't create a diff after running.\n      // See https://github.com/dequelabs/axe-core/issues/1310.\n      format(content, destFile)\n        .then(formattedContent => {\n          // write `aria supported` file contents\n          grunt.file.write(destFile, formattedContent);\n          done();\n        })\n        .catch(err => {\n          console.error(err.message);\n          done(false);\n        });\n\n      /**\n       * Get list of aria attributes, from `aria-query`\n       * @returns {Set|Object} collection of aria attributes from `aria-query` module\n       */\n      function getAriaQueryAttributes() {\n        const ariaKeys = Array.from(props).map(([key]) => key);\n        const roleAriaKeys = Array.from(roles).reduce((out, [name, rule]) => {\n          return [...out, ...Object.keys(rule.props)];\n        }, []);\n        return new Set(axe.utils.uniqueArray(roleAriaKeys, ariaKeys));\n      }\n\n      /**\n       * Given a `base` Map and `subject` Map object,\n       * The function converts the `base` Map entries to an array which is sorted then enumerated to compare each entry against the `subject` Map\n       * The function constructs a `string` to represent a `markdown table`, as well as returns notes to append to footnote\n       * @param {Map} base Base Map Object\n       * @param {Map} subject Subject Map Object\n       * @param {String} type type to compare\n       * @returns {Array<Object>[]}\n       * @example Example Output: [ [ 'alert', 'No' ], [ 'figure', 'Yes' ] ]\n       */\n      function getDiff(base, subject, type) {\n        const diff = [];\n        const notes = [];\n\n        const sortedBase = Array.from(base.entries()).sort();\n\n        sortedBase.forEach(([key] = item) => {\n          switch (type) {\n            case 'supported':\n              if (\n                subject.hasOwnProperty(key) &&\n                subject[key].unsupported === false\n              ) {\n                diff.push([`${key}`, 'Yes']);\n              }\n              break;\n            case 'unsupported':\n              if (\n                (subject[key] && subject[key].unsupported === true) ||\n                !subject.hasOwnProperty(key)\n              ) {\n                diff.push([`${key}`, 'No']);\n              } else if (\n                subject[key] &&\n                subject[key].unsupported &&\n                subject[key].unsupported.exceptions\n              ) {\n                diff.push([`${key}`, `Mixed[^${notes.length + 1}]`]);\n                notes.push(\n                  getSupportedElementsAsFootnote(\n                    subject[key].unsupported.exceptions\n                  )\n                );\n              }\n              break;\n            case 'all':\n            default:\n              diff.push([\n                `${key}`,\n                subject.hasOwnProperty(key) &&\n                subject[key].unsupported === false\n                  ? 'Yes'\n                  : 'No'\n              ]);\n              break;\n          }\n        });\n\n        return {\n          diff,\n          notes\n        };\n      }\n\n      /**\n       * Parse a list of unsupported exception elements and add a footnote\n       * detailing which HTML elements are supported.\n       *\n       * @param {Array<String|Object>} elements List of supported elements\n       * @returns {Array<String|Object>} notes\n       */\n      function getSupportedElementsAsFootnote(elements) {\n        const notes = [];\n\n        const supportedElements = elements.map(element => {\n          if (typeof element === 'string') {\n            return `\\`<${element}>\\``;\n          }\n\n          /**\n\t\t\t\t\t * if element is not a string it will be an object with structure:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnodeName: string,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\ttype: {string|string[]}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t*/\n          return Object.keys(element.properties).map(prop => {\n            const value = element.properties[prop];\n\n            // the 'type' property can be a string or an array\n            if (typeof value === 'string') {\n              return `\\`<${element.nodeName} ${prop}=\"${value}\">\\``;\n            }\n\n            // output format for an array of types:\n            // <input type=\"button\" | \"checkbox\">\n            const values = value.map(v => `\"${v}\"`).join(' | ');\n            return `\\`<${element.nodeName} ${prop}=${values}>\\``;\n          });\n        });\n\n        notes.push('Supported on elements: ' + supportedElements.join(', '));\n\n        return notes;\n      }\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/configure.js",
    "content": "/*eslint-env node */\n'use strict';\nconst buildRules = require('../configure');\nconst format = require('../shared/format');\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'configure',\n    'Task for configuring rules and checks',\n    function () {\n      const done = this.async();\n      const options = this.options({\n        rules: ['lib/rules/**/*.json'],\n        checks: ['lib/checks/**/*.json'],\n        misc: ['lib/misc/**/*.json'],\n        blacklist: ['metadata'],\n        tags: ''\n      });\n\n      this.files.forEach(function (file) {\n        // locale will always be the 2nd to last part of the\n        // filename and in the format of \"<name>.<locale>.js\"\n        const parts = file.dest.auto.split('.');\n        if (parts.length > 2) {\n          options.locale = parts[parts.length - 2];\n        }\n\n        buildRules(grunt, options, null, function (result) {\n          grunt.file.write(file.dest.auto, 'axe._load(' + result.auto + ');');\n\n          // Format the content so Prettier doesn't create a diff after running.\n          // See https://github.com/dequelabs/axe-core/issues/1310.\n          format(result.descriptions, file.dest.descriptions)\n            .then(descriptionsContent => {\n              grunt.file.write(file.dest.descriptions, descriptionsContent);\n              done();\n            })\n            .catch(err => {\n              console.error(err.message);\n              done(false);\n            });\n        });\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/esbuild.js",
    "content": "const { build } = require('esbuild');\nconst path = require('path');\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'esbuild',\n    'Task to run the esbuild javascript bundler',\n    function () {\n      const done = this.async();\n      const files = grunt.task.current.data.files;\n\n      files.forEach(file => {\n        const src = Array.isArray(file.src) ? file.src : [file.src];\n        const dest = file.dest;\n\n        src.forEach(entry => {\n          const name = path.basename(entry);\n          if (file.cwd) {\n            entry = path.join(file.cwd, entry);\n          }\n\n          build({\n            entryPoints: [entry],\n            outfile: path.join(dest, name),\n            minify: false,\n            bundle: true\n          })\n            .then(done)\n            .catch(e => {\n              grunt.fail.fatal(e);\n              done();\n            });\n        });\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/langs.js",
    "content": "/*eslint-env node */\n'use strict';\nvar http = require('http');\nmodule.exports = function (grunt) {\n  function getLine(data, start) {\n    var len = data.length;\n    var index = start;\n    while (index < len) {\n      if (data.charAt(index) === '\\n') {\n        break;\n      }\n      index += 1;\n    }\n    var retVal = data.substring(start, index + 1);\n    return retVal;\n  }\n  function getEntry(data, start) {\n    var entry = [];\n    var line;\n    var len = data.length;\n    var index = start;\n    while (index < len) {\n      line = getLine(data, index);\n      if (line.indexOf('%') === 0) {\n        index += line.length;\n        // end of entry\n        break;\n      }\n      index += line.length;\n      line = line.substring(0, line.length - 1);\n      entry.push(line);\n    }\n    entry.used = index - start;\n    return entry;\n  }\n  function generateOutput(langs, checkPath) {\n    var path = checkPath + '.js';\n    var template = [\n      '/* global axe */\\n',\n      '/*eslint quotes: 0*/\\n',\n      'var langs = ' + JSON.stringify(langs, null, '\\t') + ';\\n\\n',\n      '/**\\n',\n      ' * Returns array of valid language codes\\n',\n      ' * @method validLangs\\n',\n      ' * @memberof axe.utils\\n',\n      ' * @instance\\n',\n      ' * @return {Array<Sting>} Valid language codes\\n',\n      ' */\\n',\n      'axe.utils.validLangs = function () {\\n',\n      \"\\t'use strict';\\n\",\n      '\\treturn langs;\\n',\n      '};\\n'\n    ].join('');\n    grunt.file.write(path, template);\n  }\n  grunt.registerMultiTask(\n    'langs',\n    'Task for generating commons language codes from IANA registry',\n    function () {\n      var done = this.async();\n      var ianaLangsURL =\n        'http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry';\n      if (!this.data.check) {\n        done(false);\n        return;\n      }\n      var check = this.data.check;\n      var langs = [];\n      new Promise(function (resolve, reject) {\n        var data = '';\n        http\n          .get(ianaLangsURL, function (res) {\n            res\n              .on('data', function (chunk) {\n                data += chunk;\n              })\n              .on('end', function () {\n                resolve(data);\n              });\n          })\n          .on('error', function (e) {\n            grunt.log.error('Got error: ' + e.message);\n            reject(false);\n          });\n      })\n        .then(function (data) {\n          var entry = getEntry(data, 0);\n          var pos = entry.used;\n          while (true) {\n            entry = getEntry(data, pos);\n            pos += entry.used;\n            if (!entry.used) {\n              break;\n            }\n            if (entry[0] !== 'Type: language') {\n              continue;\n            }\n            var lang = entry[1].replace('Subtag: ', '').trim();\n            langs.push(lang);\n          }\n          generateOutput(langs, check);\n        })\n        .then(function () {\n          done();\n        })\n        .catch(function (result) {\n          done(result);\n        });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/metadata-function-map.js",
    "content": "const path = require('path').posix;\nconst glob = require('glob');\nconst fs = require('fs');\n\nfunction toTitleCase(str) {\n  return str.replace(/-\\w/g, txt => {\n    return txt.charAt(1).toUpperCase() + txt.substr(2).toLowerCase();\n  });\n}\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'metadata-function-map',\n    'Task to generate the metadata-function-map file',\n    function () {\n      const files = grunt.task.current.data.files;\n\n      files.forEach(file => {\n        const src = Array.isArray(file.src) ? file.src : [file.src];\n        const map = {};\n        let outFile =\n          '// This file is automatically generated using build/tasks/metadata-function-map.js\\n';\n\n        src.forEach(globPath => {\n          glob.sync(globPath, { posix: true }).forEach(filePath => {\n            const relativePath = path.relative(\n              path.dirname(file.dest),\n              filePath\n            );\n            const filename = path.basename(filePath, '.js');\n            const functionName = toTitleCase(filename);\n\n            outFile += `import ${functionName} from '${relativePath}';\\n`;\n            map[filename] = functionName;\n          });\n        });\n\n        outFile += `\\nconst metadataFunctionMap = {\\n`;\n        outFile += Object.keys(map)\n          .sort()\n          .map(key => `  '${key}': ${map[key]}`)\n          .join(',\\n');\n        outFile += `\\n};\\n\\nexport default metadataFunctionMap;`;\n\n        fs.writeFileSync(file.dest, outFile, 'utf-8');\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/notify.js",
    "content": "const { notify } = require('node-notifier');\n\n/*eslint-env node */\n('use strict');\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'notify',\n    'This task sends an OS notification when a task has completed',\n    function () {\n      notify(this.data);\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/test.js",
    "content": "const execSync = require('child_process').execSync;\nconst chalk = require('chalk');\n\n/*eslint-env node */\n('use strict');\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'test',\n    'This task runs unit tests based on which file was changed',\n    function () {\n      const testFile = this.data.testFile;\n      console.log(`${chalk.green('>>')} File \"${testFile}\"`);\n\n      execSync(`npm run test:unit -- testFiles=${testFile}`, {\n        stdio: 'inherit'\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/update-help.js",
    "content": "/*eslint-env node */\n'use strict';\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'update-help',\n    'Task for updating Deque University helpUrls based on rule JSON files',\n    function () {\n      var options = this.options({\n        version: '1.0.0'\n      });\n      var v = options.version.split('.');\n      v.pop();\n      var baseUrl =\n        'https://dequeuniversity.com/rules/axe/' + v.join('.') + '/';\n      this.files.forEach(function (f) {\n        f.src.forEach(function (filepath) {\n          var config = grunt.file.readJSON(filepath);\n          config.metadata.helpUrl = baseUrl + config.id;\n          grunt.file.write(filepath, JSON.stringify(config, null, '  '));\n        });\n      });\n    }\n  );\n};\n"
  },
  {
    "path": "build/tasks/validate.js",
    "content": "/*eslint-env node */\n'use strict';\n\nvar revalidator = require('revalidator').validate,\n  fs = require('fs'),\n  path = require('path');\n\nfunction fileExists(v, o) {\n  if (!v.endsWith('.js')) {\n    return true;\n  }\n\n  var file = path.resolve(path.dirname(o._path), v);\n  var exists;\n  try {\n    exists = fs.existsSync(file);\n  } catch {\n    return false;\n  }\n  return exists;\n}\n\nfunction hasUniqueId() {\n  var seen = {};\n  return function (v, o) {\n    if (!seen[v]) {\n      seen[v] = o;\n      return true;\n    }\n    return false;\n  };\n}\n\nfunction hasMultipleOutcomes(messages) {\n  const keys = Object.keys(messages);\n  if (keys.length < 2) {\n    return false;\n  }\n\n  return keys.every(key => {\n    switch (key) {\n      case 'pass':\n      case 'fail':\n      case 'incomplete':\n        return ['string', 'object'].includes(typeof messages[key]);\n\n      default:\n        return false;\n    }\n  });\n}\n\nfunction createSchemas() {\n  var schemas = {};\n\n  schemas.tool = {\n    properties: {\n      id: {\n        required: true,\n        type: 'string',\n        conform: hasUniqueId()\n      },\n      options: {\n        type: 'object'\n      },\n      source: {\n        type: 'string',\n        required: true,\n        conform: fileExists,\n        messages: {\n          conform: 'File does not exist'\n        }\n      }\n    }\n  };\n\n  schemas.check = {\n    properties: {\n      id: {\n        required: true,\n        type: 'string',\n        conform: hasUniqueId()\n      },\n      excludeHidden: {\n        type: 'boolean'\n      },\n      evaluate: {\n        type: 'string',\n        required: true,\n        conform: fileExists,\n        messages: {\n          conform: 'File does not exist'\n        }\n      },\n      matches: {\n        type: 'string',\n        required: false,\n        conform: fileExists,\n        messages: {\n          conform: 'File does not exist'\n        }\n      },\n      metadata: {\n        type: 'object',\n        required: true,\n        properties: {\n          messages: {\n            required: true,\n            type: 'object',\n            conform: hasMultipleOutcomes,\n            messages: {\n              conform: 'Must have at least two valid messages'\n            }\n          },\n          // @deprecated: Use impact on rules instead\n          impact: {\n            type: 'string',\n            enum: ['minor', 'moderate', 'serious', 'critical']\n          }\n        }\n      }\n    }\n  };\n\n  schemas.rule = {\n    seen: {},\n    properties: {\n      id: {\n        required: true,\n        type: 'string',\n        conform: hasUniqueId()\n      },\n      selector: {\n        type: 'string'\n      },\n      impact: {\n        required: true,\n        type: 'string',\n        enum: ['minor', 'moderate', 'serious', 'critical']\n      },\n      excludeHidden: {\n        type: 'boolean'\n      },\n      enabled: {\n        type: 'boolean'\n      },\n      pageLevel: {\n        type: 'boolean'\n      },\n      any: {\n        type: 'array',\n        items: {\n          type: ['string', 'object'],\n          conform: function (v) {\n            if (typeof v === 'string') {\n              return true;\n            }\n            if (typeof v === 'object' && typeof v.id === 'string') {\n              return true;\n            }\n            return false;\n          },\n          message: 'must be a string or an object with a key of id'\n        }\n      },\n      all: {\n        type: 'array',\n        items: {\n          type: ['string', 'object'],\n          conform: function (v) {\n            if (typeof v === 'string') {\n              return true;\n            }\n            if (typeof v === 'object' && typeof v.id === 'string') {\n              return true;\n            }\n            return false;\n          },\n          message: 'must be a string or an object with a key of id'\n        }\n      },\n      none: {\n        type: 'array',\n        items: {\n          type: ['string', 'object'],\n          conform: function (v) {\n            if (typeof v === 'string') {\n              return true;\n            }\n            if (typeof v === 'object' && typeof v.id === 'string') {\n              return true;\n            }\n            return false;\n          },\n          message: 'must be a string or an object with a key of id'\n        }\n      },\n      tags: {\n        type: 'array',\n        items: {\n          type: 'string'\n        }\n      },\n      actIds: {\n        type: 'array',\n        items: {\n          type: 'string'\n        }\n      },\n      matches: {\n        type: 'string',\n        required: false,\n        conform: fileExists,\n        messages: {\n          conform: 'File does not exist'\n        }\n      },\n      metadata: {\n        type: 'object',\n        required: true,\n        additionalProperties: false,\n        properties: {\n          help: {\n            required: true,\n            type: 'string'\n          },\n          description: {\n            required: true,\n            type: 'string'\n          }\n        }\n      }\n    }\n  };\n\n  return schemas;\n}\n\nfunction validateFiles(grunt, files, schema, type) {\n  var valid = true;\n  files.forEach(function (f) {\n    f.src.forEach(function (pathArg) {\n      var file = grunt.file.readJSON(pathArg);\n      file._path = pathArg;\n      var result = revalidator(file, schema);\n      if (!result.valid) {\n        result.errors.forEach(function (err) {\n          grunt.log.error(pathArg, err.property + ' ' + err.message);\n        });\n        valid = false;\n      }\n\n      const ruleIssues = type === 'rule' ? validateRule(file) : [];\n      if (ruleIssues.length > 0) {\n        ruleIssues.forEach(issue => grunt.log.error(pathArg, issue));\n        valid = false;\n      }\n\n      if (valid) {\n        grunt.verbose.ok();\n      }\n    });\n  });\n  return valid;\n}\n\nmodule.exports = function (grunt) {\n  grunt.registerMultiTask(\n    'validate',\n    'Task for validating API schema for checks and rules',\n    function () {\n      const { type } = this.options();\n      const schemas = createSchemas();\n      const schema = schemas[type];\n      if (!schema) {\n        grunt.log.error(\n          'Please specify a valid type to validate: ' + Object.keys(schemas)\n        );\n        return false;\n      }\n      const valid = validateFiles(grunt, this.files, schema, type);\n      schema.seen = {};\n      return valid;\n    }\n  );\n};\n\nfunction validateRule({ tags, metadata }) {\n  if (!Array.isArray(tags) || typeof metadata !== 'object') {\n    return [];\n  }\n  const issues = [];\n  const prohibitedWord = tags.includes('best-practice') ? 'must' : 'should';\n  const { description, help } = metadata;\n\n  if (description.toLowerCase().includes(prohibitedWord)) {\n    issues.push(\n      `metadata.description can not contain the word '${prohibitedWord}'.`\n    );\n  }\n\n  if (help.toLowerCase().includes(prohibitedWord)) {\n    issues.push(`metadata.help can not contain the word '${prohibitedWord}'.`);\n  }\n\n  issues.push(...findTagIssues(tags));\n  return issues;\n}\n\nconst miscTags = ['ACT', 'experimental', 'review-item', 'deprecated'];\n\nconst categories = [\n  'aria',\n  'color',\n  'forms',\n  'keyboard',\n  'language',\n  'name-role-value',\n  'parsing',\n  'semantics',\n  'sensory-and-visual-cues',\n  'structure',\n  'tables',\n  'text-alternatives',\n  'time-and-media'\n];\n\nconst standardsTags = [\n  {\n    // Has to be first, as others rely on the WCAG level getting picked up first\n    name: 'WCAG',\n    standardRegex: /^wcag2(1|2)?a{1,3}(-obsolete)?$/,\n    criterionRegex: /^wcag\\d{3,4}$/\n  },\n  {\n    name: 'Section 508',\n    standardRegex: /^section508$/,\n    criterionRegex: /^section508\\.\\d{1,2}\\.[a-z]$/,\n    wcagLevelRegex: /^wcag2aa?$/\n  },\n  {\n    name: 'Trusted Tester',\n    standardRegex: /^TTv5$/,\n    criterionRegex: /^TT\\d{1,3}\\.[a-z]$/,\n    wcagLevelRegex: /^wcag2aa?$/\n  },\n  {\n    name: 'EN 301 549',\n    standardRegex: /^EN-301-549$/,\n    criterionRegex: /^EN-9\\.[1-4]\\.[1-9]\\.\\d{1,2}$/,\n    wcagLevelRegex: /^wcag21?aa?$/\n  },\n  {\n    name: 'RGAA',\n    standardRegex: /^RGAAv4$/,\n    criterionRegex: /^RGAA-\\d{1,2}\\.\\d{1,2}\\.\\d{1,2}$/,\n    wcagLevelRegex: /^wcag21?aa?$/\n  }\n];\n\nfunction findTagIssues(tags) {\n  const issues = [];\n  const catTags = tags.filter(tag => tag.startsWith('cat.'));\n  const bestPracticeTags = tags.filter(tag => tag === 'best-practice');\n\n  // Category\n  if (catTags.length !== 1) {\n    issues.push(`Must have exactly one cat. tag, got ${catTags.length}`);\n  }\n  if (catTags.length && !categories.includes(catTags[0].slice(4))) {\n    issues.push(`Invalid category tag: ${catTags[0]}`);\n  }\n  if (!startsWith(tags, catTags)) {\n    issues.push(`Tag ${catTags[0]} must be before ${tags[0]}`);\n  }\n  tags = removeTags(tags, catTags);\n\n  // Best practice\n  if (bestPracticeTags.length > 1) {\n    issues.push(\n      `Only one best-practice tag is allowed, got ${bestPracticeTags.length}`\n    );\n  }\n  if (!startsWith(tags, bestPracticeTags)) {\n    issues.push(`Tag ${bestPracticeTags[0]} must be before ${tags[0]}`);\n  }\n  tags = removeTags(tags, bestPracticeTags);\n\n  const standards = {};\n  // WCAG, Section 508, Trusted Tester, EN 301 549\n  for (const {\n    name,\n    standardRegex,\n    criterionRegex,\n    wcagLevelRegex\n  } of standardsTags) {\n    const standardTags = tags.filter(tag => tag.match(standardRegex));\n    const criterionTags = tags.filter(tag => tag.match(criterionRegex));\n    if (!standardTags.length && !criterionTags.length) {\n      continue;\n    }\n\n    standards[name] = {\n      name,\n      standardTag: standardTags[0] ?? null,\n      criterionTags\n    };\n    if (name !== 'RGAA' && bestPracticeTags.length !== 0) {\n      issues.push(`${name} tags cannot be used along side best-practice tag`);\n    }\n    if (standardTags.length === 0) {\n      issues.push(`Expected one ${name} tag, got 0`);\n    } else if (standardTags.length > 1) {\n      issues.push(`Expected one ${name} tag, got: ${standardTags.join(', ')}`);\n    }\n    if (criterionTags.length === 0) {\n      issues.push(`Expected at least one ${name} criterion tag, got 0`);\n    }\n\n    if (wcagLevelRegex && standards.WCAG) {\n      const wcagLevel = standards.WCAG.standardTag;\n      if (!wcagLevel.match(wcagLevelRegex)) {\n        issues.push(`${name} rules not allowed on ${wcagLevel}`);\n      }\n    }\n\n    // Must have the same criteria listed\n    if (name === 'EN 301 549') {\n      const wcagCriteria = standards.WCAG.criterionTags.map(tag =>\n        tag.slice(4)\n      );\n      const enCriteria = criterionTags.map(tag =>\n        tag.slice(5).replaceAll('.', '')\n      );\n      if (\n        wcagCriteria.length !== enCriteria.length ||\n        !startsWith(wcagCriteria, enCriteria)\n      ) {\n        issues.push(\n          `Expect WCAG and EN criteria numbers to match: ${wcagCriteria.join(\n            ', '\n          )} vs ${enCriteria.join(', ')}}`\n        );\n      }\n    }\n    tags = removeTags(tags, [...standardTags, ...criterionTags]);\n  }\n\n  // Other tags\n  const usedMiscTags = miscTags.filter(tag => tags.includes(tag));\n  const unknownTags = removeTags(tags, usedMiscTags);\n  if (unknownTags.length) {\n    issues.push(`Invalid tags: ${unknownTags.join(', ')}`);\n  }\n\n  // At this point only misc tags are left:\n  tags = removeTags(tags, unknownTags);\n  if (!startsWith(tags, usedMiscTags)) {\n    issues.push(\n      `Tags [${tags.join(', ')}] should be sorted like [${usedMiscTags.join(\n        ', '\n      )}]`\n    );\n  }\n\n  return issues;\n}\n\nfunction startsWith(arr1, arr2) {\n  return arr2.every((item, i) => item === arr1[i]);\n}\n\nfunction removeTags(tags, tagsToRemove) {\n  return tags.filter(tag => !tagsToRemove.includes(tag));\n}\n"
  },
  {
    "path": "build/templates.js",
    "content": "module.exports = {\n  evaluate: 'function (node, options, virtualNode, context) {\\n<%=source%>\\n}',\n  after: 'function (results, options) {\\n<%=source%>\\n}',\n  gather: 'function (context, options) {\\n<%=source%>\\n}',\n  matches: 'function (node, virtualNode, context) {\\n<%=source%>\\n}',\n  source: '(function () {\\n<%=source%>\\n}())',\n  commons: '<%=source%>'\n};\n"
  },
  {
    "path": "code-of-conduct.md",
    "content": "## Introduction\n\nDeque is committed to providing a safe, inclusive, and welcoming virtual environment for contributors.\n\nWe created this policy not because we anticipate bad behavior, but because we believe that articulating our values and obligations to one another reinforces a level of respect among contributors and because having a code of conduct provides us with clear avenues to correct our virtual culture should it ever stray from that course.\n\n## Code of Conduct Policy\n\nWe expect all contributors to Deque open-source projects to uphold the principles of this Code of Conduct. We do not tolerate disruptive or disrespectful behavior, messages, images, or interactions by any participant, in any form.\n\nWe will not tolerate harassment or discrimination based on age, ancestry, color, gender identity or expression, national origin, physical or mental disability, religion, sexual orientation, or any other characteristic.\n\n## Enforcement and Reporting\n\nContributors asked to stop any harassing behavior are expected to comply immediately; regardless, Deque reserves the right to remove or block anyone from any repository at the discretion of Deque employees.\n\nIf you have any concerns, please contact a code-owner or other Deque employee via direct message on the [axe Community Slack](https://accessibility.deque.com/axe-community) with details about the nature of the concern.\n"
  },
  {
    "path": "doc/API.md",
    "content": "# Axe JavaScript Accessibility API\n\n## Table of Contents\n\n1. [Section 1: Introduction](#section-1-introduction)\n   1. [Get Started](#getting-started)\n1. [Section 2: API Reference](#section-2-api-reference)\n   1. [Overview](#overview)\n   1. [API Notes](#api-notes)\n   1. [API Name: axe.getRules](#api-name-axegetrules)\n   1. [API Name: axe.configure](#api-name-axeconfigure)\n   1. [API Name: axe.reset](#api-name-axereset)\n   1. [API Name: axe.run](#api-name-axerun)\n      1. [Parameters axe.run](#parameters-axerun)\n         1. [Context Parameter](#context-parameter)\n         2. [Options Parameter](#options-parameter)\n         3. [Callback Parameter](#callback-parameter)\n      1. [Return Promise](#return-promise)\n      1. [Error result](#error-result)\n      1. [Results Object](#results-object)\n   1. [API Name: axe.registerPlugin](#api-name-axeregisterplugin)\n   1. [API Name: axe.cleanup](#api-name-axecleanup)\n   1. [API Name: axe.setup](#api-name-axesetup)\n   1. [API Name: axe.teardown](#api-name-axeteardown)\n   1. [API Name: axe.frameMessenger](#api-name-axeframemessenger)\n   1. [API name: axe.runPartial / axe.finishRun](#api-name-axerunpartial--axefinishrun)\n   1. [Virtual DOM Utilities](#virtual-dom-utilities)\n      1. [API Name: axe.utils.querySelectorAll](#api-name-axeutilsqueryselectorall)\n      1. [API Name: axe.utils.getRule](#api-name-axeutilsgetrule)\n   1. [Common Functions](#common-functions)\n1. [Section 3: Example Reference](#section-3-example-reference)\n1. [Section 4: Performance](#section-4-performance)\n\n## Section 1: Introduction\n\nThe axe API is designed to be an improvement over the previous generation of accessibility APIs. It provides the following benefits:\n\n- Runs in any modern browser\n- Designed to work with existing testing infrastructure\n- Runs locally, no connection to a third-party server is necessary\n- Performs violation checking on multiple levels of nested iframes\n- Provides list of rules and elements that passed accessibility checking, ensuring rules have been run against entire document\n- Only checks rendered content to minimize false positives (that includes visually-hidden content)\n\n### Getting Started\n\nThis section gives a quick description of how to use the axe APIs to analyze web page content and return a JSON object that lists any accessibility violations found.\n\nThe axe API can be used as part of a broader process that is performed on many, if not all, pages of a website. The API is used to analyze web page content and return a JSON object that lists any accessibility violations found. Here is how to get started:\n\n1. Load page in testing system\n2. Optionally, set configuration options for the JavaScript API (`axe.configure`)\n3. Call analyze JavaScript API (`axe.run`)\n4. Either assert against results or save them for later processing\n5. Repeat for any inactive or non-rendered content after making it visible\n\n## Section 2: API Reference\n\n### Overview\n\nThe axe APIs are provided in the JavaScript file axe.js. It must be included in the web page under test, as well as each `iframe` under test. Parameters are sent as JavaScript function parameters. Results are returned in JSON format.\n\n### Full API Reference for Developers\n\nFor a full listing of API offered by axe, clone the repository and run `npm run api-docs`. This generates `jsdoc` documentation under `doc/api` which can be viewed using the browser.\n\n### API Notes\n\n- A Rule test is made up of sub-tests. Each sub-test is returned in an array of 'checks'\n- The `\"helpUrl\"` in the results object is a link to a broader description of the accessibility issue and suggested remediation. These links point to Deque University help pages, which do not require a login.\n- Axe does not test hidden regions, such as inactive menus or modal windows. To test those for accessibility, write tests that activate or render the regions visible and run the analysis again.\n\n### Axe-core Tags\n\nEach rule in axe-core has a number of tags. These provide metadata about the rule. Each rule has one tag that indicates which WCAG version / level it belongs to, or if it doesn't, it has the `best-practice` tag. If the rule is required by WCAG, there is a tag that references the success criterion number. For example, the `wcag111` tag means a rule is required for WCAG 2 success criterion 1.1.1.\n\nThe `experimental`, `ACT`, `TT`, and `section508` tags are only added to some rules. Each rule with a `section508` tag also has a tag to indicate what requirement in old Section 508 the rule is required by. For example `section508.22.a`.\n\n| Tag Name          | Accessibility Standard / Purpose                                                                                              |\n| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- |\n| `wcag2a`          | WCAG 2.0 Level A                                                                                                              |\n| `wcag2aa`         | WCAG 2.0 Level AA                                                                                                             |\n| `wcag2aaa`        | WCAG 2.0 Level AAA                                                                                                            |\n| `wcag21a`         | WCAG 2.1 Level A                                                                                                              |\n| `wcag21aa`        | WCAG 2.1 Level AA                                                                                                             |\n| `wcag22aa`        | WCAG 2.2 Level AA                                                                                                             |\n| `best-practice`   | Common accessibility best practices                                                                                           |\n| `wcag2a-obsolete` | WCAG 2.0 Level A, no longer required for conformance                                                                          |\n| `wcag***`         | WCAG success criterion e.g. wcag111 maps to SC 1.1.1                                                                          |\n| `ACT`             | W3C approved Accessibility Conformance Testing rules                                                                          |\n| `section508`      | Old Section 508 rules                                                                                                         |\n| `section508.*.*`  | Requirement in old Section 508                                                                                                |\n| `TTv5`            | Trusted Tester v5 rules                                                                                                       |\n| `TT*.*`           | Test ID in Trusted Tester                                                                                                     |\n| `EN-301-549`      | Rule required under [EN 301 549](https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf) |\n| `EN-9.*`          | Section in EN 301 549 listing the requirement                                                                                 |\n| `RGAAv4`          | Rule required under [RGAA](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/)                                |\n| `RGAA-*.*.*`      | Section in RGAA listing the requirement                                                                                       |\n| `experimental`    | Cutting-edge rules, disabled by default                                                                                       |\n| `cat.*`           | Category mappings used by Deque (see below)                                                                                   |\n\nAll rules have a `cat.*` tag, which indicates what type of content it is part of. The following `cat.*` tags exist in axe-core:\n\n| Category name                 |\n| ----------------------------- |\n| `cat.aria`                    |\n| `cat.color`                   |\n| `cat.forms`                   |\n| `cat.keyboard`                |\n| `cat.language`                |\n| `cat.name-role-value`         |\n| `cat.parsing`                 |\n| `cat.semantics`               |\n| `cat.sensory-and-visual-cues` |\n| `cat.structure`               |\n| `cat.tables`                  |\n| `cat.text-alternatives`       |\n| `cat.time-and-media`          |\n\n### API Name: axe.getRules\n\n#### Purpose\n\nTo get information on all the rules in the system.\n\n#### Description\n\nReturns a list of all rules with their ID and description\n\n#### Synopsis\n\n`axe.getRules([Tag Name 1, Tag Name 2...]);`\n\n#### Parameters\n\n- `tags` - **optional** Array of tags used to filter returned rules. If omitted, it will return all rules. See [axe-core tags](#axe-core-tags).\n\n**Returns:** Array of rules that match the input filter with each entry having a format of `{ruleId: <id>, description: <desc>, helpUrl: <url>, help: <help>, tags: <tags>}`\n\n#### Example 1\n\nIn this example, we pass in the WCAG 2 A and AA tags into `axe.getRules` to retrieve only those rules. The function call returns an array of rules.\n\n**Call:** `axe.getRules(['wcag2aa', 'wcag2a']);`\n\n**Returned Data:**\n\n```js\n[\n  {\n    description: \"Ensure <area> elements of image maps have alternate text\",\n    help: \"Active <area> elements must have alternate text\",\n    helpUrl: \"https://dequeuniversity.com/rules/axe/3.5/area-alt?application=axeAPI\",\n    ruleId: \"area-alt\",\n    tags: [\n      \"cat.text-alternatives\",\n      \"wcag2a\",\n      \"wcag111\",\n      \"wcag244\",\n      \"wcag412\",\n      \"section508\",\n      \"section508.22.a\"\n    ],\n    actIds: ['c487ae']\n  },\n  {\n    description: \"Ensure ARIA attributes are allowed for an element's role\",\n    help: \"Elements must only use allowed ARIA attributes\",\n    helpUrl: \"https://dequeuniversity.com/rules/axe/3.5/aria-allowed-attr?application=axeAPI\",\n    ruleId: \"aria-allowed-attr\",\n    tags: [\n      \"cat.aria\",\n      \"wcag2a\",\n      \"wcag412\"\n    ]\n  }\n  …\n]\n```\n\n### API Name: axe.configure\n\n#### Purpose\n\nTo configure the format of the data used by axe. This can be used to add new rules, which must be registered with the library to execute.\n\n**Important**: `axe.configure()` does not communicate configuration calls into iframes. Instead `axe.configure()` must be called with the same argument in each `frame` / `iframe` individually.\n\n#### Description\n\nUser specifies the format of the JSON structure passed to the callback of `axe.run`\n\n#### Synopsis\n\n```js\naxe.configure({\n  branding: String,\n  reporter: 'option' | Function,\n  checks: [Object],\n  rules: [Object],\n  standards: Object,\n  locale: Object,\n  axeVersion: String,\n  disableOtherRules: Boolean,\n  noHtml: Boolean\n});\n```\n\n#### Parameters\n\n- `configurationOptions` - Options object; where the valid name, value pairs are:\n  - `branding` - mixed(optional) Used to set the branding of the helpUrls\n    - `brand` - string(optional) sets the brand string - default \"axe\"\n    - `application` - string(optional) sets the application string - default \"axeAPI\"\n  - `reporter` - Used to set the output format that the axe.run function will pass to the callback function. Can pass a reporter name or a custom reporter function. Valid names are:\n    - `v1` to use the previous version's format: `axe.configure({ reporter: \"v1\" });`\n    - `v2` to use the current version's format: `axe.configure({ reporter: \"v2\" });`\n    - `raw` to return the raw result data without formatting: `axe.configure({ reporter: \"raw\" });`\n    - `raw-env` to return the raw result data with environment data: `axe.configure({ reporter: \"raw-env\" });`\n    - `no-passes` to return only violation results: `axe.configure({ reporter: \"no-passes\" });`\n  - `checks` - Used to add checks to the list of checks used by rules, or to override the properties of existing checks\n    - The checks attribute is an array of check objects\n    - Each check object can contain the following attributes\n      - `id` - string(required). This uniquely identifies the check. If the check already exists, this will result in any supplied check properties being overridden. The properties below that are marked required if new are optional when the check is being overridden.\n      - `evaluate` - string(required for new). The ID of the function that implements the check's functionality. See the [`metadata-function-map`](../lib/core/base/metadata-function-map.js) file for all defined IDs.\n      - `after` - string(optional). The ID of the function that gets called for checks that operate on a page-level basis, to process the results from the iframes.\n      - `options` - mixed(optional). This is the options structure that is passed to the evaluate function and is intended to be used to configure checks. It is the most common property that is intended to be overridden for existing checks.\n      - `enabled` - boolean(optional, default `true`). This is used to indicate whether the check is on or off by default. Checks that are off are not evaluated, even when included in a rule. Overriding this is a common way to disable a particular check across multiple rules.\n  - `rules` - Used to add rules to the existing set of rules, or to override the properties of existing rules\n    - The rules attribute is an Array of rule objects\n    - each rule object can contain the following attributes\n      - `id` - string(required). This uniquely identifies the rule. If the rule already exists, it will be overridden with any of the attributes supplied. The attributes below that are marked required, are only required for new rules.\n      - `impact` - string(required). Sets the impact of that rule's results\n      - `reviewOnFail` - boolean(option, default `false`). Override the result of a rule to return \"Needs Review\" rather than \"Violation\" if the rule fails.\n      - `selector` - string(optional, default `*`). A [CSS selector](./developer-guide.md#supported-css-selectors) used to identify the elements that are passed into the rule for evaluation.\n      - `excludeHidden` - boolean(optional, default `true`). This indicates whether elements that are hidden from all users are to be passed into the rule for evaluation.\n      - `enabled` - boolean(optional, default `true`). Whether the rule is turned on. This is a common attribute for overriding.\n      - `pageLevel` - boolean(optional, default `false`). When set to true, this rule is only applied when the entire page is tested. Results from nodes on different frames are combined into a single result. See [page level rules](#page-level-rules).\n      - `any` - array(optional, default `[]`). This is a list of checks that, if none \"pass\", will generate a violation.\n      - `all` - array(optional, default `[]`). This is a list of checks that, if any \"fails\", will generate a violation.\n      - `none` - array(optional, default `[]`). This is a list of checks that, if any \"pass\", will generate a violation.\n      - `tags` - array(optional, default `[]`). A list if the tags that \"classify\" the rule. See [axe-core tags](#axe-core-tags).\n      - `matches` - string(optional). The ID of the filtering function that will exclude elements that match the `selector` property. See the [`metadata-function-map`](../lib/core/base/metadata-function-map.js) file for all defined IDs.\n  - `standards` - object(optional). Used to configure the standards object. See the [Standards Object docs](./standards-object.md) for the structure of each standards object.\n  - `disableOtherRules` - Disables all rules not included in the `rules` property.\n  - `locale` - A locale object to apply (at runtime) to all rules and checks, in the same shape as `/locales/*.json`.\n  - `axeVersion` - Set the compatible version of a custom rule with the current axe version. Compatible versions are all patch and minor updates that are the same as, or newer than those of the `axeVersion` property.\n  - `noHtml` - Disables the HTML output of nodes from rules.\n  - `allowedOrigins` - Set which origins (URL domains) will communicate test data with. See [allowedOrigins](#allowedorigins).\n\n**Returns:** Nothing\n\n**Note**: The `branding` property accepts a `string`, which sets the application. Passing it an object is deprecated as of axe-core 4.4.0, as is the `branding.brand` property.\n\n##### Page level rules\n\nPage level rules split their evaluation into two phases. A 'data collection' phase which is done inside the 'evaluate' function and an assessment phase which is done inside the 'after' function. The evaluate function executes inside each individual frame and is responsible for collection data that is passed into the after function which inspects that data and makes a decision.\n\nPage level rules raise violations on the entire document and not on individual nodes or frames from which the data was collected. For an example of how this works, see the heading order check:\n\n- [lib/checks/navigation/heading-order.json](https://github.com/dequelabs/axe-core/blob/master/lib/checks/navigation/heading-order.json)\n- [lib/checks/navigation/heading-order-evaluate.js](https://github.com/dequelabs/axe-core/blob/master/lib/checks/navigation/heading-order-evaluate.js)\n- [lib/checks/navigation/heading-order-after.js](https://github.com/dequelabs/axe-core/blob/master/lib/checks/navigation/heading-order-after.js)\n\n##### allowedOrigins\n\nAxe-core will only communicate results to frames of the same origin (the URL domain). To configure axe so that it exchanges results across different origins, you can configure allowedOrigins. This configuration must happen in **every frame**. For example:\n\n```js\naxe.configure({\n  allowedOrigins: ['<same_origin>', 'https://deque.com']\n});\n```\n\nThe `allowedOrigins` option has two wildcard options. `<same_origin>` always corresponds to the current domain. If you want to block all frame communication, set `allowedOrigins` to `[]`. To configure axe-core to communicate to all origins, use `<unsafe_all_origins>`. **This is not recommended**. Because this is the only way to test iframes on `file://`, it is recommended to use a localhost server such as [http-server](https://www.npmjs.com/package/http-server) instead.\n\nUse of `allowedOrigins` is not necessary if an alternative [frameMessenger](#api-name-axeframemessenger) is used.\n\n### API Name: axe.reset\n\n#### Purpose\n\nReset the configuration to the default configuration.\n\n#### Description\n\nOverride any previous calls to `axe.configure` and restore the configuration to the default configuration. Note: this will NOT unregister any new rules or checks that were registered but will reset the configuration back to the default configuration for everything else.\n\n#### Synopsis\n\n```js\naxe.reset();\n```\n\n#### Parameters\n\nNone\n\n### API Name: axe.run\n\n#### Purpose\n\nAnalyze rendered content on the currently loaded page\n\n#### Description\n\nRuns a number of rules against the provided HTML page and returns the resulting issue list\n\n#### Synopsis\n\n```js\naxe.run(context, options, (err, results) => {\n  // ...\n});\n```\n\n#### Parameters axe.run\n\n- [`context`](#context-parameter): (optional) Defines the scope of the analysis - the part of the DOM that you would like to analyze. This will typically be the `document` or a specific selector such as class name, ID, selector, etc.\n- [`options`](#options-parameter): (optional) Set of options that change how `axe.run` works, including what rules will run. To pass options to specific checks, use `axe.configure`.\n- [`callback`](#callback-parameter): (optional) The callback function which receives either null or an [error result](#error-result) as the first parameter, and the [results object](#results-object) when analysis is completed successfully, or undefined if it did not.\n\n##### Context Parameter\n\nBy default, `axe.run` will test the entire document. The context object is an optional parameter that can be used to specify which element should and which should not be tested. It can be passed one of the following:\n\n1. An element reference that represents the portion of the document that must be analyzed\n   - Example: To limit analysis to the `<div id=\"content\">` element: `document.getElementById(\"content\")`\n1. A NodeList such as returned by `document.querySelectorAll`.\n1. A [CSS selector](./developer-guide.md#supported-css-selectors) that selects the portion(s) of the document that must be analyzed.\n1. An object with `exclude` and/or `include` properties\n1. An object with a `fromFrames` property\n1. An object with a `fromShadowDom` property\n\nRead [context.md](context.md) for details about the context object.\n\n###### Context Parameter Examples\n\n1. Test the `#navBar` and all other `nav` elements and its content.\n\n```js\naxe.run([`#navBar`, `nav`], (err, results) => {\n  // ...\n});\n```\n\n2. Test everything except `.ad-banner` elements.\n\n```js\naxe.run(\n  {\n    exclude: '.ad-banner'\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n3. Test the `form` element inside the `#payment` iframe.\n\n```js\naxe.run(\n  {\n    fromFrames: ['iframe#payment', 'form']\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n4. Exclude all `.commentBody` elements in each `.commentsShadowHost` shadow DOM tree.\n\n```js\naxe.run(\n  {\n    exclude: {\n      fromShadowDom: ['.commentsShadowHost', '.commentBody']\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nMore details on how to use the context object are described in [context.md](context.md).\n\n##### Options Parameter\n\nThe options parameter is flexible way to configure how `axe.run` operates. The different modes of operation are:\n\n- Run all rules corresponding to one of the accessibility standards\n- Run all rules defined in the system, except for the list of rules specified\n- Run a specific set of rules provided as a list of rule ids\n\nAdditionally, there are a number or properties that allow configuration of different options:\n\n| Property           | Default | Description                                                                                                                             |\n| ------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------- |\n| `runOnly`          | n/a     | Limit which rules are executed, based on names or tags                                                                                  |\n| `rules`            | n/a     | Enable or disable rules using the `enabled` property                                                                                    |\n| `reporter`         | `v1`    | Which reporter to use (see [Configuration](#api-name-axeconfigure))                                                                     |\n| `resultTypes`      | n/a     | Limit which result types are processed and aggregated                                                                                   |\n| `selectors`        | `true`  | Return CSS selector for elements, optimised for readability                                                                             |\n| `ancestry`         | `false` | Return CSS selector for elements, with all the element's ancestors                                                                      |\n| `xpath`            | `false` | Return xpath selectors for elements                                                                                                     |\n| `absolutePaths`    | `false` | Use absolute paths when creating element selectors                                                                                      |\n| `iframes`          | `true`  | Tell axe to run inside iframes                                                                                                          |\n| `elementRef`       | `false` | Return element references in addition to the target                                                                                     |\n| `frameWaitTime`    | `60000` | How long (in milliseconds) axe waits for a response from embedded frames before timing out                                              |\n| `preload`          | `true`  | Any additional assets (eg: cssom) to preload before running rules. [See here for configuration details](#preload-configuration-details) |\n| `performanceTimer` | `false` | Log rule performance metrics to the console                                                                                             |\n| `pingWaitTime`     | `500`   | Time before axe-core considers a frame unresponsive. [See frame messenger for details](frame-messenger.md)                              |\n\n###### Options Parameter Examples\n\n1. Run only Rules for an accessibility standard. See [axe-core tags](#axe-core-tags).\n\nTo run only WCAG 2.0 Level A rules, specify `options` as:\n\n```js\naxe.run(\n  {\n    runOnly: {\n      type: 'tag',\n      values: ['wcag2a']\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nTo run both WCAG 2.0 Level A and Level AA rules, you must specify both `wcag2a` and `wcag2aa`:\n\n```js\naxe.run(\n  {\n    runOnly: {\n      type: 'tag',\n      values: ['wcag2a', 'wcag2aa']\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nAlternatively, runOnly can be passed an array of tags:\n\n```js\naxe.run(\n  {\n    runOnly: ['wcag2a', 'wcag2aa']\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nIf you want to specify just one tag, you can pass in a string.\n\n```js\naxe.run(\n  {\n    runOnly: 'wcag2a'\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n2. Run only a specified list of Rules\n\nIf you only want to run certain rules, specify options as:\n\n```js\naxe.run(\n  {\n    runOnly: {\n      type: 'rule',\n      values: ['ruleId1', 'ruleId2', 'ruleId3']\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nThis example will only run the rules with the id of `ruleId1`, `ruleId2`, and `ruleId3`. No other rule will run.\n\nAlternatively, runOnly can be passed an array of rules:\n\n```js\naxe.run({\n  runOnly: ['ruleId1', 'ruleId2', 'ruleId3'];\n}, (err, results) => {\n  // ...\n})\n```\n\nIf you want to specify just one rule, you can pass in a string.\n\n```js\naxe.run(\n  {\n    runOnly: 'ruleId1'\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n3. Run all enabled Rules except for a list of rules\n\nThe default operation for axe.run is to run all rules except for rules with the \"experimental\" tag. If certain rules should be disabled from being run, specify `options` as:\n\n```js\naxe.run(\n  {\n    rules: {\n      'color-contrast': { enabled: false },\n      'valid-lang': { enabled: false }\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nThis example will disable the rules with the id of `color-contrast` and `valid-lang`. All other rules will run. The list of valid rule IDs is specified in the section below.\n\n4. Run a modified set or rules using tags and rule enable\n\nBy combining runOnly with type: tags and the rules option, a modified set can be defined. This lets you include rules with unspecified tags, and exclude rules that do have the specified tag(s).\n\n```js\naxe.run(\n  {\n    runOnly: {\n      type: 'tag',\n      values: ['wcag2a']\n    },\n    rules: {\n      'color-contrast': { enabled: true },\n      'valid-lang': { enabled: false }\n    }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nThis example includes all level A rules except for valid-lang, and in addition will include the level AA color-contrast rule.\n\n6. Only process certain types of results\n\nThe `resultTypes` option can be used to limit the number of nodes for a rule to a maximum of one. This can be useful for improving performance on very large or complicated pages when you are only interested in certain types of results.\n\nAfter axe has processed all rules normally, it generates a unique selector for all nodes in all rules. This process can be time consuming, especially for pages with lots of nodes. By limiting the nodes to a maximum of one for result types you are not interested in, you can greatly speed up the tail end performance of axe.\n\nTypes listed in this option will cause rules that fall under those types to show all nodes. Types _not_ listed will cause rules that fall under one of the missing types to show a maximum of one node. This allows you to still see those results and inform the user of them if appropriate.\n\n```js\naxe.run(\n  {\n    resultTypes: ['violations', 'incomplete', 'inapplicable']\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nThis example will return all the nodes for all rules that fall under the \"violations\", \"incomplete\", and \"inapplicable\" result types. Since the \"passes\" type was not specified, it will return at most one node for each rule that passes.\n\n###### <a id='preload-configuration-details'></a> Preload Configuration in Options Parameter\n\nThe `preload` attribute (defaults to `true`) in options parameter, accepts a `boolean` or an `object` where an array of assets can be specified.\n\n1. Specifying a `boolean`\n\n```js\naxe.run(\n  {\n    preload: true\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n2. Specifying an `object`\n\n```js\naxe.run(\n  {\n    preload: { assets: ['cssom'], timeout: 50000 }\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\nThe `assets` attribute expects an array of preload(able) constraints to be fetched. The current set of values supported for `assets` is listed in the following table:\n\n| Asset Type | Description                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `cssom`    | This asset type preloads all CSS Stylesheets rulesets specified in the page. The stylesheets can be an external cross-domain resource, a relative stylesheet or an inline style with in the head tag of the document. If the stylesheet is an external cross-domain a network request is made. An object representing the CSS Rules from each stylesheet is made available to the checks evaluate function as `preloadedAssets` at run-time |\n| `media`    | This asset type preloads metadata information of any HTMLMediaElement in the specified document                                                                                                                                                                                                                                                                                                                                             |\n\nThe `timeout` attribute in the object configuration is `optional` and has a fallback default value (10000ms). The `timeout` is essential for any network dependent assets that are preloaded, where-in if a given request takes longer than the specified/ default value, the operation is aborted.\n\nPreloading is not applicable to all rules. Even if the `preload` option is enabled, preloading steps may be skipped if no enabled rules require preloading.\n\n##### Callback Parameter\n\nThe callback parameter is a function that will be called when the asynchronous `axe.run` function completes. The callback function is passed two parameters. The first parameter will be an error thrown inside of axe if axe.run could not complete. If axe completed correctly the first parameter will be null, and the second parameter will be the results object.\n\n#### Return Promise\n\nIf the callback was not defined, axe will return a Promise instead. Axe does not polyfill a Promise library however. So on systems without support for Promises this feature is not available. If you are unsure if the systems you will need axe on has Promise support we suggest you use the callback provided by axe.run instead.\n\n#### Error Result\n\nThis will either be null or an object which is an instance of Error. If you are consistently receiving errors, please report this issue on the [Github issues list of Axe](https://github.com/dequelabs/axe-core/issues).\n\n#### Results Object\n\nThe callback function passed in as the third parameter of `axe.run` runs on the results object. This object has four components – a `passes` array, a `violations` array, an `incomplete` array and an `inapplicable` array.\n\nThe `passes` array keeps track of all the passed tests, along with detailed information on each one. This leads to more efficient testing, especially when used in conjunction with manual testing, as the user can easily find out what tests have already been passed.\n\nSimilarly, the `violations` array keeps track of all the failed tests, along with detailed information on each one.\n\nThe `incomplete` array (also referred to as the \"review items\") indicates which nodes could neither be determined to definitively pass or definitively fail. They are separated out in order that a user interface can display these to the user for manual review (hence the term \"review items\").\n\nThe `inapplicable` array lists all the rules for which no matching elements were found on the page.\n\n###### `url`\n\nThe URL of the page that was tested.\n\n###### `timestamp`\n\nThe date and time that analysis was completed.\n\n###### `testEngine`\n\nThe application and version that ran the audit.\n\n###### `testEnvironment`\n\nInformation about the current browser or node application that ran the audit.\n\n###### result arrays\n\nThe results of axe are grouped according to their outcome into the following arrays:\n\n- `passes`: These results indicate what elements passed the rules\n- `violations`: These results indicate what elements failed the rules\n- `inapplicable`: These results indicate which rules did not run because no matching content was found on the page. For example, with no video, those rules won't run.\n- `incomplete`: Also known as \"needs review,\" these results were aborted and require further testing. This can happen either because of technical restrictions to what the rule can test, or because a JavaScript error occurred.\n\nEach object returned in these arrays have the following properties:\n\n- `description` - Text string that describes what the rule does\n- `help` - Help text that describes the test that was performed\n- `helpUrl` - URL that provides more information about the specifics of the violation. Links to a page on the Deque University site.\n- `id` - Unique identifier for the rule; [see the list of rules](rule-descriptions.md)\n- `impact` - How serious the violation is. Can be one of \"minor\", \"moderate\", \"serious\", or \"critical\" if the Rule failed or `null` if the check passed\n- `tags` - Array of tags that this rule is assigned. These tags can be used in the option structure to select which rules are run ([see `axe.run` parameters for more information](#parameters-axerun)).\n- `nodes` - Array of all elements the Rule tested\n  - `html` - Snippet of HTML of the Element\n  - `impact` - How serious the violation is. Can be one of \"minor\", \"moderate\", \"serious\", or \"critical\" if the test failed or `null` if the check passed\n  - `target` - Array of either strings or Arrays of strings. If the item in the array is a string, then it is a CSS selector. If there are multiple items in the array each item corresponds to one level of iframe or frame. If there is one iframe or frame, there should be two entries in `target`. If there are three iframe levels, there should be four entries in `target`. If the item in the Array is an Array of strings, then it points to an element in a shadow DOM and each item (except the n-1th) in this array is a selector to a DOM element with a shadow DOM. The last element in the array points to the final shadow DOM node.\n  - `any` - Array of checks that were made where at least one must have passed. Each entry in the array contains:\n    - `id` - Unique identifier for this check. Check ids may be the same as Rule ids\n    - `impact` - How serious this particular check is. Can be one of \"minor\", \"moderate\", \"serious\", or \"critical\". Each check that is part of a rule can have different impacts. The highest impact of all the checks that fail is reported for the rule\n    - `message` - Description of why this check passed or failed\n    - `data` - Additional information that is specific to the type of Check which is optional. For example, a color contrast check would include the foreground color, background color, contrast ratio, etc.\n    - `relatedNodes` - Optional array of information about other nodes that are related to this check. For example, a duplicate id check violation would list the other selectors that had this same duplicate id. Each entry in the array contains the following information:\n      - `target` - Array of selectors for the related node\n      - `html` - HTML source of the related node\n  - `all` - Array of checks that were made where all must have passed. Each entry in the array contains the same information as the 'any' array\n  - `none` - Array of checks that were made where all must have not passed. Each entry in the array contains the same information as the 'any' array\n\n#### Example 2\n\nIn this example, we will pass the selector for the entire document, pass no options, which means all enabled rules will be run, and have a simple callback function that logs the entire results object to the console log:\n\n```js\naxe.run(document, function (err, results) {\n  if (err) throw err;\n  console.log(results);\n});\n```\n\n###### `passes`\n\n- `passes[0]`\n  ...\n  - `help` - `\"Elements must have sufficient color contrast\"`\n  - `helpUrl` - `\"https://dequeuniversity.com/courses/html-css/visual-layout/color-contrast\"`\n  - `id` - `\"color-contrast\"`\n    - `nodes`\n      - `target[0]` - `\"#js_off-canvas-wrap > .inner-wrap >.kinja-title.proxima.js_kinja-title-desktop\"`\n\n- `passes[1]`\n  ...\n\n###### `violations`\n\n- `violations[0]`\n  - `help` - `\"<button> elements must have alternate text\"`\n  - `helpUrl` - `\"https://dequeuniversity.com/courses/html-css/forms/form-labels#id84_example_button\"`\n  - `id` - `\"button-name\"`\n    - `nodes`\n      - `target[0]` - `\"post_5919997 > .row.content-wrapper > .column > span > iframe\"`\n      - `target[1]` - `\"#u_0_1 > .pluginConnectButton > .pluginButtonImage > button\"`\n\n- `violations[1]` ...\n\n##### `passes` Results Array\n\nIn the example above, the `passes` array contains two entries that correspond to the two rules tested. The first element in the array describes a color contrast check. It relays the information that a list of nodes was checked and subsequently passed. The `help`, `helpUrl`, and `id` fields are returned as expected for each of the entries in the `passes` array. The `target` array has one element in it with a value of\n\n`#js_off-canvas-wrap > .inner-wrap >.kinja-title.proxima.js_kinja-title-desktop`\n\nThis indicates that the element selected by the entry in `target[0]` was checked for the color contrast rule and that it passed the test.\n\nEach subsequent entry in the passes array has the same format, but will detail the different rules that were run as part of this call to `axe.run()`.\n\n##### `violations` Results Array\n\nThe array of `violations` contains one entry; this entry describes a test that check if buttons have valid alternate text (button-name). This first entry in the array has the `help`, `helpUrl` and `id` fields returned as expected.\n\nThe `target` array demonstrates how we specify the selectors when the node specified is inside of an `iframe` or `frame`. The first element in the `target` array - `target[0]` - specifies the selector to the `iframe` that contains the button. The second element in the `target` array - `target[1]` - specifies the selector to the actual button, but starting from inside the iframe selected in `target[0]`.\n\nEach subsequent entry in the violations array has the same format, but will detail the different rules that were run that generated accessibility violations as part of this call to `axe.run()`.\n\n#### Example 3\n\nIn this example, we pass the selector for the entire document, enable two additional experimental rules, and have a simple callback function that logs the entire results object to the console log:\n\n```js\naxe.run(\n  document,\n  {\n    rules: {\n      'link-in-text-block': { enabled: true },\n      'p-as-heading': { enabled: true }\n    }\n  },\n  function (err, results) {\n    if (err) throw err;\n    console.log(results);\n  }\n);\n```\n\n#### Example 4\n\nThis example shows a result object that points to an open shadow DOM element.\n\n##### `violations[0]`\n\n```json\n{\n  \"help\": \"Elements must have sufficient color contrast\",\n  \"helpUrl\": \"https://dequeuniversity.com/rules/axe/2.1/color-contrast?application=axeAPI\",\n  \"id\": \"color-contrast\",\n  \"nodes\": [\n    {\n      \"target\": [[\"header > aria-menu\", \"li.expanded\"]]\n    }\n  ]\n}\n```\n\nAs you can see the `target` array contains one item that is an array. This array contains two items, the first is a CSS selector string that finds the custom element `<aria-menu>` in the `<header>`. The second item in this array is the selector within that custom element's shadow DOM to find the `<li>` element with a class of `expanded`.\n\n### API Name: axe.registerPlugin\n\nRegister a plugin with the axe plugin system. See [implementing a plugin](plugins.md) for more information on the plugin system\n\n### API Name: axe.cleanup\n\nCall each plugin's cleanup function. See [implementing a plugin](plugins.md).\n\nThe signature is:\n\n```js\naxe.cleanup(resolve, reject);\n```\n\n`resolve` and `reject` are functions that will be invoked on success or failure respectively.\n\n`resolve` takes no arguments and `reject` takes a single argument that must be a string or have a toString() method in its prototype.\n\n### API Name: axe.setup\n\nSetup axe-cores internal `VirtualNode` tree and other required properties required to run functions in `axe.commons`.\n\nThe signature is:\n\n```js\naxe.setup(DomNode);\n```\n\n`DomNode` - is an optional DOM node to use as the root of the `VirtualNode` tree. Default is `document.documentElement`.\n\n### API Name: axe.teardown\n\nCleanup the `VirtualNode` tree and internal caches. `axe.run` will call this function at the end of the run so there's no need to call it yourself afterwards.\n\nThe signature is:\n\n```js\naxe.teardown();\n```\n\n### API Name: axe.frameMessenger\n\nSet up an alternative communication channel between parent and child frames. By default, axe-core uses `window.postMessage()`. See [frame-messenger.md](frame-messenger.md) for details.\n\n### API name: axe.runPartial / axe.finishRun\n\nRun axe without frame communication. This is the recommended way to run axe in browser drivers such as Selenium and Puppeteer. See [run-partial.md](run-partial.md) for details.\n\n### Virtual DOM Utilities\n\nNote: If you’re writing rules or checks, you’ll have both the `node` and `virtualNode` passed in.\nBut if you need to query the flattened tree, the documented function below should help. See the\n[developer guide](./developer-guide.md) for more information.\n\n#### API Name: axe.utils.querySelectorAll\n\n##### Description\n\nA querySelectorAll implementation that works on the virtual DOM and open Shadow DOM by manually walking the flattened tree instead of relying on DOM API methods which don’t step into Shadow DOM.\n\nNote: while there is no `axe.utils.querySelector` method, you can reproduce that behavior by accessing the first item returned in the array.\n\n##### Synopsis\n\n```js\naxe.utils.querySelectorAll(virtualNode, 'a[href]');\n```\n\n##### Parameters\n\n- `virtualNode` – object, the flattened DOM tree to query against. `axe._tree` is available for this purpose during an audit; see below.\n- `selector` – string, the [CSS selector](./developer-guide.md#supported-css-selectors) to use as a filter. For the most part, this should work seamlessly with `document.querySelectorAll`.\n\n##### Returns\n\nAn Array of filtered HTML nodes.\n\n#### API Name: axe.utils.getRule\n\n##### Description\n\nGet an axe-core `Rule` instance by ID.\n\n##### Synopsis\n\n```js\naxe.utils.getRule('color-contrast');\n```\n\n##### Parameters\n\n- `ruleId` - The ID of the rule.\n\n##### Returns\n\nAn axe-core `Rule` instance.\n\n### Common Functions\n\n#### axe.commons.dom.getComposedParent\n\nGet an element's parent in the flattened tree\n\n##### Synopsis\n\n```js\naxe.commons.dom.getComposedParent(node);\n```\n\n##### Parameters\n\n- `element` – HTMLElement. The element for which you want to find a parent\n\n##### Returns\n\nA DOMNode for the parent\n\n#### axe.commons.dom.getRootNode\n\nReturn the document or document fragment (open shadow DOM)\n\n##### Synopsis\n\n```js\naxe.commons.dom.getRootNode(node);\n```\n\n##### Parameters\n\n- `element` – HTMLElement. The element for which you want to find the root node\n\n##### Returns\n\nThe top-level document or shadow DOM document fragment\n\n## Section 3: Example Reference\n\nThis package contains examples for [jasmine](examples/jasmine), [mocha](examples/mocha), [qunit](examples/qunit), and [generating HTML from the violations array](examples/html-handlebars.md). Each of these examples is in the [doc/examples](examples) folder. In each folder, there is a README.md file which contains specific information about each example.\n\nSee [axe-webdriverjs](https://github.com/dequelabs/axe-webdriverjs#axe-webdriverjs) for selenium webdriver JavaScript examples.\n\n## Section 4: Performance\n\nAxe-core performs very well in general and if you are analyzing average complexity pages with the default settings, you should not need to worry about performance at all. There are some scenarios that can cause performance issues. This is the list of known issues and what you can do to mitigate and/or avoid them.\n\n### Very large pages\n\nCertain rules (like the color-contrast rule) look at almost every element on a page and some of these rules also perform somewhat expensive operations on these elements including looking up the hierarchy, looking at overlapping elements, calculating the computed styles etc. It also calculates a unique selector for each element in the results and also de-duplicates elements so that you do not get duplicate items in your results.\n\nIf your page is very large (in terms of the number of Elements on the page) i.e. >50K elements on the page, then you will see analysis times that run over 10s on a relatively decent CPU.\n\n#### Use resultTypes\n\nAn approach you can take to reducing the time is use the `resultTypes` option. By calling `axe.run` with the following options, axe-core will only return the full details of the `violations` array and will only return one instance of each of the `inapplicable`, `incomplete` and `pass` arrays for each rule that has at least one of those entries. This will reduce the amount of computation that axe-core does for the unique selectors.\n\n```js\naxe.run(\n  {\n    resultTypes: ['violations']\n  },\n  (err, results) => {\n    // ...\n  }\n);\n```\n\n### Other strategies\n\n#### Targeted color-contrast analysis\n\nIf you are analyzing multiple pages on a single Web site or application, chances are these pages all contain the same styles. It is therefore not adding any additional information to your analysis to analyze every page for color-contrast. Choose a small number of pages that represent the totality of you styles and analyze these with color-contrast and analyze all others without it.\n"
  },
  {
    "path": "doc/accessibility-supported.md",
    "content": "# How we make decisions on rules\n\nIn order to adhere to the manifesto and at the same time be useful to developers and content creators, we evaluate all rules according to strict normative WCAG 2 interpretation. We also pay very strong attention to the normative portion of WCAG 2 known as [accessibility supported](https://www.w3.org/TR/WCAG20/#accessibility-supporteddef).\n\n## Accessibility supported\n\nAccessibility supported means that in order for a technique to pass, it must work in some predefined set of browsers and assistive technologies. For axe-core this means that for a screen reader, browser, or environment to be added to the list of supported combinations, the following two criteria must be met:\n\n1. Be used by more than 1% of users (currently extrapolated from the [WebAim Screen Reader User Survey](https://webaim.org/projects/screenreadersurvey10/#browsercombos))\n1. Introduce new coverage for a screen reader, browser, or environment not currently accessibility supported\n\nWe currently test the following AT combinations for support\n\n1. VoiceOver and Safari on OS X\n1. VoiceOver and Safari on iOS\n1. JAWS and IE11 on Windows (DEPRECATED)\n1. JAWS and Chrome on Windows\n1. NVDA and Firefox on Windows\n1. Talkback and Chrome on Android\n1. Dragon and Firefox on Windows\n\n## Impact on ARIA\n\nFor some technologies, like ARIA, we have a dilemma in that the spec is approved before supported and we have to weigh a balance between something that conforms to the spec, but does not yet work. When we do this, we favor the accessibility supported principle over the spec but we temper this with the impact of using an unsupported feature. Here is how we do this:\n\n1. If the feature's use is supported by all platforms: allow, else\n1. If the feature's use does not have a negative impact on accessibility: allow, else\n1. If we can detect a fallback: allow, else\n1. disallow the feature's use until it is supported\n\nIn addition, we disallow invalid attributes starting with `aria-` and invalid attribute values as these are highly likely to have a negative impact on accessibility because of the scenarios under which these are likely to occur (this is so we can act as a useful linting tool for developers).\n\n## Best practices\n\nWe recognize that there are best practices that significantly improve the usability of an application, even though they are not strictly required in order to conform with WCAG 2. We develop the best practice rules to help content developers to identify these and adhere to them.\n\nWe recognize that this topic is somewhat controversial and the rules we have represent Deque's opinion on what constitutes a best practice.\n"
  },
  {
    "path": "doc/act-rules-format.md",
    "content": "# W3C Standardized Rules\n\nDeque Systems is one of the leading organizations in the development of standardized accessibility conformance testing rules. The [axe-core rules proposal format](./rule-proposal.md) is an adaptation of the [Accessibility Conformance Testing Rules Format](https://www.w3.org/TR/act-rules-format/).\n\nThere are two ways a rule written in the axe-core rule format can be transformed into the ACT Rules format:\n\n## Method 1: Create a single rule\n\nThis method is useful for rules with a small number of checks.\n\n1. Add the test input type to it: `rendered page`\n2. Add an `assumptions` section, including possible assumptions to it\n3. Add an `outcomes` section, describing the different possible outcomes of the rule\n4. Add a `Validation Tests` section that links to the integration tests\n5. Update the check to return pass/fail/cantTell instead of true/false/undefined\n6. Add control flow to the checks:\n\n- `any` checks should only return `fail` in the last step. All steps leading up to it either return `pass` or say `continue to the next step`.\n- `all` and `none` checks should only return `pass` in the last step. All steps leading up to it either return `fail` or say `continue to the next step`.\n\n7. Rename `checks` to `steps` and add a `step X` (where X is the step number) to the heading with the check name.\n8. Replace the `tags` section with an `Accessibility Requirements`. The requirements can be determined based on the `wcag###` tags.\n\n## Method 2: Create a rule group\n\nThis method is useful for larger rules with `any` checks. This effectively turns every check into its own rule, and turns the rule into a rule group.\n\n1. Copy each check into a new document\n2. Add a `steps` heading\n3. Add the test input type to it: `rendered page`\n4. Add an `assumptions` section, add possible assumptions to it\n5. Add an `outcomes` section, describing the different possible outcomes of the rule\n6. Copy the `selector` section from the original rule into the new rule documents\n7. Update the check to return pass/fail/cantTell instead of true/false/undefined\n8. Add a `Validation Tests` section, that links to only those integration tests relevant for this check (now a new rule).\n9. Indicate that the new rule is part of a group, using the original axe-core rule ID as the group name.\n10. Replace the `tags` section with an `Accessibility Requirements`. The requirements can be determined based on the `wcag###` tags.\n"
  },
  {
    "path": "doc/aria-supported.md",
    "content": "# ARIA Roles and Attributes unsupported in axe-core.\n\nIt can be difficult to know which features of web technologies are accessible across different platforms, and with different screen readers and other assistive technologies. Axe-core does some of this work for you, by raising issues when accessibility features are used that are known to cause problems.\n\nThis page contains a list of ARIA 1.1 features that axe-core raises as unsupported. For more information, read [We’ve got your back with “Accessibility Supported” in axe](https://www.deque.com/blog/weve-got-your-back-with-accessibility-supported-in-axe/).\n\nFor a detailed description about how accessibility support is decided, see [How we make decisions on rules](accessibility-supported.md).\n\n## Attributes\n\n| aria-attribute | axe-core support |\n| -------------- | ---------------- |\n"
  },
  {
    "path": "doc/backwards-compatibility-doc.md",
    "content": "# Backwards Compatibility in axe-core\n\n## What is part of the public axe-core API?\n\nThe axe-core API includes:\n\n- Any APIs documented here: [https://github.com/dequelabs/axe-core/blob/develop/doc/API.md](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md)\n- The selectors generated and included in the node information in the results arrays for any given HTML page\n- The JSON structures passed into and out of any API functions\n- Any functions in the `axe.utils` and `axe.commons` collections that are used by one of our standard rules (this document refers to these as the “Public Utils”). This includes use in any of the “matches”, “eval” and “after” functions.\n- The implicit function signature of the matches, eval and after functions (the names of the parameters that are passed to the functions and the values returned by them)\n\n## What is not included in the public axe-core API?\n\nAny other function or attribute on the axe object is not considered part of the public API and is therefore not covered by the guarantee in this document.\n\n## What do we guarantee for the public axe-core API?\n\nWe guarantee that the API signatures and the return values of functions will not break in patch or minor release versions except that we may add items to JSON structures in minor releases or override parameters. We will not remove items from JSON structures.\n\nIn a minor release, we may change the implementation of Public Utils to fix bugs or improve performance. This means that a call to a Public Util may return a different value across patch versions.\n\nWe will not add or remove rules in a patch release. We will not add support for new technologies in a patch release. We will endeavor to return the exact same results across patch releases with the exception of changes that are due to bug fixes. This means that the likelihood of a patch release finding issues on a page that was clean in a previous release is very close to zero but not zero.\n\nIn a minor release, we may add support for new technologies in the Public Utils or in existing rules and we may add or disable rules. We may also change an experimental rule to become a standard rule (essentially equivalent to adding rule). This means that pages that did not return violations in a particular minor release may return violations in a subsequent release. Rule tags, including the \"wcag\\*\" tags, and whether or not something is reported as best-practice can be changed in minor releases.\n\nIf the HTML page is unchanged, calls to the analysis function(s) when compared across minor or patch releases will return the same exact selector for the nodes in any of the result arrays. If the HTML page has changed, it is possible for the selector to be different but it is not guaranteed that the selector will be different.\n\nAPIs may be deprecated in a major or minor release. APIs that have been deprecated for 6 months or more will be removed in the next major release.\n\nA major or a minor release may introduce new Public Utils.\n\nMajor releases may remove rules.\n\n### Table: Summary of What Deque Guarantees with Public axe-core API\n\n|                                               | Major                                                   | Minor                 | Patch                      |\n| :-------------------------------------------- | :------------------------------------------------------ | :-------------------- | :------------------------- |\n| **New Technologies**                          |                                                         |                       |                            |\n| Support for new technologies\\*                | May add support                                         | May add support       | Will not add support       |\n| **APIS**                                      |                                                         |                       |                            |\n| API signatures and return values of functions | May break                                               | Will not break        | Will not break             |\n| APIs deprecated                               | May be deprecated                                       | May be deprecated     | Will not be deprecated     |\n| APIs removed                                  | May be removed (will remove previously deprecated APIs) | Will not be removed   | Will not be removed        |\n| **Public Utils**                              |                                                         |                       |                            |\n| Implementation of Public Utils                | May change                                              | May change            | Will not change            |\n| New Public Utils                              | May add                                                 | May add               | Will not add               |\n| **Rules**                                     |                                                         |                       |                            |\n| Add rules                                     | May add                                                 | May add               | Will not add               |\n| Disable or remove rules                       | May remove (will remove previously deprecated rules)    | May disable or remove | Will not disable or remove |\n| Rule tags                                     | May add or remove                                       | May add or remove     | Will not change            |\n| Deprecate rules                               | May deprecate                                           | May deprecate         | Will not deprecate         |\n\n\\*_New OSes, Browsers, ATs, new standards (e.g. introduction of ARIA), new versions of standards (e.g. WCAG 2.1)_\n\n## Implications\n\n### Breaking Builds\n\nPatch release upgrades can be applied in CI environments with a high degree of certainty that breakages will not be due to changes in the release. The chance is, however, not zero.\n\n### Custom Rules\n\nA custom rule configuration (with or without custom rules) is guaranteed to run on any newer version that shares the same major version number as the version for which it was created. A custom rule configuration (with or without custom rules) is not guaranteed to work with an older version of axe-core than the version for which it was created.\n\nYou can write custom rules that utilize the Public Utils and the parameters that are passed to a check function, secure in the knowledge that the API will not change unless a major version is released.\n\nHowever, because we can introduce new rules, it is possible that a custom rule configuration will return additional results when run against a new minor release and may return different results when run against a newer patch release.\n\nA custom rule configuration may return different results (more or fewer) when run on a newer patch release due to bug fixes.\n\nA major release may completely break a custom rule or even all custom rules because of a change to any of the APIs.\n\n### Table: Implication of axe-core Updates on Custom Rule Function\n\n| Axe-core Release Semantic Version              | Example | Simple Custom Rules    | Complex Custom Rules   |\n| :--------------------------------------------- | :------ | :--------------------- | :--------------------- |\n| Prior release (different major release number) | 1.3.4   | Not guaranteed to work | Not guaranteed to work |\n| Release used to create custom rules            | 2.0.0   | Guaranteed to work     | Guaranteed to work     |\n| Next release (minor release)                   | 2.1.0   | Guaranteed to work\\*   | Guaranteed to work\\*   |\n| Next +1 release (patch release)                | 2.1.1   | Guaranteed to work\\*   | Guaranteed to work\\*   |\n| Next +2 release (major release)                | 3.0.0   | Not guaranteed to work | Not guaranteed to work |\n\n\\*_Minor and patch releases will not break custom rules, but testing results may vary from those tests performed with previous minor and patch releases of axe-core. There may be inconsistencies between what Attest reports and what Comply reports._\n\nPlease note that with even small changes in versions for simple custom rules, there may be inconsistencies between what Attest reports and what Comply reports. For best results, in both cases, versions should match exactly.\n\n### Tracking Issues Over Time\n\nMany systems attempt to identify unique issues using a combination of the page and state, the rule-id and the selector to track an issue across time and across calls to the analysis functions. This will work reliably across patch releases. This could break if a rule is removed or split up across minor releases. In a major release, this could break for the additional reason of a possible change in the selector generation (this did happen in the changes between 2.6.x and 3.x).\n\n### Comply\n\nComply has the ability to add labels and comments to issues and to mark issues as ignored. Comply will lose this information when it is not able to track issues across time. This means that historical issue information may be lost when upgrading to a newer major version of axe-core.\n"
  },
  {
    "path": "doc/check-message-template.md",
    "content": "# Check Message Template\n\nAxe-core uses a custom template to handle dynamic check messages (messages that use the `data` property to output values or to determine which message to display). The structure for the messages is as follows:\n\n## Simple Message\n\nA simple message is just a string that doesn't use the `data` property. Most checks use this format.\n\n```json\n{\n  \"messages\": {\n    \"pass\": \"Simple message for a passing check\"\n  }\n}\n```\n\n## Message with Data\n\nA message can also use the `data` property to output information from the check. If `data` is a String, Boolean, or Number, you can use the syntax `${data}` to have the message output the value of the `data` property.\n\n```js\n// check.js\nthis.data(10);\n\n// check.json\n{\n    \"messages\": {\n        \"pass\": \"Passed with a value of ${data}\"\n        // => \"Passed with a value of 10\"\n    }\n}\n```\n\nIf `data` is an object, you can access properties of the object using the syntax `${data.propName}`.\n\n```js\n// check.js\nthis.data({\n  contrast: '3:1',\n  fontSize: '12px'\n});\n\n// check.json\n{\n    \"messages\": {\n        \"fail\": \"Color-contrast failed with a contrast of ${data.contrast} and font size of ${data.fontSize}\"\n        // => \"Color-contrast failed with a contrast of 3:1 and font size of 12px\"\n    }\n}\n```\n\n## Singular and Plural Messages\n\nIf the message needs to know how many items are in the `data` property to determine the type of language to use (singular or plural), you can structure the message to use `singular` and `plural` properties. Use the syntax `${data.values}` to have the message output a comma-separated list of the items (`data.values` is provided by the template code for you).\n\n```js\n// check.js\nthis.data(['item1', 'item2']);\n\n// check.json\n{\n    \"messages\": {\n        \"fail\": {\n            \"singular\": \"Attribute ${data.values} is not allowed\",\n            \"plural\": \"Attributes: ${data.values} are not allowed\"\n        }\n        // => Attributes: item1, item2 are not allowed\n    }\n}\n```\n\n## Message Determined by Data\n\nLastly, a message can use the `data` property to determine which message to display. Structure the message to use properties whose keys are the possible values of `data.messageKey`. You should also provide a `default` message that will be displayed if `messageKey` is not set.\n\n```js\n// check.js\nthis.data({\n    messageKey: 'imgNode'\n});\n\n// check.json\n{\n    \"messages\": {\n        \"incomplete\": {\n            \"default\": \"Color-contrast could not be determined\"\n            \"bgImage\": \"Element's background color could not be determined due to a background image\",\n            \"imgNode\": \"Element's background color could not be determined because element contains an image node\"\n        }\n        // => Element's background color could not be determined because element contains an image node\n    }\n}\n```\n\nThe messages can still use the syntax `${data.propName}` to access other properties on the `data` property.\n\n## Migrating From doT.js Template in Translations\n\nAxe-core formerly used doT.js for it's template library. To migrate from doT.js in a translation file, do the following:\n\n- If the message used `{{=it.data}}` or `{{=it.data.propName}}`, change the message to use the syntax `${data}` or `${data.propName}`.\n\n```diff\n{\n    \"messages\": {\n-       \"incomplete\": \"Check that the <label> does not need to be part of the ARIA {{=it.data}} field's name\"\n+       \"incomplete\": \"Check that the <label> does not need to be part of the ARIA ${data} field's name\"\n    }\n}\n```\n\n- If the message used `{{=it.data && it.data.length` to determine using singular or plural language, change the message structure of the message to instead use the `singular` and `plural` properties. Replace `{{=it.data.join(', ')}}` with `${data.values}`.\n\n```diff\n{\n    \"messages\": {\n-       \"fail\": \"Attribute{{=it.data && it.data.length > 1 ? 's' : ''}} {{=it.data.join(', ')}} {{=it.data && it.data.length > 1 ? 'are' : ' is'}} not allowed\n+       \"fail\": {\n+           \"singular\": \"Attribute ${data.values} is not allowed\",\n+           \"plural\": \"Attributes: ${data.values} are not allowed\"\n+       }\n    }\n}\n```\n"
  },
  {
    "path": "doc/check-options.md",
    "content": "# Check Options\n\n## Table of Contents\n\n- [How Checks Work](#how-checks-work)\n- [Options](#options)\n  - [Global Options](#global-options)\n  - [aria-allowed-role](#aria-allowed-role)\n  - [aria-prohibited-attr](#aria-prohibited-attr)\n  - [aria-required-children](#aria-required-children)\n  - [aria-required-parent](#aria-required-parent)\n  - [aria-roledescription](#aria-roledescription)\n  - [autocomplete-valid](#autocomplete-valid)\n  - [color-contrast](#color-contrast)\n  - [page-has-heading-one](#page-has-heading-one)\n  - [page-has-main](#page-has-main)\n  - [page-no-duplicate-banner](#page-no-duplicate-banner)\n  - [page-no-duplicate-contentinfo](#page-no-duplicate-contentinfo)\n  - [page-no-duplicate-main](#page-no-duplicate-main)\n  - [duplicate-img-label](#duplicate-img-label)\n  - [label-content-name-mismatch](#label-content-name-mismatch)\n  - [has-lang](#has-lang)\n  - [valid-lang](#valid-lang)\n  - [frame-tested](#frame-tested)\n  - [no-autoplay-audio](#no-autoplay-audio)\n  - [css-orientation-lock](#css-orientation-lock)\n  - [meta-viewport-large](#meta-viewport-large)\n  - [meta-viewport](#meta-viewport)\n  - [meta-refresh](#meta-refresh)\n  - [header-present](#header-present)\n  - [landmark](#landmark)\n  - [p-as-heading](#p-as-heading)\n  - [avoid-inline-spacing](#avoid-inline-spacing)\n  - [scope-value](#scope-value)\n  - [target-offset](#target-offset)\n  - [target-size](#target-size)\n  - [region](#region)\n  - [inline-style-property](#inline-style-property)\n  - [invalid-children](#invalid-children)\n  - [link-in-text-block](#link-in-text-block)\n\n## How Checks Work\n\n[Rules in axe-core](../lib/rules) are made up of one or more individual checks that dictate how the rule works. Each check is typically designed to look for a specific requirement and report back its findings to the rule.\n\nFor example, the rule [image-alt](../lib/rules/image-alt.json) uses the checks `has-alt`, `aria-label`, `aria-labelledby`, and `non-empty-title` to determine if the image has an accessible name from an `alt`, `aria-label`, `aria-labelledby`, or `title` attribute (respectively).\n\nMany checks allow you to change how they work through `options` properties. These options can be found in the [checks metadata file](../lib/checks).\n\nFor example, the check [has-lang](../lib/checks/language/has-lang.json) takes an `attributes` option which dictates which attributes to check for a lang value.\n\nTo customize a check's options, you can use [`axe.configure`](./API.md#api-name-axeconfigure) to configure the check and modify the options as desired.\n\n```js\n// configure has-lang check to look at the `hreflang` attribute as well\naxe.configure({\n  checks: [\n    {\n      id: 'has-lang',\n      options: {\n        attributes: ['lang', 'xml:lang', 'hreflang']\n      }\n    }\n  ]\n});\n```\n\n## Options\n\n### Global Options\n\nAll checks allow these global options:\n\n| Option         | Default | Description                                                     |\n| -------------- | :------ | :-------------------------------------------------------------- |\n| `reviewOnFail` | `false` | Have the check return as \"Needs Review\" rather than a violation |\n\n### aria-allowed-role\n\n| Option          | Default | Description                                                       |\n| --------------- | :------ | :---------------------------------------------------------------- |\n| `allowImplicit` | `true`  | Allow the explicit role to match the implicit role of the element |\n| `ignoredTags`   | `[]`    | Do not check for allowed roles in the provided HTML elements list |\n\n### aria-prohibited-attr\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>elementsAllowedAriaLabel</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  \"audio\",\n  \"applet\",\n  \"canvas\",\n  \"dl\",\n  \"embed\",\n  \"iframe\",\n  \"input\",\n  \"label\",\n  \"meter\",\n  \"object\",\n  \"svg\",\n  \"video\"\n]</code></pre>\n        </td>\n      <td align=\"left\">List of element names that without a role, are allowed an `aria-label` and `aria-labelledby` attribute</td>\n    </tr>\n  </tbody>\n</table>\n\n### aria-required-children\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>reviewEmpty</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  'doc-bibliography',\n  'doc-endnotes',\n  'grid',\n  'list',\n  'listbox',\n  'table',\n  'tablist',\n  'tree',\n  'treegrid',\n  'rowgroup'\n]</code></pre>\n        </td>\n      <td align=\"left\">List of ARIA roles that should be flagged as \"Needs Review\" rather than a violation if the element has no owned children</td>\n    </tr>\n  </tbody>\n</table>\n\n### aria-required-parent\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>ownGroupRoles</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>['listitem', 'treeitem']</code></pre>\n        </td>\n      <td align=\"left\">List of ARIA roles that when used in a group can have a grand parent with the same role. E.g. <code>list > listitem > group > listitem</code>.</td>\n    </tr>\n  </tbody>\n</table>\n\n### aria-roledescription\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>supportedRoles</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  'button',\n  'img',\n  'checkbox',\n  'radio',\n  'combobox',\n  'menuitemcheckbox',\n  'menuitemradio'\n]</code></pre>\n        </td>\n      <td align=\"left\">List of ARIA roles that support the <code>aria-roledescription</code> attribute</td>\n    </tr>\n  </tbody>\n</table>\n\n### aria-allowed-attr\n\nPreviously supported properties `validTreeRowAttrs` is no longer available. `invalidTableRowAttrs` from [aria-conditional-attr](#aria-conditional-attr) instead.\n\n### aria-conditional-attr\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>invalidTableRowAttrs</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  'aria-posinset',\n  'aria-setsize',\n  'aria-expanded',\n  'aria-level',\n]</code></pre>\n        </td>\n      <td align=\"left\">List of ARIA attributes that are not allowed on <code>role=row</code> when a descendant of a table or a grid</td>\n    </tr>\n  </tbody>\n</table>\n\n### autocomplete-valid\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>stateTerms</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  'none',\n  'false',\n  'true',\n  'disabled',\n  'enabled',\n  'undefined',\n  'null',\n]</code></pre>\n        </td>\n      <td align=\"left\">List of allowed autocomplete state terms other than \"on\" and \"off.\"</td>\n    </tr>\n    <tr>\n      <td>\n        <code>ignoredValues</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  'text',\n  'pronouns',\n  'gender',\n  'message',\n  'content'\n]</code></pre>\n        </td>\n      <td align=\"left\">List of autocomplete values that are technically invalid but will be ignored as they may not necessarily cause accessibility problems</td>\n    </tr>\n  </tbody>\n</table>\n\n### color-contrast\n\n| Option                                                      | Default | Description                                                                                                                                                                                  |\n| ----------------------------------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `ignoreUnicode`                                             | `true`  | Do not check the color contrast of Unicode characters                                                                                                                                        |\n| `ignoreLength`                                              | `false` | Do not check the color contrast of short text content                                                                                                                                        |\n| `ignorePseudo`                                              | `false` | Do not mark pseudo elements as Needs Review                                                                                                                                                  |\n| `boldValue`                                                 | `700`   | The minimum CSS `font-weight` value that designates bold text                                                                                                                                |\n| `boldTextPt`                                                | `14`    | The minimum CSS `font-size` pt value that designates bold text as being large                                                                                                                |\n| `largeTextPt`                                               | `18`    | The minimum CSS `font-size` pt value that designates text as being large                                                                                                                     |\n| `shadowOutlineEmMax`                                        | `0.1`   | The maximum `blur-radius` value (in ems) of the CSS `text-shadow` property. `blur-radius` values greater than this value will be treated as a background color rather than an outline color. |\n| `textStrokeEmMin`                                           | `0.03`  | The minimum EM width of `-webkit-text-stroke` before axe uses the text stroke color over the actual text color.                                                                              |\n| `pseudoSizeThreshold`                                       | `0.25`  | Minimum area of the pseudo element, relative to the text element, below which it will be ignored for colot contrast.                                                                         |\n| `contrastRatio`                                             | N/A     | Contrast ratio options                                                                                                                                                                       |\n| &nbsp;&nbsp;`contrastRatio.normal`                          | N/A     | Contrast ratio requirements for normal text (non-bold text or text smaller than `largeTextPt`)                                                                                               |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.normal.expected`     | `4.5`   | The expected contrast ratio for normal text                                                                                                                                                  |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.normal.minThreshold` | N/A     | The minimum contrast ratio the check will apply to. Contrast ratios less than this value will be ignored                                                                                     |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.normal.maxThreshold` | N/A     | The maximum contrast ratio the check will apply to. Contrast ratios greater than this value will be ignored                                                                                  |\n| &nbsp;&nbsp;`contrastRatio.large`                           | N/A     | Contrast ratio requirements for large text (bold text or text larger than `largeTextPt`)                                                                                                     |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.large.expected`      | `4.5`   | The expected contrast contrast ratio for large text                                                                                                                                          |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.large.minThreshold`  | N/A     | The minimum contrast ratio the check will apply to. Contrast ratios less than this value will be ignored                                                                                     |\n| &nbsp;&nbsp;&nbsp;&nbsp;`contrastRatio.large.maxThreshold`  | N/A     | The maximum contrast ratio the check will apply to. Contrast ratios greater than this value will be ignored                                                                                  |\n\n### page-has-heading-one\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>selector</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=css><code>h1:not([role]):not([aria-level]), \nh1:not([role])[aria-level=1], \nh2:not([role])[aria-level=1], \nh3:not([role])[aria-level=1], \nh4:not([role])[aria-level=1], \nh5:not([role])[aria-level=1], \nh6:not([role])[aria-level=1], \n[role=heading][aria-level=1]</code></pre>\n        </td>\n      <td align=\"left\">Selector used to determine if a page has a level one heading</td>\n    </tr>\n  </tbody>\n</table>\n\n### page-has-main\n\n| Option     | Default                                             | Description                                                |\n| ---------- | :-------------------------------------------------- | :--------------------------------------------------------- |\n| `selector` | <pre lang=css>main:not([role]), [role='main']</pre> | Selector used to determine if a page has a `main` landmark |\n\n### page-no-duplicate-banner\n\n| Option              | Default                                                | Description                                                                             |\n| ------------------- | :----------------------------------------------------- | :-------------------------------------------------------------------------------------- |\n| `selector`          | <pre lang=css>header:not([role]), [role=banner]</pre>  | Selector used to determine if a page has a `banner` landmark                            |\n| `nativeScopeFilter` | <pre lang=css>article, aside, main, nav, section</pre> | Selector used to ignore `banner` landmarks that have a parent that matches the selector |\n\n### page-no-duplicate-contentinfo\n\n| Option              | Default                                                    | Description                                                                                           |\n| ------------------- | :--------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- |\n| `selector`          | <pre lang=css>footer:not([role]), [role=contentinfo]</pre> | Selector used to determine if a page has a `contentinfo` landmark                                     |\n| `nativeScopeFilter` | <pre lang=css>article, aside, main, nav, section</pre>     | Option values used to ignore `contentinfo` landmarks that have a selector matching the parent element |\n\n### page-no-duplicate-main\n\n| Option     | Default                                             | Description                                                |\n| ---------- | :-------------------------------------------------- | :--------------------------------------------------------- |\n| `selector` | <pre lang=css>main:not([role]), [role='main']</pre> | Selector used to determine if a page has a `main` landmark |\n\n### duplicate-img-label\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>parentSelector</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=css><code>button,\n[role=button],\na[href],\np,\nli,\ntd,\nth</code></pre>\n        </td>\n      <td align=\"left\">Selector used to look at an image parent that may duplicate the image label</td>\n    </tr>\n  </tbody>\n</table>\n\n### label-content-name-mismatch\n\n| Option                | Default | Description                                                                                                                                                               |\n| --------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `pixelThreshold`      | `0.1`   | Percent of difference in pixel data or pixel width required to determine if a font is a ligature font. Ligature fonts are ignored when comparing the label to the content |\n| `occurrenceThreshold` | `3`     | Number of times the font is encountered before auto-assigning the font as a ligature or not                                                                               |\n\n### has-lang\n\n| Option       | Default                                 | Description                         |\n| ------------ | :-------------------------------------- | :---------------------------------- |\n| `attributes` | <pre lang=js>['lang', 'xml:lang']</pre> | Attributes to check for lang values |\n\n### valid-lang\n\n| Option       | Default                                                      | Description                               |\n| ------------ | :----------------------------------------------------------- | :---------------------------------------- |\n| `attributes` | <pre lang=js>['lang', 'xml:lang']</pre>                      | Attributes to check for valid lang values |\n| `value`      | [Array of all valid langs](../lib/core/utils/valid-langs.js) | List of valid lang values                 |\n\n### frame-tested\n\n| Option        | Default | Description                                                                               |\n| ------------- | :------ | :---------------------------------------------------------------------------------------- |\n| `isViolation` | `false` | If an `iframe` that has not been injected with axe-core should be reported as a violation |\n\n### no-autoplay-audio\n\n| Option            | Default | Description                                                                         |\n| ----------------- | :------ | :---------------------------------------------------------------------------------- |\n| `allowedDuration` | `3`     | Maximum time in seconds an audio clip may autoplay before being marked as violation |\n\n### css-orientation-lock\n\n| Option            | Default | Description                                                                                                                                                             |\n| ----------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `degreeThreshold` | `3`     | The difference of degrees from 180 and 90 that are considered locked to a specific display orientation (for example, 93° rotated is not considered locked while 94° is) |\n\n### meta-viewport-large\n\n| Option         | Default | Description                                                                                     |\n| -------------- | :------ | :---------------------------------------------------------------------------------------------- |\n| `scaleMinimum` | `5`     | The `scale-maximum` CSS value of the check applies to. Values above this number will be ignored |\n| `lowerBound`   | `2`     | The `scale-minimum` CSS value the check applies to. Values below this number will be ignored    |\n\n### meta-viewport\n\n| Option         | Default | Description                                                                                  |\n| -------------- | :------ | :------------------------------------------------------------------------------------------- |\n| `scaleMinimum` | `2`     | The `scale-maximum` CSS value the check applies to. Values above this number will be ignored |\n\n### meta-refresh\n\n| Option     | Default | Description                                                                         |\n| ---------- | :------ | :---------------------------------------------------------------------------------- |\n| `minDelay` | `0`     | Passes if the redirect is equal or less than this. Can be set to `false` to disable |\n| `maxDelay` | `7200`  | Passes if the redirect is greater than this. Can be set to `false` to disable       |\n\n### header-present\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>selector</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=css><code>h1:not([role]), \nh2:not([role]), \nh3:not([role]), \nh4:not([role]), \nh5:not([role]), \nh6:not([role]), \n[role=heading]</code></pre>\n        </td>\n      <td align=\"left\">Selector used to determine if a page has a heading</td>\n    </tr>\n  </tbody>\n</table>\n\n### landmark\n\n| Option     | Default                               | Description                                                |\n| ---------- | :------------------------------------ | :--------------------------------------------------------- |\n| `selector` | <pre lang=css>main, [role=main]</pre> | Selector used to determine if a page has a landmark region |\n\n### p-as-heading\n\n<table>\n  <thead>\n    <tr>\n      <th>Option</th>\n      <th align=\"left\">Default</th>\n      <th align=\"left\">Description</th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>\n        <code>margins</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>[\n  { \"weight\": 150, \"italic\": true }, \n  { \"weight\": 150, \"size\": 1.15 }, \n  { \"italic\": true, \"size\": 1.15 }, \n  { \"size\": 1.4 }\n]</code></pre>\n        </td>\n      <td align=\"left\">Common CSS values used to display `p` elements as `h1-h6` elements determining if a `p` element is being improperly repurposed</td>\n    </tr>\n    <tr>\n     <td>\n        <code>passLength</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>\"passLength\": 1</code></pre>\n        </td>\n      <td align=\"left\">Relative length, if the the candidate heading is X times or greater the length of the candidate paragraph, it will pass.</td>\n      </tr>\n       <tr>\n     <td>\n        <code>faiLength</code>\n      </td>\n      <td align=\"left\">\n        <pre lang=js><code>\"failLength\": 0.5</code></pre>\n        </td>\n      <td align=\"left\">Relative length, if the the candidate heading is X times or less the length of the candidate paragraph, it can fail.</td>\n      </tr>\n  </tbody>\n</table>\n\n### avoid-inline-spacing\n\n| Option          | Default                                                              | Description                                   |\n| --------------- | :------------------------------------------------------------------- | :-------------------------------------------- |\n| `cssProperties` | <pre lang=js>['line-height', 'letter-spacing', 'word-spacing']</pre> | List of inline spacing CSS properties to flag |\n\n### scope-value\n\n| Option   | Default                                                   | Description                |\n| -------- | :-------------------------------------------------------- | :------------------------- |\n| `values` | <pre lang=js>['row', 'col', 'rowgroup', 'colgroup']</pre> | List of valid scope values |\n\n### target-offset\n\n| Option      | Default | Description                                                                                                |\n| ----------- | :------ | :--------------------------------------------------------------------------------------------------------- |\n| `minOffset` | `24`    | Minimum space required from the farthest edge of the target, to the closest edge of the neighboring target |\n\n### target-size\n\n| Option    | Default | Description                                                                                              |\n| --------- | :------ | :------------------------------------------------------------------------------------------------------- |\n| `minSize` | `24`    | Minimum width and height a component should have, that is not obscured by some other interactive element |\n\n### region\n\n| Option          | Default                                        | Description                                                                 |\n| --------------- | :--------------------------------------------- | :-------------------------------------------------------------------------- |\n| `regionMatcher` | <pre lang=css>dialog, [role=dialog], svg</pre> | A matcher object or CSS selector to allow elements to be treated as regions |\n\n### inline-style-property-evaluate\n\nThis evaluate method is used in the following checks. Default vary between checks\n\n- important-letter-spacing\n- important-word-spacing\n- important-line-height\n\n| Option           | Description                                                                   |\n| ---------------- | :---------------------------------------------------------------------------- |\n| `cssProperty`    | Which property to check the value of, for example letter-spacing or font-size |\n| `absoluteValues` | Whether or not to calculate value in pixels (true) or in em (false)           |\n| `noImportant`    | While false, the check returns `true` except if !important is used            |\n| `multiLineOnly`  | If true,                                                                      |\n| `minValue`       | Returns `false` when the value is less than `minValue`                        |\n| `maxValue`       | Returns `false` when the value is more than `maxValue`                        |\n| `normalValue`    | The value to use when `normal` is set, defaults to `0`                        |\n\nIf `minValue` and `maxValue` are both undefined, the check returns `false` if the property is used with !important. If done along with `noImportant: true`, the check returns false if the property is set at all in the style attribute.\n\n### invalid-children\n\nThis evaluation method is used in the `list` and `definition-list` rule to determine whether its child nodes are allowed.\n\n| Option           | Description                                                                         |\n| ---------------- | :---------------------------------------------------------------------------------- |\n| `validNodeNames` | Nodes without role allowed as children                                              |\n| `validRoles`     | Roles allowed on child elements                                                     |\n| `divGroups`      | Whether the child nodes can be grouped in a div without any role (false by default) |\n\n### link-in-text-block\n\nThis evaluation method is used in the `link-in-text-block` rule and tests that either the foreground color or the background color has sufficient contrast between the link text and the surrounding text.\n\n| Option                  | Default | Description                                                                 |\n| ----------------------- | :------ | :-------------------------------------------------------------------------- |\n| `requiredContrastRatio` | `3`     | Minimum contrast needed to pass the check between text or background colors |\n| `allowSameColor`        | `true`  | Whether links with colors identical to its surroundings should pass         |\n"
  },
  {
    "path": "doc/code-submission-guidelines.md",
    "content": "# Code Submission Guidelines\n\nWe've enacted standards for commits and pull requests to effectively manage the project over\ntime. We expect all code contributed to follow these standards. If your code doesn't follow them, we\nwill kindly ask you to resubmit it in the correct format.\n\n- [Code Guidelines](#code-guidelines)\n- [Git Commits](#git-commits)\n- [Submitting a pull request](#submitting-a-pull-request)\n- [Merging a pull request](#merging-a-pull-request)\n- [Squashing Commits](#squashing-everything-into-one-commit)\n\n## Code Guidelines\n\n### Default Export at Top\n\nWhen coding using [JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), the `default export` should be at the top of the file right after any import statements and module level variables. This ensures that when you open the file the main code path is the first thing you read.\n\nIf you encounter any code that we maintain that does not put the `default export` at the top, you should update the file to do so.\n\n### Return Early / Happy Path Coding\n\n[Return Early Coding](https://medium.com/swlh/return-early-pattern-3d18a41bba8) (also called \"[Happy Path Coding](https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88)\") is a coding pattern where you try to keep the main execution flow of the function aligned to a single column edge. Doing so allows someone to quickly scan down the column to see the expected flow or happy path of the function.\n\nIn return early coding, the idea is to keep the main execution flow to the left column and use if statements to primarily handle errors or edge cases. This typically means that the function will include many `return` statements instead of the concept of a single return.\n\nIn return early coding, we also try to declare variables at the moment they are needed rather than listing them all at the top of the function.\n\n<details>\n  <summary>Example of nested execution flow</summary>\n\n```js\nimport path from 'path';\nimport { promises as fs } from 'fs';\n\nexport default async function validateAxeReport(filePath = '') {\n  let valid;\n  let file;\n  let results;\n\n  if (filePath.trim()) {\n    try {\n      if (!path.isAbsolute(filePath)) {\n        filePath = path.relative(process.cwd(), filePath);\n      }\n      file = await fs.readFile(filePath, 'utf8');\n    } catch (err) {\n      throw new Error(`Unable to read file \"${filePath}\"`);\n    }\n\n    try {\n      results = JSON.parse(file);\n    } catch (err) {\n      throw new TypeError(`File \"${filePath}\" is not a valid JSON file`);\n    }\n\n    if (results?.testRunner?.name !== 'axe') {\n      throw new TypeError(`File \"${filePath}\" is not a valid axe results file`);\n    }\n\n    valid = validateReportStructure(results);\n  } else {\n    throw new SyntaxError('No file path provided');\n  }\n\n  return valid;\n}\n```\n\n</details>\n\n<details>\n  <summary>Example of return early coding</summary>\n\n```js\nimport path from 'path';\nimport { promises as fs } from 'fs';\n\nexport default async function validateAxeReport(filePath = '') {\n  if (!filePath.trim()) {\n    throw new SyntaxError('No file path provided');\n  }\n\n  if (!path.isAbsolute(filePath)) {\n    filePath = path.relative(process.cwd(), filePath);\n  }\n\n  let file;\n  try {\n    file = await fs.readFile(filePath, 'utf8');\n  } catch (err) {\n    throw new Error(`Unable to read file \"${filePath}\"`);\n  }\n\n  let results;\n  try {\n    results = JSON.parse(file);\n  } catch (err) {\n    throw new TypeError(`File \"${filePath}\" is not a valid JSON file`);\n  }\n\n  if (results?.testRunner?.name !== 'axe') {\n    throw new TypeError(`File \"${filePath}\" is not a valid axe results file`);\n  }\n\n  return validateReportStructure(results);\n}\n```\n\n</details>\n\n### DocBlock Comments\n\nWe use [DocBlock comments](https://en.wikipedia.org/wiki/Docblock) in our code. DocBlock comments are a way to describe what a function does, its signature, and describe its inputs.\n\n<details>\n  <summary>Example of DocBlock comment</summary>\n\n```ts\n/**\n * Calculate the distance between two points.\n * @param {number[]} pointA The first point represented by the array [x,y]\n * @param {number[]} pointB The second point represented by the array [x,y]\n * @return {number}\n */\nfunction distance(pointA, pointB) {\n  return Math.hypot(pointA[0] - pointB[0], pointA[1] - pointB[1]);\n}\n```\n\n</details>\n\n### Tests\n\nAll code changes should include tests to validate that the code works as expected. Both unit tests and integration tests (where applicable) should be included in all `fix` and `feat` pull requests. `chore` pull requests do not typically need tests.\n\n#### Integration Test\n\nAll changes that affect a rule should include an integration test. Most rules integration tests can be found in `test/integration/rules`. When updating tests in this directory, you'll need to write the new HTML code to be tested and give the element that should trigger the rule a unique `id`. Then you'll need to update the companion JSON file to add the id to either the `violations` or `passes` array.\n\nFor example, if we were updating the `aria-roles` rule to fail when using the `command` role, the changes would look as follows:\n\n`test/integration/rules/aria-roles/aria-roles.html`\n\n```diff\n+<div role=\"command\" id=\"fail-command\">fail</div>\n```\n\n`test/integration/rules/aria-roles/aria-roles.json`\n\n```diff\n{\n  \"description\": \"aria-roles tests\",\n  \"rule\": \"aria-roles\",\n  \"violations\": [\n     [\"#fail1\"],\n-    [\"#fail2\"]\n+    [\"#fail2\"],\n+    [\"#fail-command\"]\n  ]\n}\n```\n\nNotice that the id added to the `violations` array is inside an array. This is because the `violations` and `passes` arrays are axe-core selectors, which follow the format:\n\n- a single string - the string is the CSS selector\n- multiple strings\n  - The last string is the final CSS selector\n  - All other's are the nested structure of iframes inside the document\n\n## Git Commits\n\nWe follow Angular's code contribution style with precise rules for formatting git commit messages.\nThis leads to more readable messages that are easy to follow when looking through the project\nhistory. We will also use commit messages to generate the axe Changelog document.\n\nA detailed explanation of Angular's guidelines and conventions can be found [on Google Docs](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#).\n\n### Commit Message Format\n\nEach commit message should consist of a header, a body and a footer. The header has a special format\nthat includes a type, a scope and a subject. Here's a sample of the format:\n\n```\n<type>(<scope>): <subject>\n<BLANK LINE>\n<body>\n<BLANK LINE>\n<footer>\n```\n\n### Here's an example:\n\n```sh\nperf(rule): improve speed of color contrast rules\n\nUse async process to compare elements without UI lockup\n\nCloses issue #1\n```\n\n**Note:** We do not link issues to be closed as we have our QA team verify the issue is resolved before closing. Instead use `Closes issue #` to link to the issue the pr resolves but won't close it once merged.\n\n> Commit messages should be 100 characters or less to make them easy to read on GitHub and\n> various git tools.\n\n### How to structure your commits:\n\n#### Type\n\nMust be one of the following:\n\n- **feat:** A new feature\n- **fix:** A bug fix\n- **docs:** Documentation only changes\n- **style:** Changes that do not affect the meaning of the code (white-space, formatting, missing\n  semi-colons, etc)\n- **refactor:** A code change that neither fixes a bug nor adds a feature\n- **perf:** A code change that improves performance\n- **test:** Adding missing tests\n- **chore:** Changes to the build process or auxiliary tools and libraries such as documentation generation\n- **ci:** Changes or fixes to CI configuration such as CircleCI\n\n#### Scope\n\nThe scope specifies the place of the commit change in the codebase along with the type. It could\nreference a rule, a commons file, or anything really. E.g. `feat(rule)` or\n`test(commons/aria)`. It would help us call to out rule changes in our changelog with `rule` used as the scope.\n\nIf the scope is too broad to summarize, use the type only and leave off the parentheses. E.g. `type: some subject`. Keep in mind that a long scope often pushes your commit message over 100 characters. Brevity is helpful for everyone!\n\n#### Subject\n\nThe subject contains succinct description of the change:\n\n- use the imperative, present tense: \"change\" not \"changed\" nor \"changes\"\n- don't capitalize first letter\n- no dot (.) at the end\n\n#### Body\n\nUse the imperative, present tense: \"change\" not \"changed\" nor \"changes\", just like the subject. Include the motivation for the change and contrast it with how the code worked before.\n\n#### Footer\n\nReference any issue that this commit closes with its fully qualified URL to support both Bitbucket and GitHub.\n\nIf needed, the footer should contain any information about [Breaking Changes](https://www.conventionalcommits.org/en/v1.0.0/). Deprecation notices or breaking changes in the Changelog should inform users if they'll need to modify their code after this commit.\n\nA breaking change should be noted with `BREAKING CHANGE:` (all caps, followed by a colon) and a message.\n\n```\nfeat(rules): remove deprecated rules\n\nBREAKING CHANGE: remove rules: th-has-headers, checkboxgroup, radiogroup\n```\n\n## Submitting a pull request\n\nWe want to keep our commit log clean by avoiding merge messages in branches. Before submitting a pull request, make sure your branch is up to date with the develop branch by either:\n\n- Pulling from develop before creating your branch\n- Doing a rebase from origin/develop (will require a force push **on your branch**)\n\nTo rebase from origin/develop if we've pushed changes since you created your branch:\n\n```sh\ngit checkout your-branch\ngit fetch\ngit rebase origin/develop\ngit push origin head -f\n```\n\n## Merging a pull request\n\nIf a pull request has many commits (especially if they don't follow our [commit policy](#git-commits)), you'll want to squash them into one clean commit.\n\nIn the GitHub UI, you can use the new [Squash and Merge](https://github.com/blog/2141-squash-your-commits) feature to make this easy. If there are merge conflicts preventing this, either ask the committer to rebase from develop following the [PR submission steps above](#submitting-a-pull-request), or use the manual method below.\n\nTo apply a pull request manually, make sure your local develop branch is up to date. Then, create a new branch for that pull request.\n\nCreate a temporary, local branch:\n\n```sh\ngit checkout -b temp-feature-branch\n```\n\nRun the following commands to apply all commits from that pull request on top of your branch's local history:\n\n```console\ncurl -L https://github.com/dequelabs/axe-core/pull/205.patch | git am -3\n```\n\nIf the merge succeeds, use `git diff origin/develop` to review all the changes that will happen post-merge.\n\n## Squashing everything into one commit\n\nBefore merging a pull request with many commits into develop, make sure there is only one commit representing the changes in the pull request, so the git log stays lean. We particularly want to avoid merge messages and vague commits that don't follow our commit policy (like `Merged develop into featurebranch` or `fixed some stuff`).\n\nYou can use git's interactive rebase to manipulate, merge, and rename commits in your local history. If these steps are followed, a force push shouldn't be necessary.\n\n**Do not force push to develop or master under any circumstances.**\n\nTo interactively rebase all of your commits on top of the latest in develop, run:\n\n```sh\ngit rebase --interactive origin/develop\n```\n\nThis brings up an interactive dialog in your text editor. Follow the instructions to squash all of your commits into the top one. Rename the top one.\n\nOnce this is done, run `git log` and you will see only one commit after develop, representing everything from the pull request.\n\nFinally, pull from develop with `rebase` to put all of our local commits on top of the latest remote.\n\n```sh\ngit pull --rebase origin develop\n```\n\nYou can then push the latest code to develop (note that force push isn't needed if these steps are followed):\n\n```console\ngit push origin develop\n```\n\n## Writing Integration Tests\n\nFor each rule, axe-core needs to have integration tests. These tests are located in `tests/integration`. This directory contains two other directories. `rules`, which contains integration tests that can be run on a single page, and `full` which contains tests that can only be tested by running on multiple pages.\n\nEnsure that for each check used in the rule, there is an integration test for both pass and fail results. Integration tests put in `rules` can be described using simple code snippets in an HTML file, and a JSON file that describes the expected outcome. For `full` tests, a complete Jasmine test should be created, including at least one HTML file that has the tested code, and a JS file that has the test statements.\n"
  },
  {
    "path": "doc/context.md",
    "content": "# Axe Testing Context\n\nAxe-core's `context` argument is a powerful tool for controlling precisely which elements are tested and which are ignored. The context lets you do many things, including:\n\n1. [Test specific elements](#test-specific-elements)\n1. [Test DOM nodes](#test-dom-nodes)\n1. [Exclude Elements from Test](#exclude-elements-from-test)\n1. [Select from prior tests](#select-from-prior-tests)\n1. [Limit frame testing](#limit-frame-testing)\n1. [Limit shadow DOM testing](#limit-shadow-dom-testing)\n1. [Combine Shadow DOM and Frame Context](#combine-shadow-dom-and-frame-context)\n1. [Implicit frame and shadow DOM selection](#implicit-frame-and-shadow-dom-selection)\n\n## Test Specific Elements\n\nWhen passed a CSS selector or array of CSS selectors, axe will test only the elements that match those selectors, along with any content inside those elements:\n\n```js\n// Test every <nav> and <main> element, and everything inside them:\nawait axe.run('nav, main');\n// Test every <nav> element, every element with the sideBar class,\n// and the element with the #header ID, along with all their content:\nawait axe.run(['nav', '.sideBar', '#header']);\n```\n\n**Tip**: Sometimes, pages are worked on by multiple teams. If you mark your team's sections up with a unique class, axe can use that class to only test the sections your team works on.\n\nAxe-core is commonly used through browser driver APIs such as Selenium, Puppeteer, and Playwright. Most of these APIs do not pass a context object in directly. Instead, the following is achieved through the `.include()` method. Visit the docs of your API for more information. For most cases, this will look like the following:\n\n```js\nconst axe = new AxeBuilder({ page });\n// Test every <nav> and <main> element, and everything inside them:\naxe.include('nav, main');\n```\n\n## Test DOM Nodes\n\nAxe can accept native DOM elements for testing. These must be attached to the DOM tree first. Testing DOM elements is useful in test environments such as Karma and Jest. The following example shows axe testing DOM elements:\n\n```js\n// Test a single DOM node\nconst img = document.createElement('img');\ndocument.body.appendChild(img);\nawait axe.run(img);\n\n// Test a Node list:\nconst nodes = document.querySelector('main, header');\nawait axe.run(nodes);\n\n// Test an array of nodes:\nconst navbar = document.getElementById('navbar');\nconst cookiePopup = document.getElementById('cookie-popup');\nawait axe.run([navbar, cookiePopup]);\n```\n\n### Component Frameworks\n\nBecause axe requires a DOM to test, components such as those created for React, Vue, and Angular must be rendered before they are tested. The following example shows how to render the `<MyApp>` React component before testing it with axe:\n\n```jsx\nconst appRoot = document.getElementById('app');\nReactDOM.createRoot(appRoot).render(MyApp);\nawait axe.run(appRoot);\n```\n\n**Important**: Component testing libraries like [Enzyme](https://enzymejs.github.io/enzyme/) include both a `render` and `shallow` method. Because axe requires a complete render, attached to the DOM tree, it is unable to test components built with `shallow` methods.\n\n## Exclude Elements from Test\n\nThere are often areas of a page that as a developer you have no control over. You can tell axe to skip such elements in the test by using an object with the `exclude` property. The `exclude` property accepts all the same properties used before, including CSS selectors and DOM nodes:\n\n```js\n// Test everything except the ad banners:\nawait axe.run({ exclude: '.ad-banner' });\n\n// Test everything except these DOM nodes:\nconst youtubeVids = document.querySelector('iframe[src^=\"youtube.com\"]');\nawait axe.run({ exclude: youtubeVids });\n```\n\nWhen using an axe API such as axe-core/playwright, you must use the AxeBuilder's `.exclude` method to exclude elements from the test. See the docs of your API for details:\n\n```js\nconst axe = new AxeBuilder({ page });\n// Test everything except the ad banner and YouTube frames:\naxe.exclude('.ad-banner, iframe[src^=\"youtube.com\"]');\n```\n\n### The `include` Property\n\nWhen axe is passed a CSS selector or an array of DOM nodes, this is treated as an implicit `include`. All examples from [Test specific elements](#test-specific-elements) and [Test DOM nodes](#test-dom-nodes) use this implicit `include`. All of these could be rewritten using an object with the `include` property. The following examples function identically:\n\n```js\nawait axe.run('main'); // is the same as:\nawait axe.run({ include: 'main' });\n```\n\nWhen axe isn't passed an `include` (explicit or not), it uses `document` instead:\n\n```js\nawait axe.run({ exclude: '.ad-banner' }); // is the same as:\nawait axe.run({\n  include: document,\n  exclude: '.ad-banner'\n});\n```\n\n### Combine `exclude` with `include`\n\nTo test specific sections of the page while skipping parts within that section, you can pass an object with `include` and `exclude` properties. `include` tells axe what elements to test, and `exclude` tells axe to skip certain included sections. The following shows how to test the `main` and `footer` elements in a page, except for any `.ad-banner` element:\n\n```js\nawait axe.run({\n  include: ['main', 'footer'],\n  exclude: '.ad-banner'\n});\n```\n\n**Note**: When an element is both included and excluded, the selector that matches the nearest ancestor takes priority. I.e. if a node's grandparent is included, but the parent is excluded, then the node is excluded. If the node's grandparent is excluded, but its parent is included, the node is included.\n\n## Select From Prior Tests\n\nIt is possible to `include` or `exclude` nodes from prior tests. This lets you either skip those issues, or repeat a test while you are working on a solution. This can be done using the `target` property from an axe result:\n\n```js\n// Grab the target property from all color-contrast violations:\nconst violation = priorResult.violations.find(\n  ({ id }) => id === 'color-contrast'\n);\nconst targets = violation.nodes.map(issue => issue.target);\n// Run axe, only testing the prior color-contrast violations\nawait axe.run(targets);\n\n// Or, to exclude those nodes from the test:\nawait axe.run({ exclude: targets });\n```\n\n**Important**: It is not possible to pass a single `target` property to axe. This must be wrapped in an array. This is because axe-core's `target` property is itself an array. See [Implicit Frame And Shadow DOM Selection](#implicit-frame-and-shadow-dom-selection) for details.\n\n## Limit Frame Testing\n\nIncluding or excluding specific sections within a frame can be done with a `fromFrames` selector object. This property takes an array of selectors: the first to select the frame element(s) and the last to select the element(s) to include or exclude. The following shows how to test all `form` elements a `#paymentFrame` frame or iframe:\n\n```js\n// Test each <form> inside each #paymentFrame frame or iframe:\naxe.run({ fromFrames: ['#paymentFrame', 'form'] });\n```\n\nTo select elements in nested frames, axe will need a selector for each level of nesting. Because axe tests frames recursively, this can be any number of levels deep. The following shows how to test the `form`, inside an `#inner`, inside an `#outer` iframe:\n\n```js\n// Test <form> inside, #inner, inside #outer:\naxe.run({ fromFrames: ['iframe#outer', 'iframe#inner', 'form'] });\n```\n\nThe `fromFrames` object can be used as part of an `exclude` or `include` property. It can be by itself or as part of an array along with other selectors. The following example shows how to exclude all `.ad-banner` elements on the top window, as well as any that are part of the first level of iframes:\n\n```js\n// Skip any .ad-banner, as well as any .ad-banner inside iframes:\naxe.run({\n  exclude: [\n    '.ad-banner',\n    {\n      fromFrames: ['iframe', '.ad-banner']\n    }\n  ]\n});\n```\n\nThe `fromFrames` selector object can be used on both the `include` and `exclude` property. The following shows how to test the `form` inside the `#payment` iframe, except for the `.ad-banner` in that `form`:\n\n```js\naxe.run({\n  include: {\n    fromFrames: ['iframe#payment', 'form']\n  },\n  exclude: {\n    fromFrames: ['iframe#payment', 'form > .ad-banner']\n  }\n});\n```\n\n**Note**: The `fromFrames` property cannot be used on the same object as `include` and `exclude`.\n\n## Limit Shadow DOM Testing\n\nIncluding or excluding specific sections of a [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) tree can be done with a `fromShadowDom` selector object. This works similar to the [`fromFrames` selector](#limit-frame-testing). The `fromShadowDom` property takes an array of strings: the first to select the shadow DOM host element(s) and the last to select the element(s) to include or exclude. The following example shows how to test the `#search` form inside the shadow DOM tree attached to the `.app-header` element:\n\n```js\n// Test each search form inside each <app-header> shadow DOM tree.\naxe.run({ fromShadowDom: ['.app-header', 'form#search'] });\n```\n\nTo select elements in nested shadow DOM trees, axe will need a selector for each level of nesting. The following shows how to test the `#search` element inside the `.header` element's Shadow DOM tree, inside the `app-root` custom element's shadow DOM tree:\n\n```js\n// Test #search, inside each .header, inside each <app-root>\naxe.run({ fromShadowDom: ['app-root', '.header', '#search'] });\n```\n\nThe `fromShadowDom` selector object can also be used as part of an `exclude` or `include` property. It can be by itself, or part of an array along with other selectors. The following example shows how to exclude all `.comment` elements inside the `<blog-comments>` custom element, as well as excluding the `footer` element:\n\n```js\n// Skip footer, as well as any .comment element inside the shadow DOM tree of <blog-comments>\naxe.run({\n  exclude: [\n    'footer',\n    {\n      fromShadowDom: ['blog-comments', '.comment']\n    }\n  ]\n});\n```\n\nThe `fromShadowDom` selector object can be used on both the `include` and `exclude` property. The following shows how to test the `<app-footer>` custom component, inside the shadow DOM of the `#root` element, but to exclude any `.ad-banner` inside the `<app-footer>`'s shadow DOM tree:\n\n```js\naxe.run({\n  include: {\n    fromShadowDom: ['#root', 'app-footer']\n  },\n  exclude: {\n    fromShadowDom: ['#root', 'app-footer', '.ad-banner']\n  }\n});\n```\n\n**Note**: The `fromShadowDom` property cannot be used on the same object as `include` and `exclude`.\n\n### Slotted Elements\n\nAxe uses the flattened DOM tree to determine whether an element is included or excluded. Because of this when a shadow DOM node is selected, all descendants inserted through the [`<slot />` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Slot) are also selected.\n\n## Combine Shadow DOM and Frame Context\n\nTo select frames inside shadow DOM trees or shadow DOM trees inside frames, it is possible to use [`fromShadowDom`](#limit-shadow-dom-testing) as a selector in the [`fromFrames`](#limit-frame-testing) selector object. The following example shows how to test the `main` element, inside each `iframe` that is part of the shadow DOM tree of `#appRoot`:\n\n```js\nawait axe.run({\n  fromFrames: [\n    {\n      fromShadowDom: ['#appRoot', 'iframe']\n    },\n    'main'\n  ]\n});\n```\n\nThese selectors can also be used on `include` or `exclude`. The following shows how to exclude the `footer`, as well as any `.commentBody` elements in the `#userComments` shadow DOM tree, inside the `#blog-comments` iframe:\n\n```js\nawait axe.run({\n  exclude: [\n    'footer',\n    {\n      fromFrames: [\n        'iframe#blog-comments',\n        {\n          fromShadowDom: ['#userComments', '.commentBody']\n        }\n      ]\n    }\n  ]\n});\n```\n\n**Note**: Even though the iframe is inside the shadow DOM tree, the `fromShadowDom` selector object must be part of the `fromFrames` selector. Doing the reverse does not work and will cause an error.\n\n## Implicit Frame and Shadow DOM Selection\n\nIn earlier versions of axe, using nested arrays was the only way to include or exclude elements inside iframes. These are still supported in axe and is how axe works internally. This nested array syntax is used in the `target` property. For example, the following is a possible selector for a `.commentBody` element, in the shadow DOM tree of `#userComments`, inside the `#blog-comments` iframe:\n\n```js\nresult = await axe.run();\nresult.violations[0].nodes[0].target; // ['#blog-comments', ['#userComments', '.commentBody']]\n```\n\nTo pass a `target` property into axe, it must be wrapped in another array. This creates three nested arrays: the outer array to allow multiple selectors, the middle array to select into frames, and the inner array, which is optional, to select elements inside shadow DOM trees.\n\nWhile this syntax continues to be supported, it is recommended to avoid it and to use the [fromFrames](#limit-frame-testing) and [fromShadowDom](#limit-shadow-dom-testing) object selectors instead since these are clearer and don't need to be wrapped in otherwise empty arrays.\n"
  },
  {
    "path": "doc/developer-guide.md",
    "content": "# Axe Developer Guide\n\nAxe runs a series of tests to check for accessibility of content and functionality on a website. A test is made up of a series of Rules which are, themselves, made up of Checks. Axe executes these Rules asynchronously and, when the Rules are finished running, runs a callback function which is passed a Result structure. Since some Rules run on the page level while others do not, tests will also run in one of two ways. If a document is specified, the page level rules will run, otherwise they will not.\n\nAxe 3.0 supports open Shadow DOM: see our virtual DOM APIs and test utilities for developing axe-core moving forward. Note: we do not and cannot support closed Shadow DOM.\n\n1. [Getting Started](#getting-started)\n   1. [Environment Pre-requisites](#environment-pre-requisites)\n   1. [Building axe.js](#building-axejs)\n   1. [Watching for Changes](#watching-for-changes)\n   1. [Running Tests](#running-tests)\n   1. [API Reference](#api-reference)\n   1. [Supported CSS Selectors](#supported-css-selectors)\n1. [Architecture Overview](#architecture-overview)\n   1. [Rules](#rules)\n   1. [Checks](#checks)\n   1. [Common Functions](#common-functions)\n   1. [Virtual Nodes](#virtual-nodes)\n   1. [Core Utilities](#core-utilities)\n1. [Virtual DOM APIs](#virtual-dom-apis)\n   1. [API Name: axe.utils.getFlattenedTree](#api-name-axeutilsgetflattenedtree)\n   1. [API Name: axe.utils.getNodeFromTree](#api-name-axeutilsgetnodefromtree)\n1. [Test Utilities](#test-utilities)\n   1. [Test Util Name: axe.testUtils.MockCheckContext](#test-util-name-mockcheckcontext)\n   1. [Test Util Name: axe.testUtils.shadowSupport](#test-util-name-shadowsupport)\n   1. [Test Util Name: axe.testUtils.fixtureSetup](#test-util-name-fixturesetup)\n   1. [Test Util Name: axe.testUtils.checkSetup](#test-util-name-checksetup)\n1. [Using Rule Generation CLI](#using-rule-generation-cli)\n\n## Getting Started\n\n### Environment Pre-requisites\n\n1. You must have Node.js version 24 or higher installed.\n   If you have [nvm](https://github.com/nvm-sh/nvm) installed, simply do `nvm use` in the root of this repository.\n1. Install npm development dependencies. In the root folder of your axe-core repository, run `npm install`\n\n### Building axe.js\n\nTo build axe.js, simply run `npm run build` in the root folder of the axe-core repository. axe.js and axe.min.js are placed into the root folder.\n\n## Watching for Changes\n\nYou can watch for changes and automatically build axe and run relevant tests using `npm run develop`. Once run, any changes to files inside the [lib directory](../lib) will rebuild axe. After axe is built, it will try to run the relevant tests for the files changed. If you change a file inside the [test directory](../test) it will run the tests for the file changed.\n\nChanges to files in the [full integration test directory](../test/integration/full) will not run the tests. This is because these tests require the browser to navigate to the page directly, which is something Mocha / Karma does not support.\n\n**Note:** We are still working on knowing which tests are relevant to the changed file so this may not correctly run tests every time. In these cases you should run the tests manually. If you encounter a test that does not run when a relevant file is changed, please [open an issue](https://github.com/dequelabs/axe-core/issues).\n\n### Running Tests\n\nTo run all tests from the command line you can run `npm test`, which will run all unit and integration tests using headless Chrome. Having axe built and up-to-date is required in order to run tests. If you update files inside the [lib directory](../lib) you will need to rebuild axe before running tests.\n\nYou can scope which set of tests to run through various npm scripts:\n\n- `npm run test:unit:core` - Run only [core tests](../test/core/)\n- `npm run test:unit:commons` - Run only [commons tests](../test/commons/)\n- `npm run test:unit:checks` - Run only [check tests](../test/checks/)\n- `npm run test:unit:rule-matches` - Run only [rule matches](../test/rule-matches/)\n- `npm run test:unit:integration` - Run only [rule integration tests](../test/integration/rules/)\n- `npm run test:unit:virtual-rules` - Run only [virtual rule tests](../test/integration/virtual-rules)\n- `npm run test:unit:api` - Run only [api tests](../test/integration/api)\n\nThere are also a set of tests that are not considered unit tests that you can run through various npm scripts:\n\n- `npm run test:act` - Run the [act tests](../test/act-mapping)\n- `npm run test:apg` - Run the [aria-practices tests](../test/aria-practices)\n- `npm run test:examples` - Run the [example tests](../doc/examples)\n- `npm run test:locales` - Run the [local tests](../test/test-locales.js)\n- `npm run test:node` - Run the [node tests](../test/node)\n- `npm run test:tsc` - Run the [typescript tests](../typeings/axe-core)\n\nAdditionally, you can [watch for changes](#watching-for-changes) to files and automatically run the relevant tests.\n\nIf you need to debug a test in a non-headless browser, you can run `npm run test:debug` which will run the Karma tests in non-headless Chrome. You can either use that browser's debugger or attach an external debugger on port 9765; [a VS Code launch profile](../.vscode/launch.json) is provided. You can also navigate to the newly opened page using any supported browser.\n\nYou can scope which set of tests to debug by passing the `testDirs` argument. Supported values are:\n\n- `core`\n- `commons`\n- `checks`\n- `rule-matches`\n- `integration`\n- `virtual-rules`\n- `api`\n\nExample:\n\n- `npm run test:debug -- testDirs=core`\n\nLastly, you can run the [full integration tests](../test/integration/full) by starting a local server by running `npm start`. Once started, you can open any supported browser and navigate to any test in the full integration tests directory.\n\n### API Reference\n\n[See API exposed on axe](./API.md#section-2-api-reference)\n\n### Supported CSS Selectors\n\nAxe supports the following CSS selectors:\n\n- Type, Class, ID, and Universal selectors. E.g `div.main, #main`\n- Pseudo selector `not`. E.g `th:not([scope])`\n- Descendant and Child combinators. E.g. `table td`, `ul > li`\n- Attribute selectors `=`, `^=`, `$=`, `*=`. E.g `a[href^=\"#\"]`\n\n## Architecture Overview\n\nAxe tests for accessibility using objects called Rules. Each Rule tests for a high-level aspect of accessibility, such as color contrast, button labels, and alternate text for images. Each rule is made up of a series of Checks. Depending on the rule; all, some, or none of these checks must pass in order for the rule to pass.\n\nUpon execution, a Rule runs each of its Checks against all relevant nodes. Which nodes are relevant is determined by the Rule's `selector` property and `matches` function. If a Rule has no Checks that apply to a given node, the Rule will result in an inapplicable result.\n\nAfter execution, a Check will return `true`, `false`, or `undefined` depending on whether or not the tested condition was satisfied. The result, as well as more information on what caused the Check to pass or fail, will be stored in either the `passes`, `violations`, or `incomplete` arrays.\n\n### Rules\n\nRules are defined by JSON files in the [lib/rules directory](../lib/rules). The JSON object is used to seed the [Rule object](../lib/core/base/rule.js#L30). A valid Rule JSON consists of the following:\n\n- `id` - `String` A unique name of the Rule.\n- `impact` - `String` (one of `minor`, `moderate`, `serious`, or `critical`). Sets the impact of the results of this rule\n- `selector` - **optional** `String` which is a [CSS selector](#supported-css-selectors) that specifies the elements of the page on which the Rule runs. axe-core will look inside of the light DOM and _open_ [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) trees for elements matching the provided selector. If omitted, the rule will run against every node.\n- `excludeHidden` - **optional** `Boolean` Whether the rule should exclude hidden elements. Defaults to `true`.\n- `enabled` - **optional** `Boolean` Whether the rule is enabled by default. Defaults to `true`.\n- `pageLevel` - **optional** `Boolean` Whether the rule is page level. Page level rules will only run if given an entire `document` as context.\n- `matches` - **optional** `String` The ID of the filtering function that will exclude elements that match the `selector` property. See the [`metadata-function-map`](../lib/core/base/metadata-function-map.js) file for all defined IDs.\n- `tags` - **optional** `Array` Strings of the accessibility guidelines of which the Rule applies.\n- `metadata` - `Object` Consisting of:\n  - `description` - `String` Text string that describes what the rule does.\n  - `helpUrl` - `String` **optional** URL that provides more information about the specifics of the violation. Links to a page on the Deque University site.\n  - `help` - `String` Help text that describes the test that was performed.\n- `any` - `Array` Checks that make up this Rule; one of these checks must return `true` for a Rule to pass.\n- `all` - `Array` Checks that make up this Rule; all these checks must return `true` for a Rule to pass.\n- `none` - `Array` Checks that make up this Rule; none of these checks must return `true` for a Rule to pass.\n\nThe `any`, `all` and `none` arrays must contain either a `String` which references the `id` of the Check; or an object of the following format:\n\n- `id` - `String` The unique ID of the Check.\n- `options` - `Mixed` Any options the Check requires that are specific to the Rule.\n\nThere is a Grunt target which will ensure each Rule has a valid format, which can be run with `npx grunt validate`.\n\n#### Matches Function\n\nA Rule's `matches` function is executed against each node which matches the Rule's `selector` and receive two parameters:\n\n- `node` – node, the DOM Node to test\n- `virtualNode`– object, the virtual DOM representation of the node. See [virtualNode documentation](#virtual-nodes) for more.\n\nThe matches function must return either `true` or `false`. Common functions are provided as `commons`. [See the data-table matches function for an example.](../lib/rules/data-table-matches.js)\n\n### Checks\n\nSimilar to Rules, Checks are defined by JSON files in the [lib/checks directory](../lib/checks). The JSON object is used to seed the [Check object](../lib/core/base/check.js). A valid Check JSON consists of the following:\n\n- `id` - `String` A unique name of the Check\n- `evaluate` - `String` The ID of the function that implements the check's functionality. See the [`metadata-function-map`](../lib/core/base/metadata-function-map.js) file for all defined IDs.\n- `after` - **optional** `String` The ID of the function that gets called for checks that operate on a page-level basis, to process the results from the iframes.\n- `options` - **optional** `Object` Any information the Check needs that you might need to customize and/or is locale specific. Options can be overridden at runtime (with the options parameter) or config-time. For example, the [valid-lang](../lib/checks/language/valid-lang.json) Check defines what ISO 639-1 language codes it should accept as valid. Options do not need to follow any specific format or type; it is up to the author of a Check to determine the most appropriate format.\n- `metadata` - `Object` Consisting of:\n  - `impact` - **Deprecated** `String` (one of `minor`, `moderate`, `serious`, or `critical`)\n  - `messages` - `Object` These messages are displayed when the Check passes or fails\n    - `pass` - `String` [doT.js](http://olado.github.io/doT/) template string displayed when the Check passes\n    - `fail` - `String` [doT.js](http://olado.github.io/doT/) template string displayed when the Check fails\n    - `incomplete` – `String|Object` – [doT.js](http://olado.github.io/doT/) template string displayed when the Check is incomplete OR an object with `missingData` on why it returned incomplete. Refer to [rules.md](./rule-development.md).\n\n#### Check `evaluate`\n\nA Check's evaluate function is run a special context in order to give access to APIs which provide more information. Checks will run against a single node and do not have access to other frames. A Check must either return `true` or `false`.\n\nThe following variables are defined for `Check#evaluate`:\n\n- `node` - `HTMLElement` The element that the Check is run against\n- `options` - `Mixed` Any options specific to this Check that may be necessary. If not specified by the user at run-time or configure-time; it will use `options` as defined by the Check's JSON file.\n- `virtualNode` – `Object` The virtualNode object for use with Shadow DOM. See [virtualNode documentation](#virtual-nodes).\n- `this.data()` - `Function` Free-form data that either the Check message requires or is presented as `data` in the CheckResult object. Subsequent calls to `this.data()` will overwrite previous. See [aria-valid-attr](../lib/checks/aria/aria-valid-attr-value-evaluate.js) for example usage.\n- `this.relatedNodes()` - `Function` Array or NodeList of elements that are related to this Check. For example the [duplicate-id](../lib/checks/parsing/duplicate-id-evaluate.js) Check will add all Elements which share the same ID.\n\n#### Check `after`\n\nYou can use the `after` function to evaluate nodes that might be in other frames or to filter the number of violations or passes produced. The `after` function runs once for each Rule in the top-most (or originating) frame. Due to this, you should not perform DOM operations in after functions, but instead operate on `data` defined by the Check.\n\nFor example, the [duplicate-id](../lib/checks/parsing/duplicate-id.json) Check include an [after function](../lib/checks/parsing/duplicate-id-after.js) which reduces the number of violations so that only one violation per instance of a duplicate ID is found.\n\nThe following variables are defined for `Check#after`:\n\n- `results` - `Array` Contains [CheckResults](#checkresult) for every matching node.\n\nThe after function must return an `Array` of CheckResults, due to this, it is a very common pattern to just use `Array#filter` to filter results:\n\n```js\nvar uniqueIds = [];\nreturn results.filter(function (r) {\n  if (uniqueIds.indexOf(r.data) === -1) {\n    uniqueIds.push(r.data);\n    return true;\n  }\n  return false;\n});\n```\n\n#### Pass, Fail and Incomplete Templates\n\nOccasionally, you may want to add additional information about why a Check passed, failed or returned undefined into its message. For example, the [aria-valid-attr](../lib/checks/aria/valid-attr.json) will add information about any invalid ARIA attributes to its fail message. The message uses a [custom message format](./check-message-template.md). In the Check message, you have access to the `data` object as `data`.\n\n```js\n// aria-valid-attr check\n\"messages\": {\n  \"pass\": \"ARIA attributes are used correctly for the defined role\",\n  \"fail\": {\n    \"singular\": \"ARIA attribute is not allowed: ${data.values}\",\n    \"plural\": \"ARIA attributes are not allowed: ${data.values}\"\n  },\n  \"incomplete\": \"axe-core couldn't tell because of ${data.missingData}\"\n}\n```\n\nSee [Developing Axe-core Rules](./rule-development.md) for more information\non writing rules and checks, including incomplete results.\n\n#### CheckResult\n\nWhen a Check is executed, its result is then added to a [CheckResult object](../lib/core/base/check-result.js). Much like the RuleResult object, the CheckResult object only contains information that is required to determine whether a Check, and its parent Rule passed or failed. `metadata` from the originating Check is combined later and will not be available until axe reaches the reporting stage.\n\nA CheckResult has the following properties:\n\n- `id` - `String` The ID of the Check this CheckResult belongs to.\n- `data` - `Mixed` Any data the Check's evaluate function added with `this.data()`. Typically used to insert data from analysis into a message or to perform further tests in the post-processing function.\n- `relatedNodes` - `Array` Nodes that are related to the current Check as defined by [check.evaluate](#check-evaluate).\n- `result` - `Boolean` The return value of [check.evaluate](#check-evaluate).\n\n### Common Functions\n\nCommon functions are an internal library used by the rules and checks. If you have code repeated across rules and checks, you can use these functions and contribute to them. Documentation is available in [source code](../lib/commons/).\n\n#### Commons and Shadow DOM\n\nTo support open Shadow DOM while maintaining backwards compatibility, commons functions that query DOM nodes must operate on an in-memory representation of the DOM using axe-core’s built-in [API methods and utility functions](./API.md#virtual-dom-utilities).\n\nCommons functions should do the virtual tree lookup and call a `virtual` function including the rest of the commons code. The naming of this special function should contain the original commons function name with `Virtual` added to signify it expects to operate on a virtual DOM tree.\n\nLet’s look at an example:\n\n```js\n// lib/commons/text/accessible-text.js\nimport { getNodeFromTree } from '../../core/utils';\nimport accessibleTextVirtual from './accessible-text-virtual';\n\nfunction accessibleText(element, inLabelledbyContext) {\n  let virtualNode = getNodeFromTree(axe._tree[0], element); // throws an exception on purpose if axe._tree not correct\n  return accessibleTextVirtual(virtualNode, inLabelledbyContext);\n}\n\nexport default accessibleText;\n\n// lib/commons/text/accessible-text-virtual.js\nfunction accessibleTextVirtual(element, inLabelledbyContext) {\n  // rest of the commons code minus the virtual tree lookup, since it’s passed in\n}\n```\n\n`accessibleTextVirtual` would only be called directly if you’ve got a virtual node you can use. If you don’t already have one, call the `accessibleText` lookup function, which passes on a virtual DOM node with both the light DOM and Shadow DOM (if applicable).\n\n### Virtual Nodes\n\nTo support open Shadow DOM, axe-core has the ability to handle virtual nodes in [rule matches](#matches-function) and [check evaluate](#check-evaluate) functions. The full set of API methods for Shadow DOM can be found in the [API documentation](./API.md#virtual-dom-utilities), but the general structure for a virtualNode is as follows:\n\n```js\n{\n  actualNode: <HTMLElement>,\n  children: <Array>,\n  parent: <VirtualNode>,\n  shadowId: <String>,\n  attr: <Function>,\n  hasAttr: <Function>,\n  props: <Object>,\n}\n```\n\n- A virtualNode is an object containing an HTML DOM element (`actualNode`).\n- Children contains an array of child VirtualNodes.\n- Parent is the VirtualNode parent\n- The shadowID indicates whether the node is in an open shadow root and if it is, which one it is inside the boundary.\n- Attr is a function which returns the value of the passed in attribute, similar to `node.getAttribute()` (e.g. `vNode.attr('aria-label')`)\n- HasAttr is a function which returns true if the VirtualNode has the attribute, similar to `node.hasAttribute()` (e.g. `vNode.hasAttr('aria-label')`)\n- Props is an object of HTML DOM element properties. The general structure is as follows:\n  ```js\n  {\n    nodeName: <String>,\n    nodeType: <Number>,\n    id: <String>,\n    nodeValue: <String>\n  }\n  ```\n\n### Core Utilities\n\nCore Utilities are an internal library that provides axe with functionality used throughout its core processes. Most notably among these are the queue function and the DqElement constructor.\n\n#### Common Utility Functions\n\nIn addition to the ARIA lookupTable, there are also utility functions on the axe.commons.aria and axe.commons.dom namespaces:\n\n- `axe.commons.aria.implicitRole` - Get the implicit role for a given node\n- `axe.commons.aria.label` - Gets the accessible ARIA label text of a given element\n- `axe.commons.dom.isVisible` - Determine whether an element is visible\n\n#### Queue Function\n\nThe queue function creates an asynchronous \"queue\", list of functions to be invoked in parallel, but not necessarily returned in order. The queue function returns an object with the following methods:\n\n- `defer(func)` Defer a function that may or may not run asynchronously\n- `then(callback)` The callback to execute once all \"deferred\" functions have completed. Will only be invoked once.\n- `abort()` Abort the \"queue\" and prevent `then` function from firing\n\n#### DqElement Class\n\nThe DqElement is a \"serialized\" `HTMLElement`. It will calculate the CSS selector, grab the source outerHTML and offer an array for storing frame paths. The DqElement class takes the following parameters:\n\n- `Element` - `HTMLElement` The element to serialize\n- `Spec` - `Object` Properties to use in place of the element when instantiated on Elements from other frames\n\n```js\nvar firstH1 = document.getElementByTagName('h1')[0];\nvar dqH1 = new axe.utils.DqElement(firstH1);\n```\n\nElements returned by the DqElement class have the following methods and properties:\n\n- `selector` - `string` A unique CSS selector for the element\n- `source` - `string` The generated HTML source code of the element\n- `element` - `DOMNode` The element which this object is based off or the containing frame, used for sorting.\n- `toJSON()` - Returns an object containing the selector and source properties\n\n## Virtual DOM APIs\n\nNote: You shouldn’t need the Shadow DOM APIs below unless you’re working on the axe-core\nengine, as rules and checks already have `virtualNode` objects passed in. However, these APIs\nwill make it easier to work with the virtual DOM.\n\n### API Name: axe.utils.getFlattenedTree\n\n#### Description\n\nRecursively return an array containing the virtual DOM tree for the node specified, excluding comment nodes\nand shadow DOM nodes `<content>` and `<slot>`. This method will return a flattened tree containing both\nlight and shadow DOM, if applicable.\n\n#### Synopsis\n\n```js\nvar element = document.body;\naxe.utils.getFlattenedTree(element, shadowId);\n```\n\n#### Parameters\n\n- `node` – HTMLElement. The current HTML node for which you want a flattened DOM tree.\n- `shadowId` – string(optional). ID of the shadow DOM that is the closest shadow ancestor of the node\n\n#### Returns\n\nAn array of objects, where each object is a virtualNode:\n\n```js\n[\n  {\n    actualNode: body,\n    children: [virtualNodes],\n    shadowId: undefined\n  }\n];\n```\n\n### API Name: axe.utils.getNodeFromTree\n\n#### Description\n\nRecursively return a single node from a virtual DOM tree. This is commonly used in rules and checks where the node is readily available without querying the DOM.\n\n#### Synopsis\n\n```js\naxe.utils.getNodeFromTree(axe._tree[0], node);\n```\n\n#### Parameters\n\n- `vNode` – object. The flattened DOM tree to fetch a virtual node from\n- `node` – HTMLElement. The HTML DOM node for which you need a virtual representation\n\n#### Returns\n\nA virtualNode object:\n\n```js\n{\n  actualNode: div,\n  children: [virtualNodes],\n  shadowId: undefined\n}\n```\n\n## Test Utilities\n\nAll tests must support open Shadow DOM, so we created some test utilities to make this easier.\n\n### Test Util Name: MockCheckContext\n\nCreate a check context for mocking and resetting data and relatedNodes in tests.\n\n#### Synopsis\n\n```js\ndescribe('region', function () {\n  var fixture = document.getElementById('fixture');\n\n  var checkContext = new axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true when all content is inside the region', function () {\n    assert.isTrue(checks.region.evaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._relatedNodes.length, 0);\n  });\n});\n```\n\n#### Parameters\n\nNone\n\n#### Returns\n\nAn object containing the data, relatedNodes, and a way to reset them.\n\n```js\n{\n  data: (){},\n  relatedNodes: (){},\n  reset: (){}\n}\n```\n\n### Test Util Name: shadowSupport\n\nProvides an API for determining Shadow DOM v0 and v1 support in tests. For example: PhantomJS doesn't have Shadow DOM support, while some browsers do.\n\n#### Synopsis\n\n```js\n(axe.testUtils.shadowSupport.v1 ? it : xit)(\n  'should test Shadow tree content',\n  function () {\n    // The rest of the shadow DOM test\n  }\n);\n```\n\n#### Parameters\n\nNone\n\n#### Returns\n\nAn object containing booleans for the following Shadow DOM supports: `v0`, `v1`, or `undefined`.\n\n### Test Util Name: fixtureSetup\n\nMethod for injecting content into a fixture and caching the flattened DOM tree (light and Shadow DOM together).\n\n#### Synopsis\n\n```js\nit(\n  'should return true if there is only one ' +\n    type +\n    ' element with the same name',\n  function () {\n    axe.testUtils.fixtureSetup(\n      '<input type=\"' +\n        type +\n        '\" id=\"target\" name=\"uniqueyname\">' +\n        '<input type=\"' +\n        type +\n        '\" name=\"differentname\">'\n    );\n\n    var node = fixture.querySelector('#target');\n    assert.isTrue(check.evaluate.call(checkContext, node));\n  }\n);\n```\n\n#### Parameters\n\n- `content` – Node|String. Stuff to go into the fixture (html or DOM node)\n\n#### Returns\n\nAn HTML Element for the fixture\n\n### Test Util Name: checkSetup\n\nCreate check arguments.\n\n#### Synopsis\n\n```js\nit('should return true when all content is inside the region', function () {\n  var checkArgs = checkSetup(\n    '<div id=\"target\"><div role=\"main\"><a href=\"a.html#mainheader\">Click Here</a><div><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div></div></div>'\n  );\n\n  assert.isTrue(checks.region.evaluate.apply(checkContext, checkArgs));\n  assert.equal(checkContext._relatedNodes.length, 0);\n});\n```\n\n#### Parameters\n\n- `content` – String|Node. Stuff to go into the fixture (html or node)\n- `options` – Object. Options argument for the check (optional, default: {})\n- `target` – String. Target for the check, CSS selector (default: '#target')\n\n#### Returns\n\nAn array with the DOM Node, options and virtualNode\n\n```js\n[node, options, virtualNode];\n```\n\n## Using Rule Generation CLI\n\nAxe provides a CLI for generating the necessary files and configuration assets for authoring a rule.\n\nTo invoke the rule generator, run:\n\n```sh\nnpm run rule-gen\n```\n\nThe CLI acts a wizard, by asking a series of questions related to generation of the rule, for example:\n\n```sh\n- What is the name of the RULE? (Eg: aria-valid): sample-rule\n- Does the RULE need a MATCHES file to be created?: Yes\n- Would you like to create a CHECK for the RULE?: No\n- Would you like to create UNIT test files? Yes\n- Would you like to create INTEGRATION test files? Yes\n```\n\nUpon answering of which the assets are created in the respective directories.\n"
  },
  {
    "path": "doc/examples/chrome-debugging-protocol/README.md",
    "content": "# axe-chrome-debugging-protocol-example\n\nThis (very minimal) example demonstrates how to use `axe-core` with the [Chrome Debugging Protocol](https://chromedevtools.github.io/devtools-protocol/).\n\nThe example does not have feature parity with [`axe-webdriverjs`](https://github.com/dequelabs/axe-webdriverjs), and does not run on `<iframe>`s.\n\n## To run the example\n\n- Ensure Node v8+ is installed and on `PATH`\n- Move to the `doc/examples/chrome-debugging-protocol` directory\n- Run `npm install`\n- Run `google-chrome --headless --remote-debugging-port=9222`. If you don't have a `google-chrome` binary, you can alias one with `alias google-chrome='/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome'` on OSX.\n- Run `node axe-cdp.js http://www.deque.com` to run `axe-core` via Puppeteer against http://www.deque.com and output results to the terminal\n"
  },
  {
    "path": "doc/examples/chrome-debugging-protocol/axe-cdp.js",
    "content": "const CDP = require('chrome-remote-interface');\nconst axeCore = require('axe-core');\nconst assert = require('assert');\nconst { parse: parseURL } = require('url');\n\n// Cheap URL validation\nconst isValidURL = input => {\n  const u = parseURL(input);\n  return u.protocol && u.host;\n};\n\nconst example = async url => {\n  // eslint-disable-next-line new-cap\n  const client = await CDP();\n  const { Runtime: runtime, Page: page } = client;\n\n  let results;\n\n  try {\n    await page.enable();\n    await runtime.enable();\n\n    await page.navigate({ url });\n\n    // This function is injected into the browser and is responsible for\n    // running `axe-core`.\n    const browserCode = () => {\n      /* eslint-env browser */\n      return new Promise((resolve, reject) => {\n        const axe = window.axe;\n        if (!axe) {\n          throw new Error('Unable to find axe-core');\n        }\n\n        // Finally, run axe-core\n        axe\n          .run()\n          // For some reason, when resolving with an object, CDP ignores\n          // its value (`results.result.value` is undefined). By\n          // `JSON.stringify()`ing it, we can `JSON.parse()` it later on\n          // and return a valid results set.\n          .then(res => JSON.stringify(res))\n          .then(resolve)\n          .catch(reject);\n      });\n    };\n\n    // Inject axe-core\n    await runtime.evaluate({\n      expression: axeCore.source\n    });\n\n    // Run axe-core\n    const ret = await runtime.evaluate({\n      expression: `(${browserCode})()`,\n      awaitPromise: true\n    });\n\n    // re-parse\n    results = JSON.parse(ret.result.value);\n  } catch (err) {\n    // Ensure we close the client before exiting the fn\n    client.close();\n    throw err;\n  }\n  client.close();\n  return results;\n};\n\n// node axe-cdp.js <url>\nconst url = process.argv[2];\nassert(isValidURL(url), 'Invalid URL');\n\nexample(url)\n  .then(results => {\n    console.log(results);\n  })\n  .catch(err => {\n    console.error('Error running axe-core:', err.message);\n    process.exit(1);\n  });\n"
  },
  {
    "path": "doc/examples/chrome-debugging-protocol/package.json",
    "content": "{\n  \"name\": \"axe-cdp\",\n  \"private\": true,\n  \"scripts\": {\n    \"test\": \"echo 'No test specified.'\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"chrome-remote-interface\": \"^0.31.3\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/code-patterns.md",
    "content": "# Code Pattern Examples\n\nQuick-reference examples for axe-core coding conventions.\n\n## Default Export at Top\n\n```javascript\n// GOOD: Default export right after imports\nimport { getRole } from '../../commons/aria';\nimport { isVisible } from '../../commons/dom';\n\nexport default function myFunction(node, options) {\n  // function body\n}\n\n// BAD: Export buried at bottom of file\nimport { getRole } from '../../commons/aria';\n\nfunction myFunction(node, options) {\n  // body\n}\n\n// ... more code ...\n\nexport default myFunction; // Too far from top\n```\n\n## Return Early Pattern\n\n```javascript\n// GOOD: Main path left-aligned, edge cases exit early\nexport default function processValue(value) {\n  if (!value) {\n    return null;\n  }\n\n  if (value.length < 3) {\n    throw new Error('Value too short');\n  }\n\n  const normalized = normalize(value);\n  const result = transform(normalized);\n  return result;\n}\n\n// BAD: Nested conditionals\nexport default function processValue(value) {\n  let result;\n  if (value) {\n    if (value.length >= 3) {\n      const normalized = normalize(value);\n      result = transform(normalized);\n    } else {\n      throw new Error('Value too short');\n    }\n  } else {\n    result = null;\n  }\n  return result;\n}\n```\n\n## Import Restrictions\n\n```javascript\n// GOOD: commons importing from core/utils via index\nimport { getNodeFromTree } from '../../core/utils';\n\n// GOOD: commons importing other commons directly\nimport getExplicitRole from '../aria/get-explicit-role';\n\n// BAD: core/utils importing from commons — NEVER DO THIS\nimport { isDisabled } from '../../commons/forms';\n\n// BAD: importing from index in core/utils — use direct path\nimport { someUtil } from './index'; // Use: import someUtil from './some-util';\n```\n\n## JSDoc Comments\n\n### Standard function\n\n```javascript\n/**\n * Determines if an element is a native select element\n * @method isNativeSelect\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if select\n * @returns {Boolean}\n */\nimport nodeLookup from '../../core/utils/node-lookup';\n\nfunction isNativeSelect(node) {\n  const { vNode } = nodeLookup(node);\n  const nodeName = vNode.props.nodeName;\n  return nodeName === 'select';\n}\n```\n\n### Check evaluate function\n\n```javascript\n/**\n * Check if an element's `role` attribute uses any abstract role values.\n *\n * Abstract roles are taken from the `ariaRoles` standards object from the roles `type` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all abstract roles</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element uses an `abstract` role. False otherwise.\n */\nfunction abstractroleEvaluate(node, options, virtualNode) {\n  // implementation\n}\n```\n\n## Virtual Node vs HTMLElement\n\n```javascript\n// Use Virtual Node for attribute access and property reads\nfunction myCheck(node, options, virtualNode) {\n  const role = virtualNode.attr('role'); // Cached attribute access\n  const nodeName = virtualNode.props.nodeName;\n\n  // Only access real node when you need DOM APIs\n  const rect = node.getBoundingClientRect();\n  const rootNode = node.getRootNode();\n}\n\n// Convert ambiguous input using nodeLookup\nimport nodeLookup from '../../core/utils/node-lookup';\n\nfunction myFunction(nodeOrVirtual) {\n  const { vNode, domNode } = nodeLookup(nodeOrVirtual);\n  // vNode = VirtualNode, domNode = real DOM node\n}\n```\n"
  },
  {
    "path": "doc/examples/html-handlebars.md",
    "content": "# Turning violation nodes into readable HTML\n\nThe violations returns a list of rules that had failures. Each rule has a list of nodes that failed the rule. Each node may have failed for one or more reasons. This information is encoded in a set of structures that can be somewhat difficult to comprehend. This code shows how to turn a list of violations into a table and then for each node in a rule's nodes list, how to generate a summary of the reason(s) that rule failed.\n\nThe example uses handlebars templates but can be easily adapted to other formats\n\n## The JavaScript\n\n```js\nfunction helperItemIterator(items, template) {\n  var out = '';\n  if (items) {\n    for (var i = 0; i < items.length; i++) {\n      out += template(items[i]);\n    }\n  }\n  return out;\n}\nHandlebars.registerHelper('violations', function (items) {\n  return helperItemIterator(items, compiledRowTemplate);\n});\nHandlebars.registerHelper('related', function (items) {\n  return helperItemIterator(items, compiledRelatedNodeTemplate);\n});\nHandlebars.registerHelper('reasons', function (items) {\n  return helperItemIterator(items, compiledFailureTemplate);\n});\n\n// Setup handlebars templates\n\ncompiledRowTemplate = Handlebars.compile(rowTemplate.innerHTML);\ncompiledTableTemplate = Handlebars.compile(tableTemplate.innerHTML);\ncompiledRelatedListTemplate = Handlebars.compile(relatedListTemplate.innerHTML);\ncompiledRelatedNodeTemplate = Handlebars.compile(relatedNodeTemplate.innerHTML);\ncompiledFailureTemplate = Handlebars.compile(failureTemplate.innerHTML);\ncompiledReasonsTemplate = Handlebars.compile(reasonsTemplate.innerHTML);\n\nfunction messageFromRelatedNodes(relatedNodes) {\n  var retVal = '';\n  if (relatedNodes.length) {\n    var list = relatedNodes.map(function (node) {\n      return {\n        targetArrayString: JSON.stringify(node.target),\n        targetString: node.target.join(' ')\n      };\n    });\n    retVal += compiledRelatedListTemplate({ relatedNodeList: list });\n  }\n  return retVal;\n}\n\nfunction messagesFromArray(nodes) {\n  var list = nodes.map(function (failure) {\n    return {\n      message: failure.message.replace(/</gi, '&lt;').replace(/>/gi, '&gt;'),\n      relatedNodesMessage: messageFromRelatedNodes(failure.relatedNodes)\n    };\n  });\n  return compiledReasonsTemplate({ reasonsList: list });\n}\n\nfunction summary(node) {\n  var retVal = '';\n  if (node.any.length) {\n    retVal += '<h3 class=\"error-title\">Fix any of the following</h3>';\n    retVal += messagesFromArray(node.any);\n  }\n\n  var all = node.all.concat(node.none);\n  if (all.length) {\n    retVal += '<h3 class=\"error-title\">Fix all of the following</h3>';\n    retVal += messagesFromArray(all);\n  }\n  return retVal;\n}\n\n/*\n * This code will generate a table of the rules that failed including counts and links to the Deque University help\n * for each rule.\n *\n * When used, you should attach click handlers to the anchors in order to generate the details for each of the\n * violations for each rule.\n */\n\nif (results.violations.length) {\n  var violations = results.violations.map(function (rule, i) {\n    return {\n      impact: rule.impact,\n      help: rule.help.replace(/</gi, '&lt;').replace(/>/gi, '&gt;'),\n      bestpractice: rule.tags.indexOf('best-practice') !== -1,\n      helpUrl: rule.helpUrl,\n      count: rule.nodes.length,\n      index: i\n    };\n  });\n\n  html = compiledTableTemplate({ violationList: violations });\n}\n\n/*\n * To generate the human readable summary, call the `summary` function with the node. This will return HTML for that node.\n */\n\nreasonHtml = summary(node);\n```\n\n## The Handlebars Templates\n\n```handlebars\n<script id='rowTemplate' type='text/x-handlebars-template'>\n  <tr> <th scope=\"row\" class=\"help\"> <a href=\"javascript:;\" class=\"rule\"\n  data-index=\"{{index}}\">\n  {{{help}}}\n  </a> </th> <td scope=\"row\"> <a target=\"_blank\" href=\"{{helpUrl}}\">?</a> </td>\n  <td class=\"count\">\n  {{count}}\n  </td> <td class=\"impact\">\n  {{impact}}\n  </td> </tr>\n</script>\n<script id='tableTemplate' type='text/x-handlebars-template'>\n  <table> <tr> <th scope=\"col\">Description</th> <th scope=\"col\">Info</th> <th\n  scope=\"col\">Count</th> <th scope=\"col\">Impact</th> </tr>\n  {{#violations violationList}}{{/violations}}\n  </table>\n</script>\n<script id='relatedListTemplate' type='text/x-handlebars-template'>\n  <ul>Related Nodes:\n  {{#related relatedNodeList}}{{/related}}\n  </ul>\n</script>\n<script id='relatedNodeTemplate' type='text/x-handlebars-template'>\n  <li> <a href=\"javascript:;\" class=\"related-node\" data-element=\"{{targetArrayString}}\">\n  {{targetString}}\n  </a> </li>\n</script>\n<script id='reasonsTemplate' type='text/x-handlebars-template'>\n  <p class=\"summary\"> <ul class=\"failure-message\">\n  {{#reasons reasonsList}}{{/reasons}}\n  </ul> </p>\n</script>\n<script id='failureTemplate' type='text/x-handlebars-template'>\n  <li>\n  {{message}}\n  {{{relatedNodesMessage}}}\n  </li>\n</script>\n```\n"
  },
  {
    "path": "doc/examples/jasmine/README.md",
    "content": "# Jasmine README\n\nThis example demonstrates how to use axe with the Jasmine unit testing framework.\n\nThe unit test is in `spec/a11y.js`, and has two test cases: One that shows the\nexpected results from HTML with no errors, and one that shows the expected\nresult from HTML with a single error.\n\n## To configure the example\n\n- Node must be installed; please follow the directions at http://www.nodejs.org\n  to install it.\n- Move to the `doc/examples/jasmine` directory\n- `npm install` to install dependencies\n\n## To run the example\n\n- Move to the `doc/examples/jasmine` directory\n- `npm test` to run Jasmine\n\nYou should see output indicating that the tests ran successfully, with zero\nfailures.\n\n## To modify the example\n\nTo run the example on your own HTML, such as widgets or controls, insert the\nHTML into the document, retrieve the root element of your widget (with e.g.,\n`document.getElementById()`), and pass that as the first argument into a call\nto `axe.run`.\n\nThe third argument to the `axe.run` call should be the function to test\nthe results. The example is simply looking at the count of violations, but much\nmore detailed information is available if desired. The axe documentation\nshould be consulted for more details on customizing and analyzing calls to\n`axe.run`.\n"
  },
  {
    "path": "doc/examples/jasmine/karma.conf.js",
    "content": "module.exports = function (config) {\n  config.set({\n    basePath: '',\n\n    frameworks: ['jasmine'],\n\n    files: ['spec/**/*.js', 'node_modules/axe-core/axe.js'],\n\n    exclude: [],\n\n    preprocessors: {},\n\n    reporters: ['progress'],\n\n    port: 9876,\n\n    colors: true,\n\n    logLevel: config.LOG_INFO,\n\n    autoWatch: true,\n\n    browsers: ['ChromeHeadless'],\n\n    singleRun: true,\n\n    concurrency: Infinity\n  });\n};\n"
  },
  {
    "path": "doc/examples/jasmine/package.json",
    "content": "{\n  \"name\": \"axe-jasmine-example\",\n  \"description\": \"Axe Jasmine Example\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"David Sturley\",\n    \"organization\": \"Deque Systems, Inc.\",\n    \"url\": \"http://deque.com/\"\n  },\n  \"scripts\": {\n    \"test\": \"karma start karma.conf.js\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"karma\": \"^6.4.1\",\n    \"karma-chrome-launcher\": \"^3.1.1\",\n    \"karma-jasmine\": \"^5.1.0\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/jasmine/spec/a11y.js",
    "content": "/* global describe, it, expect, axe, document */\n\ndescribe('axe', function () {\n  'use strict';\n\n  document\n    .getElementsByTagName('body')[0]\n    .insertAdjacentHTML(\n      'beforeend',\n      '<div id=\"working\">' +\n        '<label for=\"has-label\">Label for this text field.</label>' +\n        '<input type=\"text\" id=\"has-label\">' +\n        '</div>' +\n        '<div id=\"broken\">' +\n        '<p>Not a label</p><input type=\"text\" id=\"no-label\">' +\n        '</div>'\n    );\n\n  it('should report that good HTML is good', function (done) {\n    var n = document.getElementById('working');\n    axe.run(n, function (err, result) {\n      expect(err).toBe(null);\n      expect(result.violations.length).toBe(0);\n      done();\n    });\n  });\n\n  it('should report that bad HTML is bad', function (done) {\n    var n = document.getElementById('broken');\n    axe.run(n, function (err, result) {\n      expect(err).toBe(null);\n      expect(result.violations.length).toBe(1);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "doc/examples/jest_react/.babelrc",
    "content": "{\n  \"presets\": [\n    [\n      \"@babel/preset-env\",\n      {\n        \"targets\": {\n          \"node\": \"current\"\n        }\n      }\n    ],\n    \"@babel/preset-react\"\n  ]\n}\n"
  },
  {
    "path": "doc/examples/jest_react/README.md",
    "content": "# Jest + React README\n\nThis example demonstrates how to use axe to test React components using the\nJest unit testing framework.\n\nThe unit test is in `link.test.js`, and has one test cases, showing how to run\naxe-core in Jest (using JSDOM and Enzyme).\n\n## To configure the example\n\n- Node must be installed; please follow the directions at http://www.nodejs.org\n  to install it.\n- Move to the `doc/examples/jest_react` directory\n- `npm install` to install dependencies\n\n## To run the example\n\n- Move to the `doc/examples/jest_react` directory\n- `npm test` to run Jest\n\nYou should see output indicating that the tests ran successfully, with zero\nfailures.\n\nNote: to work better with JSDOM (which has limited support for necessary DOM APIs),\nthe color-contrast and link-in-text-block rules have been disabled in this example.\nYou can test for these rules more reliably using full browser DOM integration\ntesting using [axe-webdriverjs](https://github.com/dequelabs/axe-webdriverjs).\n\n## To modify the example\n\nThis example can be modified to test components in other test frameworks as well. To use axe-core with JSDOM (Like Jest does), you will need to ensure that JSDOM variables are made available on the global object. An easy way to do this is to use [jsdom-global](https://github.com/rstacruz/jsdom-global).\n\nFor example, when running Mocha, you should require `jsdom-global/register`. The command for this is as follows:\n\n```sh\nmocha *.test.js --require jsdom-global/register\n```\n\n## Timeout Issues\n\nAxe-core is very fast for what it does, but when testing larger components, it may take a few seconds to complete. This is because axe will be running thousands of tests in a single call. When testing composite components, you may have to increase the timeout setting.\n"
  },
  {
    "path": "doc/examples/jest_react/link.js",
    "content": "import React from 'react';\n\nexport default class Link extends React.Component {\n  render() {\n    return <a href={this.props.page || '#'}>{this.props.children}</a>;\n  }\n}\n"
  },
  {
    "path": "doc/examples/jest_react/link.test.js",
    "content": "import React from 'react';\nimport { render } from '@testing-library/react';\nimport axe from 'axe-core';\n\nimport Link from './link';\n\ntest('Link has no axe violations', done => {\n  const { container } = render(\n    <Link page=\"http://www.axe-core.org\">axe website</Link>\n  );\n\n  const config = {\n    rules: {\n      'color-contrast': { enabled: false },\n      'link-in-text-block': { enabled: false }\n    }\n  };\n  axe.run(container, config, (err, { violations }) => {\n    expect(err).toBe(null);\n    expect(violations).toHaveLength(0);\n    done();\n  });\n});\n"
  },
  {
    "path": "doc/examples/jest_react/package.json",
    "content": "{\n  \"name\": \"axe-jest-react-example\",\n  \"description\": \"Axe Jest + React Example\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"Wilco Fiers\",\n    \"organization\": \"Deque Systems, Inc.\",\n    \"url\": \"http://deque.com/\"\n  },\n  \"scripts\": {\n    \"test\": \"jest\"\n  },\n  \"devDependencies\": {\n    \"@babel/preset-env\": \"^7.25.3\",\n    \"@babel/preset-react\": \"^7.24.7\",\n    \"@testing-library/jest-dom\": \"^6.4.8\",\n    \"@testing-library/react\": \"^16.0.0\",\n    \"jest\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^29.7.0\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\"\n  },\n  \"jest\": {\n    \"testEnvironment\": \"jsdom\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/jsdom/package.json",
    "content": "{\n  \"name\": \"axe-jsdom-example\",\n  \"description\": \"Axe JSDOM Example\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"scripts\": {\n    \"test\": \"mocha\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"jsdom\": \"^21.0.0\",\n    \"mocha\": \"^10.2.0\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/jsdom/test/a11y.js",
    "content": "/* global describe, it */\nconst axe = require('axe-core');\nconst jsdom = require('jsdom');\nconst { JSDOM } = jsdom;\nconst assert = require('assert');\n\ndescribe('axe', () => {\n  const { document } = new JSDOM(`<!DOCTYPE html>\n  <html lang=\"en\">\n    <head>\n      <title>JSDOM Example</title>\n    </head>\n    <body>\n      <div id=\"working\">\n        <label for=\"has-label\">Label for this text field.</label>\n        <input type=\"text\" id=\"has-label\">\n      </div>\n      <div id=\"broken\">\n        <p>Not a label</p><input type=\"text\" id=\"no-label\">\n      </div>\n    </body>\n  </html>`).window;\n  const config = {\n    rules: {\n      'color-contrast': { enabled: false }\n    }\n  };\n\n  it('reports that good HTML is good', async () => {\n    const node = document.getElementById('working');\n    const result = await axe.run(node, config);\n    assert.equal(result.violations.length, 0, 'Violations is not empty');\n  });\n\n  it('reports that bad HTML is bad', async () => {\n    const node = document.getElementById('broken');\n    const results = await axe.run(node, config);\n    assert.equal(results.violations.length, 1, 'Violations.length is not 1');\n  });\n\n  it('allows commons after axe.setup() is called', () => {\n    axe.setup(document);\n    const input = document.querySelector('input');\n    const role = axe.commons.aria.getRole(input);\n    assert.equal(role, 'textbox');\n    axe.teardown();\n  });\n});\n"
  },
  {
    "path": "doc/examples/mocha/README.md",
    "content": "# Mocha README\n\nThis example demonstrates how to use axe with the Mocha unit testing framework.\n\nThe unit test is in `test/a11y.js`, and has two test cases: One that shows the\nexpected results from HTML with no errors, and one that shows the expected\nresult from HTML with a single error.\n\n## To configure the example\n\n- Node must be installed; please follow the directions at http://www.nodejs.org\n  to install it.\n- Move to the `doc/examples/mocha` directory\n- `npm install` to install dependencies\n\n## To run the example\n\n- Move to the `doc/examples/mocha` directory\n- `npm test` to run Mocha\n\nYou should see output indicating that the tests ran successfully, with zero\nfailures.\n\n## To modify the example\n\nTo run the example on your own HTML, such as widgets or controls, insert the\nHTML into the document, retrieve the root element of your widget (with e.g.,\n`document.getElementById()`), and pass that as the first argument into a call\nto `axe.run`.\n\nThe third argument to the `axe.run` call should be the function to test\nthe results. The example is simply looking at the count of violations, but much\nmore detailed information is available if desired. The axe documentation\nshould be consulted for more details on customizing and analyzing calls to\n`axe.run`.\n"
  },
  {
    "path": "doc/examples/mocha/karma.conf.js",
    "content": "module.exports = function (config) {\n  config.set({\n    basePath: '',\n\n    frameworks: ['mocha', 'chai'],\n\n    files: ['test/**/*.js', 'node_modules/axe-core/axe.js'],\n\n    exclude: [],\n\n    preprocessors: {},\n\n    reporters: ['progress'],\n\n    port: 9876,\n\n    colors: true,\n\n    logLevel: config.LOG_INFO,\n\n    autoWatch: true,\n\n    browsers: ['ChromeHeadless'],\n\n    singleRun: true,\n\n    concurrency: Infinity\n  });\n};\n"
  },
  {
    "path": "doc/examples/mocha/package.json",
    "content": "{\n  \"name\": \"axe-mocha-example\",\n  \"description\": \"Axe Mocha Example\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"David Sturley\",\n    \"organization\": \"Deque Systems, Inc.\",\n    \"url\": \"http://deque.com/\"\n  },\n  \"scripts\": {\n    \"test\": \"karma start karma.conf.js\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"chai\": \"^4.3.7\",\n    \"karma\": \"^6.4.1\",\n    \"karma-chai\": \"^0.1.0\",\n    \"karma-chrome-launcher\": \"^3.1.1\",\n    \"karma-mocha\": \"^2.0.1\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/mocha/test/a11y.js",
    "content": "/* global describe, it, expect, axe, document, expect */\n\ndescribe('axe', function () {\n  'use strict';\n\n  document\n    .getElementsByTagName('body')[0]\n    .insertAdjacentHTML(\n      'beforeend',\n      '<div id=\"working\">' +\n        '<label for=\"has-label\">Label for this text field.</label>' +\n        '<input type=\"text\" id=\"has-label\">' +\n        '</div>' +\n        '<div id=\"broken\">' +\n        '<p>Not a label</p><input type=\"text\" id=\"no-label\">' +\n        '</div>'\n    );\n\n  it('should report that good HTML is good', function (done) {\n    var n = document.getElementById('working');\n    axe.run(n, function (err, result) {\n      expect(err).to.be.null;\n      expect(result.violations.length).to.equal(0);\n      done();\n    });\n  });\n\n  it('should report that bad HTML is bad', function (done) {\n    var n = document.getElementById('broken');\n    axe.run(n, function (err, result) {\n      expect(err).to.be.null;\n      expect(result.violations.length).to.equal(1);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "doc/examples/pr-review-patterns.md",
    "content": "# PR Review Patterns\n\nCommon feedback and anti-patterns observed in axe-core code reviews.\n\n## What Gets Called Out\n\n### 1. Missing Tests\n\n- Every behavior-changing code change needs unit tests\n- Rule changes need integration tests (HTML + JSON pair)\n- Shadow DOM test coverage required for relevant checks/rules\n\n### 2. Commit Message Format\n\n- Wrong type/scope\n- Not imperative present tense\n- Subject too long or capitalized\n- Missing issue reference in footer\n\n### 3. Import Violations\n\n- `core/utils` importing from `commons` (forbidden)\n- Using index imports where direct file paths are required\n- Importing node modules outside `core/imports`\n\n### 4. Code Style Issues\n\n- Not using return early pattern\n- Default export not at top of file\n- Nested conditionals when early return would work\n- Missing JSDoc comments\n\n### 5. Performance Concerns\n\n- Unnecessary DOM queries in loops\n- Not caching Virtual Node properties\n- Computing same value repeatedly instead of storing it\n\n### 6. Incomplete Results\n\n- Not returning `undefined` when a check can't determine the result\n- Missing `incomplete` message variants in check JSON\n- Not setting appropriate `this.data()` for incomplete cases\n\n### 7. Accessibility Edge Cases\n\n- Not considering all ARIA states\n- Missing edge cases for hidden elements\n- Not handling Shadow DOM properly\n\n## What Reviewers Love\n\n1. Comprehensive test coverage including edge cases\n2. Clear, detailed commit messages with motivation\n3. JSDoc comments that explain \"why\" not just \"what\"\n4. Performance-conscious code that caches appropriately\n5. Following existing patterns in similar files\n6. Integration tests that cover both pass and fail cases\n\n## Common PR Mistakes\n\n1. Not running `npm test` locally before pushing\n2. Not committing auto-generated `locales/_template.json` in the same commit as message source changes\n3. Changing multiple unrelated things in one PR — split refactoring from feature work\n4. Not updating integration tests when changing rule behavior\n5. `console.log` statements should not be committed\n6. Not handling `null` or `undefined` gracefully\n7. Hardcoding strings that should come from `standards/` data\n8. Changing public APIs without `BREAKING CHANGE` in commit footer\n"
  },
  {
    "path": "doc/examples/puppeteer/README.md",
    "content": "# axe-puppeteer-example\n\nThis (very minimal) example demonstrates how to use `axe-core` with [Puppeteer](https://github.com/GoogleChrome/puppeteer).\n\nThe example does not have feature parity with [`axe-webdriverjs`](https://github.com/dequelabs/axe-webdriverjs), and does not run on `<iframe>`s.\n\n## To run the example\n\n- Ensure Node v8+ is installed and on `PATH`\n- Move to the `doc/examples/puppeteer` directory\n- Run `npm install`\n- Run `node axe-puppeteer.js http://www.deque.com` to run `axe-core` via Puppeteer against http://www.deque.com and output results to the terminal\n"
  },
  {
    "path": "doc/examples/puppeteer/axe-puppeteer.js",
    "content": "const puppeteer = require('puppeteer');\nconst axeCore = require('axe-core');\nconst { parse: parseURL } = require('url');\nconst assert = require('assert');\n\n// Cheap URL validation\nconst isValidURL = input => {\n  const u = parseURL(input);\n  return u.protocol && u.host;\n};\n\n(async () => {\n  // node axe-puppeteer.js <url>\n  const url = process.argv[2];\n  assert(isValidURL(url), 'Invalid URL');\n\n  let browser;\n  let results;\n  try {\n    // Setup Puppeteer\n    browser = await puppeteer.launch();\n\n    // Get new page\n    const page = await browser.newPage();\n    await page.goto(url);\n\n    // Inject and run axe-core\n    const handle = await page.evaluateHandle(`\n\t\t\t// Inject axe source code\n\t\t\t${axeCore.source}\n\t\t\t// Run axe\n\t\t\taxe.run()\n\t\t`);\n\n    // Get the results from `axe.run()`.\n    results = await handle.jsonValue();\n    console.log(results);\n\n    // Destroy the handle & return axe results.\n    await handle.dispose();\n  } catch (err) {\n    // Ensure we close the puppeteer connection when possible\n    if (browser) {\n      await browser.close();\n    }\n\n    console.error('Error running axe-core:', err.message);\n    process.exit(1);\n  }\n\n  await browser.close();\n})();\n"
  },
  {
    "path": "doc/examples/puppeteer/package.json",
    "content": "{\n  \"name\": \"axe-puppeteer\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"axe-puppeteer.js\",\n  \"scripts\": {\n    \"test\": \"npm run test:url && npm run test:set-content\",\n    \"test:url\": \"node axe-puppeteer.js https://deque.com\",\n    \"test:set-content\": \"node set-content.js\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"puppeteer\": \"^19.8.2\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/puppeteer/set-content.js",
    "content": "const assert = require('assert');\nconst puppeteer = require('puppeteer');\nconst axe = require('axe-core');\n\n(async () => {\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n\n  await page.setContent(`\n    <html lang=\"en\">\n    <head>\n      <title>Test Page</title>\n    </head>\n    <body>\n      <main>\n        <h1>Hello World</h1>\n        <button id=\"empty-button\"></button>\n        <iframe title=\"iframe\" srcdoc=\"<button id='iframe-empty-button'></button>\"></iframe>\n      </main>\n    </body>\n    </html>\n  `);\n\n  await page.evaluate(axe.source);\n  const frames = page.frames();\n  for (let i = 0; i < frames.length; i++) {\n    await frames[i].evaluate(axe.source);\n  }\n\n  const results = await page.evaluate(`window.axe.run()`);\n  assert(results.violations.length);\n\n  await browser.close();\n})();\n"
  },
  {
    "path": "doc/examples/qunit/Gruntfile.js",
    "content": "module.exports = function (grunt) {\n  'use strict';\n\n  grunt.loadNpmTasks('grunt-contrib-qunit');\n\n  grunt.initConfig({\n    qunit: {\n      all: ['test/**/*.html'],\n      options: {\n        puppeteer: {\n          args: ['--disable-web-security', '--allow-file-access-from-files']\n        },\n        timeout: 10000\n      }\n    }\n  });\n};\n"
  },
  {
    "path": "doc/examples/qunit/README.md",
    "content": "# QUnit README\n\nThis example demonstrates how to use axe with the QUnit unit testing framework.\n\nThe unit test is in `test/a11y.js`, and has two test cases: One that shows the\nexpected results from HTML with no errors, and one that shows the expected\nresult from HTML with a single error.\n\n## To configure the example\n\n- Node must be installed; please follow the directions at http://www.nodejs.org\n  to install it.\n- `npm install -g grunt-cli` to install the Grunt task runner (may need to be\n  run with `sudo` on Unix or as Administrator on Windows)\n- Move to the `doc/examples/qunit` directory\n- `npm install` to install dependencies\n\n## To run the example\n\n- Move to the `doc/examples/qunit` directory\n- `grunt qunit` to run QUnit\n\nYou should see output indicating that the tests ran successfully, with zero\nfailures.\n\n## To modify the example\n\nTo run the example on your own HTML, such as widgets or controls, insert the\nHTML into the document, retrieve the root element of your widget (with e.g.,\n`document.getElementById()`), and pass that as the first argument into a call\nto `axe.run`.\n\nThe third argument to the `axe.run` call should be the function to test\nthe results. The example is simply looking at the count of violations, but much\nmore detailed information is available if desired. The axe documentation\nshould be consulted for more details on customizing and\nanalyzing calls to `axe.run`.\n"
  },
  {
    "path": "doc/examples/qunit/package.json",
    "content": "{\n  \"name\": \"axe-qunit-example\",\n  \"description\": \"Axe QUnit Example\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"author\": {\n    \"name\": \"David Sturley\",\n    \"organization\": \"Deque Systems, Inc.\",\n    \"url\": \"http://deque.com/\"\n  },\n  \"scripts\": {\n    \"test\": \"grunt qunit\"\n  },\n  \"devDependencies\": {\n    \"axe-core\": \"^4.6.2\",\n    \"grunt\": \"^1.6.1\",\n    \"grunt-contrib-qunit\": \"^10.1.1\",\n    \"puppeteer\": \"^23.1.0\",\n    \"qunit\": \"^2.22.0\"\n  }\n}\n"
  },
  {
    "path": "doc/examples/qunit/test/a11y.js",
    "content": "/* global QUnit, document, axe */\n\nQUnit.module('axe');\n\nQUnit.test('should report that good HTML is good', function (assert) {\n  var n = document.getElementById('working');\n  assert.expect(2);\n\n  var done = assert.async();\n  axe.run(n, function (err, result) {\n    assert.equal(err, null);\n    assert.equal(result.violations.length, 0);\n    done();\n  });\n});\n\nQUnit.test('should report that bad HTML is bad', function (assert) {\n  var n = document.getElementById('broken');\n  assert.expect(2);\n\n  var done = assert.async();\n  axe.run(n, function (err, result) {\n    assert.equal(err, null);\n    assert.equal(result.violations.length, 1);\n    done();\n  });\n});\n"
  },
  {
    "path": "doc/examples/qunit/test/test.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Basic Test Suite</title>\n    <!-- Load local QUnit. -->\n    <link\n      rel=\"stylesheet\"\n      href=\"../node_modules/qunit/qunit/qunit.css\"\n      media=\"screen\"\n    />\n    <script src=\"../node_modules/qunit/qunit/qunit.js\"></script>\n    <!-- Load local lib and tests. -->\n    <script src=\"../node_modules/axe-core/axe.min.js\"></script>\n    <script src=\"a11y.js\"></script>\n  </head>\n  <body>\n    <div id=\"qunit\"></div>\n    <div id=\"qunit-fixture\"></div>\n    <div id=\"working\">\n      <label for=\"labelfld\">Label for this text field.</label>\n      <input type=\"text\" id=\"labelfld\" />\n    </div>\n    <div id=\"broken\">\n      <p>Label for this text field.</p>\n      <input type=\"text\" id=\"nolabelfld\" />\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "doc/examples/rule-check-templates.md",
    "content": "# Rule & Check JSON Templates\n\nQuick-reference templates for creating axe-core rules and checks.\n\n## Rule JSON\n\n```json\n{\n  \"id\": \"aria-input-field-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"textbox\\\"], [role=\\\"combobox\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\"cat.aria\", \"wcag2a\", \"wcag412\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA input field has an accessible name\",\n    \"help\": \"ARIA input fields must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": [\"no-implicit-explicit-label\"]\n}\n```\n\n**Key properties:**\n\n| Property    | Description                                                   |\n| ----------- | ------------------------------------------------------------- |\n| `selector`  | CSS selector for candidate elements                           |\n| `matches`   | Optional function to further filter elements                  |\n| `all`       | All checks must pass                                          |\n| `any`       | At least one check must pass                                  |\n| `none`      | All checks must fail (for the rule to pass)                   |\n| `impact`    | `\"minor\"`, `\"moderate\"`, `\"serious\"`, `\"critical\"`            |\n| `enabled`   | Defaults to `true`; set `false` for disabled/deprecated rules |\n| `tags`      | Grouping: `wcag2a`, `wcag2aa`, `best-practice`, `cat.*`, etc. |\n| `pageLevel` | `true` if rule should only run on the top-level window        |\n\n## Check JSON\n\n```json\n{\n  \"id\": \"aria-allowed-attr\",\n  \"evaluate\": \"aria-allowed-attr-evaluate\",\n  \"options\": {\n    \"validTreeRowAttrs\": [\"aria-posinset\", \"aria-setsize\"]\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA attributes are used correctly for the defined role\",\n      \"fail\": {\n        \"singular\": \"ARIA attribute is not allowed: ${data.values}\",\n        \"plural\": \"ARIA attributes are not allowed: ${data.values}\"\n      },\n      \"incomplete\": \"Check that there is no problem if the ARIA attribute is ignored: ${data.values}\"\n    }\n  }\n}\n```\n\n**Message templates:**\n\n- Use `${data.property}` for dynamic values set via `this.data()`\n- Support singular/plural variants via object with `singular`/`plural` keys\n- `locales/_template.json` is auto-generated by `npm run build` — do not edit it manually, but commit the regenerated file alongside source changes\n\n## Check Evaluate Function\n\n```javascript\nexport default function myCheckEvaluate(node, options, virtualNode) {\n  // node: HTMLElement\n  // options: from check JSON or runtime config\n  // virtualNode: Virtual Node representation\n\n  // Early returns for edge cases\n  if (someEdgeCase) {\n    return true; // Pass\n  }\n\n  // Compute what you need\n  const relevantData = computeStuff(virtualNode);\n\n  // Set data for messages (optional)\n  // this.data() sets an arbitrary object passed to message templates\n  if (relevantData.hasIssues) {\n    this.data({\n      messageKey: 'someIssue', // Selects which message variant to use (e.g., singular/plural)\n      values: relevantData.issueList // ${data.values} in templates resolves arrays\n    });\n  }\n\n  // Return boolean or undefined\n  if (cannotDetermine) {\n    this.data({ reason: 'could not determine background color' });\n    return undefined; // Incomplete\n  }\n\n  return isValid; // true = pass, false = fail\n}\n```\n\n**Return values:**\n\n- `true` — check passed\n- `false` — check failed\n- `undefined` — incomplete (cannot determine result)\n\n**`this.data()` usage:**\n\n- Sets an arbitrary data object that gets passed to message templates via `${data.propertyName}`\n- `messageKey` — selects which message variant to use (e.g., `singular`/`plural`)\n- Any other keys are available in templates as `${data.keyName}` — arrays are automatically joined\n"
  },
  {
    "path": "doc/examples/test-examples.js",
    "content": "const { readdirSync, statSync } = require('fs');\nconst { join } = require('path');\nconst execa = require('execa');\nconst exampleDirs = readdirSync(__dirname)\n  .map(dir => join(__dirname, dir))\n  .filter(dir => statSync(dir).isDirectory());\n\nconst config = { stdio: 'inherit', shell: true };\n\n// run npm install in parallel\nasync function install(dir) {\n  await execa('npm install', { cwd: dir, ...config });\n\n  // override the package version of axe-core with the local version.\n  // this allows the examples to stay examples while allowing us to\n  // test them against our changes\n  return await execa('npm install --no-save file:..\\\\/..\\\\/..\\\\/', {\n    cwd: dir,\n    ...config\n  });\n}\n\n// run tests synchronously so we can see which one threw an error\nfunction test(dir) {\n  return execa('npm test', { cwd: dir, ...config });\n}\n\nPromise.all(exampleDirs.map(install))\n  .then(async () => {\n    for (const dir of exampleDirs) {\n      await test(dir);\n    }\n\n    // Return successful exit\n    process.exit();\n  })\n  .catch(err => {\n    console.error(err);\n    process.exit(1);\n  });\n"
  },
  {
    "path": "doc/examples/test-patterns.md",
    "content": "# Test Pattern Examples\n\nQuick-reference examples for axe-core test conventions.\n\n## Unit Test — Commons Function\n\n```javascript\ndescribe('text.sanitize', function () {\n  it('should collapse whitespace and trim', function () {\n    assert.equal(axe.commons.text.sanitize('\\thi\\t'), 'hi');\n    assert.equal(axe.commons.text.sanitize('\\t\\nhi \\t'), 'hi');\n    assert.equal(axe.commons.text.sanitize('hello\\u00A0there'), 'hello there');\n  });\n\n  it('should accept null', function () {\n    assert.equal(axe.commons.text.sanitize(null), '');\n  });\n});\n```\n\n## Unit Test — Check Evaluate\n\n```javascript\ndescribe('aria-allowed-attr', function () {\n  const fixture = document.getElementById('fixture');\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if all ARIA attributes are allowed', function () {\n    const vNode = queryFixture(\n      '<div role=\"textbox\" aria-placeholder=\"foo\" id=\"target\"></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, vNode.actualNode, {}, vNode)\n    );\n  });\n});\n```\n\n## Shadow DOM Test\n\n```javascript\nit('should work with Shadow DOM', function () {\n  const vNode = queryShadowFixture(\n    '<div id=\"host\"></div>',\n    '<div role=\"button\" id=\"target\">Test</div>'\n  );\n  // Test your function against the shadow DOM content\n});\n```\n\n## Integration Test — Rule\n\nEach rule change requires an HTML + JSON pair in `test/integration/rules/<rule-name>/` (mocha-hosted) or `test/integration/full/<rule-name>/` (full HTML page). Virtual-rules tests in `test/virtual-rules/` should also be updated or created for appropriate rules. See sections below for details.\n\n### HTML file (`aria-allowed-attr.html`)\n\n```html\n<div role=\"textbox\" aria-placeholder=\"foo\" id=\"pass1\">Valid</div>\n<div role=\"button\" aria-placeholder=\"invalid\" id=\"fail1\">Invalid</div>\n```\n\n### JSON file (`aria-allowed-attr.json`)\n\n```json\n{\n  \"description\": \"aria-allowed-attr test\",\n  \"rule\": \"aria-allowed-attr\",\n  \"violations\": [[\"#fail1\"]],\n  \"passes\": [[\"#pass1\"]]\n}\n```\n\nIDs use axe selector array format. For elements inside iframes:\n\n```json\n{\n  \"violations\": [[\"iframe\", \"#fail-inside-iframe\"]]\n}\n```\n\n### Full-page integration tests\n\nFor rules that need a complete HTML page (e.g., landmark rules, page-level rules), use `test/integration/full/` instead. These tests run against a full HTML document rather than injected fragments.\n\n### Virtual rules tests\n\nRules that can run without a DOM (using only virtual nodes) should have tests in `test/virtual-rules/`. These tests verify that rules work correctly with `axe.run()` on serialized node data.\n"
  },
  {
    "path": "doc/frame-messenger.md",
    "content": "# Frame Messenger\n\nAxe frameMessenger can be used to configure how axe-core communicates information between frames. By default, axe-core uses `window.postMessage()`. Since other scripts on the page may also use `window.postMessage`, axe-core's use of it can sometimes disrupt page functionality. This can be avoided by providing `axe.frameMessenger()` a way to communicate to frames that does not use `window.postMessage`. If reliable and secure frame communication is not possible, use [axe.runPartial and axe.finishRun instead](run-partial.md).\n\nTools like browser extensions and testing environments often have different channels through which information can be communicated. `axe.frameMessenger` must be set up in **every frame** axe-core is included.\n\n```js\naxe.frameMessenger({\n  // Called to initialize message handling\n  open(topicHandler) {\n    // Map data from the bridge to topicHandler\n    function subscriber(frameWin, data, response) {\n      // Data deserializations / validation / etc. here\n      topicHandler(data, response);\n    }\n    // Start listening for \"axe-core\" events\n    const unsubscribe = bridge.subscribe('axe-core', subscriber);\n    // Tell axe how to close the connection if it needs to\n    return unsubscribe;\n  },\n\n  // Called when axe needs to send a message to another frame\n  async post(frameWindow, data, replyHandler) {\n    // Send a message to another frame for \"axe-core\"\n    const replies = bridge.send(frameWindow, 'axe-core', data);\n    // Async handling replies as they come back\n    for await (let data of replies) {\n      replyHandler(data);\n    }\n  }\n});\n```\n\n## axe.frameMessenger({ open })\n\n`open` is a function that should set up the communication channel with iframes. It is passed a `topicHandler` function, which must be called when a message is received from another frame.\n\nThe `topicHandler` function takes two arguments: the `data` object and a callback function that is called when the subscribed listener completes. The `data` object is exclusively passed data that can be serialized with `JSON.stringify()`, which depending on the system may need to be used.\n\nThe `open` function can `return` an optional `close` function. Axe-core will only ever have one frameMessenger open at a time. The `close` function is called when another frameMessenger is registered.\n\n## axe.frameMessenger({ post })\n\n`post` is a function that dictates how axe-core communicates with frames. It is passed three arguments: `frameWindow`, which is the frame's `contentWindow`, the `data` object, and a `replyHandler` that must be called when responses are received. To inform axe-core that no message was sent, return `false`. This informs axe-core not to await for the ping to time out.\n\nCurrently, axe-core will only require `replyHandler` to be called once, so promises can also be used here. This may change in the future, so it is preferable to make it possible for `replyHandler` to be called multiple times. Some axe-core [plugins](plugins.md) may rely on this feature.\n\nA second frameMessenger feature available to plugins, but not used in axe-core by default is to reply to a reply. This works by passing `replyHandler` a `responder` callback as a second argument. This requires a different setup in which callbacks are stored based on their `channelId` property.\n\n```js\n// store handlers based on channelId\nconst channels = {};\n\naxe.frameMessenger({\n  post(frameWindow, data, replyHandler) {\n    // Store the handler so it can be called later\n    channels[data.channelId] = replyHandler;\n    // Send a message to the frame\n    bridge.send(frameWindow, data);\n  },\n\n  open(topicHandler) {\n    function subscriber(frameWin, data) {\n      const { channelId, message, keepalive } = data;\n      // Create a callback to invoke on a reply.\n      const responder = createResponder(frameWin, channelId);\n\n      // If there is a topic, pass it to the axe supplied topic-handler\n      if (data.topic) {\n        topicHandler(data, responder);\n\n        // If there is a replyHandler stored, invoke it\n      } else if (channels[channelId]) {\n        const replyHandler = channels[channelId];\n        replyHandler(message, keepalive, responder);\n\n        // Clean up replyHandler, as no further messages are expected\n        if (!keepalive) delete channels[channelId];\n      }\n    }\n\n    // Start listening for \"axe-core\" events\n    const unsubscribe = bridge.subscribe('axe-core', subscriber);\n    // Tell axe how to close the connection if it needs to\n    return unsubscribe;\n  }\n});\n\n// Return a function to be called when a reply is received\nfunction createResponder(frameWin, channelId) {\n  return function responder(message, keepalive, replyHandler) {\n    // Store the new reply handler, possibly replacing a previous one\n    //   to avoid receiving a message twice.\n    channels[channelId] = replyHandler;\n    // Send a message to the frame\n    bridge.send(frameWin, { channelId, message, keepalive });\n  };\n}\n```\n\n## Error handling & Timeouts\n\nIf for some reason the frameMessenger fails to open, post, or close you should not throw an error. Axe-core will handle missing results by reporting them in the `frame-tested` rule. It should not be possible for the `topicHandler` and `replyHandler` callbacks to throw an error. If this happens, please file an issue.\n\nAxe-core has a built-in timeout mechanism, which pings frames to see if they respond before instructing them to run. There is no retry behavior in axe-core, which assumes that whatever channel is used is stable. If this isn't the case, this will need to be built into frameMessenger.\n\nThe `message` passed to responder may be an `Error`. If axe-core passes an `Error`, this should be propagated \"as is\". If this is not possible because the message needs to be serialized, a new `Error` object must be constructed as part of deserialization.\n\n### pingWaitTime\n\nWhen axe-core tests frames, it first sends a ping to that frame, to check that the frame has a compatible version of axe-core in it that can respond to the message. If it gets no response, that frame will be skipped in the test. Axe-core does this to avoid a situation where it waits the full frame timeout, just to find out the frame didn't have axe-core in it in the first place.\n\nIn situations where communication between frames can be slow, it may be necessary to increase the ping timeout. This can be done with the `pingWaitTime` option. By default, this is 500ms. This can be configured in the following way:\n\n```js\nconst results = await axe.run(context, { pingWaitTime: 1000 });\n```\n\nIt is possible to skip this ping altogether by setting `pingWaitTime` to `0`. This can slightly speed up performance, but should only be used when long wait times for unresponsive frames are avoided. Axe-core handles timeout errors the same way it handles any other frame communication errors. Therefore if a custom frame messenger has a timeout, it can inform axe by calling `replyHandler` with an `Error` object.\n"
  },
  {
    "path": "doc/issue_impact.md",
    "content": "# Issue Impacts\n\nAxe-core assigns an impact according to our assessment of the likely impact of an issue on users with disabilities who would be affected by this issue. In any given context, the actual impact for the user could be lower; in some instances, it could be higher. For this reason, we encourage users of tools to evaluate each individual issue and assess the impact in the context of their application or content.\n\nFor a list of all rules and their associated impacts, see [rule-descriptions.md](./rule-descriptions.md)\n\n## Definitions\n\n### Minor\n\nConsidered to be a nuisance or an annoyance bug. Prioritize fixing it if the fix only takes a few minutes and the developer is working on the same screen/feature at the same time, otherwise the issue should not be prioritized. Will still get in the way of compliance if not fixed.\n\n### Moderate\n\nResults in some difficulty for people with disabilities, but will generally not prevent them from accessing fundamental features or content. Users may be frustrated and abandon non-critical workflows. Prioritize fixing in this release, if there are no higher-priority issues. Will get in the way of compliance if not fixed.\n\n### Serious\n\nResults in serious barriers for people with disabilities, and will partially or fully prevent them from accessing fundamental features or content. People relying on assistive technologies will experience significant frustration and may abandon essential workflows. Issues falling under this category are major problems, and remediation should be a priority.\n\n### Critical\n\nResults in blocked content for people with disabilities, and will definitely prevent them from accessing fundamental features or content. This type of issue puts your organization at risk. Prioritize fixing as soon as possible, within the week if possible. Remediation should be a top priority.\n"
  },
  {
    "path": "doc/plugins.md",
    "content": "# Plugins\n\nAxe implements a general purpose plugin system that takes advantage of the cross-domain iframe capabilities of axe and allows for adding functionality that extends the axe library outside of its core automated accessibility auditing realm.\n\nThe plugin system was initially designed to support functionality like highlighting of elements but has also been utilized for a variety of tasks including implementing functionality that aids with manual accessibility auditing.\n\n## What is a plugin?\n\nPlugins can be viewed as a registry of tools. The plugins themselves are registered with axe and then allow themselves for the registration of plugin instances.\n\nLet's walk through a plugin implementation as an example to illustrate how plugins and plugin instances work.\n\n### A simple \"act\" plugin\n\nThe act plugin will simply perform an action of some sort inside every iframe on the page. An example of how such a plugin might be used is to implement an instance of this plugin that performs highlighting of all of the elements of a particular type on the page.\n\nPlugins currently support two functions: a \"run\" function and a \"collect\" function. Together these functions can be combined to implement complex behaviors on top of the axe system.\n\nTo create such a plugin, we need to implement the `run` function and the command that registers and executes the `run` function within each iframe on the page containing axe. Let's look at what a noop implementation of this run function would look like:\n\n#### Basic plugin\n\n```js\naxe.registerPlugin({\n  id: 'doStuff',\n  run: function (id, action, options, callback) {\n    var frames;\n    var q = axe.utils.queue();\n    var that = this;\n    frames = axe.utils.toArray(document.querySelectorAll('iframe, frame'));\n\n    frames.forEach(function (frame) {\n      q.defer(function (done) {\n        axe.utils.sendCommandToFrame(\n          frame,\n          {\n            options: options,\n            command: 'run-doStuff',\n            parameter: id,\n            action: action\n          },\n          function () {\n            done();\n          }\n        );\n      });\n    });\n\n    if (!options.context.length) {\n      q.defer(function (done) {\n        that._registry[id][action].call(\n          that._registry[id],\n          document,\n          options,\n          done\n        );\n      });\n    }\n    q.then(callback);\n  },\n  commands: [\n    {\n      id: 'run-doStuff',\n      callback: function (data, callback) {\n        return axe.plugins.doStuff.run(\n          data.parameter,\n          data.action,\n          data.options,\n          callback\n        );\n      }\n    }\n  ]\n});\n```\n\nLooking at the code, you will see the following things:\n\n1. The plugin contains an id. This id is then used to access the plugin and its implementations.\n2. The plugin is registered with axe (in each iframe) using the `axe.registerPlugin()` function.\n3. The plugin registers the \"run\" function and the \"commands\" with the axe system. This allows plugin implementations to be registered with the plugin, and to be executed. It also registers handlers for each of the commands within each of the iframes, so that the plugin can coordinate with itself across the iframe boundaries.\n\nWhen the caller wants to call a plugin instance, it does so by calling the plugin's \"run\" function in the top level document and passing the id of the plugin instance it would like to call, which plugin instance action it would like to call, the options and a callback function.\n\nThe plugin takes this information and sends the same instructions to its implementation in each iframe by communicating to its own command(s) using the axe utility function `axe.utils.sendCommandToFrame()`.\n\nThe plugin waits for the commands in the iframes to complete and then executes its instances' action function within the current document.\n\nIn the above implementation, the axe promise utility `axe.utils.queue()` is used to coordinate the asynchronous handling of communication across iframes.\n\nThe command handler callback runs the plugin's run function within each iframe. This essentially operates like a recursive call to the run function for the plugin within each iframe.\n\nOnce all the iframes' run functions have been executed, the callback is called. This essentially operates as a recursive \"return\" up the iframe hierarchy until at the top document, the actual callback function is executed. This can be leveraged to pass data back up the iframe hierarchy back to the caller (but this is a more advanced topic).\n\n#### Basic plugin instance\n\nLet's implement a basic plugin instance to see how this works. This instance will implement a \"highlight\" function (to place a basic frame around the bounding box of an element on each iframe on a page)\n\n```js\nvar highlight = {\n  id: 'highlight',\n  highlighter: new Highlighter(),\n  run: function (contextNode, options, done) {\n    var that = this;\n    Array.prototype.slice\n      .call(contextNode.querySelectorAll(options.selector))\n      .forEach(function (node) {\n        that.highlighter.highlight(node, options);\n      });\n    done();\n  },\n  cleanup: function (done) {\n    this.highlighter.clear();\n    done();\n  }\n};\n\naxe.plugins.doStuff.add(highlight);\n```\n\nAbove you can see the implementation of a `doStuff` \"highlight\" instance (the actual highlighting code is not included so as to simplify the example and is left as an exercise for the reader). Plugin instances have an id (which is used to address them), a cleanup function and any number of private or action members. The doStuff `add()` function is called to register this instance with the plugin (notice that we did not have to implement this add function, axe did that for us). In this case, the action is called \"run\", so after registration, this instance can be called by calling `axe.plugins.doStuff.run('highlight', 'run', options, callback);` in the top-level iframe on the page.\n\nThe cleanup functions for all plugin instances are called when the `axe.cleanup()` function is called. Note that this cleanup function will automatically call all the cleanup functions for all the plugin instances in all iframes on the page.\n"
  },
  {
    "path": "doc/projects.md",
    "content": "# Projects that use axe-core®\n\nAdd your project/integration to this file and submit a pull request.\n\n## Deque Projects\n\n1. [Axe® DevTools](https://www.deque.com/axe/devtools/)\n1. [Axe Auditor](https://www.deque.com/axe/auditor/)\n1. [Axe Monitor](https://www.deque.com/axe/monitor/)\n1. [Axe Chrome Extension](https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd)\n1. [Axe Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/)\n1. [axe-core/react](https://www.npmjs.com/package/@axe-core/react)\n1. [axe-core/cli](https://www.npmjs.com/package/@axe-core/cli)\n1. [axe-core/webdriverjs](https://www.npmjs.com/package/@axe-core/webdriverjs)\n1. [axe-core/webdriverio](https://www.npmjs.com/package/@axe-core/webdriverio)\n1. [axe-core/playwright](https://www.npmjs.com/package/@axe-core/playwright)\n1. [axe-core/puppeteer](https://www.npmjs.com/package/@axe-core/puppeteer)\n1. [axe-core-selenium (Java)](https://search.maven.org/artifact/com.deque.html.axe-core/selenium)\n1. [axe-core-capybara (Ruby)](https://rubygems.org/gems/axe-core-capybara/)\n1. [axe-core-cucumber (Ruby)](https://rubygems.org/gems/axe-core-cucumber/)\n1. [axe-core-rspec (Ruby)](https://rubygems.org/gems/axe-core-rspec/)\n1. [axe-core-selenium (Ruby)](https://rubygems.org/gems/axe-core-selenium/)\n1. [axe-core-watir (Ruby)](https://rubygems.org/gems/axe-core-watir/)\n\n## Community Projects\n\n1. [Lighthouse](https://github.com/GoogleChrome/lighthouse)\n1. [Webhint.io](https://webhint.io/)\n1. [Accessibility Insights for Web](https://accessibilityinsights.io/)\n1. [Web Accessibility Checker for Visual Studio](https://visualstudiogallery.msdn.microsoft.com/3aabefab-1681-4fea-8f95-6a62e2f0f1ec)\n1. [Axe + Azure Pipelines samples](https://github.com/microsoft/axe-pipelines-samples)\n1. [Ace, by DAISY](https://daisy.github.io/ace)\n1. [Axe Selenium Python](https://github.com/mozilla-services/axe-selenium-python)\n1. [gulp-axe-webdriver](https://github.com/felixzapata/gulp-axe-webdriver)\n1. [Ghost-Axe](https://www.npmjs.com/package/ghost-axe)\n1. [ember-a11y-testing](https://www.npmjs.com/package/ember-a11y-testing)\n1. [Protractor accessibility plugin](https://github.com/angular/protractor-accessibility-plugin)\n1. [storybook-addon-a11y](https://github.com/storybooks/storybook/tree/master/addons/a11y)\n1. [Intern](https://github.com/theintern/intern-a11y)\n1. [Protractor-axe-report Plugin](https://github.com/E1Edatatracker/protractor-axe-report-plugin)\n1. [Cypress-axe](https://github.com/avanslaars/cypress-axe)\n1. [Taiko-accessibility](https://github.com/andreas-ku/taiko-accessibility)\n1. [Jest-axe](https://github.com/nickcolley/jest-axe)\n1. [axe audit runner for CrawlKit](https://github.com/crawlkit/runner-axe)\n1. [Selenium IDE axe Extension](https://github.com/bkardell/selenium-ide-axe)\n1. [Axegrinder](https://github.com/claflamme/axegrinder)\n1. [Rocket Validator](https://rocketvalidator.com)\n1. [axe Reports](https://github.com/louis-reed/axe-reports)\n1. [axe WebdriverIO](https://github.com/snagi/axe-webdriverio)\n1. [axe-TestCafe](https://github.com/helen-dikareva/axe-testcafe)\n1. [Web Audit University of Nebraska-Lincoln](https://webaudit.unl.edu/)\n1. [Vorlon.js Remote Debugger](https://github.com/MicrosoftDX/Vorlonjs)\n1. [Terra Toolkit](https://github.com/cerner/terra-toolkit)\n1. [axe-sarif-converter](https://github.com/microsoft/axe-sarif-converter)\n1. [Selenium.Axe for .NET](https://github.com/TroyWalshProf/SeleniumAxeDotnet)\n1. [vue-axe](https://github.com/vue-a11y/vue-axe-next)\n1. [a11y-sitechecker](https://github.com/forsti0506/a11y-sitechecker)\n1. [Parcel](https://parcel.io)\n1. [mitm-play](https://github.com/mitmplay/mitm-play)\n1. [Sitebulb](https://sitebulb.com/hints/accessibility/)\n1. [Webatool](https://github.com/balajihambeere/webatool)\n1. [A11y Audit Elixir](https://github.com/angelikatyborska/a11y-audit-elixir)\n1. [Accented](https://accented.dev)\n1. [Happo](https://happo.io)\n\nAxe® and axe-core® are registered trademarks of Deque Systems Inc.\n"
  },
  {
    "path": "doc/pull-request-checklist.md",
    "content": "# axe-core — PR Checklist\n\nComplete all applicable items before opening a PR. For items that do not apply to this PR (for example, tests for a `chore`-only change), mark them as \"N/A\" and follow `doc/code-submission-guidelines.md`. Reviewers will not merge until all required boxes are checked.\n\n## Code\n\n- [ ] Default export is at the top of the file, immediately after imports\n- [ ] Return early pattern used — no nested conditionals where an early return works\n- [ ] DocBlock comments where appropriate (especially for public/exported APIs), per `doc/code-submission-guidelines.md`\n- [ ] No `console.log` statements committed\n- [ ] No hardcoded ARIA/HTML lists — queried from `standards/` via `commons/standards`\n- [ ] Imports follow directory restrictions — especially no `commons` from `core/utils`\n\n## Tests\n\n- [ ] Unit tests cover all code paths (`null`/`undefined` input handling only needed for public API functions)\n- [ ] Integration test HTML + JSON pair added/updated for any rule changes — use `integration/rules` for mocha-hosted tests or `integration/full` when the rule requires a full HTML page\n- [ ] Virtual-rules tests updated or created for appropriate rules\n- [ ] Shadow DOM test case added where relevant\n- [ ] Code built with `npm run build` before testing\n- [ ] All applicable tests pass locally: `npm test` (unit), `test:integration`, `test:virtual-rules`, `test:act`, `test:apg`, `test:examples`, `test:node`, `test:jsdom`\n\n## Formatting & Build\n\n- [ ] `npm run fmt` passes (Prettier)\n- [ ] `npm run eslint` passes\n- [ ] `npm run build` run — generated artifacts that are tracked in git (for example `locales/_template.json`) are committed in the same commit as source changes\n\n## Commits\n\n- [ ] All commits follow Angular format: `<type>(<scope>): <subject>`\n- [ ] Subject is imperative, lowercase, no trailing period, ≤100 chars total\n- [ ] Commit body explains motivation (not just what changed)\n- [ ] Footer includes issue reference: `Closes issue: #123` or full URL\n- [ ] Breaking changes avoided — prefer supporting both old and new formats simultaneously. If unavoidable, document in footer: `BREAKING CHANGE: description`\n\n## Docs\n\n- [ ] `doc/rule-descriptions.md` is auto-generated by `npm run build` — verify it regenerates correctly for new rules\n- [ ] `doc/API.md` and `axe.d.ts` updated for API changes\n- [ ] `locales/_template.json` updated if messages changed\n- [ ] `CHANGELOG.md` updated with migration guide for breaking changes\n"
  },
  {
    "path": "doc/release-and-support.md",
    "content": "# Axe-core Release & Support Policy\n\n## Release Cadence\n\nAxe-core is used in many [projects and environments](./projects.md). Not all of these are able to upgrade at a rapid pace. Because of this, updates to axe-core are limited in the following ways. For details on what types of changes can come in these releases, see [backward compatibility](./backwards-compatibility-doc.md).\n\n- **Major releases**: Major axe-core releases likely include breaking changes, and provide opportunities for Deque to remove previously deprecated features. As an absolute minimum, there will be a 12 month period between major releases of axe-core, except if this is necessary for security.\n\n- **Minor Releases**: Axe-core strives to publish three to five minor releases every year. There will be at least 3 weeks between each minor release, except if this is necessary for security.\n\n- **Patch Releases**: There are no restrictions on the number of patches released for axe-core.\n\nFor all major and minor releases a milestone will be created at least three weeks ahead of the release. The axe-core team strives to complete all issues in that milestone, although on occasion issues lower in the milestone may be dropped, and high priority issues may be added. Axe-core will observe a code freeze one week before releasing a major or minor version. Only documentation, metadata, and localizations may be modified during code freeze.\n\n## Security Updates\n\nOnce a new major or minor version is released, the prior versions will no longer be updated, except if this is necessary for security. Security updates will be provided for major and minor versions **up to 18 months** old. For example, if version 4.0.0 was released 17 months ago, and a security issue is discovered a new patch will be released on the 4.0 line. However if 3.5.0 was released 20 months ago, even if 3.5.2 was released 17 months ago, a security patch for the 3.5 line may **not** be provided.\n\nThe axe-core team considers security its very highest priority. While security vulnerabilities in axe-core are rare, they do happen. When they do, resolving the issue becomes our highest priority. Any commitments made prior to the discovery may be dropped.\n\n## Recommended Use of Versions\n\nIn order to ensure the best quality from axe-core, we encourage everyone to regularly upgrade their version of axe-core, to try to stay as close to the latest release as possible. Depending on how axe-core is used, upgrading to a new minor or major version may result in new issues getting reported. To handle this, we recommend that you set aside time to upgrade your version of axe-core at least twice a year.\n\nAdditionally, we recommend that you always use the latest patch version of whichever minor version you are on. For example if you are using axe-core 3.5.5, and 3.5.6 is released it is best to upgrade immediately. Patch releases of axe-core should not find new issues, although they occasionally resolve issues in the case of false positives.\n\nEnsuring that you always use the latest available patch version of axe-core on any minor line guarantees that you are using the most secure version. This minor line must have been released within the last 18 months. See [security updates](#security-updates).\n"
  },
  {
    "path": "doc/rule-descriptions.md",
    "content": "<!--- This file is automatically generated using build/configure.js --->\n\n# Rule Descriptions\n\n## Table of Contents\n\n- [WCAG 2.0 Level A & AA Rules](#wcag-20-level-a--aa-rules)\n- [WCAG 2.1 Level A & AA Rules](#wcag-21-level-a--aa-rules)\n- [WCAG 2.2 Level A & AA Rules](#wcag-22-level-a--aa-rules)\n- [Best Practices Rules](#best-practices-rules)\n- [WCAG 2.x level AAA rules](#wcag-2x-level-aaa-rules)\n- [Experimental Rules](#experimental-rules)\n- [Deprecated Rules](#deprecated-rules)\n\n## WCAG 2.0 Level A & AA Rules\n\n| Rule ID                                                                                                                           | Description                                                                                                                                          | Impact   | Tags                                                                                                                                                   | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/)                                                                                                                                                                                                |\n| :-------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [area-alt](https://dequeuniversity.com/rules/axe/4.11/area-alt?application=RuleDescription)                                       | Ensure &lt;area&gt; elements of image maps have alternative text                                                                                     | Critical | cat.text-alternatives, wcag2a, wcag244, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.2.4.4, EN-9.4.1.2, ACT, RGAAv4, RGAA-1.1.2 | failure, needs&nbsp;review | [c487ae](https://act-rules.github.io/rules/c487ae)                                                                                                                                                                                                                 |\n| [aria-allowed-attr](https://dequeuniversity.com/rules/axe/4.11/aria-allowed-attr?application=RuleDescription)                     | Ensure an element&apos;s role supports its ARIA attributes                                                                                           | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure, needs&nbsp;review | [5c01ea](https://act-rules.github.io/rules/5c01ea)                                                                                                                                                                                                                 |\n| [aria-braille-equivalent](https://dequeuniversity.com/rules/axe/4.11/aria-braille-equivalent?application=RuleDescription)         | Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent                                                               | Serious  | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2                                                                                                      | needs&nbsp;review          |                                                                                                                                                                                                                                                                    |\n| [aria-command-name](https://dequeuniversity.com/rules/axe/4.11/aria-command-name?application=RuleDescription)                     | Ensure every ARIA button, link and menuitem has an accessible name                                                                                   | Serious  | cat.aria, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.9.1                                                               | failure, needs&nbsp;review | [97a4e1](https://act-rules.github.io/rules/97a4e1)                                                                                                                                                                                                                 |\n| [aria-conditional-attr](https://dequeuniversity.com/rules/axe/4.11/aria-conditional-attr?application=RuleDescription)             | Ensure ARIA attributes are used as described in the specification of the element&apos;s role                                                         | Serious  | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure                    | [5c01ea](https://act-rules.github.io/rules/5c01ea)                                                                                                                                                                                                                 |\n| [aria-deprecated-role](https://dequeuniversity.com/rules/axe/4.11/aria-deprecated-role?application=RuleDescription)               | Ensure elements do not use deprecated roles                                                                                                          | Minor    | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure                    | [674b10](https://act-rules.github.io/rules/674b10)                                                                                                                                                                                                                 |\n| [aria-hidden-body](https://dequeuniversity.com/rules/axe/4.11/aria-hidden-body?application=RuleDescription)                       | Ensure aria-hidden=&quot;true&quot; is not present on the document body.                                                                             | Critical | cat.aria, wcag2a, wcag131, wcag412, EN-301-549, EN-9.1.3.1, EN-9.4.1.2, RGAAv4, RGAA-10.8.1                                                            | failure                    |                                                                                                                                                                                                                                                                    |\n| [aria-hidden-focus](https://dequeuniversity.com/rules/axe/4.11/aria-hidden-focus?application=RuleDescription)                     | Ensure aria-hidden elements are not focusable nor contain focusable elements                                                                         | Serious  | cat.name-role-value, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-10.8.1                                                         | failure, needs&nbsp;review | [6cfa84](https://act-rules.github.io/rules/6cfa84)                                                                                                                                                                                                                 |\n| [aria-input-field-name](https://dequeuniversity.com/rules/axe/4.11/aria-input-field-name?application=RuleDescription)             | Ensure every ARIA input field has an accessible name                                                                                                 | Serious  | cat.aria, wcag2a, wcag412, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.1.1                                                               | failure, needs&nbsp;review | [e086e5](https://act-rules.github.io/rules/e086e5)                                                                                                                                                                                                                 |\n| [aria-meter-name](https://dequeuniversity.com/rules/axe/4.11/aria-meter-name?application=RuleDescription)                         | Ensure every ARIA meter node has an accessible name                                                                                                  | Serious  | cat.aria, wcag2a, wcag111, EN-301-549, EN-9.1.1.1, RGAAv4, RGAA-11.1.1                                                                                 | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [aria-progressbar-name](https://dequeuniversity.com/rules/axe/4.11/aria-progressbar-name?application=RuleDescription)             | Ensure every ARIA progressbar node has an accessible name                                                                                            | Serious  | cat.aria, wcag2a, wcag111, EN-301-549, EN-9.1.1.1, RGAAv4, RGAA-11.1.1                                                                                 | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [aria-prohibited-attr](https://dequeuniversity.com/rules/axe/4.11/aria-prohibited-attr?application=RuleDescription)               | Ensure ARIA attributes are not prohibited for an element&apos;s role                                                                                 | Serious  | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure, needs&nbsp;review | [5c01ea](https://act-rules.github.io/rules/5c01ea)                                                                                                                                                                                                                 |\n| [aria-required-attr](https://dequeuniversity.com/rules/axe/4.11/aria-required-attr?application=RuleDescription)                   | Ensure elements with ARIA roles have all required ARIA attributes                                                                                    | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure                    | [4e8ab6](https://act-rules.github.io/rules/4e8ab6)                                                                                                                                                                                                                 |\n| [aria-required-children](https://dequeuniversity.com/rules/axe/4.11/aria-required-children?application=RuleDescription)           | Ensure elements with an ARIA role that require child roles contain them                                                                              | Critical | cat.aria, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.1                                                                                  | failure, needs&nbsp;review | [bc4a75](https://act-rules.github.io/rules/bc4a75), [ff89c9](https://act-rules.github.io/rules/ff89c9)                                                                                                                                                             |\n| [aria-required-parent](https://dequeuniversity.com/rules/axe/4.11/aria-required-parent?application=RuleDescription)               | Ensure elements with an ARIA role that require parent roles are contained by them                                                                    | Critical | cat.aria, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.1                                                                                  | failure                    | [ff89c9](https://act-rules.github.io/rules/ff89c9)                                                                                                                                                                                                                 |\n| [aria-roles](https://dequeuniversity.com/rules/axe/4.11/aria-roles?application=RuleDescription)                                   | Ensure all elements with a role attribute use a valid value                                                                                          | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure                    | [674b10](https://act-rules.github.io/rules/674b10)                                                                                                                                                                                                                 |\n| [aria-tab-name](https://dequeuniversity.com/rules/axe/4.11/aria-tab-name?application=RuleDescription)                             | Ensure every ARIA tab node has an accessible name                                                                                                    | Serious  | cat.aria, wcag2a, wcag412, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-7.1.1                                                                | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [aria-toggle-field-name](https://dequeuniversity.com/rules/axe/4.11/aria-toggle-field-name?application=RuleDescription)           | Ensure every ARIA toggle field has an accessible name                                                                                                | Serious  | cat.aria, wcag2a, wcag412, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-7.1.1                                                                | failure, needs&nbsp;review | [e086e5](https://act-rules.github.io/rules/e086e5)                                                                                                                                                                                                                 |\n| [aria-tooltip-name](https://dequeuniversity.com/rules/axe/4.11/aria-tooltip-name?application=RuleDescription)                     | Ensure every ARIA tooltip node has an accessible name                                                                                                | Serious  | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2                                                                                                      | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [aria-valid-attr-value](https://dequeuniversity.com/rules/axe/4.11/aria-valid-attr-value?application=RuleDescription)             | Ensure all ARIA attributes have valid values                                                                                                         | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure, needs&nbsp;review | [6a7281](https://act-rules.github.io/rules/6a7281)                                                                                                                                                                                                                 |\n| [aria-valid-attr](https://dequeuniversity.com/rules/axe/4.11/aria-valid-attr?application=RuleDescription)                         | Ensure attributes that begin with aria- are valid ARIA attributes                                                                                    | Critical | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                                  | failure                    | [5f99a7](https://act-rules.github.io/rules/5f99a7)                                                                                                                                                                                                                 |\n| [blink](https://dequeuniversity.com/rules/axe/4.11/blink?application=RuleDescription)                                             | Ensure &lt;blink&gt; elements are not used                                                                                                           | Serious  | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j, TTv5, TT2.b, EN-301-549, EN-9.2.2.2, RGAAv4, RGAA-13.8.1                             | failure                    |                                                                                                                                                                                                                                                                    |\n| [button-name](https://dequeuniversity.com/rules/axe/4.11/button-name?application=RuleDescription)                                 | Ensure buttons have discernible text                                                                                                                 | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.9.1                       | failure, needs&nbsp;review | [97a4e1](https://act-rules.github.io/rules/97a4e1), [m6b1q3](https://act-rules.github.io/rules/m6b1q3)                                                                                                                                                             |\n| [bypass](https://dequeuniversity.com/rules/axe/4.11/bypass?application=RuleDescription)                                           | Ensure each page has at least one mechanism for a user to bypass navigation and jump straight to the content                                         | Serious  | cat.keyboard, wcag2a, wcag241, section508, section508.22.o, TTv5, TT9.a, EN-301-549, EN-9.2.4.1, RGAAv4, RGAA-12.7.1                                   | needs&nbsp;review          | [cf77f2](https://act-rules.github.io/rules/cf77f2), [047fe0](https://act-rules.github.io/rules/047fe0), [b40fd1](https://act-rules.github.io/rules/b40fd1), [3e12e1](https://act-rules.github.io/rules/3e12e1), [ye5d6e](https://act-rules.github.io/rules/ye5d6e) |\n| [color-contrast](https://dequeuniversity.com/rules/axe/4.11/color-contrast?application=RuleDescription)                           | Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds                                       | Serious  | cat.color, wcag2aa, wcag143, TTv5, TT13.c, EN-301-549, EN-9.1.4.3, ACT, RGAAv4, RGAA-3.2.1                                                             | failure, needs&nbsp;review | [afw4f7](https://act-rules.github.io/rules/afw4f7), [09o5cg](https://act-rules.github.io/rules/09o5cg)                                                                                                                                                             |\n| [definition-list](https://dequeuniversity.com/rules/axe/4.11/definition-list?application=RuleDescription)                         | Ensure &lt;dl&gt; elements are structured correctly                                                                                                  | Serious  | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.3                                                                             | failure                    |                                                                                                                                                                                                                                                                    |\n| [dlitem](https://dequeuniversity.com/rules/axe/4.11/dlitem?application=RuleDescription)                                           | Ensure &lt;dt&gt; and &lt;dd&gt; elements are contained by a &lt;dl&gt;                                                                              | Serious  | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.3                                                                             | failure                    |                                                                                                                                                                                                                                                                    |\n| [document-title](https://dequeuniversity.com/rules/axe/4.11/document-title?application=RuleDescription)                           | Ensure each HTML document contains a non-empty &lt;title&gt; element                                                                                 | Serious  | cat.text-alternatives, wcag2a, wcag242, TTv5, TT12.a, EN-301-549, EN-9.2.4.2, ACT, RGAAv4, RGAA-8.5.1                                                  | failure                    | [2779a5](https://act-rules.github.io/rules/2779a5)                                                                                                                                                                                                                 |\n| [duplicate-id-aria](https://dequeuniversity.com/rules/axe/4.11/duplicate-id-aria?application=RuleDescription)                     | Ensure every id attribute value used in ARIA and in labels is unique                                                                                 | Critical | cat.parsing, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-8.2.1                                                                               | needs&nbsp;review          | [3ea0c8](https://act-rules.github.io/rules/3ea0c8)                                                                                                                                                                                                                 |\n| [form-field-multiple-labels](https://dequeuniversity.com/rules/axe/4.11/form-field-multiple-labels?application=RuleDescription)   | Ensure form field does not have multiple label elements                                                                                              | Moderate | cat.forms, wcag2a, wcag332, TTv5, TT5.c, EN-301-549, EN-9.3.3.2, RGAAv4, RGAA-11.2.1                                                                   | needs&nbsp;review          |                                                                                                                                                                                                                                                                    |\n| [frame-focusable-content](https://dequeuniversity.com/rules/axe/4.11/frame-focusable-content?application=RuleDescription)         | Ensure &lt;frame&gt; and &lt;iframe&gt; elements with focusable content do not have tabindex=-1                                                      | Serious  | cat.keyboard, wcag2a, wcag211, TTv5, TT4.a, EN-301-549, EN-9.2.1.1, RGAAv4, RGAA-7.3.2                                                                 | failure, needs&nbsp;review | [akn7bn](https://act-rules.github.io/rules/akn7bn)                                                                                                                                                                                                                 |\n| [frame-title-unique](https://dequeuniversity.com/rules/axe/4.11/frame-title-unique?application=RuleDescription)                   | Ensure &lt;iframe&gt; and &lt;frame&gt; elements contain a unique title attribute                                                                    | Serious  | cat.text-alternatives, wcag2a, wcag412, TTv5, TT12.d, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-2.2.1                                                       | needs&nbsp;review          | [4b1c6c](https://act-rules.github.io/rules/4b1c6c)                                                                                                                                                                                                                 |\n| [frame-title](https://dequeuniversity.com/rules/axe/4.11/frame-title?application=RuleDescription)                                 | Ensure &lt;iframe&gt; and &lt;frame&gt; elements have an accessible name                                                                             | Serious  | cat.text-alternatives, wcag2a, wcag412, section508, section508.22.i, TTv5, TT12.d, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-2.1.1                          | failure, needs&nbsp;review | [cae760](https://act-rules.github.io/rules/cae760)                                                                                                                                                                                                                 |\n| [html-has-lang](https://dequeuniversity.com/rules/axe/4.11/html-has-lang?application=RuleDescription)                             | Ensure every HTML document has a lang attribute                                                                                                      | Serious  | cat.language, wcag2a, wcag311, TTv5, TT11.a, EN-301-549, EN-9.3.1.1, ACT, RGAAv4, RGAA-8.3.1                                                           | failure                    | [b5c3f8](https://act-rules.github.io/rules/b5c3f8)                                                                                                                                                                                                                 |\n| [html-lang-valid](https://dequeuniversity.com/rules/axe/4.11/html-lang-valid?application=RuleDescription)                         | Ensure the lang attribute of the &lt;html&gt; element has a valid value                                                                              | Serious  | cat.language, wcag2a, wcag311, TTv5, TT11.a, EN-301-549, EN-9.3.1.1, ACT, RGAAv4, RGAA-8.4.1                                                           | failure                    | [bf051a](https://act-rules.github.io/rules/bf051a)                                                                                                                                                                                                                 |\n| [html-xml-lang-mismatch](https://dequeuniversity.com/rules/axe/4.11/html-xml-lang-mismatch?application=RuleDescription)           | Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page                                        | Moderate | cat.language, wcag2a, wcag311, EN-301-549, EN-9.3.1.1, ACT, RGAAv4, RGAA-8.3.1                                                                         | failure                    | [5b7ae0](https://act-rules.github.io/rules/5b7ae0)                                                                                                                                                                                                                 |\n| [image-alt](https://dequeuniversity.com/rules/axe/4.11/image-alt?application=RuleDescription)                                     | Ensure &lt;img&gt; elements have alternative text or a role of none or presentation                                                                  | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, TT7.b, EN-301-549, EN-9.1.1.1, ACT, RGAAv4, RGAA-1.1.1               | failure, needs&nbsp;review | [23a2a8](https://act-rules.github.io/rules/23a2a8)                                                                                                                                                                                                                 |\n| [input-button-name](https://dequeuniversity.com/rules/axe/4.11/input-button-name?application=RuleDescription)                     | Ensure input buttons have discernible text                                                                                                           | Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.9.1                       | failure, needs&nbsp;review | [97a4e1](https://act-rules.github.io/rules/97a4e1)                                                                                                                                                                                                                 |\n| [input-image-alt](https://dequeuniversity.com/rules/axe/4.11/input-image-alt?application=RuleDescription)                         | Ensure &lt;input type=&quot;image&quot;&gt; elements have alternative text                                                                           | Critical | cat.text-alternatives, wcag2a, wcag111, wcag412, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, EN-9.4.1.2, ACT, RGAAv4, RGAA-1.1.3 | failure, needs&nbsp;review | [59796f](https://act-rules.github.io/rules/59796f)                                                                                                                                                                                                                 |\n| [label](https://dequeuniversity.com/rules/axe/4.11/label?application=RuleDescription)                                             | Ensure every form element has a label                                                                                                                | Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.1.1                                 | failure, needs&nbsp;review | [e086e5](https://act-rules.github.io/rules/e086e5)                                                                                                                                                                                                                 |\n| [link-in-text-block](https://dequeuniversity.com/rules/axe/4.11/link-in-text-block?application=RuleDescription)                   | Ensure links are distinguished from surrounding text in a way that does not rely on color                                                            | Serious  | cat.color, wcag2a, wcag141, TTv5, TT13.a, EN-301-549, EN-9.1.4.1, RGAAv4, RGAA-10.6.1                                                                  | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [link-name](https://dequeuniversity.com/rules/axe/4.11/link-name?application=RuleDescription)                                     | Ensure links have discernible text                                                                                                                   | Serious  | cat.name-role-value, wcag2a, wcag244, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.2.4.4, EN-9.4.1.2, ACT, RGAAv4, RGAA-6.2.1   | failure, needs&nbsp;review | [c487ae](https://act-rules.github.io/rules/c487ae)                                                                                                                                                                                                                 |\n| [list](https://dequeuniversity.com/rules/axe/4.11/list?application=RuleDescription)                                               | Ensure that lists are structured correctly                                                                                                           | Serious  | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.1                                                                             | failure                    |                                                                                                                                                                                                                                                                    |\n| [listitem](https://dequeuniversity.com/rules/axe/4.11/listitem?application=RuleDescription)                                       | Ensure &lt;li&gt; elements are used semantically                                                                                                     | Serious  | cat.structure, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.3.1                                                                             | failure                    |                                                                                                                                                                                                                                                                    |\n| [marquee](https://dequeuniversity.com/rules/axe/4.11/marquee?application=RuleDescription)                                         | Ensure &lt;marquee&gt; elements are not used                                                                                                         | Serious  | cat.parsing, wcag2a, wcag222, TTv5, TT2.b, EN-301-549, EN-9.2.2.2, RGAAv4, RGAA-13.8.1                                                                 | failure                    |                                                                                                                                                                                                                                                                    |\n| [meta-refresh](https://dequeuniversity.com/rules/axe/4.11/meta-refresh?application=RuleDescription)                               | Ensure &lt;meta http-equiv=&quot;refresh&quot;&gt; is not used for delayed refresh                                                                   | Critical | cat.time-and-media, wcag2a, wcag221, TTv5, TT8.a, EN-301-549, EN-9.2.2.1, RGAAv4, RGAA-13.1.2                                                          | failure                    | [bc659a](https://act-rules.github.io/rules/bc659a), [bisz58](https://act-rules.github.io/rules/bisz58)                                                                                                                                                             |\n| [meta-viewport](https://dequeuniversity.com/rules/axe/4.11/meta-viewport?application=RuleDescription)                             | Ensure &lt;meta name=&quot;viewport&quot;&gt; does not disable text scaling and zooming                                                              | Moderate | cat.sensory-and-visual-cues, wcag2aa, wcag144, EN-301-549, EN-9.1.4.4, ACT, RGAAv4, RGAA-10.4.2                                                        | failure                    | [b4f0c3](https://act-rules.github.io/rules/b4f0c3)                                                                                                                                                                                                                 |\n| [nested-interactive](https://dequeuniversity.com/rules/axe/4.11/nested-interactive?application=RuleDescription)                   | Ensure interactive controls are not nested as they are not always announced by screen readers or can cause focus problems for assistive technologies | Serious  | cat.keyboard, wcag2a, wcag412, TTv5, TT6.a, EN-301-549, EN-9.4.1.2, RGAAv4, RGAA-7.1.1                                                                 | failure, needs&nbsp;review | [307n5z](https://act-rules.github.io/rules/307n5z)                                                                                                                                                                                                                 |\n| [no-autoplay-audio](https://dequeuniversity.com/rules/axe/4.11/no-autoplay-audio?application=RuleDescription)                     | Ensure &lt;video&gt; or &lt;audio&gt; elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio   | Moderate | cat.time-and-media, wcag2a, wcag142, TTv5, TT2.a, EN-301-549, EN-9.1.4.2, ACT, RGAAv4, RGAA-4.10.1                                                     | needs&nbsp;review          | [80f0bf](https://act-rules.github.io/rules/80f0bf)                                                                                                                                                                                                                 |\n| [object-alt](https://dequeuniversity.com/rules/axe/4.11/object-alt?application=RuleDescription)                                   | Ensure &lt;object&gt; elements have alternative text                                                                                                 | Serious  | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, EN-301-549, EN-9.1.1.1, RGAAv4, RGAA-1.1.6                                        | failure, needs&nbsp;review | [8fc3b6](https://act-rules.github.io/rules/8fc3b6)                                                                                                                                                                                                                 |\n| [role-img-alt](https://dequeuniversity.com/rules/axe/4.11/role-img-alt?application=RuleDescription)                               | Ensure [role=&quot;img&quot;] elements have alternative text                                                                                         | Serious  | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, ACT, RGAAv4, RGAA-1.1.1                      | failure, needs&nbsp;review | [23a2a8](https://act-rules.github.io/rules/23a2a8)                                                                                                                                                                                                                 |\n| [scrollable-region-focusable](https://dequeuniversity.com/rules/axe/4.11/scrollable-region-focusable?application=RuleDescription) | Ensure elements that have scrollable content are accessible by keyboard in Safari                                                                    | Serious  | cat.keyboard, wcag2a, wcag211, wcag213, TTv5, TT4.a, EN-301-549, EN-9.2.1.1, EN-9.2.1.3, RGAAv4, RGAA-7.3.2                                            | failure                    | [0ssw9k](https://act-rules.github.io/rules/0ssw9k)                                                                                                                                                                                                                 |\n| [select-name](https://dequeuniversity.com/rules/axe/4.11/select-name?application=RuleDescription)                                 | Ensure select element has an accessible name                                                                                                         | Critical | cat.forms, wcag2a, wcag412, section508, section508.22.n, TTv5, TT5.c, EN-301-549, EN-9.4.1.2, ACT, RGAAv4, RGAA-11.1.1                                 | failure, needs&nbsp;review | [e086e5](https://act-rules.github.io/rules/e086e5)                                                                                                                                                                                                                 |\n| [server-side-image-map](https://dequeuniversity.com/rules/axe/4.11/server-side-image-map?application=RuleDescription)             | Ensure that server-side image maps are not used                                                                                                      | Minor    | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f, TTv5, TT4.a, EN-301-549, EN-9.2.1.1, RGAAv4, RGAA-1.1.4                           | needs&nbsp;review          |                                                                                                                                                                                                                                                                    |\n| [summary-name](https://dequeuniversity.com/rules/axe/4.11/summary-name?application=RuleDescription)                               | Ensure summary elements have discernible text                                                                                                        | Serious  | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a, TTv5, TT6.a, EN-301-549, EN-9.4.1.2                                                 | failure, needs&nbsp;review |                                                                                                                                                                                                                                                                    |\n| [svg-img-alt](https://dequeuniversity.com/rules/axe/4.11/svg-img-alt?application=RuleDescription)                                 | Ensure &lt;svg&gt; elements with an img, graphics-document or graphics-symbol role have accessible text                                              | Serious  | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a, TTv5, TT7.a, EN-301-549, EN-9.1.1.1, ACT, RGAAv4, RGAA-1.1.5                      | failure, needs&nbsp;review | [7d6734](https://act-rules.github.io/rules/7d6734)                                                                                                                                                                                                                 |\n| [td-headers-attr](https://dequeuniversity.com/rules/axe/4.11/td-headers-attr?application=RuleDescription)                         | Ensure that each cell in a table that uses the headers attribute refers only to other &lt;th&gt; elements in that table                              | Serious  | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-5.7.4                                     | failure, needs&nbsp;review | [a25f45](https://act-rules.github.io/rules/a25f45)                                                                                                                                                                                                                 |\n| [th-has-data-cells](https://dequeuniversity.com/rules/axe/4.11/th-has-data-cells?application=RuleDescription)                     | Ensure that &lt;th&gt; elements and elements with role=columnheader/rowheader have data cells they describe                                          | Serious  | cat.tables, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-5.7.1                                     | failure, needs&nbsp;review | [d0f69e](https://act-rules.github.io/rules/d0f69e)                                                                                                                                                                                                                 |\n| [valid-lang](https://dequeuniversity.com/rules/axe/4.11/valid-lang?application=RuleDescription)                                   | Ensure lang attributes have valid values                                                                                                             | Serious  | cat.language, wcag2aa, wcag312, TTv5, TT11.b, EN-301-549, EN-9.3.1.2, ACT, RGAAv4, RGAA-8.8.1                                                          | failure                    | [de46e4](https://act-rules.github.io/rules/de46e4)                                                                                                                                                                                                                 |\n| [video-caption](https://dequeuniversity.com/rules/axe/4.11/video-caption?application=RuleDescription)                             | Ensure &lt;video&gt; elements have captions                                                                                                          | Critical | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a, TTv5, TT17.a, EN-301-549, EN-9.1.2.2, RGAAv4, RGAA-4.3.1                          | needs&nbsp;review          | [eac66b](https://act-rules.github.io/rules/eac66b)                                                                                                                                                                                                                 |\n\n## WCAG 2.1 Level A & AA Rules\n\n| Rule ID                                                                                                             | Description                                                                                   | Impact  | Tags                                                                            | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/)                                                                                        |\n| :------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | :------ | :------------------------------------------------------------------------------ | :------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [autocomplete-valid](https://dequeuniversity.com/rules/axe/4.11/autocomplete-valid?application=RuleDescription)     | Ensure the autocomplete attribute is correct and suitable for the form field                  | Serious | cat.forms, wcag21aa, wcag135, EN-301-549, EN-9.1.3.5, ACT, RGAAv4, RGAA-11.13.1 | failure, needs&nbsp;review | [73f2c2](https://act-rules.github.io/rules/73f2c2)                                                                                                         |\n| [avoid-inline-spacing](https://dequeuniversity.com/rules/axe/4.11/avoid-inline-spacing?application=RuleDescription) | Ensure that text spacing set through style attributes can be adjusted with custom stylesheets | Serious | cat.structure, wcag21aa, wcag1412, EN-301-549, EN-9.1.4.12, ACT                 | failure                    | [24afc2](https://act-rules.github.io/rules/24afc2), [9e45ec](https://act-rules.github.io/rules/9e45ec), [78fd32](https://act-rules.github.io/rules/78fd32) |\n\n## WCAG 2.2 Level A & AA Rules\n\nThese rules are disabled by default, until WCAG 2.2 is more widely adopted and required.\n\n| Rule ID                                                                                           | Description                                         | Impact  | Tags                                           | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/) |\n| :------------------------------------------------------------------------------------------------ | :-------------------------------------------------- | :------ | :--------------------------------------------- | :------------------------- | :------------------------------------------------------------------ |\n| [target-size](https://dequeuniversity.com/rules/axe/4.11/target-size?application=RuleDescription) | Ensure touch targets have sufficient size and space | Serious | cat.sensory-and-visual-cues, wcag22aa, wcag258 | failure, needs&nbsp;review |                                                                     |\n\n## Best Practices Rules\n\nRules that do not necessarily conform to WCAG success criterion but are industry accepted practices that improve the user experience.\n\n| Rule ID                                                                                                                                       | Description                                                                                                                                   | Impact   | Tags                                             | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/) |\n| :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :----------------------------------------------- | :------------------------- | :------------------------------------------------------------------ |\n| [accesskeys](https://dequeuniversity.com/rules/axe/4.11/accesskeys?application=RuleDescription)                                               | Ensure every accesskey attribute value is unique                                                                                              | Serious  | cat.keyboard, best-practice                      | failure                    |                                                                     |\n| [aria-allowed-role](https://dequeuniversity.com/rules/axe/4.11/aria-allowed-role?application=RuleDescription)                                 | Ensure role attribute has an appropriate value for the element                                                                                | Minor    | cat.aria, best-practice                          | failure, needs&nbsp;review |                                                                     |\n| [aria-dialog-name](https://dequeuniversity.com/rules/axe/4.11/aria-dialog-name?application=RuleDescription)                                   | Ensure every ARIA dialog and alertdialog node has an accessible name                                                                          | Serious  | cat.aria, best-practice                          | failure, needs&nbsp;review |                                                                     |\n| [aria-text](https://dequeuniversity.com/rules/axe/4.11/aria-text?application=RuleDescription)                                                 | Ensure role=&quot;text&quot; is used on elements with no focusable descendants                                                                | Serious  | cat.aria, best-practice                          | failure, needs&nbsp;review |                                                                     |\n| [aria-treeitem-name](https://dequeuniversity.com/rules/axe/4.11/aria-treeitem-name?application=RuleDescription)                               | Ensure every ARIA treeitem node has an accessible name                                                                                        | Serious  | cat.aria, best-practice                          | failure, needs&nbsp;review |                                                                     |\n| [empty-heading](https://dequeuniversity.com/rules/axe/4.11/empty-heading?application=RuleDescription)                                         | Ensure headings have discernible text                                                                                                         | Minor    | cat.name-role-value, best-practice               | failure, needs&nbsp;review | [ffd0e9](https://act-rules.github.io/rules/ffd0e9)                  |\n| [empty-table-header](https://dequeuniversity.com/rules/axe/4.11/empty-table-header?application=RuleDescription)                               | Ensure table headers have discernible text                                                                                                    | Minor    | cat.name-role-value, best-practice               | failure, needs&nbsp;review |                                                                     |\n| [frame-tested](https://dequeuniversity.com/rules/axe/4.11/frame-tested?application=RuleDescription)                                           | Ensure &lt;iframe&gt; and &lt;frame&gt; elements contain the axe-core script                                                                  | Critical | cat.structure, best-practice, review-item        | failure, needs&nbsp;review |                                                                     |\n| [heading-order](https://dequeuniversity.com/rules/axe/4.11/heading-order?application=RuleDescription)                                         | Ensure the order of headings is semantically correct                                                                                          | Moderate | cat.semantics, best-practice                     | failure, needs&nbsp;review |                                                                     |\n| [image-redundant-alt](https://dequeuniversity.com/rules/axe/4.11/image-redundant-alt?application=RuleDescription)                             | Ensure image alternative is not repeated as text                                                                                              | Minor    | cat.text-alternatives, best-practice             | failure                    |                                                                     |\n| [label-title-only](https://dequeuniversity.com/rules/axe/4.11/label-title-only?application=RuleDescription)                                   | Ensure that every form element has a visible label and is not solely labeled using hidden labels, or the title or aria-describedby attributes | Serious  | cat.forms, best-practice                         | failure                    |                                                                     |\n| [landmark-banner-is-top-level](https://dequeuniversity.com/rules/axe/4.11/landmark-banner-is-top-level?application=RuleDescription)           | Ensure the banner landmark is at top level                                                                                                    | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-contentinfo-is-top-level](https://dequeuniversity.com/rules/axe/4.11/landmark-contentinfo-is-top-level?application=RuleDescription) | Ensure the contentinfo landmark is at top level                                                                                               | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-main-is-top-level](https://dequeuniversity.com/rules/axe/4.11/landmark-main-is-top-level?application=RuleDescription)               | Ensure the main landmark is at top level                                                                                                      | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-no-duplicate-banner](https://dequeuniversity.com/rules/axe/4.11/landmark-no-duplicate-banner?application=RuleDescription)           | Ensure the document has at most one banner landmark                                                                                           | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-no-duplicate-contentinfo](https://dequeuniversity.com/rules/axe/4.11/landmark-no-duplicate-contentinfo?application=RuleDescription) | Ensure the document has at most one contentinfo landmark                                                                                      | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-no-duplicate-main](https://dequeuniversity.com/rules/axe/4.11/landmark-no-duplicate-main?application=RuleDescription)               | Ensure the document has at most one main landmark                                                                                             | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-one-main](https://dequeuniversity.com/rules/axe/4.11/landmark-one-main?application=RuleDescription)                                 | Ensure the document has a main landmark                                                                                                       | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [landmark-unique](https://dequeuniversity.com/rules/axe/4.11/landmark-unique?application=RuleDescription)                                     | Ensure landmarks are unique                                                                                                                   | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [meta-viewport-large](https://dequeuniversity.com/rules/axe/4.11/meta-viewport-large?application=RuleDescription)                             | Ensure &lt;meta name=&quot;viewport&quot;&gt; can scale a significant amount                                                                  | Minor    | cat.sensory-and-visual-cues, best-practice       | failure                    |                                                                     |\n| [page-has-heading-one](https://dequeuniversity.com/rules/axe/4.11/page-has-heading-one?application=RuleDescription)                           | Ensure that the page, or at least one of its frames contains a level-one heading                                                              | Moderate | cat.semantics, best-practice                     | failure                    |                                                                     |\n| [presentation-role-conflict](https://dequeuniversity.com/rules/axe/4.11/presentation-role-conflict?application=RuleDescription)               | Ensure elements marked as presentational do not have global ARIA or tabindex so that all screen readers ignore them                           | Minor    | cat.aria, best-practice, ACT                     | failure                    | [46ca7f](https://act-rules.github.io/rules/46ca7f)                  |\n| [region](https://dequeuniversity.com/rules/axe/4.11/region?application=RuleDescription)                                                       | Ensure all page content is contained by landmarks                                                                                             | Moderate | cat.keyboard, best-practice, RGAAv4, RGAA-9.2.1  | failure                    |                                                                     |\n| [scope-attr-valid](https://dequeuniversity.com/rules/axe/4.11/scope-attr-valid?application=RuleDescription)                                   | Ensure the scope attribute is used correctly on tables                                                                                        | Moderate | cat.tables, best-practice                        | failure                    |                                                                     |\n| [skip-link](https://dequeuniversity.com/rules/axe/4.11/skip-link?application=RuleDescription)                                                 | Ensure all skip links have a focusable target                                                                                                 | Moderate | cat.keyboard, best-practice, RGAAv4, RGAA-12.7.1 | failure, needs&nbsp;review |                                                                     |\n| [tabindex](https://dequeuniversity.com/rules/axe/4.11/tabindex?application=RuleDescription)                                                   | Ensure tabindex attribute values are not greater than 0                                                                                       | Serious  | cat.keyboard, best-practice                      | failure                    |                                                                     |\n| [table-duplicate-name](https://dequeuniversity.com/rules/axe/4.11/table-duplicate-name?application=RuleDescription)                           | Ensure the &lt;caption&gt; element does not contain the same text as the summary attribute                                                    | Minor    | cat.tables, best-practice, RGAAv4, RGAA-5.2.1    | failure, needs&nbsp;review |                                                                     |\n\n## WCAG 2.x level AAA rules\n\nRules that check for conformance to WCAG AAA success criteria that can be fully automated. These are disabled by default in axe-core.\n\n| Rule ID                                                                                                                             | Description                                                                                                      | Impact  | Tags                                           | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/) |\n| :---------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | :------ | :--------------------------------------------- | :------------------------- | :------------------------------------------------------------------ |\n| [color-contrast-enhanced](https://dequeuniversity.com/rules/axe/4.11/color-contrast-enhanced?application=RuleDescription)           | Ensure the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds | Serious | cat.color, wcag2aaa, wcag146, ACT              | failure, needs&nbsp;review | [09o5cg](https://act-rules.github.io/rules/09o5cg)                  |\n| [identical-links-same-purpose](https://dequeuniversity.com/rules/axe/4.11/identical-links-same-purpose?application=RuleDescription) | Ensure that links with the same accessible name serve a similar purpose                                          | Minor   | cat.semantics, wcag2aaa, wcag249               | needs&nbsp;review          | [b20e66](https://act-rules.github.io/rules/b20e66)                  |\n| [meta-refresh-no-exceptions](https://dequeuniversity.com/rules/axe/4.11/meta-refresh-no-exceptions?application=RuleDescription)     | Ensure &lt;meta http-equiv=&quot;refresh&quot;&gt; is not used for delayed refresh                               | Minor   | cat.time-and-media, wcag2aaa, wcag224, wcag325 | failure                    | [bisz58](https://act-rules.github.io/rules/bisz58)                  |\n\n## Experimental Rules\n\nRules we are still testing and developing. They are disabled by default in axe-core, but are enabled for the axe browser extensions.\n\n| Rule ID                                                                                                                           | Description                                                                                                               | Impact   | Tags                                                                                                                             | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/) |\n| :-------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :------- | :------------------------------------------------------------------------------------------------------------------------------- | :------------------------- | :------------------------------------------------------------------ |\n| [css-orientation-lock](https://dequeuniversity.com/rules/axe/4.11/css-orientation-lock?application=RuleDescription)               | Ensure content is not locked to any specific display orientation, and the content is operable in all display orientations | Serious  | cat.structure, wcag134, wcag21aa, EN-301-549, EN-9.1.3.4, RGAAv4, RGAA-13.9.1, experimental                                      | failure, needs&nbsp;review | [b33eff](https://act-rules.github.io/rules/b33eff)                  |\n| [focus-order-semantics](https://dequeuniversity.com/rules/axe/4.11/focus-order-semantics?application=RuleDescription)             | Ensure elements in the focus order have a role appropriate for interactive content                                        | Minor    | cat.keyboard, best-practice, RGAAv4, RGAA-12.8.1, experimental                                                                   | failure                    |                                                                     |\n| [hidden-content](https://dequeuniversity.com/rules/axe/4.11/hidden-content?application=RuleDescription)                           | Inform users about hidden content.                                                                                        | Minor    | cat.structure, best-practice, experimental, review-item                                                                          | failure, needs&nbsp;review |                                                                     |\n| [label-content-name-mismatch](https://dequeuniversity.com/rules/axe/4.11/label-content-name-mismatch?application=RuleDescription) | Ensure that elements labelled through their content must have their visible text as part of their accessible name         | Serious  | cat.semantics, wcag21a, wcag253, EN-301-549, EN-9.2.5.3, RGAAv4, RGAA-6.1.5, experimental                                        | failure                    | [2ee8b8](https://act-rules.github.io/rules/2ee8b8)                  |\n| [p-as-heading](https://dequeuniversity.com/rules/axe/4.11/p-as-heading?application=RuleDescription)                               | Ensure bold, italic text and font-size is not used to style &lt;p&gt; elements as a heading                               | Serious  | cat.semantics, wcag2a, wcag131, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-9.1.3, experimental                                         | failure, needs&nbsp;review |                                                                     |\n| [table-fake-caption](https://dequeuniversity.com/rules/axe/4.11/table-fake-caption?application=RuleDescription)                   | Ensure that tables with a caption use the &lt;caption&gt; element.                                                        | Serious  | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-5.4.1               | failure                    |                                                                     |\n| [td-has-header](https://dequeuniversity.com/rules/axe/4.11/td-has-header?application=RuleDescription)                             | Ensure that each non-empty data cell in a &lt;table&gt; larger than 3 by 3 has one or more table headers                  | Critical | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g, TTv5, TT14.b, EN-301-549, EN-9.1.3.1, RGAAv4, RGAA-5.7.4 | failure                    |                                                                     |\n\n## Deprecated Rules\n\nDeprecated rules are disabled by default and will be removed in the next major release.\n\n| Rule ID                                                                                                                                           | Description                                                                            | Impact   | Tags                                                                                                 | Issue Type                 | [ACT Rules](https://www.w3.org/WAI/standards-guidelines/act/rules/)                                    |\n| :------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------- | :------- | :--------------------------------------------------------------------------------------------------- | :------------------------- | :----------------------------------------------------------------------------------------------------- |\n| [aria-roledescription](https://dequeuniversity.com/rules/axe/4.11/aria-roledescription?application=RuleDescription)                               | Ensure aria-roledescription is only used on elements with an implicit or explicit role | Serious  | cat.aria, wcag2a, wcag412, EN-301-549, EN-9.4.1.2, deprecated                                        | failure, needs&nbsp;review |                                                                                                        |\n| [audio-caption](https://dequeuniversity.com/rules/axe/4.11/audio-caption?application=RuleDescription)                                             | Ensure &lt;audio&gt; elements have captions                                            | Critical | cat.time-and-media, wcag2a, wcag121, EN-301-549, EN-9.1.2.1, section508, section508.22.a, deprecated | needs&nbsp;review          | [2eb176](https://act-rules.github.io/rules/2eb176), [afb423](https://act-rules.github.io/rules/afb423) |\n| [duplicate-id-active](https://dequeuniversity.com/rules/axe/4.11/duplicate-id-active?application=RuleDescription)                                 | Ensure every id attribute value of active elements is unique                           | Serious  | cat.parsing, wcag2a-obsolete, wcag411, deprecated                                                    | failure                    | [3ea0c8](https://act-rules.github.io/rules/3ea0c8)                                                     |\n| [duplicate-id](https://dequeuniversity.com/rules/axe/4.11/duplicate-id?application=RuleDescription)                                               | Ensure every id attribute value is unique                                              | Minor    | cat.parsing, wcag2a-obsolete, wcag411, deprecated                                                    | failure                    | [3ea0c8](https://act-rules.github.io/rules/3ea0c8)                                                     |\n| [landmark-complementary-is-top-level](https://dequeuniversity.com/rules/axe/4.11/landmark-complementary-is-top-level?application=RuleDescription) | Ensure the complementary landmark or aside is at top level                             | Moderate | cat.semantics, best-practice, deprecated                                                             | failure                    |                                                                                                        |\n"
  },
  {
    "path": "doc/rule-development.md",
    "content": "# Developing Axe-core Rules\n\nBefore you start writing axe-core rules, be sure to create a proposal for them in a GitHub issue. Read [Proposing Axe-core rules](./rule-proposal.md) for details.\n\nA rule is a JSON Object that defines a test for axe-core to run. At a high level, think of a rule as doing two things. First it finds all elements that it should test, and after that it runs a number of checks to see if those selected elements pass or fail the rule. The code for rules is in the [`lib/rules`](../lib/rules/) and the checks in [`lib/checks`](../lib/checks/).\n\n## Rule Select and Matches\n\nEach rule has a `selector` and optionally a `matches` property. The selector is a CSS selector. Each element matching this selector will be tested by the rule, unless the matches function says otherwise. The `matches` property is a reference to a function that returns a boolean, which indicates if the element should be tested.\n\nThe last thing that may influence if an element is selected for testing in the rule is its visibility. By default, hidden elements are ignored by the rule, unless `excludeHidden` is set to 'false'.\n\n## Using Checks in Rules\n\nThe actual testing of elements in axe-core is done by checks. A rule has one or more checks which end up generating a result. There are three properties with which to define a rule's checks. Each of them deals differently:\n\n- **All**: Takes an array of check names, **all** of which has to return true for the rule to pass.\n- **none**: Takes an array of check names, **none** of which can return true for the rule to pass.\n- **any**: Takes an array of check names, **at least one** of which has to return true for the rule to pass.\n\n## Rule Properties\n\n| Prop. Name           | Description                                                                  |\n| -------------------- | ---------------------------------------------------------------------------- |\n| id                   | Unique identifier for the rule                                               |\n| selector             | CSS Selector that matches elements to test                                   |\n| matches              | Function to further filter the outcome of the selector                       |\n| excludeHidden        | Should hidden elements be excluded (default: true)                           |\n| reviewOnFail         | Whether any fail on the rule should be reported as needs review / incomplete |\n| impact               | \"minor\", \"serious\", \"critical\"                                               |\n| all                  | Checks that must all return true                                             |\n| any                  | Checks of which at least one must return true                                |\n| none                 | Checks that must all return false                                            |\n| pageLevel            | Should the rule only run on the main window                                  |\n| enabled              | Does the rule run by default                                                 |\n| tags                 | Grouping for the rule, such as wcag2a, best-practice                         |\n| metadata.description | Description of what a rule does                                              |\n| metadata.help        | Short description of a violation, used in the axe extension sidebar          |\n\n## Check Properties\n\n| Prop. Name                   | Description                                    |\n| ---------------------------- | ---------------------------------------------- |\n| id                           | Unique identifier for the check                |\n| evaluate                     | Evaluating function, returning a boolean value |\n| options                      | Configurable value for the check               |\n| after                        | Cleanup function, run after check is done      |\n| metadata.messages.pass       | Describes why the check passed                 |\n| metadata.messages.fail       | Describes why the check failed                 |\n| metadata.messages.incomplete | Describes why the check didn’t complete        |\n\nIncomplete results occur when axe-core can’t produce a clear pass or fail result,\ngiving users the opportunity to manually review the result. Incomplete messages can take\nthe form of a string, or an object with arbitrary keys matching the data returned\nfrom the check.\n\nA pass message is required, while fail and incomplete are dependent on the check result.\n\n### Incomplete message string\n\nFor example, the audio and video caption checks return an incomplete string:\n\n```js\nmessages: {\n  pass: 'Why the check passed',\n  fail: 'Why the check failed',\n  incomplete: 'Why the check returned undefined'\n}\n```\n\n### Incomplete message object with missingData\n\nAs another example, the color-contrast check returns missingData to aid in\nremediation. Here’s the message format:\n\n```js\nmessages: {\n  pass: 'Why the check passed',\n  fail: 'Why the check failed',\n  incomplete: {\n    bgImage: 'The background color could not be determined due to a background image',\n    default: 'fallback string'\n  }\n}\n```\n\nTo wire up an incomplete message with a specific reason it returned undefined,\nthe check needs matching data. Otherwise, it falls back to the `default` message.\nReasons are arbitrary for the check (such as 'bgImage') but they must match the\ndata returned:\n\n```js\nthis.data({\n  missingData: 'bgImage'\n});\n```\n\n# Hierarchical rules\n\nAxe-core handles shadow DOM and cross-domain iframe rules very well - as long as you take care in the design and implementation of your rule. Any rule that evaluates the DOM hierarchy needs to think about what happens across shadow DOM boundaries and iframe boundaries.\n\nThe rule callbacks all receive both a `node` and a `virtualNode` argument (in addition to the `options` argument). `node` points to the DOM Node that is to be evaluated, whereas `virtualNode` points to the node in the flattened tree (the hierarchy that shadow DOM creates that is used for parent child relationships across shadow DOM boundaries).\n\nIf your rule looks at any hierarchical context (parents or children) then you need to operate on the `virtualNode` for those operations. Calls to `isHidden` and the accessible name calculation calls will all evaluate the hierarchy. The commons and utils functions will all fetch the `virtualNode` from the flattened tree if you use the `node` implementation (and then simply call the virtualNode one) and are there for backward compatibility. This backward compatibility comes at a performance cost that can be avoided by simply using the `virtualNode` to start with. We will ask you to change this during PR review, so you might as well just start out by using the `virtualNode`.\n\n## iframes\n\nRules that evaluate the structure and/or the number of elements on the entire page (for example the heading nesting rule, or the landmark rules) will need to do this evaluation across iframe boundaries. What this means is that the check function instead of determining pass/fail/incomplete, performs a data gathering function. This data is then passed up the iframe hierarchy to the top window where it is passed into the `after` function, which does the evaluation of the gathered data and determines pass/fail.\n\n## Rules of Thumb for Rule Code Reviewers\n\nRules of thumb for determining whether `virtualNode` should be used - if any of the answers to the following questions is yes, then it should use shadow DOM and `virtualNode`:\n\n1. Is it using the non-virtualNode version of a function that wraps the flattened tree lookup?\n2. Is it evaluating the DOM hierarchy?\n\nRules that do this MUST have shadow DOM test cases that show passes and fails across the shadow DOM boundary.\n\nRules of thumb for determining whether an `after` function is required - if any of the answers to the following questions is yes:\n\n1. Does the rule evaluate number of things on a page?\n2. Does the rule evaluate hierarchy of things across a whole page?\n\nRules that use an `after` function MUST have iframe test cases that assert correct data passing between iframes and handle all the relevant cases across iframes.\n\n## rule generation\n\nAxe comes with a script to help generate rule files. To use this, call `npm run rule-gen` from the root of the project. If you haven't already, make sure to call `npm install` before to get all dependencies installed first.\n"
  },
  {
    "path": "doc/rule-proposal.md",
    "content": "# Proposing Axe-core Rules\n\nThis document outlines the process of proposing a rule. For a technical description on how to build a rule, read [Developing Axe-core Rules](./rule-development.md)\n\nBefore you start coding a new rule for axe-core, you _must_ create a GitHub issue to document the rule you want to create. There are many considerations to writing a good rule. It must have no false positives, which is difficult, because there are always many many edge cases to consider. Additionally, a known problem of accessibility testing is that not all testers hold the same interpretation of the accessibility guidelines. All rules _must_ be consistent with the interpretation of Deque Systems, the developer behind Axe-core.\n\nIn addition to giving the axe-core development team an opportunity to provide feedback on the proposed rule, the GitHub Issue will serve as documentation of that rule for the future.\n\n## WCAG interpretation\n\nPlease read our [principles for deciding on rules](./accessibility-supported.md).\n\n## Rules Format\n\nAll GitHub issues that propose a rule must be tagged as _rule_, and must use the following format:\n\n### Intro\n\nIn one sentence, describe what the rule does.\n\nExample: \"Ensure ARIA attributes are allowed for an element's role\"\n\n#### Rule help\n\nIn one sentence, describe how to resolve the issue.\n\nExample: \"Elements must only use allowed ARIA attributes\"\n\n#### Tags\n\nIndicate which tags the rule should use. More information available in the [how we assign tags documentation](https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#axe-core-tags).\n\nExample: wcag2a, wcag211, cat.keyboard\n\n### Selector\n\nIf possible using a CSS selector, otherwise describe in one sentence using plain language what elements the rule selects.\n\nExample 1: `input[type=checkbox][name]`\n\nExample 2: Select each node that has an attribute starting with `aria-`.\n\n### Checks:\n\nMake each check a subheading of `checks`. Give the check name in the heading, and indicate if the check type is `any`, `all` or `none`.\n\nIn short sentences, using plain language, describe what conditions will lead to the check returning false, true or undefined. Keep the steps simple and short. You don't have to write out all the logic. Preferably not, just give the high level view of what the check does.\n\n**Example 1:**\n\n`###` aria/allow-attr (any)\n\n1. Look up the element role\n2. Look up a list of aria attributes allowed for that role\n3. Return false if the element has aria attributes not in the list\n4. Otherwise return true\n\n**Example 2:**\n\n`###` keyboard/focusable-no-name (none)\n\n1. If the element is not in the focus order, return false\n2. If the element has an accessible name, return false\n3. Otherwise return true\n\n## Best practices\n\nFor rule design, consider the following as best practices:\n\n1. Rules should only have one `none` check so that the error message is specific\n2. Rules should not combine `any` and `none`, these should be broken out into separate rules\n3. Checks should each only test a single specific case (either a passing technique or a single failing test)\n\n## Template\n\nUse this template when creating the issue:\n\n```markdown\n# {{ Rule name }}\n\n{{ Rule description }}\n\n{{ Rule help }}\n\n**Tags:** {{ tag, tag, tag }}\n\n## Selector\n\n{{ selector }}\n\n## Checks\n\n### {{ Check name 1 }} ( any / all / none )\n\n1.\n\n### {{ Check name 2, optional }} ( any / all / none )\n\n1.\n```\n\n## W3C Standardized Rules\n\nDeque Systems is one of leading organizations in the development of standardized accessibility conformance testing rules. The above format is an adaptation of the [Accessibility Conformance Testing Rules Format](https://www.w3.org/TR/act-rules-format/).\n\nFor details on how the above format maps to the ACT Rules format, see [act-rules-format.md](./act-rules-format.md).\n"
  },
  {
    "path": "doc/run-partial.md",
    "content": "# axe runPartial / finishRun\n\n`axe.runPartial` and `axe.finishRun` are two methods which allow axe to test a page in two stages. This is different from `axe.run()`, in that `axe.runPartial` and `axe.finishRun` do not require communication between frames. This can be useful when frame communication is not possible or not secure. See [axe.frameMessenger](frame-messenger.md) for information on frame communication.\n\nTo use these methods, call `axe.runPartial()` in the top window, and in all nested frames and iframes. The results are put into an array, and then passed to `axe.finishRun()`. `axe.finishRun()` then completes the test and generates the axe report.\n\nThis results in code that looks something like the following. The `context` and `options` arguments used are the same as would be passed to `axe.run`. See [API.md](api.md) for details.\n\n```js\nconst partialResults = await Promise.all(runPartialRecursive(context, options));\nconst axeResults = await axe.finishRun(partialResults, options);\n```\n\n**note**: The code in this page uses native DOM methods. This will only work on frames with the same origin. Scripts do not have access to `contentWindow` of cross-origin frames. Use of `runPartial` and `finishRun` in browser drivers like Selenium and Puppeteer works in the same way.\n\n**note**: Because `axe.runPartial()` is designed to be serialized, it will not return element references even if the `elementRef` option is set.\n\n## axe.runPartial(context, options): Promise<PartialResult>\n\nWhen using `axe.runPartial()` it is important to keep in mind that the `context` may be different for different frames. For example, `context` can be done in such a way that in frame A, `main` is excluded, and in frame B `footer` is. The `axe.utils.getFrameContexts` method will provide a list of frames that must be tested, and what context to test it with.\n\n```js\nfunction runPartialRecursive(context, options = {}, win = window) {\n  const { axe, document } = win;\n  // Find all frames in context, and determine what context object to use in that frame\n  const frameContexts = axe.utils.getFrameContexts(context, options);\n  // Run the current context, in the current window.\n  const promiseResults = [axe.runPartial(context, options)];\n\n  // Loop over all frames in context\n  frameContexts.forEach(({ frameSelector, frameContext }) => {\n    // Find the window of the frame\n    const frame = axe.utils.shadowSelect(frameSelector);\n    const frameWin = frame.contentWindow;\n    // Call axe.runPartial() in the frame, and its child frame\n    const frameResults = runPartialRecursive(frameContext, options, frameWin);\n    promiseResults.push(...frameResults);\n  });\n  return promiseResults;\n}\n```\n\n**important**: The order in which these methods are called matters for performance. Internally, axe-core constructs a flattened tree when `axe.utils.getFrameContexts` is called. This is fairly slow, and so should not happen more than once per frame. When `axe.runPartial` is called, that tree will be used if it still exists. Since this tree can get out of sync with the actual DOM, it is important to call `axe.runPartial` immediately after `axe.utils.getFrameContexts`.\n\nTo run efficiently, `axe.runPartial` calls should happen in parallel so that, when possible, browsers can test multiple frames simultaneously.\n\n## axe.finishRun(partialResults, options): Promise<AxeResults>\n\nThe `axe.finishRun` method does two things: It calls the `after` methods of checks that have them, and it creates the final report. For `axe.finishRun` to work, the order in which the partial results are passed to it is important. Starting from the top window, partial results must include child frame content before sibling frames. For example:\n\n```js\n// Given the following frame structure:\n// top\n// - frame_1\n//   - frame_1a\n// - frame_2\n// The partial results are passed in the following order:\naxe.finishRun([top, frame_1, frame_1a, frame_2]);\n```\n\nIf for some reason `axe.runPartial` fails to run, the integration API **must** include `null` in the data in place of the results object, so that axe-core knows to skip over it. If a frame fails to run, results from any descending frames **must be omitted**. To illustrate this, consider the following:\n\n```js\n// Given the following frame structure:\n// top\n// - frame_1\n//   - frame_1a\n// - frame_2\n\n// If axe.runPartial throws an error, the results must be passed to finishRun like this:\naxe.finishRun([top, null, /* nothing for frame 1a, */ frame_2]);\n```\n\n**important**: Since `axe.finishRun` may have access to cross-origin information, it should only be called in an environment that is known not to have third-party scripts. When using a browser driver, this can for example by done in a blank page.\n\n## axe.utils.getFrameContexts(context, options): FrameContext[]\n\nThe `axe.utils.getFrameContexts` method takes any valid context, and returns an array of objects. Each object represents a frame that is in the context. The object has the following properties:\n\n- `frameSelector`: This is a CSS selector, or array of CSS selectors in case of nodes in a shadow DOM tree to locate the frame element to be tested.\n- `frameContext`: This is an object is a context object that should be tested in the particular frame.\n\nThe `options` object takes the same RunOptions object that axe.run accepts. When the `iframes` property is `false`, it returns an empty array.\n\n## Custom Rulesets and Reporters\n\nBecause `axe.finishRun` does not run inside the page, the `reporter` and `after` methods do not have access to the top-level `window` and `document` objects, and might not have access to common browser APIs. Axe-core reporters use the `environmentData` property that is set on the partialResult object of the initiator.\n\nBecause of this constraint, custom reporters, and custom rulesets that add `after` methods must not rely on browser APIs or globals. Any data needed for either should either be taken from the `environmentData` property, or collected in an `evaluate` method of a check, and stored using its `.data()` method.\n\n## Recommendations\n\nWhen building integrations with browser drivers using axe-core, it is safer and more stable to use `axe.runPartial` and `axe.finishRun` then to use `axe.run`. These two methods ensure that no information from one frame is ever handed off to another. That way if any script in a frame interferes with the `axe` object, or with `window.postMessage`, other frames will not be affected.\n\nBecause `axe.runPartial` does not require frame communication, it can run in any frame, regardless of its origin. This is different from `axe.run()`, which is limited by `axe.configure({ allowedOrigins })`.\n"
  },
  {
    "path": "doc/standards-object.md",
    "content": "# Standards Object\n\nThe [standards object](../lib/standards) is a JSON object of ARIA and HTML spec information that axe-core uses to validate ARIA roles, attributes, and use. For example, the `aria-valid-attr` rule uses the [`ariaAttrs`](../lib/standards/aria-attrs.js) object to determine if the aria attribute is a valid ARIA attribute. To configure how axe-core validates ARIA information, you'll most likely configure the standards object to your needs. Below is a list of each object, its structure, and which rules will use that information\n\n```js\naxe.configure({\n  standards: {\n    ariaAttrs: {\n      'aria-relevant': {\n        // make the aria-relevant attribute global\n        global: true\n      }\n    }\n  }\n});\n```\n\nThe following properties are currently available in axe-core `standards`:\n\n1.  [ARIA Attrs](#aria-attrs)\n1.  [ARIA Roles](#aria-roles)\n1.  [DPUB Roles](#dpub-roles)\n1.  [Graphics Roles](#graphics-roles)\n1.  [HTML Elms](#html-elms)\n1.  [CSS Colors](#css-colors)\n\n## Aria Attrs\n\nThe [`ariaAttrs`](../lib/standards/aria-attrs.js) object defines valid ARIA attributes, their allowed value types, and if it is a global attribute.\n\n### Used by Rules\n\n- `aria-valid-attr` - Checks if the attribute name exists in the object.\n- `aria-valid-attr-value` - Checks if the attribute value is valid for its type.\n- `aria-unsupported-attr` - Checks if the attribute has the `unsupported` property.\n\n### Structure\n\n- `type` - string(required). The attribute type which dictates the valid values of the attribute. Valid types are:\n  - `boolean` - Boolean attributes only accept `true` or `false` as valid values (e.g. `aria-selected`).\n  - `nmtoken` - Name token attributes accept a single value from a list of valid values (e.g. `aria-orientation`).\n  - `mntokens` - Name tokens attributes accept a space separated list of values from a list of valid values (e.g. `aria-relevant`).\n  - `idref` - ID reference attributes accept an ID to point to another element in the DOM (e.g. `aria-activedescendant`).\n  - `idrefs` - ID references attributes accept a space separated list of IDs that point to multiple elements in the DOM (e.g. `aria-labelledby`).\n  - `string` - String attributes accept any string (e.g. `aria-label`).\n  - `decimal` - Decimal attributes accept any number or decimal value (e.g. `aria-valuemax`).\n  - `int` - Integer attributes only accept whole number values (e.g. `aria-level`).\n- `values` - array(required for only `mntoken` and `mntokens`). The list of valid values for the attribute.\n- `minValue` - number(required for only `int`). The minimum value allowed for the attribute.\n- `allowEmpty` - boolean(optional, default `false`). If the attribute is allowed to have no value.\n- `global` - boolean(optional, default `false`). If the attribute is a [global ARIA attribute](https://www.w3.org/TR/wai-aria-1.1/#global_states).\n- `unsupported` - boolean(optional, default `false`). If the attribute is unsupported. Use this property to disable an attribute.\n\n## Aria Roles\n\nThe [`ariaRoles`](../lib/standards/aria-roles.js) object defines valid ARIA roles and their use.\n\n### Used by Rules\n\n- `aria-allowed-attr` - Checks if the attribute can be used on the role from the `allowedAttrs` and `requiredAttrs` properties, as well as any global ARIA attributes (from the `ariaAttrs` standard).\n- `aria-required-attr` - Checks if the role has all required attributes listed in the `requiredAttrs` property.\n- `aria-required-context` - Checks if the role has at least one required parent role listed in the `requiredContext` property.\n- `aria-required-owned` - Checks if the role has at least one required owned role listed in the `requiredOwned` property.\n- `unsupportedrole` - Checks if the role has the `unsupported` property.\n\n### Structure\n\n- `type` - string(required). [The role type](https://www.w3.org/TR/wai-aria-1.1/#roles_categorization). Valid types are:\n  - `abstract`\n  - `composite`\n  - `landmark`\n  - `structure`\n  - `widget`\n  - `window`\n- `requiredContext` - array(optional). List of required parent roles.\n- `requiredOwned` - array(optional). List of required owned roles.\n- `requiredAttrs` - array(optional). List of required attributes.\n- `allowedAttrs` - array(optional). List of allowed attributes (besides any required and global ARIA attributes).\n- `superclassRole` - array(optional). List of superclass roles.\n- `accessibleNameRequired` - boolean(optional. Default `false`). If elements with this role require an accessible name.\n- `nameFromContent` - boolean(optional. Default `false`). If the role allows name from content when calculating the accessible name.\n- `unsupported` - boolean(optional. Default `false`). If the role is unsupported. Use this property to disable a role.\n\n### Dpub Roles\n\nDpub roles are defined in the [dpub-roles](../lib/standards/dpub-roles.js) file. Their structure is the exact same as the aria roles standard (albeit they have different `type`s) and is combined into the `ariaRoles` table. They are only separated for organizational purposes.\n\n### Graphics Roles\n\nGraphics roles are defined in the [graphics-roles](../lib/standards/graphics-roles.js) file. They are extensions of the standard ARIA roles and therefore have the exact same structure. The graphics roles are combined into the `ariaRoles` table and they are only separated for organizational purposes.\n\n## HTML Elms\n\nThe [`htmlElms`](../lib/standards/html-elms.js) object defines valid HTML elements, their content type, and if they are allowed ARIA roles or attributes.\n\n### Used by Rules\n\n- `aria-allowed-attr` - Checks if the attribute can be used on the element from the `noAriaAttrs` property.\n- `aria-allowed-role` - Checks if the role can be used on the HTML element from the `allowedRoles` property.\n- `aria-required-attrs` - Checks if any required attrs are defined implicitly on the element from the `implicitAttrs` property.\n\n### Structure\n\n- `contentTypes` - array(required). List of [content models](https://html.spec.whatwg.org/multipage/dom.html#content-models) for the element. Valid values are:\n  - `flow`\n  - `sectioning`\n  - `heading`\n  - `phrasing`\n  - `embedded`\n  - `interactive`\n- `allowedRoles` - boolean or array(required). If element is allowed to use ARIA roles, a value of `true` means any role while a list of roles means only those are allowed. A value of `false` means no roles are allowed.\n- `noAriaAttrs` - boolean(optional. Defaults `true`). If the element is allowed to use global ARIA attributes and any allowed for the elements role.\n- `shadowRoot` - boolean(optional. Default `false`). If the element is allowed to have a shadow root.\n- `implicitAttrs` - object(optional. Default `{}`). Any implicit ARIA attributes for the element and their default value.\n- `namingMethods` - array(optional. Default `[]`). The [native text method](../lib/commons/text/native-text-methods.js) used to calculate the accessible name of the element.\n- `variant` - object(optional). Object defining different property values based on which attributes exist on the element.\n\n### Variants\n\nSometimes an element can have different properties and values based on which attributes are defined on it. For example, an `img` is only considered interactive if it has the `usemap` attribute.\n\nFor these cases, the `variant` property allows different definitions for the element. The `variant` object gives a name to each variant, defines when the variant applies through the `matches` property (a `matcher` object), and provides a `default` variant in the case none of the other variants match. A variant should only define what can change based on the attributes. Any properties that always apply regardless of attributes should be defined at the top level.\n\nBelow is an example for the `meta` element which only has a `contentType` property when it has the `itemprop` attribute, but always does not allow ARIA roles or attributes.\n\n```js\nmeta: {\n    variant: {\n        itemprop: {\n            matches: '[itemprop]',\n            contentTypes: ['phrasing', 'flow']\n        }\n    },\n    allowedRoles: false,\n    noAriaAttrs: true\n}\n```\n\n## CSS Colors\n\nThe [`cssColors`](../lib/standards/css-colors.js) object defines the list of valid CSS color names and their equivalent RGB values.\n\n### Used by Rules\n\n- `color-contrast` - Gets the RGB color from the CSS color name.\n\n### Structure\n\nEach entry is the name of the CSS color and the value is an array of numbers consisting of the RGB values for the color respectively.\n"
  },
  {
    "path": "eslint.config.js",
    "content": "const prettier = require('eslint-config-prettier');\nconst globals = require('globals');\nconst mochaNoOnly = require('eslint-plugin-mocha-no-only');\n\nmodule.exports = [\n  prettier,\n  {\n    rules: {\n      'mocha-no-only/mocha-no-only': ['error'],\n      'no-bitwise': 2,\n      camelcase: 2,\n      curly: 2,\n      eqeqeq: 2,\n      'guard-for-in': 2,\n      'wrap-iife': [2, 'any'],\n      'no-use-before-define': [\n        2,\n        {\n          functions: false\n        }\n      ],\n      'new-cap': 2,\n      'no-caller': 2,\n      'no-empty': 2,\n      'no-new': 2,\n      'no-plusplus': 0,\n      'no-undef': 2,\n      'no-unused-vars': 2,\n      strict: 0,\n      'max-params': [2, 6],\n      'max-depth': [2, 5],\n      'max-len': 0,\n      semi: 0,\n      'no-cond-assign': 0,\n      'no-debugger': 2,\n      'no-eq-null': 0,\n      'no-eval': 2,\n      'no-unused-expressions': 0,\n      'block-scoped-var': 0,\n      'no-iterator': 0,\n      'linebreak-style': 0,\n      'no-loop-func': 0,\n      'no-multi-str': 0,\n      'no-proto': 0,\n      'no-script-url': 0,\n      'dot-notation': 2,\n      'no-new-func': 0,\n      'no-new-wrappers': 0,\n      'no-shadow': 2,\n      'no-restricted-syntax': [\n        'error',\n        {\n          selector: 'MemberExpression[property.name=tagName]',\n          message: \"Don't use node.tagName, use node.nodeName instead.\"\n        },\n        // node.attributes can be clobbered so is unsafe to use\n        // @see https://github.com/dequelabs/axe-core/pull/1432\n        {\n          // node.attributes\n          selector:\n            'MemberExpression[object.name=node][property.name=attributes]',\n          message:\n            \"Don't use node.attributes, use node.hasAttributes() or axe.utils.getNodeAttributes(node) instead.\"\n        },\n        {\n          // vNode.actualNode.attributes\n          selector:\n            'MemberExpression[object.property.name=actualNode][property.name=attributes]',\n          message:\n            \"Don't use node.attributes, use node.hasAttributes() or axe.utils.getNodeAttributes(node) instead.\"\n        },\n        // node.contains doesn't work with shadow dom\n        // @see https://github.com/dequelabs/axe-core/issues/4194\n        {\n          // node.contains()\n          selector:\n            'CallExpression[callee.object.name=node][callee.property.name=contains]',\n          message:\n            \"Don't use node.contains(node2) as it doesn't work across shadow DOM. Use axe.utils.contains(node, node2) instead.\"\n        },\n        {\n          // vNode.actualNode.contains()\n          selector:\n            'CallExpression[callee.object.property.name=actualNode][callee.property.name=contains]',\n          message:\n            \"Don't use node.contains(node2) as it doesn't work across shadow DOM. Use axe.utils.contains(node, node2) instead.\"\n        }\n      ]\n    },\n    plugins: {\n      'mocha-no-only': mochaNoOnly\n    }\n  },\n  {\n    languageOptions: {\n      ecmaVersion: 2023,\n      globals: {\n        axe: true,\n        Promise: true,\n        ...globals.node,\n        ...globals.es2015\n      }\n    }\n  },\n  {\n    files: ['lib/**/*.js'],\n    // reporters and check after methods should not have access to window or document\n    ignores: ['lib/core/reporters/**/*.js', 'lib/**/*-after.js'],\n    languageOptions: {\n      sourceType: 'module',\n      // lib files should not access global window properties without going through window (i.e. do not add globals.browser)\n      globals: {\n        window: true,\n        document: true,\n        ...globals.node,\n        ...globals.es2015\n      }\n    },\n    rules: {\n      'func-names': [2, 'as-needed'],\n      'prefer-const': 2,\n      'no-use-before-define': 'off'\n    }\n  },\n  {\n    // disallow imports from node modules\n    ignores: ['lib/core/imports/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              regex: '^[^.]',\n              message: 'Only core/imports files should import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // disallow imports in standards\n    files: ['lib/standards/**/*.js'],\n    // index file can import other standards and from utils\n    ignores: ['lib/standards/index.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              group: ['*'],\n              message:\n                \"Standard files shouldn't use imports as they are just hard coded data objects\"\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // restrict imports to core/utils files to other core/utils, core, core/base, standards, imports, or reporters/helpers\n    files: ['lib/core/utils/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              // e.g. \"../commons/aria/\" or \"../public/\"\n              regex:\n                '.*\\\\.\\\\.\\\\/(commons|public|checks|rules)(\\\\/|$)|.*\\\\.\\\\.\\\\/reporters\\\\/.*?\\\\.js',\n              message:\n                'Util files should only import from other utils, core, or standard files'\n            },\n            // disallow imports from node modules\n            // seems only 1 regex pattern is allowed to match as not having this allows node module imports even while having the general rule above for all files)\n            {\n              regex: '^[^.]',\n              message: 'Only core/imports files should import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // restrict imports to core/public files to other core/public, or imports allowed by core/utils\n    files: ['lib/core/public/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              // e.g. \"../commons/aria/\" or \"../checks/\"\n              regex:\n                '.*\\\\.\\\\.\\\\/(commons|checks|rules)(\\\\/|$)|.*\\\\.\\\\.\\\\/reporters\\\\/.*?\\\\.js',\n              message:\n                'Public files should only import from other public, util, core, or standard files'\n            },\n            // disallow imports from node modules\n            {\n              regex: '^[^.]',\n              message: 'Only core/imports files should import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // disallow imports in core/imports files to any non-node module\n    files: ['lib/core/imports/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              // relative file paths\n              regex: '\\\\\\.\\\\\\.\\\\/',\n              message: 'Import files should only import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // disallow imports in core/reporters files to any non-util file\n    files: ['lib/core/reporters/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              // e.g. \"../commons/aria/\" or \"../checks/\"\n              regex: '.*\\\\.\\\\.\\\\/(commons|base|public|checks|rules)(\\\\/|$)',\n              message: 'Reporter files should only import util functions'\n            },\n            // disallow imports from node modules\n            {\n              regex: '^[^.]',\n              message: 'Only core/imports files should import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // disallow imports in commons files to any check or rule\n    files: ['lib/commons/**/*.js'],\n    rules: {\n      'no-restricted-imports': [\n        'error',\n        {\n          patterns: [\n            {\n              // e.g. ../checks/\"\n              regex: '.*\\\\.\\\\.\\\\/(checks|rules)(\\\\/|$)',\n              message: 'Commons files cannot import from checks and rules'\n            },\n            // disallow imports from node modules\n            {\n              regex: '^[^.]',\n              message: 'Only core/imports files should import from node modules'\n            }\n          ]\n        }\n      ]\n    }\n  },\n  {\n    // Utils should be functions that can be used without setting up the virtual tree, as opposed to commons which require the virtual tree\n    files: ['lib/core/utils/**/*.js'],\n    ignores: [\n      // these are files with known uses of virtual node that are legacy before this rule was enforced\n      'lib/core/utils/closest.js',\n      'lib/core/utils/contains.js',\n      'lib/core/utils/query-selector-all-filter.js',\n      'lib/core/utils/selector-cache.js',\n      // this will create a virtual node if one doesn't exist already in order to truncate the html output properly\n      'lib/core/utils/dq-element.js',\n      // this sets up the virtual tree so is allowed vNode\n      'lib/core/utils/get-flattened-tree.js'\n    ],\n    rules: {\n      'no-restricted-syntax': [\n        'error',\n        {\n          selector: 'MemberExpression[object.name=vNode]',\n          message:\n            \"Utils is meant for utility functions that work independently of axe's state; utilities that require the virtual tree to be set up should go in commons, not utils.\"\n        },\n        {\n          selector: 'MemberExpression[object.name=virtualNode]',\n          message:\n            \"Utils is meant for utility functions that work independently of axe's state; utilities that require the virtual tree to be set up should go in commons, not utils.\"\n        }\n      ]\n    }\n  },\n  {\n    files: ['doc/examples/chrome-debugging-protocol/axe-cdp.js'],\n    languageOptions: {\n      globals: {\n        window: true\n      }\n    }\n  },\n  {\n    // after functions and reporters will not be run inside the same context as axe.run so should not access browser globals that require context specific information (window.location, window.getComputedStyles, etc.)\n    files: ['lib/**/*-after.js', 'lib/core/reporters/**/*.js'],\n    languageOptions: {\n      sourceType: 'module'\n    }\n  },\n  {\n    // polyfills are mostly copy-pasted from sources so we don't control their styling\n    files: [\n      'lib/core/imports/polyfills.js',\n      'lib/core/utils/pollyfill-elements-from-point.js'\n    ],\n    rules: {\n      'func-names': 0,\n      'no-bitwise': 0,\n      curly: 0,\n      eqeqeq: 0\n    }\n  },\n  {\n    files: ['test/act-rules/**/*.js', 'test/aria-practices/**/*.js'],\n    languageOptions: {\n      globals: {\n        ...globals.mocha\n      }\n    },\n    rules: {\n      'new-cap': 0,\n      'no-use-before-define': 0\n    }\n  },\n  {\n    files: ['test/**/*.js'],\n    languageOptions: {\n      globals: {\n        ...globals.mocha,\n        ...globals.browser,\n        ...globals.es2015,\n        ...globals.node,\n        assert: true,\n        helpers: true,\n        checks: true,\n        sinon: true\n      }\n    },\n    rules: {\n      'new-cap': 0,\n      'no-use-before-define': 0\n    }\n  },\n  {\n    files: ['.github/bin/*.mjs'],\n    languageOptions: {\n      ecmaVersion: 'latest',\n      sourceType: 'module',\n      globals: {\n        ...globals.node,\n        ...globals.es2024\n      }\n    },\n    rules: {\n      // Helper scripts for github can import from anywhere\n      'no-restricted-imports': ['off']\n    }\n  },\n  {\n    ignores: [\n      '**/node_modules/*',\n      '**/tmp/*',\n      'patches/*',\n      'build/tasks/aria-supported.js',\n      'doc/api/*',\n      'doc/examples/jest_react/*.js',\n      'lib/core/imports/polyfills.js',\n      'lib/core/utils/uuid.js',\n      'axe.js',\n      'axe.min.js'\n    ]\n  }\n];\n"
  },
  {
    "path": "lib/checks/aria/abstractrole-evaluate.js",
    "content": "import { tokenList } from '../../core/utils';\nimport { getRoleType } from '../../commons/aria';\n/**\n * Check if an element's `role` attribute uses any abstract role values.\n *\n * Abstract roles are taken from the `ariaRoles` standards object from the roles `type` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all abstract roles</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element uses an `abstract` role. False otherwise.\n */\nfunction abstractroleEvaluate(node, options, virtualNode) {\n  const abstractRoles = tokenList(virtualNode.attr('role')).filter(\n    role => getRoleType(role) === 'abstract'\n  );\n\n  if (abstractRoles.length > 0) {\n    this.data(abstractRoles);\n    return true;\n  }\n\n  return false;\n}\n\nexport default abstractroleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/abstractrole.json",
    "content": "{\n  \"id\": \"abstractrole\",\n  \"evaluate\": \"abstractrole-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Abstract roles are not used\",\n      \"fail\": {\n        \"singular\": \"Abstract role cannot be directly used: ${data.values}\",\n        \"plural\": \"Abstract roles cannot be directly used: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-allowed-attr-evaluate.js",
    "content": "import { uniqueArray, isHtmlElement } from '../../core/utils';\nimport { getRole, allowedAttr, validateAttr } from '../../commons/aria';\nimport { isFocusable } from '../../commons/dom';\n\n/**\n * Check if each ARIA attribute on an element is allowed for its semantic role.\n *\n * Allowed ARIA attributes are taken from the `ariaRoles` standards object combining the roles `requiredAttrs` and `allowedAttrs` properties, as well as any global ARIA attributes from the `ariaAttrs` standards object.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all unallowed aria attributes and their value</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if each aria attribute is allowed. False otherwise.\n */\nexport default function ariaAllowedAttrEvaluate(node, options, virtualNode) {\n  const invalid = [];\n  const role = getRole(virtualNode);\n  let allowed = allowedAttr(role);\n\n  // @deprecated: allowed attr options to pass more attrs.\n  // configure the standards spec instead\n  if (Array.isArray(options[role])) {\n    allowed = uniqueArray(options[role].concat(allowed));\n  }\n\n  // Unknown ARIA attributes are tested in aria-valid-attr\n  for (const attrName of virtualNode.attrNames) {\n    if (\n      validateAttr(attrName) &&\n      !allowed.includes(attrName) &&\n      !ignoredAttrs(attrName, virtualNode.attr(attrName), virtualNode)\n    ) {\n      invalid.push(attrName);\n    }\n  }\n\n  if (!invalid.length) {\n    return true;\n  }\n\n  this.data(\n    invalid.map(attrName => attrName + '=\"' + virtualNode.attr(attrName) + '\"')\n  );\n\n  if (!role && !isHtmlElement(virtualNode) && !isFocusable(virtualNode)) {\n    return undefined;\n  }\n  return false;\n}\n\nfunction ignoredAttrs(attrName, attrValue, vNode) {\n  // allow aria-required=false as screen readers consistently ignore it\n  // @see https://github.com/dequelabs/axe-core/issues/3756\n  if (attrName === 'aria-required' && attrValue === 'false') {\n    return true;\n  }\n\n  // allow aria-multiline=false when contenteditable is set\n  // @see https://github.com/dequelabs/axe-core/issues/4463\n  if (\n    attrName === 'aria-multiline' &&\n    attrValue === 'false' &&\n    vNode.hasAttr('contenteditable')\n  ) {\n    return true;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-allowed-attr.json",
    "content": "{\n  \"id\": \"aria-allowed-attr\",\n  \"evaluate\": \"aria-allowed-attr-evaluate\",\n  \"options\": {\n    \"validTreeRowAttrs\": [\n      \"aria-posinset\",\n      \"aria-setsize\",\n      \"aria-expanded\",\n      \"aria-level\"\n    ]\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA attributes are used correctly for the defined role\",\n      \"fail\": {\n        \"singular\": \"ARIA attribute is not allowed: ${data.values}\",\n        \"plural\": \"ARIA attributes are not allowed: ${data.values}\"\n      },\n      \"incomplete\": \"Check that there is no problem if the ARIA attribute is ignored on this element: ${data.values}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-allowed-role-evaluate.js",
    "content": "import { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getElementUnallowedRoles } from '../../commons/aria';\n\n/**\n * Check if an element is allowed to have its explicit role value.\n *\n * Allowed ARIA roles are taken from the `htmlElms` standards object from the elements `allowedRoles` property.\n *\n * @memberof checks\n * @param {Boolean} [options.allowImplicit=true] Allow the explicit role to match the implicit role of the element.\n * @param {String[]} [options.ignoredTags=[]] Do not check for allowed roles in the provided HTML elements list.\n * @data {String[]} List of all unallowed roles.\n * @return {Boolean} True if the role is allowed on the element. False otherwise.\n */\nfunction ariaAllowedRoleEvaluate(node, options = {}, virtualNode) {\n  /**\n   * Implements allowed roles defined at:\n   * https://www.w3.org/TR/html-aria/#docconformance\n   * https://www.w3.org/TR/SVG2/struct.html#implicit-aria-semantics\n   */\n  const { allowImplicit = true, ignoredTags = [] } = options;\n  const { nodeName } = virtualNode.props;\n\n  // check if the element should be ignored, by an user setting\n  if (ignoredTags.map(tag => tag.toLowerCase()).includes(nodeName)) {\n    return true;\n  }\n\n  const unallowedRoles = getElementUnallowedRoles(virtualNode, allowImplicit);\n  if (unallowedRoles.length) {\n    this.data(unallowedRoles);\n    if (!isVisibleToScreenReaders(virtualNode)) {\n      // flag hidden elements for review\n      return undefined;\n    }\n    return false;\n  }\n  return true;\n}\n\nexport default ariaAllowedRoleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-allowed-role.json",
    "content": "{\n  \"id\": \"aria-allowed-role\",\n  \"evaluate\": \"aria-allowed-role-evaluate\",\n  \"options\": {\n    \"allowImplicit\": true,\n    \"ignoredTags\": []\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"ARIA role is allowed for given element\",\n      \"fail\": {\n        \"singular\": \"ARIA role ${data.values} is not allowed for given element\",\n        \"plural\": \"ARIA roles ${data.values} are not allowed for given element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA role ${data.values} must be removed when the element is made visible, as it is not allowed for the element\",\n        \"plural\": \"ARIA roles ${data.values} must be removed when the element is made visible, as they are not allowed for the element\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-busy-evaluate.js",
    "content": "/**\n * Checks if element has aria-busy tag set to \"true\"\n *\n * @memberof checks\n * @return {Boolean} True if node has \"aria-busy\" attribute. False otherwise\n */\nexport default function ariaBusyEvaluate(node, options, virtualNode) {\n  return virtualNode.attr('aria-busy') === 'true';\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-busy.json",
    "content": "{\n  \"id\": \"aria-busy\",\n  \"evaluate\": \"aria-busy-evaluate\",\n  \"deprecated\": true,\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element has an aria-busy attribute\",\n      \"fail\": \"Element uses aria-busy=\\\"true\\\" while showing a loader\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-conditional-attr-evaluate.js",
    "content": "import getRole from '../../commons/aria/get-role';\nimport ariaConditionalCheckboxAttr from './aria-conditional-checkbox-attr-evaluate';\nimport ariaConditionalRowAttr from './aria-conditional-row-attr-evaluate';\n\nconst conditionalRoleMap = {\n  row: ariaConditionalRowAttr,\n  checkbox: ariaConditionalCheckboxAttr\n};\n\nexport default function ariaConditionalAttrEvaluate(\n  node,\n  options,\n  virtualNode\n) {\n  const role = getRole(virtualNode);\n  if (!conditionalRoleMap[role]) {\n    return true;\n  }\n  return conditionalRoleMap[role].call(this, node, options, virtualNode);\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-conditional-attr.json",
    "content": "{\n  \"id\": \"aria-conditional-attr\",\n  \"evaluate\": \"aria-conditional-attr-evaluate\",\n  \"options\": {\n    \"invalidTableRowAttrs\": [\n      \"aria-posinset\",\n      \"aria-setsize\",\n      \"aria-expanded\",\n      \"aria-level\"\n    ]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"ARIA attribute is allowed\",\n      \"fail\": {\n        \"checkbox\": \"Remove aria-checked, or set it to \\\"${data.checkState}\\\" to match the real checkbox state\",\n        \"rowSingular\": \"This attribute is supported with treegrid rows, but not ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"These attributes are supported with treegrid rows, but not ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-conditional-checkbox-attr-evaluate.js",
    "content": "export default function ariaConditionalCheckboxAttr(\n  node,\n  options,\n  virtualNode\n) {\n  const { nodeName, type } = virtualNode.props;\n  const ariaChecked = normalizeAriaChecked(virtualNode.attr('aria-checked'));\n  if (nodeName !== 'input' || type !== 'checkbox' || !ariaChecked) {\n    return true;\n  }\n\n  const checkState = getCheckState(virtualNode);\n  if (ariaChecked === checkState) {\n    return true;\n  }\n  this.data({\n    messageKey: 'checkbox',\n    checkState\n  });\n  return false;\n}\n\nfunction getCheckState(vNode) {\n  if (vNode.props.indeterminate) {\n    return 'mixed';\n  }\n  return vNode.props.checked ? 'true' : 'false';\n}\n\nfunction normalizeAriaChecked(ariaCheckedVal) {\n  if (!ariaCheckedVal) {\n    return '';\n  }\n  ariaCheckedVal = ariaCheckedVal.toLowerCase();\n  if (['mixed', 'true'].includes(ariaCheckedVal)) {\n    return ariaCheckedVal;\n  }\n  return 'false';\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-conditional-row-attr-evaluate.js",
    "content": "import getRole from '../../commons/aria/get-role';\nimport { closest } from '../../core/utils';\n\nexport default function ariaConditionalRowAttr(\n  node,\n  { invalidTableRowAttrs } = {},\n  virtualNode\n) {\n  const invalidAttrs =\n    invalidTableRowAttrs?.filter?.(invalidAttr => {\n      return virtualNode.hasAttr(invalidAttr);\n    }) ?? [];\n  if (invalidAttrs.length === 0) {\n    return true;\n  }\n\n  const owner = getRowOwner(virtualNode);\n  const ownerRole = owner && getRole(owner);\n  if (!ownerRole || ownerRole === 'treegrid') {\n    return true;\n  }\n\n  const messageKey = `row${invalidAttrs.length > 1 ? 'Plural' : 'Singular'}`;\n  this.data({ messageKey, invalidAttrs, ownerRole });\n  return false;\n}\n\nfunction getRowOwner(virtualNode) {\n  // check if the parent exists otherwise a TypeError will occur (virtual-nodes specifically)\n  if (!virtualNode.parent) {\n    return;\n  }\n  const rowOwnerQuery =\n    'table:not([role]), [role~=\"treegrid\"], [role~=\"table\"], [role~=\"grid\"]';\n  return closest(virtualNode, rowOwnerQuery);\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-errormessage-evaluate.js",
    "content": "import standards from '../../standards';\nimport { idrefs, isVisibleToScreenReaders } from '../../commons/dom';\nimport { tokenList } from '../../core/utils';\nimport { getExplicitRole } from '../../commons/aria';\n/**\n * Check if `aria-errormessage` references an element that also uses a technique to announce the message (aria-live, aria-describedby, etc.).\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>Mixed</code></td>\n *       <td>The value of the `aria-errormessage` attribute</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Mixed} True if aria-errormessage references an existing element that uses a supported technique. Undefined if it does not reference an existing element. False otherwise.\n */\nexport default function ariaErrormessageEvaluate(node, options, virtualNode) {\n  options = Array.isArray(options) ? options : [];\n\n  const errorMessageAttr = virtualNode.attr('aria-errormessage');\n  const hasAttr = virtualNode.hasAttr('aria-errormessage');\n  const invaid = virtualNode.attr('aria-invalid');\n  const hasInvallid = virtualNode.hasAttr('aria-invalid');\n\n  // pass if aria-invalid is not set or set to false as we don't\n  // need to check the referenced node since it is not applicable\n  if (!hasInvallid || invaid === 'false') {\n    return true;\n  }\n\n  function validateAttrValue(attr) {\n    if (attr.trim() === '') {\n      return standards.ariaAttrs['aria-errormessage'].allowEmpty;\n    }\n\n    const errormessageTokens = tokenList(attr);\n    if (errormessageTokens.length > 1) {\n      this.data({ messageKey: 'unsupported', values: errormessageTokens });\n      return false;\n    }\n    let idref;\n\n    try {\n      idref = attr && idrefs(virtualNode, 'aria-errormessage')[0];\n    } catch {\n      this.data({\n        messageKey: 'idrefs',\n        values: errormessageTokens\n      });\n      return undefined;\n    }\n\n    if (idref) {\n      if (!isVisibleToScreenReaders(idref)) {\n        this.data({\n          messageKey: 'hidden',\n          values: errormessageTokens\n        });\n        return false;\n      }\n      const describedbyTokens = tokenList(virtualNode.attr('aria-describedby'));\n      return (\n        getExplicitRole(idref) === 'alert' ||\n        idref.getAttribute('aria-live') === 'assertive' ||\n        idref.getAttribute('aria-live') === 'polite' ||\n        errormessageTokens.some(token => describedbyTokens.includes(token))\n      );\n    }\n\n    return;\n  }\n\n  // limit results to elements that actually have this attribute\n  if (options.indexOf(errorMessageAttr) === -1 && hasAttr) {\n    this.data(tokenList(errorMessageAttr));\n    return validateAttrValue.call(this, errorMessageAttr);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-errormessage.json",
    "content": "{\n  \"id\": \"aria-errormessage\",\n  \"evaluate\": \"aria-errormessage-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"aria-errormessage exists and references elements visible to screen readers that use a supported aria-errormessage technique\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage value `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\",\n        \"plural\": \"aria-errormessage values `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\",\n        \"unsupported\": \"Multiple IDs in aria-errormessage is not widely supported in assistive technologies\",\n        \"hidden\": \"aria-errormessage value `${data.values}` cannot reference a hidden element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Ensure aria-errormessage value `${data.values}` references an existing element\",\n        \"plural\": \"Ensure aria-errormessage values `${data.values}` reference existing elements\",\n        \"idrefs\": \"Unable to determine if aria-errormessage element exists on the page: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-hidden-body-evaluate.js",
    "content": "/**\n * Check that the element does not have the `aria-hidden` attribute.\n *\n * @memberof checks\n * @return {Boolean} True if the `aria-hidden` attribute is not present. False otherwise.\n */\nfunction ariaHiddenBodyEvaluate(node, options, virtualNode) {\n  return virtualNode.attr('aria-hidden') !== 'true';\n}\n\nexport default ariaHiddenBodyEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-hidden-body.json",
    "content": "{\n  \"id\": \"aria-hidden-body\",\n  \"evaluate\": \"aria-hidden-body-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"No aria-hidden attribute is present on document body\",\n      \"fail\": \"aria-hidden=true should not be present on the document body\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-level-evaluate.js",
    "content": "/**\n * Check that an element does not have aria-level values exceeding 6\n * VO and NVDA allow any positive value\n * JAWS and TalkBack will give the default value if level > 6\n * See browser/screenreader support research https://codepen.io/straker/pen/jOBjNNe\n * @memberof checks\n * @return {Boolean} Undefined if the element uses aria-level > 6. True otherwise.\n */\nfunction ariaLevelEvaluate(node, options, virtualNode) {\n  const ariaHeadingLevel = virtualNode.attr('aria-level');\n  const ariaLevel = parseInt(ariaHeadingLevel, 10);\n  if (ariaLevel > 6) {\n    return undefined;\n  }\n  return true;\n}\n\nexport default ariaLevelEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-level.json",
    "content": "{\n  \"id\": \"aria-level\",\n  \"evaluate\": \"aria-level-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-level values are valid\",\n      \"incomplete\": \"aria-level values greater than 6 are not supported in all screenreader and browser combinations\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-prohibited-attr-evaluate.js",
    "content": "import { getRole, getRoleType } from '../../commons/aria';\nimport { sanitize, subtreeText } from '../../commons/text';\nimport standards from '../../standards';\nimport memoize from '../../core/utils/memoize';\n\n/**\n * Check that an element does not use any prohibited ARIA attributes.\n *\n * Prohibited attributes are taken from the `ariaAttrs` standards object from the attributes `prohibitedAttrs` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all prohibited attributes</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element uses any prohibited ARIA attributes. False otherwise.\n */\nexport default function ariaProhibitedAttrEvaluate(\n  node,\n  options = {},\n  virtualNode\n) {\n  const elementsAllowedAriaLabel = options?.elementsAllowedAriaLabel || [];\n  const { nodeName } = virtualNode.props;\n  const role = getRole(virtualNode, {\n    chromium: true,\n    // this check allows fallback roles. For example, `<div role=\"foo img\" aria-label=\"...\">` is legal.\n    fallback: true\n  });\n\n  const prohibitedList = listProhibitedAttrs(\n    virtualNode,\n    role,\n    nodeName,\n    elementsAllowedAriaLabel\n  );\n  const prohibited = prohibitedList.filter(attrName => {\n    if (!virtualNode.attrNames.includes(attrName)) {\n      return false;\n    }\n    return sanitize(virtualNode.attr(attrName)) !== '';\n  });\n\n  if (prohibited.length === 0) {\n    return false;\n  }\n\n  let messageKey = role !== null ? 'hasRole' : 'noRole';\n  messageKey += prohibited.length > 1 ? 'Plural' : 'Singular';\n  this.data({ role, nodeName, messageKey, prohibited });\n\n  // `subtreeDescendant` to override namedFromContents\n  const textContent = subtreeText(virtualNode, { subtreeDescendant: true });\n  if (sanitize(textContent) !== '') {\n    // Don't fail if there is text content to announce\n    return undefined;\n  }\n  return true;\n}\n\nfunction listProhibitedAttrs(vNode, role, nodeName, elementsAllowedAriaLabel) {\n  const roleSpec = standards.ariaRoles[role];\n  if (roleSpec) {\n    return roleSpec.prohibitedAttrs || [];\n  }\n  if (\n    !!role ||\n    elementsAllowedAriaLabel.includes(nodeName) ||\n    getClosestAncestorRoleType(vNode) === 'widget'\n  ) {\n    return [];\n  }\n  return ['aria-label', 'aria-labelledby'];\n}\n\nconst getClosestAncestorRoleType = memoize(\n  function getClosestAncestorRoleTypeMemoized(vNode) {\n    if (!vNode) {\n      return;\n    }\n\n    const role = getRole(vNode, { noPresentational: true, chromium: true });\n    if (role) {\n      return getRoleType(role);\n    }\n\n    return getClosestAncestorRoleType(vNode.parent);\n  }\n);\n"
  },
  {
    "path": "lib/checks/aria/aria-prohibited-attr.json",
    "content": "{\n  \"id\": \"aria-prohibited-attr\",\n  \"evaluate\": \"aria-prohibited-attr-evaluate\",\n  \"options\": {\n    \"elementsAllowedAriaLabel\": [\"applet\", \"input\"]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"ARIA attribute is allowed\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} attributes cannot be used with role \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"${data.prohibited} attribute cannot be used with role \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"${data.prohibited} attributes cannot be used on a ${data.nodeName} with no valid role attribute.\",\n        \"noRoleSingular\": \"${data.prohibited} attribute cannot be used on a ${data.nodeName} with no valid role attribute.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"${data.prohibited} attribute is not well supported with role \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"${data.prohibited} attributes are not well supported with role \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"${data.prohibited} attribute is not well supported on a ${data.nodeName} with no valid role attribute.\",\n        \"noRolePlural\": \"${data.prohibited} attributes are not well supported on a ${data.nodeName} with no valid role attribute.\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-required-attr-evaluate.js",
    "content": "import {\n  requiredAttr as getRequiredAttrs,\n  getExplicitRole\n} from '../../commons/aria';\nimport { getElementSpec } from '../../commons/standards';\nimport { uniqueArray } from '../../core/utils';\nimport { isFocusable } from '../../commons/dom';\n\n/**\n * Check that the element has all required attributes for its explicit role.\n *\n * Required ARIA attributes are taken from the `ariaRoles` standards object from the roles `requiredAttrs` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all missing require attributes</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if all required attributes are present. False otherwise.\n */\nexport default function ariaRequiredAttrEvaluate(\n  node,\n  options = {},\n  virtualNode\n) {\n  const role = getExplicitRole(virtualNode);\n  const attrs = virtualNode.attrNames;\n  // @deprecated: required attr options to pass more attrs.\n  // configure the standards spec instead\n  let requiredAttrs = getRequiredAttrs(role);\n  if (Array.isArray(options[role])) {\n    requiredAttrs = uniqueArray(options[role], requiredAttrs);\n  }\n  // Nothing to test\n  if (!role || !attrs.length || !requiredAttrs.length) {\n    return true;\n  }\n  // Some required props are conditional:\n  if (\n    isStaticSeparator(virtualNode, role) ||\n    isClosedCombobox(virtualNode, role)\n  ) {\n    return true;\n  }\n  // Non-normative exception for things like media player seek slider.\n  // Tested to work in various screen readers.\n  if (role === 'slider' && virtualNode.attr('aria-valuetext')?.trim()) {\n    return true;\n  }\n\n  const elmSpec = getElementSpec(virtualNode);\n  const missingAttrs = requiredAttrs.filter(\n    requiredAttr =>\n      !virtualNode.attr(requiredAttr) && !hasImplicitAttr(elmSpec, requiredAttr)\n  );\n\n  if (missingAttrs.length) {\n    this.data(missingAttrs);\n    return false;\n  }\n  return true;\n}\n\nfunction isStaticSeparator(vNode, role) {\n  return role === 'separator' && !isFocusable(vNode);\n}\n\nfunction hasImplicitAttr(elmSpec, attr) {\n  return elmSpec.implicitAttrs?.[attr] !== undefined;\n}\n\nfunction isClosedCombobox(vNode, role) {\n  return role === 'combobox' && vNode.attr('aria-expanded') === 'false';\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-required-attr.json",
    "content": "{\n  \"id\": \"aria-required-attr\",\n  \"evaluate\": \"aria-required-attr-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"All required ARIA attributes are present\",\n      \"fail\": {\n        \"singular\": \"Required ARIA attribute not present: ${data.values}\",\n        \"plural\": \"Required ARIA attributes not present: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-required-children-evaluate.js",
    "content": "import {\n  requiredOwned,\n  getRole,\n  getExplicitRole,\n  getOwnedVirtual\n} from '../../commons/aria';\nimport { getGlobalAriaAttrs } from '../../commons/standards';\nimport {\n  hasContentVirtual,\n  isFocusable,\n  isVisibleToScreenReaders\n} from '../../commons/dom';\n\n/**\n * Check that an element owns all required children for its explicit role.\n *\n * Required roles are taken from the `ariaRoles` standards object from the roles `requiredOwned` property.\n *\n * @memberof checks\n * @param {Boolean} options.reviewEmpty List of ARIA roles that should be flagged as \"Needs Review\" rather than a violation if the element has no owned children.\n * @data {String[]} List of all missing owned roles.\n * @returns {Mixed} True if the element owns all required roles. Undefined if `options.reviewEmpty=true` and the element has no owned children. False otherwise.\n */\nexport default function ariaRequiredChildrenEvaluate(\n  node,\n  options,\n  virtualNode\n) {\n  const reviewEmpty =\n    options && Array.isArray(options.reviewEmpty) ? options.reviewEmpty : [];\n  const explicitRole = getExplicitRole(virtualNode, { dpub: true });\n  const required = requiredOwned(explicitRole);\n  if (required === null) {\n    return true;\n  }\n\n  const ownedRoles = getOwnedRoles(virtualNode, required);\n  const unallowed = ownedRoles.filter(\n    ({ role, vNode }) => vNode.props.nodeType === 1 && !required.includes(role)\n  );\n\n  if (unallowed.length) {\n    this.relatedNodes(unallowed.map(({ vNode }) => vNode));\n    const messageKey =\n      virtualNode.attr('aria-busy') === 'true' ? 'aria-busy-fail' : 'unallowed';\n\n    this.data({\n      messageKey,\n      values: unallowed\n        .map(({ vNode, attr }) => getUnallowedSelector(vNode, attr))\n        .filter((selector, index, array) => array.indexOf(selector) === index)\n        .join(', ')\n    });\n    return false;\n  }\n\n  if (hasRequiredChildren(required, ownedRoles)) {\n    return true;\n  }\n\n  if (virtualNode.attr('aria-busy') === 'true') {\n    this.data({ messageKey: 'aria-busy' });\n    return true;\n  }\n\n  this.data(required);\n\n  // Only review empty nodes when a node is both empty and does not have an aria-owns relationship\n  if (reviewEmpty.includes(explicitRole) && !ownedRoles.some(isContent)) {\n    return undefined;\n  }\n\n  return false;\n}\n\n/**\n * Get all owned roles of an element\n */\nfunction getOwnedRoles(virtualNode, required) {\n  let vNode;\n  const ownedRoles = [];\n  const ownedVirtual = getOwnedVirtual(virtualNode);\n  while ((vNode = ownedVirtual.shift())) {\n    if (vNode.props.nodeType === 3) {\n      ownedRoles.push({ vNode, role: null });\n    }\n    if (vNode.props.nodeType !== 1 || !isVisibleToScreenReaders(vNode)) {\n      continue;\n    }\n\n    const role = getRole(vNode, { noPresentational: true });\n    const globalAriaAttr = getGlobalAriaAttr(vNode);\n    const hasGlobalAriaOrFocusable = !!globalAriaAttr || isFocusable(vNode);\n\n    // if owned node has no role or is presentational, or if role\n    // allows group or rowgroup, we keep parsing the descendant tree.\n    // this means intermediate roles between a required parent and\n    // child will fail the check\n    if (\n      (!role && !hasGlobalAriaOrFocusable) ||\n      (['group', 'rowgroup'].includes(role) &&\n        required.some(requiredRole => requiredRole === role))\n    ) {\n      ownedVirtual.push(...vNode.children);\n    } else if (role || hasGlobalAriaOrFocusable) {\n      const attr = globalAriaAttr || 'tabindex';\n      ownedRoles.push({ role, attr, vNode });\n    }\n  }\n\n  return ownedRoles;\n}\n\n/**\n * See if any required roles are in the ownedRoles array\n */\nfunction hasRequiredChildren(required, ownedRoles) {\n  return ownedRoles.some(({ role }) => role && required.includes(role));\n}\n\n/**\n * Get the first global ARIA attribute the element has.\n * @param {VirtualNode} vNode\n * @return {String|null}\n */\nfunction getGlobalAriaAttr(vNode) {\n  return getGlobalAriaAttrs().find(attr => vNode.hasAttr(attr));\n}\n\n/**\n * Return a simple selector for an unallowed element.\n * @param {VirtualNode} vNode\n * @param {String} [attr] - Optional attribute which made the element unallowed\n * @return {String}\n */\nfunction getUnallowedSelector(vNode, attr) {\n  const { nodeName, nodeType } = vNode.props;\n  if (nodeType === 3) {\n    return `#text`;\n  }\n\n  const role = getExplicitRole(vNode, { dpub: true });\n  if (role) {\n    return `[role=${role}]`;\n  }\n  if (attr) {\n    return nodeName + `[${attr}]`;\n  }\n  return nodeName;\n}\n\n/**\n * Check if the node has content, or is itself content\n * @Object {Object} OwnedRole\n * @property {VirtualNode} vNode\n * @returns {Boolean}\n */\nfunction isContent({ vNode }) {\n  if (vNode.props.nodeType === 3) {\n    return vNode.props.nodeValue.trim().length > 0;\n  }\n  return hasContentVirtual(vNode, false, true);\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-required-children.json",
    "content": "{\n  \"id\": \"aria-required-children\",\n  \"evaluate\": \"aria-required-children-evaluate\",\n  \"options\": {\n    \"reviewEmpty\": [\n      \"doc-bibliography\",\n      \"doc-endnotes\",\n      \"grid\",\n      \"list\",\n      \"listbox\",\n      \"menu\",\n      \"menubar\",\n      \"table\",\n      \"tablist\",\n      \"tree\",\n      \"treegrid\",\n      \"rowgroup\"\n    ]\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": {\n        \"default\": \"Required ARIA children are present\",\n        \"aria-busy\": \"Element has an aria-busy attribute, so it is allowed to omit required children\"\n      },\n      \"fail\": {\n        \"singular\": \"Required ARIA child role not present: ${data.values}\",\n        \"plural\": \"Required ARIA children role not present: ${data.values}\",\n        \"unallowed\": \"Element has children which are not allowed: ${data.values}\",\n        \"aria-busy-fail\": \"Element has children which are not allowed: ${data.values}; Having aria-busy=\\\"true\\\" does not allow children with roles that are not allowed\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Expecting ARIA child role to be added: ${data.values}\",\n        \"plural\": \"Expecting ARIA children role to be added: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-required-parent-evaluate.js",
    "content": "import { getExplicitRole, getRole, requiredContext } from '../../commons/aria';\nimport { getRootNode } from '../../commons/dom';\nimport { getNodeFromTree, escapeSelector } from '../../core/utils';\n\nfunction getMissingContext(\n  virtualNode,\n  ownGroupRoles,\n  reqContext,\n  includeElement\n) {\n  const explicitRole = getExplicitRole(virtualNode);\n\n  if (!reqContext) {\n    reqContext = requiredContext(explicitRole);\n  }\n\n  if (!reqContext) {\n    return null;\n  }\n\n  const allowsGroup = reqContext.includes('group');\n  let vNode = includeElement ? virtualNode : virtualNode.parent;\n\n  while (vNode) {\n    const role = getRole(vNode, { noPresentational: true });\n\n    // if parent node has no role or is presentational, or if role\n    // allows group, we keep parsing the parent tree.\n    // this means intermediate roles between a required parent and\n    // child will fail the check\n    if (!role) {\n      vNode = vNode.parent;\n    } else if (role === 'group' && allowsGroup) {\n      // Allow the own role; i.e. tree > treeitem > group > treeitem\n      if (ownGroupRoles.includes(explicitRole)) {\n        reqContext.push(explicitRole);\n      }\n      reqContext = reqContext.filter(r => r !== 'group');\n      vNode = vNode.parent;\n    } else if (reqContext.includes(role)) {\n      return null;\n    } else {\n      return reqContext;\n    }\n  }\n\n  return reqContext;\n}\n\nfunction getAriaOwners(element) {\n  const owners = [];\n  let o = null;\n\n  while (element) {\n    if (element.getAttribute('id')) {\n      const id = escapeSelector(element.getAttribute('id'));\n      const doc = getRootNode(element);\n      o = doc.querySelector(`[aria-owns~=${id}]`);\n      if (o) {\n        owners.push(o);\n      }\n    }\n    element = element.parentElement;\n  }\n\n  return owners.length ? owners : null;\n}\n\n/**\n * Check if the element has a parent with a required role.\n *\n * Required parent roles are taken from the `ariaRoles` standards object from the roles `requiredContext` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all missing required parent roles</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element has a parent with a required role. False otherwise.\n */\nfunction ariaRequiredParentEvaluate(node, options, virtualNode) {\n  const ownGroupRoles =\n    options && Array.isArray(options.ownGroupRoles)\n      ? options.ownGroupRoles\n      : [];\n  let missingParents = getMissingContext(virtualNode, ownGroupRoles);\n\n  if (!missingParents) {\n    return true;\n  }\n  const owners = getAriaOwners(node);\n  if (owners) {\n    for (let i = 0, l = owners.length; i < l; i++) {\n      missingParents = getMissingContext(\n        getNodeFromTree(owners[i]),\n        ownGroupRoles,\n        missingParents,\n        true\n      );\n      if (!missingParents) {\n        return true;\n      }\n    }\n  }\n\n  this.data(missingParents);\n  return false;\n}\n\nexport default ariaRequiredParentEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-required-parent.json",
    "content": "{\n  \"id\": \"aria-required-parent\",\n  \"evaluate\": \"aria-required-parent-evaluate\",\n  \"options\": {\n    \"ownGroupRoles\": [\"listitem\", \"treeitem\"]\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Required ARIA parent role present\",\n      \"fail\": {\n        \"singular\": \"Required ARIA parent role not present: ${data.values}\",\n        \"plural\": \"Required ARIA parents role not present: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-roledescription-evaluate.js",
    "content": "import { getRole } from '../../commons/aria';\n\n/**\n * Check that `aria-roledescription` is used on a supported semantic role.\n *\n * @memberof checks\n * @param {String[]} options.supportedRoles List of ARIA roles that support the `aria-roledescription` attribute\n * @return {Mixed} True if the semantic role supports `aria-roledescription`. Undefined if the semantic role is `presentation` or `none`. False otherwise.\n */\nfunction ariaRoledescriptionEvaluate(node, options = {}, virtualNode) {\n  const role = getRole(virtualNode);\n  const supportedRoles = options.supportedRoles || [];\n\n  if (supportedRoles.includes(role)) {\n    return true;\n  }\n\n  if (role && role !== 'presentation' && role !== 'none') {\n    return undefined;\n  }\n\n  return false;\n}\n\nexport default ariaRoledescriptionEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-roledescription.json",
    "content": "{\n  \"id\": \"aria-roledescription\",\n  \"evaluate\": \"aria-roledescription-evaluate\",\n  \"options\": {\n    \"supportedRoles\": [\n      \"button\",\n      \"img\",\n      \"checkbox\",\n      \"radio\",\n      \"combobox\",\n      \"menuitemcheckbox\",\n      \"menuitemradio\"\n    ]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-roledescription used on a supported semantic role\",\n      \"incomplete\": \"Check that the aria-roledescription is announced by supported screen readers\",\n      \"fail\": \"Give the element a role that supports aria-roledescription\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-unsupported-attr-evaluate.js",
    "content": "import { validateAttr } from '../../commons/aria';\nimport standards from '../../standards';\nimport matches from '../../commons/matches';\n\n/**\n * Check that an element does not use any unsupported ARIA attributes.\n *\n * Unsupported attributes are taken from the `ariaAttrs` standards object from the attributes `unsupported` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all unsupported attributes</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element does not use any unsupported ARIA attributes. False otherwise.\n */\nfunction ariaUnsupportedAttrEvaluate(node, options, virtualNode) {\n  const unsupportedAttrs = virtualNode.attrNames.filter(name => {\n    const attribute = standards.ariaAttrs[name];\n\n    if (!validateAttr(name)) {\n      return false;\n    }\n\n    const { unsupported } = attribute;\n\n    if (typeof unsupported !== 'object') {\n      return !!unsupported;\n    }\n\n    return !matches(node, unsupported.exceptions);\n  });\n\n  if (unsupportedAttrs.length) {\n    this.data(unsupportedAttrs);\n    return true;\n  }\n  return false;\n}\n\nexport default ariaUnsupportedAttrEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-unsupported-attr.json",
    "content": "{\n  \"id\": \"aria-unsupported-attr\",\n  \"evaluate\": \"aria-unsupported-attr-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA attribute is supported\",\n      \"fail\": \"ARIA attribute is not widely supported in screen readers and assistive technologies: ${data.values}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-valid-attr-evaluate.js",
    "content": "import { validateAttr } from '../../commons/aria';\n\n/**\n * Check that each `aria-` attribute on an element is a valid ARIA attribute.\n *\n * Valid ARIA attributes are listed in the `ariaAttrs` standards object.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all invalid attributes</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if all `aria-` attributes are valid. False otherwise.\n */\nfunction ariaValidAttrEvaluate(node, options, virtualNode) {\n  options = Array.isArray(options.value) ? options.value : [];\n\n  const invalid = [];\n  const aria = /^aria-/;\n\n  virtualNode.attrNames.forEach(attr => {\n    if (\n      options.indexOf(attr) === -1 &&\n      aria.test(attr) &&\n      !validateAttr(attr)\n    ) {\n      invalid.push(attr);\n    }\n  });\n\n  if (invalid.length) {\n    this.data(invalid);\n    return false;\n  }\n\n  return true;\n}\n\nexport default ariaValidAttrEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/aria-valid-attr-value-evaluate.js",
    "content": "import { validateAttrValue } from '../../commons/aria';\nimport standards from '../../standards';\n\n/**\n * Check that each ARIA attribute on an element has a valid value.\n *\n * Valid ARIA attribute values are taken from the `ariaAttrs` standards object from an attributes `type` property.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>Mixed</code></td>\n *       <td>Object with Strings `messageKey` and `needsReview` if `aria-current` or `aria-describedby` are invalid. Otherwise a list of all invalid ARIA attributes and their value</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Mixed} True if all ARIA attributes have a valid value. Undefined for invalid `aria-current` or `aria-describedby` values. False otherwise.\n */\nexport default function ariaValidAttrValueEvaluate(node, options, virtualNode) {\n  options = Array.isArray(options.value) ? options.value : [];\n\n  let needsReview = '';\n  let messageKey = '';\n  const invalid = [];\n  const aria = /^aria-/;\n  const skipAttrs = ['aria-errormessage'];\n\n  const preChecks = {\n    // aria-controls should only check if element exists if the element\n    // doesn't have aria-expanded=false, aria-selected=false (tabs),\n    // or aria-haspopup (may load later)\n    // @see https://github.com/dequelabs/axe-core/issues/1463\n    // @see https://github.com/dequelabs/axe-core/issues/4363\n    'aria-controls': () => {\n      const hasPopup =\n        ['false', null].includes(virtualNode.attr('aria-haspopup')) === false;\n\n      if (hasPopup) {\n        needsReview = `aria-controls=\"${virtualNode.attr('aria-controls')}\"`;\n        messageKey = 'controlsWithinPopup';\n      }\n\n      return (\n        virtualNode.attr('aria-expanded') !== 'false' &&\n        virtualNode.attr('aria-selected') !== 'false' &&\n        hasPopup === false\n      );\n    },\n    // aria-current should mark as needs review if any value is used that is\n    // not one of the valid values (since any value is treated as \"true\")\n    'aria-current': validValue => {\n      if (!validValue) {\n        needsReview = `aria-current=\"${virtualNode.attr('aria-current')}\"`;\n        messageKey = 'ariaCurrent';\n      }\n\n      return;\n    },\n    // aria-owns should only check if element exists if the element\n    // doesn't have aria-expanded=false (combobox)\n    // @see https://github.com/dequelabs/axe-core/issues/1524\n    'aria-owns': () => {\n      return virtualNode.attr('aria-expanded') !== 'false';\n    },\n    // aria-describedby should not mark missing element as violation but\n    // instead as needs review\n    // @see https://github.com/dequelabs/axe-core/issues/1151\n    'aria-describedby': validValue => {\n      if (!validValue) {\n        needsReview = `aria-describedby=\"${virtualNode.attr(\n          'aria-describedby'\n        )}\"`;\n        // TODO: es-modules_tree\n        messageKey =\n          axe._tree && axe._tree[0]._hasShadowRoot ? 'noIdShadow' : 'noId';\n      }\n\n      return;\n    },\n    // aria-labelledby should not mark missing element as violation but\n    // instead as needs review\n    // @see https://github.com/dequelabs/axe-core/issues/2621\n    'aria-labelledby': validValue => {\n      if (!validValue) {\n        needsReview = `aria-labelledby=\"${virtualNode.attr(\n          'aria-labelledby'\n        )}\"`;\n        // TODO: es-modules_tree\n        messageKey =\n          axe._tree && axe._tree[0]._hasShadowRoot ? 'noIdShadow' : 'noId';\n      }\n    }\n  };\n\n  virtualNode.attrNames.forEach(attrName => {\n    if (\n      skipAttrs.includes(attrName) ||\n      options.includes(attrName) ||\n      !aria.test(attrName)\n    ) {\n      return;\n    }\n\n    let validValue;\n    const attrValue = virtualNode.attr(attrName);\n\n    try {\n      validValue = validateAttrValue(virtualNode, attrName);\n    } catch {\n      needsReview = `${attrName}=\"${attrValue}\"`;\n      messageKey = 'idrefs';\n      return;\n    }\n\n    if (\n      (preChecks[attrName] ? preChecks[attrName](validValue) : true) &&\n      !validValue\n    ) {\n      if (attrValue === '' && !isStringType(attrName)) {\n        needsReview = attrName;\n        messageKey = 'empty';\n      } else {\n        invalid.push(`${attrName}=\"${attrValue}\"`);\n      }\n    }\n  });\n\n  if (invalid.length) {\n    this.data(invalid);\n    return false;\n  }\n\n  if (needsReview) {\n    this.data({ messageKey, needsReview });\n    return undefined;\n  }\n\n  return true;\n}\n\nfunction isStringType(attrName) {\n  return standards.ariaAttrs[attrName]?.type === 'string';\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-valid-attr-value.json",
    "content": "{\n  \"id\": \"aria-valid-attr-value\",\n  \"evaluate\": \"aria-valid-attr-value-evaluate\",\n  \"options\": [],\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA attribute values are valid\",\n      \"fail\": {\n        \"singular\": \"Invalid ARIA attribute value: ${data.values}\",\n        \"plural\": \"Invalid ARIA attribute values: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"ARIA attribute element ID does not exist on the page: ${data.needsReview}\",\n        \"noIdShadow\": \"ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA attribute value is invalid and will be treated as \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}\",\n        \"empty\": \"ARIA attribute value is ignored while empty: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/aria-valid-attr.json",
    "content": "{\n  \"id\": \"aria-valid-attr\",\n  \"evaluate\": \"aria-valid-attr-evaluate\",\n  \"options\": [],\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA attribute name is valid\",\n      \"fail\": {\n        \"singular\": \"Invalid ARIA attribute name: ${data.values}\",\n        \"plural\": \"Invalid ARIA attribute names: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/braille-label-equivalent-evaluate.js",
    "content": "import { sanitize, accessibleTextVirtual } from '../../commons/text';\n\n/**\n * Check that if aria-braillelabel is not empty, the element has an accessible text\n * @memberof checks\n * @return {Boolean}\n */\nexport default function brailleLabelEquivalentEvaluate(\n  node,\n  options,\n  virtualNode\n) {\n  const brailleLabel = virtualNode.attr('aria-braillelabel') ?? '';\n  if (!brailleLabel.trim()) {\n    return true;\n  }\n  try {\n    return sanitize(accessibleTextVirtual(virtualNode)) !== '';\n  } catch {\n    return undefined;\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/braille-label-equivalent.json",
    "content": "{\n  \"id\": \"braille-label-equivalent\",\n  \"evaluate\": \"braille-label-equivalent-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-braillelabel is used on an element with accessible text\",\n      \"fail\": \"aria-braillelabel is used on an element with no accessible text\",\n      \"incomplete\": \"Unable to compute accessible text\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/braille-roledescription-equivalent-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\n\n/**\n * Check that if aria-brailleroledescription is not empty,\n * the element has a non-empty aria-roledescription\n * @memberof checks\n * @return {Boolean}\n */\nexport default function brailleRoleDescriptionEquivalentEvaluate(\n  node,\n  options,\n  virtualNode\n) {\n  const brailleRoleDesc = virtualNode.attr('aria-brailleroledescription') ?? '';\n  if (sanitize(brailleRoleDesc) === '') {\n    return true;\n  }\n  const roleDesc = virtualNode.attr('aria-roledescription');\n  if (typeof roleDesc !== 'string') {\n    this.data({ messageKey: 'noRoleDescription' });\n    return false;\n  }\n\n  if (sanitize(roleDesc) === '') {\n    this.data({ messageKey: 'emptyRoleDescription' });\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/checks/aria/braille-roledescription-equivalent.json",
    "content": "{\n  \"id\": \"braille-roledescription-equivalent\",\n  \"evaluate\": \"braille-roledescription-equivalent-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-brailleroledescription is used on an element with aria-roledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription is used on an element with no aria-roledescription\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription is used on an element with an empty aria-roledescription\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/deprecatedrole-evaluate.js",
    "content": "import standards from '../../standards';\nimport { getRole } from '../../commons/aria';\n\n/**\n * Check that an elements semantic role is deprecated.\n *\n * Deprecated roles are taken from the `ariaRoles` standards object from the roles `deprecated` property.\n *\n * @memberof checks\n * @return {Boolean} True if the elements semantic role is deprecated. False otherwise.\n */\nexport default function deprecatedroleEvaluate(node, options, virtualNode) {\n  const role = getRole(virtualNode, { dpub: true, fallback: true });\n  const roleDefinition = standards.ariaRoles[role];\n  if (!roleDefinition?.deprecated) {\n    return false;\n  }\n\n  this.data(role);\n  return true;\n}\n"
  },
  {
    "path": "lib/checks/aria/deprecatedrole.json",
    "content": "{\n  \"id\": \"deprecatedrole\",\n  \"evaluate\": \"deprecatedrole-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"ARIA role is not deprecated\",\n      \"fail\": \"The role used is deprecated: ${data}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/fallbackrole-evaluate.js",
    "content": "import { tokenList } from '../../core/utils';\nimport { getImplicitRole } from '../../commons/aria';\n\n/**\n * NOTE: This check is no longer used, but it was not deprecated\n * so it can be used in custom rulesets.\n * @return {Boolean} True if the element has no implicit role and uses both none and presentation as explicit roles\n */\nfunction nonePresentationOnElementWithNoImplicitRole(\n  virtualNode,\n  explicitRoles\n) {\n  const hasImplicitRole = getImplicitRole(virtualNode);\n  return (\n    !hasImplicitRole &&\n    explicitRoles.length === 2 &&\n    explicitRoles.includes('none') &&\n    explicitRoles.includes('presentation')\n  );\n}\n/**\n * Check that an element does not use more than one explicit role.\n *\n * @memberof checks\n * @return {Boolean} True if the element uses more than one explicit role. False otherwise.\n */\nfunction fallbackroleEvaluate(node, options, virtualNode) {\n  const explicitRoles = tokenList(virtualNode.attr('role'));\n  if (explicitRoles.length <= 1) {\n    return false;\n  }\n  return nonePresentationOnElementWithNoImplicitRole(virtualNode, explicitRoles)\n    ? undefined\n    : true;\n}\n\nexport default fallbackroleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/fallbackrole.json",
    "content": "{\n  \"id\": \"fallbackrole\",\n  \"evaluate\": \"fallbackrole-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Only one role value used\",\n      \"fail\": \"Use only one role value, since fallback roles are not supported in older browsers\",\n      \"incomplete\": \"Use only role 'presentation' or 'none' since they are synonymous.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/has-global-aria-attribute-evaluate.js",
    "content": "import getGlobalAriaAttrs from '../../commons/standards/get-global-aria-attrs';\n\nfunction hasGlobalAriaAttributeEvaluate(node, options, virtualNode) {\n  const globalAttrs = getGlobalAriaAttrs().filter(attr =>\n    virtualNode.hasAttr(attr)\n  );\n  this.data(globalAttrs);\n  return globalAttrs.length > 0;\n}\n\nexport default hasGlobalAriaAttributeEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/has-global-aria-attribute.json",
    "content": "{\n  \"id\": \"has-global-aria-attribute\",\n  \"evaluate\": \"has-global-aria-attribute-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": {\n        \"singular\": \"Element has global ARIA attribute: ${data.values}\",\n        \"plural\": \"Element has global ARIA attributes: ${data.values}\"\n      },\n      \"fail\": \"Element does not have global ARIA attribute\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/has-widget-role-evaluate.js",
    "content": "import { getRoleType, getExplicitRole } from '../../commons/aria';\n\n/**\n * Check if an elements `role` attribute uses any widget or composite role values.\n *\n * Widget roles are taken from the `ariaRoles` standards object from the roles `type` property.\n *\n * @memberof checks\n * @return {Boolean} True if the element uses a `widget` or `composite` role. False otherwise.\n */\nfunction hasWidgetRoleEvaluate(node, options, virtualNode) {\n  const role = getExplicitRole(virtualNode);\n  if (role === null) {\n    return false;\n  }\n  const roleType = getRoleType(role);\n  return roleType === 'widget' || roleType === 'composite';\n}\n\nexport default hasWidgetRoleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/has-widget-role.json",
    "content": "{\n  \"id\": \"has-widget-role\",\n  \"evaluate\": \"has-widget-role-evaluate\",\n  \"options\": [],\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element has a widget role.\",\n      \"fail\": \"Element does not have a widget role.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/invalidrole-evaluate.js",
    "content": "import { isValidRole } from '../../commons/aria';\nimport { tokenList } from '../../core/utils';\n\n/**\n * Check that each role on an element is a valid ARIA role.\n *\n * Valid ARIA roles are listed in the `ariaRoles` standards object.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String[]</code></td>\n *       <td>List of all invalid roles</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Boolean} True if the element uses an invalid role. False otherwise.\n */\nfunction invalidroleEvaluate(node, options, virtualNode) {\n  const allRoles = tokenList(virtualNode.attr('role'));\n  const allInvalid = allRoles.every(\n    role => !isValidRole(role.toLowerCase(), { allowAbstract: true })\n  );\n\n  /**\n   * Only fail when all the roles are invalid\n   */\n  if (allInvalid) {\n    this.data(allRoles);\n    return true;\n  }\n\n  return false;\n}\n\nexport default invalidroleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/invalidrole.json",
    "content": "{\n  \"id\": \"invalidrole\",\n  \"evaluate\": \"invalidrole-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA role is valid\",\n      \"fail\": {\n        \"singular\": \"Role must be one of the valid ARIA roles: ${data.values}\",\n        \"plural\": \"Roles must be one of the valid ARIA roles: ${data.values}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/is-element-focusable-evaluate.js",
    "content": "import { isFocusable } from '../../commons/dom';\n\nfunction isElementFocusableEvaluate(node, options, virtualNode) {\n  return isFocusable(virtualNode);\n}\n\nexport default isElementFocusableEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/is-element-focusable.json",
    "content": "{\n  \"id\": \"is-element-focusable\",\n  \"evaluate\": \"is-element-focusable-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element is focusable.\",\n      \"fail\": \"Element is not focusable.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/no-implicit-explicit-label-evaluate.js",
    "content": "import { getRole } from '../../commons/aria';\nimport { sanitize, labelText, accessibleTextVirtual } from '../../commons/text';\n\n/**\n * Check that an elements explicit label matches its accessible name.\n *\n * ##### Data:\n * <table class=\"props\">\n *   <thead>\n *     <tr>\n *       <th>Type</th>\n *       <th>Description</th>\n *     </tr>\n *   </thead>\n *   <tbody>\n *     <tr>\n *       <td><code>String</code></td>\n *       <td>The elements explicit role</td>\n *     </tr>\n *   </tbody>\n * </table>\n *\n * @memberof checks\n * @return {Mixed} False if the element does not have an explicit label and accessible name or if there is no mismatch between them. Undefined if the element has an explicit label but no accessible name or if the accessible name does not include the label text.\n */\nfunction noImplicitExplicitLabelEvaluate(node, options, virtualNode) {\n  const role = getRole(virtualNode, { noImplicit: true });\n  this.data(role);\n\n  let label;\n  let accText;\n  try {\n    label = sanitize(labelText(virtualNode)).toLowerCase();\n    accText = sanitize(accessibleTextVirtual(virtualNode)).toLowerCase();\n  } catch {\n    return undefined;\n  }\n\n  if (!accText && !label) {\n    return false;\n  }\n\n  if (!accText && label) {\n    return undefined;\n  }\n\n  if (!accText.includes(label)) {\n    return undefined;\n  }\n\n  return false;\n}\n\nexport default noImplicitExplicitLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/no-implicit-explicit-label.json",
    "content": "{\n  \"id\": \"no-implicit-explicit-label\",\n  \"evaluate\": \"no-implicit-explicit-label-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"There is no mismatch between a <label> and accessible name\",\n      \"incomplete\": \"Check that the <label> does not need be part of the ARIA ${data} field's name\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/unsupportedrole-evaluate.js",
    "content": "import { isUnsupportedRole, getRole } from '../../commons/aria';\n\n/**\n * Check that an elements semantic role is unsupported.\n *\n * Unsupported roles are taken from the `ariaRoles` standards object from the roles `unsupported` property.\n *\n * @memberof checks\n * @return {Boolean} True if the elements semantic role is unsupported. False otherwise.\n */\nfunction unsupportedroleEvaluate(node, options, virtualNode) {\n  const role = getRole(virtualNode, { dpub: true, fallback: true });\n  const isUnsupported = isUnsupportedRole(role);\n  if (isUnsupported) {\n    this.data(role);\n  }\n  return isUnsupported;\n}\n\nexport default unsupportedroleEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/unsupportedrole.json",
    "content": "{\n  \"id\": \"unsupportedrole\",\n  \"evaluate\": \"unsupportedrole-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"ARIA role is supported\",\n      \"fail\": \"The role used is not widely supported in screen readers and assistive technologies: ${data}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/aria/valid-scrollable-semantics-evaluate.js",
    "content": "import { getExplicitRole } from '../../commons/aria';\n\n/**\n * A map from HTML tag names to a boolean which reflects whether it is\n * appropriate for scrollable elements found in the focus order.\n */\nconst VALID_TAG_NAMES_FOR_SCROLLABLE_REGIONS = {\n  ARTICLE: true,\n  ASIDE: true,\n  NAV: true,\n  SECTION: true\n};\n\n/**\n * A map from each landmark role to a boolean which reflects whether it is\n * appropriate for scrollable elements found in the focus order.\n */\nconst VALID_ROLES_FOR_SCROLLABLE_REGIONS = {\n  alert: true,\n  alertdialog: true,\n  application: true,\n  article: true,\n  banner: false,\n  complementary: true,\n  contentinfo: true,\n  dialog: true,\n  form: true,\n  log: true,\n  main: true,\n  navigation: true,\n  region: true,\n  search: false,\n  status: true,\n  tabpanel: true\n};\n\n/**\n * @param {HTMLElement} node\n * @return {Boolean} Whether the element has a tag appropriate for a scrollable\n *\t\t region.\n */\nfunction validScrollableTagName(node) {\n  // Some elements with nonsensical roles will pass this check, but should be\n  // flagged by other checks.\n  const nodeName = node.nodeName.toUpperCase();\n  return VALID_TAG_NAMES_FOR_SCROLLABLE_REGIONS[nodeName] || false;\n}\n\n/**\n * @param {HTMLElement} node\n * @return {Boolean} Whether the node has a role appropriate for a scrollable\n *\t\t region.\n */\nfunction validScrollableRole(node, options) {\n  const role = getExplicitRole(node);\n  if (!role) {\n    return false;\n  }\n  return (\n    VALID_ROLES_FOR_SCROLLABLE_REGIONS[role] ||\n    options.roles.includes(role) ||\n    false\n  );\n}\n\n/**\n * Check if the element has a valid scrollable role or tag.\n *\n * @memberof checks\n * @param {HTMLElement} node\n * @return {Boolean} True if the elements role or tag name is a valid scrollable region. False otherwise.\n */\nfunction validScrollableSemanticsEvaluate(node, options) {\n  return validScrollableRole(node, options) || validScrollableTagName(node);\n}\n\nexport default validScrollableSemanticsEvaluate;\n"
  },
  {
    "path": "lib/checks/aria/valid-scrollable-semantics.json",
    "content": "{\n  \"id\": \"valid-scrollable-semantics\",\n  \"evaluate\": \"valid-scrollable-semantics-evaluate\",\n  \"options\": {\n    \"roles\": [\"tooltip\"]\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element has valid semantics for an element in the focus order.\",\n      \"fail\": \"Element has invalid semantics for an element in the focus order.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/color/color-contrast-enhanced.json",
    "content": "{\n  \"id\": \"color-contrast-enhanced\",\n  \"evaluate\": \"color-contrast-evaluate\",\n  \"options\": {\n    \"ignoreUnicode\": true,\n    \"ignoreLength\": false,\n    \"ignorePseudo\": false,\n    \"boldValue\": 700,\n    \"boldTextPt\": 14,\n    \"largeTextPt\": 18,\n    \"contrastRatio\": {\n      \"normal\": {\n        \"expected\": 7,\n        \"minThreshold\": 4.5\n      },\n      \"large\": {\n        \"expected\": 4.5,\n        \"minThreshold\": 3\n      }\n    },\n    \"pseudoSizeThreshold\": 0.25,\n    \"shadowOutlineEmMax\": 0.1,\n    \"textStrokeEmMin\": 0.03\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element has sufficient color contrast of ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"Element has insufficient color contrast of ${data.contrastRatio} (foreground color: ${data.fgColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the foreground and shadow color (foreground color: ${data.fgColor}, text-shadow color: ${data.shadowColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the shadow color and background color (text-shadow color: ${data.shadowColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Unable to determine contrast ratio\",\n        \"bgImage\": \"Element's background color could not be determined due to a background image\",\n        \"bgGradient\": \"Element's background color could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's background color could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's background color could not be determined because it is overlapped by another element\",\n        \"fgAlpha\": \"Element's foreground color could not be determined because of alpha transparency\",\n        \"elmPartiallyObscured\": \"Element's background color could not be determined because it's partially obscured by another element\",\n        \"elmPartiallyObscuring\": \"Element's background color could not be determined because it partially overlaps other elements\",\n        \"outsideViewport\": \"Element's background color could not be determined because it's outside the viewport\",\n        \"equalRatio\": \"Element has a 1:1 contrast ratio with the background\",\n        \"shortTextContent\": \"Element content is too short to determine if it is actual text content\",\n        \"nonBmp\": \"Element content contains only non-text characters\",\n        \"pseudoContent\": \"Element's background color could not be determined due to a pseudo element\",\n        \"colorParse\": \"Could not parse color string ${data.colorParse}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/color/color-contrast-evaluate.js",
    "content": "import { isVisibleOnScreen } from '../../commons/dom';\nimport {\n  visibleVirtual,\n  hasUnicode,\n  sanitize,\n  removeUnicode\n} from '../../commons/text';\nimport {\n  getBackgroundColor,\n  getForegroundColor,\n  incompleteData,\n  getContrast,\n  getOwnBackgroundColor,\n  getTextShadowColors,\n  flattenShadowColors\n} from '../../commons/color';\nimport { memoize } from '../../core/utils';\n\nexport default function colorContrastEvaluate(node, options, virtualNode) {\n  const {\n    ignoreUnicode,\n    ignoreLength,\n    ignorePseudo,\n    boldValue,\n    boldTextPt,\n    largeTextPt,\n    contrastRatio,\n    shadowOutlineEmMax,\n    pseudoSizeThreshold\n  } = options;\n\n  if (!isVisibleOnScreen(node)) {\n    this.data({ messageKey: 'hidden' });\n    return true;\n  }\n\n  const visibleText = visibleVirtual(virtualNode, false, true);\n  if (ignoreUnicode && textIsEmojis(visibleText)) {\n    this.data({ messageKey: 'nonBmp' });\n    return undefined;\n  }\n\n  const nodeStyle = window.getComputedStyle(node);\n  const fontSize = parseFloat(nodeStyle.getPropertyValue('font-size'));\n  const fontWeight = nodeStyle.getPropertyValue('font-weight');\n  const bold = parseFloat(fontWeight) >= boldValue || fontWeight === 'bold';\n\n  const ptSize = Math.ceil(fontSize * 72) / 96;\n  const isSmallFont =\n    (bold && ptSize < boldTextPt) || (!bold && ptSize < largeTextPt);\n\n  const { expected, minThreshold, maxThreshold } = isSmallFont\n    ? contrastRatio.normal\n    : contrastRatio.large;\n\n  // if element or a parent has pseudo content then we need to mark\n  // as needs review\n  const pseudoElm = findPseudoElement(virtualNode, {\n    ignorePseudo,\n    pseudoSizeThreshold\n  });\n  if (pseudoElm) {\n    this.data({\n      fontSize: `${((fontSize * 72) / 96).toFixed(1)}pt (${fontSize}px)`,\n      fontWeight: bold ? 'bold' : 'normal',\n      messageKey: 'pseudoContent',\n      expectedContrastRatio: expected + ':1'\n    });\n\n    this.relatedNodes(pseudoElm.actualNode);\n    return undefined;\n  }\n\n  // Thin shadows only. Thicker shadows are included in the background instead\n  const shadowColors = getTextShadowColors(node, {\n    minRatio: 0.001,\n    maxRatio: shadowOutlineEmMax\n  });\n  if (shadowColors === null) {\n    this.data({ messageKey: 'complexTextShadows' });\n    return undefined;\n  }\n\n  const bgNodes = [];\n  const bgColor = getBackgroundColor(node, bgNodes, shadowOutlineEmMax);\n  const fgColor = getForegroundColor(node, false, bgColor, options);\n\n  let contrast = null;\n  let contrastContributor = null;\n  let shadowColor = null;\n  if (shadowColors.length === 0) {\n    contrast = getContrast(bgColor, fgColor);\n  } else if (fgColor && bgColor) {\n    shadowColor = [...shadowColors, bgColor].reduce(flattenShadowColors);\n    // Compare shadow, bgColor, textColor. Check passes if any is sufficient\n    const fgBgContrast = getContrast(bgColor, fgColor);\n    const bgShContrast = getContrast(bgColor, shadowColor);\n    const fgShContrast = getContrast(shadowColor, fgColor);\n    contrast = Math.max(fgBgContrast, bgShContrast, fgShContrast);\n    if (contrast !== fgBgContrast) {\n      contrastContributor =\n        bgShContrast > fgShContrast ? 'shadowOnBgColor' : 'fgOnShadowColor';\n    }\n  }\n\n  const isValid = contrast > expected;\n\n  // ratio is outside range\n  if (\n    (typeof minThreshold === 'number' &&\n      (typeof contrast !== 'number' || contrast < minThreshold)) ||\n    (typeof maxThreshold === 'number' &&\n      (typeof contrast !== 'number' || contrast > maxThreshold))\n  ) {\n    this.data({ contrastRatio: contrast });\n    return true;\n  }\n\n  // truncate ratio to three digits while rounding down\n  // 4.499 = 4.49, 4.019 = 4.01\n  const truncatedResult = Math.floor(contrast * 100) / 100;\n\n  // if fgColor or bgColor are missing, get more information.\n  let missing;\n  let colorParse;\n\n  if (bgColor === null) {\n    if (incompleteData.get('colorParse')) {\n      missing = 'colorParse';\n      colorParse = incompleteData.get('colorParse');\n    } else {\n      missing = incompleteData.get('bgColor');\n    }\n  } else if (!isValid) {\n    missing = contrastContributor;\n  }\n\n  if (fgColor === null && incompleteData.get('colorParse')) {\n    missing = 'colorParse';\n    colorParse = incompleteData.get('colorParse');\n  }\n\n  const equalRatio = truncatedResult === 1;\n  const shortTextContent = visibleText.length === 1;\n  if (equalRatio) {\n    missing = incompleteData.set('bgColor', 'equalRatio');\n  } else if (!isValid && shortTextContent && !ignoreLength) {\n    // Check that the text content is a single character long\n    missing = 'shortTextContent';\n  }\n\n  // need both independently in case both are missing\n  this.data({\n    fgColor: fgColor ? fgColor.toHexString() : undefined,\n    bgColor: bgColor ? bgColor.toHexString() : undefined,\n    contrastRatio: truncatedResult,\n    fontSize: `${((fontSize * 72) / 96).toFixed(1)}pt (${fontSize}px)`,\n    fontWeight: bold ? 'bold' : 'normal',\n    messageKey: missing,\n    expectedContrastRatio: expected + ':1',\n    shadowColor: shadowColor ? shadowColor.toHexString() : undefined,\n    colorParse: colorParse\n  });\n\n  // We don't know, so we'll put it into Can't Tell\n  if (\n    fgColor === null ||\n    bgColor === null ||\n    equalRatio ||\n    (shortTextContent && !ignoreLength && !isValid)\n  ) {\n    missing = null;\n    incompleteData.clear();\n    this.relatedNodes(bgNodes);\n    return undefined;\n  }\n\n  if (!isValid) {\n    this.relatedNodes(bgNodes);\n  }\n\n  return isValid;\n}\n\nfunction findPseudoElement(\n  vNode,\n  { pseudoSizeThreshold = 0.25, ignorePseudo = false }\n) {\n  if (ignorePseudo) {\n    return;\n  }\n  const rect = vNode.boundingClientRect;\n  const minimumSize = rect.width * rect.height * pseudoSizeThreshold;\n  do {\n    const beforeSize = getPseudoElementArea(vNode.actualNode, ':before');\n    const afterSize = getPseudoElementArea(vNode.actualNode, ':after');\n    if (beforeSize + afterSize > minimumSize) {\n      return vNode; // Combined area of before and after exceeds the minimum size\n    }\n  } while ((vNode = vNode.parent));\n}\n\nconst getPseudoElementArea = memoize(\n  function getPseudoElementArea(node, pseudo) {\n    const style = window.getComputedStyle(node, pseudo);\n    const matchPseudoStyle = (prop, value) =>\n      style.getPropertyValue(prop) === value;\n    if (\n      matchPseudoStyle('content', 'none') ||\n      matchPseudoStyle('display', 'none') ||\n      matchPseudoStyle('visibility', 'hidden') ||\n      matchPseudoStyle('position', 'absolute') === false\n    ) {\n      return 0; // The pseudo element isn't visible\n    }\n\n    if (\n      getOwnBackgroundColor(style).alpha === 0 &&\n      matchPseudoStyle('background-image', 'none')\n    ) {\n      return 0; // There is no background\n    }\n\n    // Find the size of the pseudo element;\n    const pseudoWidth = parseUnit(style.getPropertyValue('width'));\n    const pseudoHeight = parseUnit(style.getPropertyValue('height'));\n    if (pseudoWidth.unit !== 'px' || pseudoHeight.unit !== 'px') {\n      // IE doesn't normalize to px. Infinity gets everything to undefined\n      return pseudoWidth.value === 0 || pseudoHeight.value === 0 ? 0 : Infinity;\n    }\n    return pseudoWidth.value * pseudoHeight.value;\n  }\n);\n\nfunction textIsEmojis(visibleText) {\n  const options = { nonBmp: true };\n  const hasUnicodeChars = hasUnicode(visibleText, options);\n  const hasNonUnicodeChars =\n    sanitize(removeUnicode(visibleText, options)) === '';\n  return hasUnicodeChars && hasNonUnicodeChars;\n}\n\nfunction parseUnit(str) {\n  const unitRegex = /^([0-9.]+)([a-z]+)$/i;\n  const [, value = '', unit = ''] = str.match(unitRegex) || [];\n  return {\n    value: parseFloat(value),\n    unit: unit.toLowerCase()\n  };\n}\n"
  },
  {
    "path": "lib/checks/color/color-contrast.json",
    "content": "{\n  \"id\": \"color-contrast\",\n  \"evaluate\": \"color-contrast-evaluate\",\n  \"options\": {\n    \"ignoreUnicode\": true,\n    \"ignoreLength\": false,\n    \"ignorePseudo\": false,\n    \"boldValue\": 700,\n    \"boldTextPt\": 14,\n    \"largeTextPt\": 18,\n    \"contrastRatio\": {\n      \"normal\": {\n        \"expected\": 4.5\n      },\n      \"large\": {\n        \"expected\": 3\n      }\n    },\n    \"pseudoSizeThreshold\": 0.25,\n    \"shadowOutlineEmMax\": 0.2,\n    \"textStrokeEmMin\": 0.03\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": {\n        \"default\": \"Element has sufficient color contrast of ${data.contrastRatio}\",\n        \"hidden\": \"Element is hidden\"\n      },\n      \"fail\": {\n        \"default\": \"Element has insufficient color contrast of ${data.contrastRatio} (foreground color: ${data.fgColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the foreground and shadow color (foreground color: ${data.fgColor}, text-shadow color: ${data.shadowColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the shadow color and background color (text-shadow color: ${data.shadowColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Unable to determine contrast ratio\",\n        \"bgImage\": \"Element's background color could not be determined due to a background image\",\n        \"bgGradient\": \"Element's background color could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's background color could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's background color could not be determined because it is overlapped by another element\",\n        \"complexTextShadows\": \"Element's contrast could not be determined because it uses complex text shadows\",\n        \"fgAlpha\": \"Element's foreground color could not be determined because of alpha transparency\",\n        \"elmPartiallyObscured\": \"Element's background color could not be determined because it's partially obscured by another element\",\n        \"elmPartiallyObscuring\": \"Element's background color could not be determined because it partially overlaps other elements\",\n        \"outsideViewport\": \"Element's background color could not be determined because it's outside the viewport\",\n        \"equalRatio\": \"Element has a 1:1 contrast ratio with the background\",\n        \"shortTextContent\": \"Element content is too short to determine if it is actual text content\",\n        \"nonBmp\": \"Element content contains only non-text characters\",\n        \"pseudoContent\": \"Element's background color could not be determined due to a pseudo element\",\n        \"colorParse\": \"Could not parse color string ${data.colorParse}\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/color/link-in-text-block-evaluate.js",
    "content": "import { getComposedParent } from '../../commons/dom';\nimport {\n  getForegroundColor,\n  getBackgroundColor,\n  incompleteData\n} from '../../commons/color';\n\nfunction getContrast(color1, color2) {\n  const c1lum = color1.getRelativeLuminance();\n  const c2lum = color2.getRelativeLuminance();\n  return (Math.max(c1lum, c2lum) + 0.05) / (Math.min(c1lum, c2lum) + 0.05);\n}\n\nconst blockLike = [\n  'block',\n  'list-item',\n  'table',\n  'flex',\n  'grid',\n  'inline-block'\n];\nfunction isBlock(elm) {\n  const display = window.getComputedStyle(elm).getPropertyValue('display');\n  return blockLike.indexOf(display) !== -1 || display.substr(0, 6) === 'table-';\n}\n\nfunction linkInTextBlockEvaluate(node, options) {\n  const { requiredContrastRatio, allowSameColor } = options;\n\n  if (isBlock(node)) {\n    return false;\n  }\n\n  let parentBlock = getComposedParent(node);\n  while (parentBlock && parentBlock.nodeType === 1 && !isBlock(parentBlock)) {\n    parentBlock = getComposedParent(parentBlock);\n  }\n\n  if (!parentBlock) {\n    return undefined;\n  }\n\n  this.relatedNodes([parentBlock]);\n\n  // Capture colors\n  const nodeColor = getForegroundColor(node);\n  const parentColor = getForegroundColor(parentBlock);\n  const nodeBackgroundColor = getBackgroundColor(node);\n  const parentBackgroundColor = getBackgroundColor(parentBlock);\n\n  // Compute contrasts, giving preference to foreground color and doing as little work as possible\n  let textContrast =\n    nodeColor && parentColor ? getContrast(nodeColor, parentColor) : undefined;\n  if (textContrast) {\n    textContrast = Math.floor(textContrast * 100) / 100;\n  }\n\n  if (textContrast && textContrast >= requiredContrastRatio) {\n    return true;\n  }\n\n  let backgroundContrast =\n    nodeBackgroundColor && parentBackgroundColor\n      ? getContrast(nodeBackgroundColor, parentBackgroundColor)\n      : undefined;\n\n  if (backgroundContrast) {\n    backgroundContrast = Math.floor(backgroundContrast * 100) / 100;\n  }\n\n  if (backgroundContrast && backgroundContrast >= requiredContrastRatio) {\n    return true;\n  }\n\n  // Report incomplete instead of failure if we're not sure\n  if (!backgroundContrast) {\n    const reason = incompleteData.get('bgColor') ?? 'bgContrast';\n    this.data({\n      messageKey: reason\n    });\n    incompleteData.clear();\n    return undefined;\n  }\n\n  if (!textContrast) {\n    return undefined;\n  }\n\n  if (allowSameColor && textContrast === 1 && backgroundContrast === 1) {\n    return true;\n  }\n\n  // Report bgContrast only if the background changes but text color stays the same\n  if (textContrast === 1 && backgroundContrast > 1) {\n    this.data({\n      messageKey: 'bgContrast',\n      contrastRatio: backgroundContrast,\n      requiredContrastRatio,\n      nodeBackgroundColor: nodeBackgroundColor\n        ? nodeBackgroundColor.toHexString()\n        : undefined,\n      parentBackgroundColor: parentBackgroundColor\n        ? parentBackgroundColor.toHexString()\n        : undefined\n    });\n    return false;\n  }\n\n  this.data({\n    messageKey: 'fgContrast',\n    contrastRatio: textContrast,\n    requiredContrastRatio,\n    nodeColor: nodeColor ? nodeColor.toHexString() : undefined,\n    parentColor: parentColor ? parentColor.toHexString() : undefined\n  });\n  return false;\n}\n\nexport default linkInTextBlockEvaluate;\n"
  },
  {
    "path": "lib/checks/color/link-in-text-block-style-evaluate.js",
    "content": "import { getComposedParent } from '../../commons/dom';\nimport { elementIsDistinct } from '../../commons/color';\n\nconst blockLike = [\n  'block',\n  'list-item',\n  'table',\n  'flex',\n  'grid',\n  'inline-block'\n];\n\nexport default function linkInTextBlockStyleEvaluate(node) {\n  if (isBlock(node)) {\n    return false;\n  }\n\n  let parentBlock = getComposedParent(node);\n  while (parentBlock && parentBlock.nodeType === 1 && !isBlock(parentBlock)) {\n    parentBlock = getComposedParent(parentBlock);\n  }\n\n  if (!parentBlock) {\n    return undefined;\n  }\n\n  this.relatedNodes([parentBlock]);\n\n  if (elementIsDistinct(node, parentBlock)) {\n    return true;\n  }\n  if (hasPseudoContent(node)) {\n    this.data({ messageKey: 'pseudoContent' });\n    return undefined;\n  }\n  return false;\n}\n\nfunction isBlock(elm) {\n  const display = window.getComputedStyle(elm).getPropertyValue('display');\n  return blockLike.indexOf(display) !== -1 || display.substr(0, 6) === 'table-';\n}\n\nfunction hasPseudoContent(node) {\n  for (const pseudo of ['before', 'after']) {\n    const style = window.getComputedStyle(node, `:${pseudo}`);\n    const content = style.getPropertyValue('content');\n    if (content !== 'none') {\n      return true;\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "lib/checks/color/link-in-text-block-style.json",
    "content": "{\n  \"id\": \"link-in-text-block-style\",\n  \"evaluate\": \"link-in-text-block-style-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Links can be distinguished from surrounding text by visual styling\",\n      \"incomplete\": {\n        \"default\": \"Check if the link needs styling to distinguish it from nearby text\",\n        \"pseudoContent\": \"Check if the link's pseudo style is sufficient to distinguish it from the surrounding text\"\n      },\n      \"fail\": \"The link has no styling (such as underline) to distinguish it from the surrounding text\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/color/link-in-text-block.json",
    "content": "{\n  \"id\": \"link-in-text-block\",\n  \"evaluate\": \"link-in-text-block-evaluate\",\n  \"options\": {\n    \"requiredContrastRatio\": 3,\n    \"allowSameColor\": true\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Links can be distinguished from surrounding text in some way other than by color\",\n      \"fail\": {\n        \"fgContrast\": \"The link has insufficient color contrast of ${data.contrastRatio}:1 with the surrounding text. (Minimum contrast is ${data.requiredContrastRatio}:1, link text: ${data.nodeColor}, surrounding text: ${data.parentColor})\",\n        \"bgContrast\": \"The link background has insufficient color contrast of ${data.contrastRatio} (Minimum contrast is ${data.requiredContrastRatio}:1, link background color: ${data.nodeBackgroundColor}, surrounding background color: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element's foreground contrast ratio could not be determined\",\n        \"bgContrast\": \"Element's background contrast ratio could not be determined\",\n        \"bgImage\": \"Element's contrast ratio could not be determined due to a background image\",\n        \"bgGradient\": \"Element's contrast ratio could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's contrast ratio could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's contrast ratio could not be determined because of element overlap\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/forms/autocomplete-appropriate-evaluate.js",
    "content": "import { autocomplete, sanitize } from '../../commons/text';\nimport { validInputTypes } from '../../core/utils';\n\nfunction autocompleteAppropriateEvaluate(node, options, virtualNode) {\n  // Select and textarea is always allowed\n  if (virtualNode.props.nodeName !== 'input') {\n    return true;\n  }\n\n  const number = ['text', 'search', 'number', 'tel'];\n  const url = ['text', 'search', 'url'];\n  const allowedTypesMap = {\n    bday: ['text', 'search', 'date'],\n    email: ['text', 'search', 'email'],\n    username: ['text', 'search', 'email'],\n    'street-address': ['text'], // Issue: https://github.com/dequelabs/axe-core/issues/1161\n    tel: ['text', 'search', 'tel'],\n    'tel-country-code': ['text', 'search', 'tel'],\n    'tel-national': ['text', 'search', 'tel'],\n    'tel-area-code': ['text', 'search', 'tel'],\n    'tel-local': ['text', 'search', 'tel'],\n    'tel-local-prefix': ['text', 'search', 'tel'],\n    'tel-local-suffix': ['text', 'search', 'tel'],\n    'tel-extension': ['text', 'search', 'tel'],\n    'cc-number': number,\n    'cc-exp': ['text', 'search', 'month', 'tel'],\n    'cc-exp-month': number,\n    'cc-exp-year': number,\n    'cc-csc': number,\n    'transaction-amount': number,\n    'bday-day': number,\n    'bday-month': number,\n    'bday-year': number,\n    'new-password': ['text', 'search', 'password'],\n    'current-password': ['text', 'search', 'password'],\n    url: url,\n    photo: url,\n    impp: url\n  };\n\n  if (typeof options === 'object') {\n    // Merge in options\n    Object.keys(options).forEach(key => {\n      if (!allowedTypesMap[key]) {\n        allowedTypesMap[key] = [];\n      }\n      allowedTypesMap[key] = allowedTypesMap[key].concat(options[key]);\n    });\n  }\n\n  const autocompleteAttr = virtualNode.attr('autocomplete');\n  const autocompleteTerms = autocompleteAttr\n    .split(/\\s+/g)\n    .map(term => term.toLowerCase());\n\n  const purposeTerm = autocompleteTerms[autocompleteTerms.length - 1];\n  if (autocomplete.stateTerms.includes(purposeTerm)) {\n    return true;\n  }\n\n  const allowedTypes = allowedTypesMap[purposeTerm];\n\n  /**\n   * Note:\n   * Inconsistent response for `node.type` across browsers, hence resolving and sanitizing using getAttribute\n   * Example:\n   * Firefox returns `node.type` as `text` for `type='month'`\n   *\n   * Reference HTML Spec - https://html.spec.whatwg.org/multipage/input.html#the-input-element to filter allowed values for `type`\n   * and sanitize (https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm)\n   */\n  let type = virtualNode.hasAttr('type')\n    ? sanitize(virtualNode.attr('type')).toLowerCase()\n    : 'text';\n  type = validInputTypes().includes(type) ? type : 'text';\n\n  if (typeof allowedTypes === 'undefined') {\n    return type === 'text';\n  }\n\n  return allowedTypes.includes(type);\n}\n\nexport default autocompleteAppropriateEvaluate;\n"
  },
  {
    "path": "lib/checks/forms/autocomplete-appropriate.json",
    "content": "{\n  \"id\": \"autocomplete-appropriate\",\n  \"evaluate\": \"autocomplete-appropriate-evaluate\",\n  \"deprecated\": true,\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"The autocomplete value is on an appropriate element\",\n      \"fail\": \"The autocomplete value is inappropriate for this type of input\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/forms/autocomplete-valid-evaluate.js",
    "content": "import { isValidAutocomplete } from '../../commons/text';\n\nfunction autocompleteValidEvaluate(_node, options, virtualNode) {\n  const autocomplete = virtualNode.attr('autocomplete') || '';\n  return isValidAutocomplete(autocomplete, options);\n}\n\nexport default autocompleteValidEvaluate;\n"
  },
  {
    "path": "lib/checks/forms/autocomplete-valid.json",
    "content": "{\n  \"id\": \"autocomplete-valid\",\n  \"evaluate\": \"autocomplete-valid-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"the autocomplete attribute is correctly formatted\",\n      \"fail\": \"the autocomplete attribute is incorrectly formatted\",\n      \"incomplete\": \"the autocomplete attribute has a non-standard value. Check whether any standard value could be used instead.\"\n    }\n  },\n  \"options\": {\n    \"stateTerms\": [\n      \"none\",\n      \"false\",\n      \"true\",\n      \"disabled\",\n      \"enabled\",\n      \"undefined\",\n      \"null\",\n      \"xoff\",\n      \"xon\"\n    ],\n    \"ignoredValues\": [\"text\", \"pronouns\", \"gender\", \"message\", \"content\"]\n  }\n}\n"
  },
  {
    "path": "lib/checks/generic/README.md",
    "content": "Generic checks are evaluate functions that are used by multiple checks. They cannot be used directly by a rule (thus there is no check meatadata file associated with them) and must be used by another check passing in the required options.\n\nTo use these checks, pass the check id (found in the metadata-function-map file) as the value of a checks `evaluate` property and pass any required options.\n\n```json\n{\n  \"id\": \"my-check\",\n  \"evaluate\": \"generic-check-id\",\n  \"options\": {\n    \"required\": true\n  }\n}\n```\n"
  },
  {
    "path": "lib/checks/generic/attr-non-space-content-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\n\nfunction attrNonSpaceContentEvaluate(node, options = {}, vNode) {\n  if (!options.attribute || typeof options.attribute !== 'string') {\n    throw new TypeError(\n      'attr-non-space-content requires options.attribute to be a string'\n    );\n  }\n\n  if (!vNode.hasAttr(options.attribute)) {\n    this.data({\n      messageKey: 'noAttr'\n    });\n    return false;\n  }\n\n  const attribute = vNode.attr(options.attribute);\n  const attributeIsEmpty = !sanitize(attribute);\n  if (attributeIsEmpty) {\n    this.data({\n      messageKey: 'emptyAttr'\n    });\n    return false;\n  }\n  return true;\n}\n\nexport default attrNonSpaceContentEvaluate;\n"
  },
  {
    "path": "lib/checks/generic/has-descendant-after.js",
    "content": "function pageHasElmAfter(results) {\n  const elmUsedAnywhere = results.some(\n    frameResult => frameResult.result === true\n  );\n\n  // If the element exists in any frame, set them all to true\n  if (elmUsedAnywhere) {\n    results.forEach(result => {\n      result.result = true;\n    });\n  }\n  return results;\n}\n\nexport default pageHasElmAfter;\n"
  },
  {
    "path": "lib/checks/generic/has-descendant-evaluate.js",
    "content": "import { querySelectorAllFilter } from '../../core/utils';\nimport { isVisibleToScreenReaders, isModalOpen } from '../../commons/dom';\n\nfunction hasDescendant(node, options, virtualNode) {\n  if (!options || !options.selector || typeof options.selector !== 'string') {\n    throw new TypeError(\n      'has-descendant requires options.selector to be a string'\n    );\n  }\n\n  if (options.passForModal && isModalOpen()) {\n    return true;\n  }\n\n  const matchingElms = querySelectorAllFilter(\n    virtualNode,\n    options.selector,\n    vNode => isVisibleToScreenReaders(vNode)\n  );\n  this.relatedNodes(matchingElms.map(vNode => vNode.actualNode));\n  return matchingElms.length > 0;\n}\n\nexport default hasDescendant;\n"
  },
  {
    "path": "lib/checks/generic/has-text-content-evaluate.js",
    "content": "import { sanitize, subtreeText } from '../../commons/text';\n\nexport default function hasTextContentEvaluate(node, options, virtualNode) {\n  try {\n    return sanitize(subtreeText(virtualNode)) !== '';\n  } catch {\n    return undefined;\n  }\n}\n"
  },
  {
    "path": "lib/checks/generic/matches-definition-evaluate.js",
    "content": "import matches from '../../commons/matches';\n\nfunction matchesDefinitionEvaluate(_, options, virtualNode) {\n  return matches(virtualNode, options.matcher);\n}\n\nexport default matchesDefinitionEvaluate;\n"
  },
  {
    "path": "lib/checks/generic/page-no-duplicate-after.js",
    "content": "function pageNoDuplicateAfter(results) {\n  // ignore results\n  return results.filter(checkResult => checkResult.data !== 'ignored');\n}\n\nexport default pageNoDuplicateAfter;\n"
  },
  {
    "path": "lib/checks/generic/page-no-duplicate-evaluate.js",
    "content": "import cache from '../../core/base/cache';\nimport { querySelectorAllFilter } from '../../core/utils';\nimport { isVisibleToScreenReaders, findUpVirtual } from '../../commons/dom';\nimport { getRole } from '../../commons/aria';\n\nfunction pageNoDuplicateEvaluate(node, options, virtualNode) {\n  if (!options || !options.selector || typeof options.selector !== 'string') {\n    throw new TypeError(\n      'page-no-duplicate requires options.selector to be a string'\n    );\n  }\n\n  // only look at the first node and it's related nodes\n  const key = 'page-no-duplicate;' + options.selector;\n  if (cache.get(key)) {\n    this.data('ignored');\n    return;\n  }\n  cache.set(key, true);\n\n  let elms = querySelectorAllFilter(axe._tree[0], options.selector, elm =>\n    isVisibleToScreenReaders(elm)\n  );\n\n  // @deprecated options.nativeScopeFilter\n  // Filter elements that, within certain contexts, don't map their role.\n  // e.g. a <footer> inside a <main> is not a banner, but in the <body> context it is\n  if (typeof options.nativeScopeFilter === 'string') {\n    elms = elms.filter(elm => {\n      return (\n        elm.actualNode.hasAttribute('role') ||\n        !findUpVirtual(elm, options.nativeScopeFilter)\n      );\n    });\n  }\n\n  if (typeof options.role === 'string') {\n    elms = elms.filter(elm => getRole(elm) === options.role);\n  }\n\n  this.relatedNodes(\n    elms.filter(elm => elm !== virtualNode).map(elm => elm.actualNode)\n  );\n\n  return elms.length <= 1;\n}\n\nexport default pageNoDuplicateEvaluate;\n"
  },
  {
    "path": "lib/checks/index.js",
    "content": "/**\n * Custom checks can use any of axe-cores internal checks by using the checks ID for the `evaluate` property in the metadata file. All check IDs can be found in the metadata-function-map.\n *\n * @example\n * {\n *   \"id\": \"my-custom-check\",\n *   \"evaluate\": \"abstractrole-evaluate\"\n * }\n *\n * @namespace checks\n */\n"
  },
  {
    "path": "lib/checks/keyboard/accesskeys-after.js",
    "content": "function accesskeysAfter(results) {\n  const seen = {};\n  return results\n    .filter(r => {\n      if (!r.data) {\n        return false;\n      }\n      const key = r.data.toUpperCase();\n      if (!seen[key]) {\n        seen[key] = r;\n        r.relatedNodes = [];\n        return true;\n      }\n      seen[key].relatedNodes.push(r.relatedNodes[0]);\n      return false;\n    })\n    .map(r => {\n      r.result = !!r.relatedNodes.length;\n      return r;\n    });\n}\n\nexport default accesskeysAfter;\n"
  },
  {
    "path": "lib/checks/keyboard/accesskeys-evaluate.js",
    "content": "import { isHiddenForEveryone } from '../../commons/dom';\n\nfunction accesskeysEvaluate(node, options, vNode) {\n  if (!isHiddenForEveryone(vNode)) {\n    this.data(vNode.attr('accesskey'));\n    this.relatedNodes([node]);\n  }\n  return true;\n}\n\nexport default accesskeysEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/accesskeys.json",
    "content": "{\n  \"id\": \"accesskeys\",\n  \"evaluate\": \"accesskeys-evaluate\",\n  \"after\": \"accesskeys-after\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Accesskey attribute value is unique\",\n      \"fail\": \"Document has multiple elements with the same accesskey\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-content-evaluate.js",
    "content": "function focusableContentEvaluate(node, options, virtualNode) {\n  /**\n   * Note:\n   * Check if given node contains focusable elements (excluding thyself)\n   */\n  const tabbableElements = virtualNode.tabbableElements;\n\n  if (!tabbableElements) {\n    return false;\n  }\n\n  // remove thyself from tabbable elements (if exists)\n  const tabbableContentElements = tabbableElements.filter(\n    el => el !== virtualNode\n  );\n\n  return tabbableContentElements.length > 0;\n}\n\nexport default focusableContentEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-content.json",
    "content": "{\n  \"id\": \"focusable-content\",\n  \"evaluate\": \"focusable-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element contains focusable elements\",\n      \"fail\": \"Element should have focusable content\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-disabled-evaluate.js",
    "content": "import { isModalOpen } from '../../commons/dom';\n\nfunction focusableDisabledEvaluate(node, options, virtualNode) {\n  const elementsThatCanBeDisabled = [\n    'button',\n    'fieldset',\n    'input',\n    'select',\n    'textarea'\n  ];\n\n  const tabbableElements = virtualNode.tabbableElements;\n\n  if (!tabbableElements || !tabbableElements.length) {\n    return true;\n  }\n\n  const relatedNodes = tabbableElements.filter(vNode => {\n    return elementsThatCanBeDisabled.includes(vNode.props.nodeName);\n  });\n\n  this.relatedNodes(relatedNodes.map(vNode => vNode.actualNode));\n\n  if (relatedNodes.length === 0 || isModalOpen()) {\n    return true;\n  }\n\n  return relatedNodes.every(vNode => {\n    const pointerEvents = vNode.getComputedStylePropertyValue('pointer-events');\n    const width = parseInt(vNode.getComputedStylePropertyValue('width'));\n    const height = parseInt(vNode.getComputedStylePropertyValue('height'));\n\n    return (\n      vNode.actualNode.onfocus ||\n      ((width === 0 || height === 0) && pointerEvents === 'none')\n    );\n  })\n    ? undefined\n    : false;\n}\n\nexport default focusableDisabledEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-disabled.json",
    "content": "{\n  \"id\": \"focusable-disabled\",\n  \"evaluate\": \"focusable-disabled-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"No focusable elements contained within element\",\n      \"incomplete\": \"Check if the focusable elements immediately move the focus indicator\",\n      \"fail\": \"Focusable content should be disabled or be removed from the DOM\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-element-evaluate.js",
    "content": "import { isInTabOrder } from '../../commons/dom';\nimport { closest } from '../../core/utils';\n\nfunction focusableElementEvaluate(node, options, virtualNode) {\n  /**\n   * Note:\n   * Check\n   * - if element is focusable\n   * - if element is in focus order via `tabindex`\n   */\n  if (\n    virtualNode.hasAttr('contenteditable') &&\n    isContenteditable(virtualNode)\n  ) {\n    return true;\n  }\n\n  return isInTabOrder(virtualNode);\n\n  // contenteditable is focusable when it is an empty string (whitespace\n  // is not considered empty) or \"true\". if the value is \"false\"\n  // you can't edit it, but if it's anything else it inherits the value\n  // from the first valid ancestor\n  // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable\n  function isContenteditable(vNode) {\n    const contenteditable = vNode.attr('contenteditable');\n    if (contenteditable === 'true' || contenteditable === '') {\n      return true;\n    }\n\n    if (contenteditable === 'false') {\n      return false;\n    }\n\n    const ancestor = closest(virtualNode.parent, '[contenteditable]');\n    if (!ancestor) {\n      return false;\n    }\n\n    return isContenteditable(ancestor);\n  }\n}\n\nexport default focusableElementEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-element.json",
    "content": "{\n  \"id\": \"focusable-element\",\n  \"evaluate\": \"focusable-element-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element is focusable\",\n      \"fail\": \"Element should be focusable\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-modal-open-evaluate.js",
    "content": "import { isModalOpen } from '../../commons/dom';\n\nfunction focusableModalOpenEvaluate(node, options, virtualNode) {\n  const tabbableElements = virtualNode.tabbableElements.map(\n    ({ actualNode }) => actualNode\n  );\n\n  if (!tabbableElements || !tabbableElements.length) {\n    return true;\n  }\n\n  if (isModalOpen()) {\n    this.relatedNodes(tabbableElements);\n    return undefined;\n  }\n\n  return true;\n}\n\nexport default focusableModalOpenEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-modal-open.json",
    "content": "{\n  \"id\": \"focusable-modal-open\",\n  \"evaluate\": \"focusable-modal-open-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"No focusable elements while a modal is open\",\n      \"incomplete\": \"Check that focusable elements are not tabbable in the current state\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-no-name-evaluate.js",
    "content": "import { isInTabOrder } from '../../commons/dom';\nimport { accessibleTextVirtual } from '../../commons/text';\n\nfunction focusableNoNameEvaluate(node, options, virtualNode) {\n  if (!isInTabOrder(virtualNode)) {\n    return false;\n  }\n\n  try {\n    return !accessibleTextVirtual(virtualNode);\n  } catch {\n    return undefined;\n  }\n}\n\nexport default focusableNoNameEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-no-name.json",
    "content": "{\n  \"id\": \"focusable-no-name\",\n  \"evaluate\": \"focusable-no-name-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element is not in tab order or has accessible text\",\n      \"fail\": \"Element is in tab order and does not have accessible text\",\n      \"incomplete\": \"Unable to determine if element has an accessible name\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-not-tabbable-evaluate.js",
    "content": "import { isModalOpen } from '../../commons/dom';\n\nfunction focusableNotTabbableEvaluate(node, options, virtualNode) {\n  const elementsThatCanBeDisabled = [\n    'button',\n    'fieldset',\n    'input',\n    'select',\n    'textarea'\n  ];\n\n  const tabbableElements = virtualNode.tabbableElements;\n\n  if (!tabbableElements || !tabbableElements.length) {\n    return true;\n  }\n\n  const relatedNodes = tabbableElements.filter(vNode => {\n    return !elementsThatCanBeDisabled.includes(vNode.props.nodeName);\n  });\n\n  this.relatedNodes(relatedNodes.map(vNode => vNode.actualNode));\n\n  if (relatedNodes.length === 0 || isModalOpen()) {\n    return true;\n  }\n\n  return relatedNodes.every(vNode => {\n    const pointerEvents = vNode.getComputedStylePropertyValue('pointer-events');\n    const width = parseInt(vNode.getComputedStylePropertyValue('width'));\n    const height = parseInt(vNode.getComputedStylePropertyValue('height'));\n\n    return (\n      vNode.actualNode.onfocus ||\n      ((width === 0 || height === 0) && pointerEvents === 'none')\n    );\n  })\n    ? undefined\n    : false;\n}\n\nexport default focusableNotTabbableEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/focusable-not-tabbable.json",
    "content": "{\n  \"id\": \"focusable-not-tabbable\",\n  \"evaluate\": \"focusable-not-tabbable-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"No focusable elements contained within element\",\n      \"incomplete\": \"Check if the focusable elements immediately move the focus indicator\",\n      \"fail\": \"Focusable content should have tabindex=\\\"-1\\\" or be removed from the DOM\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/frame-focusable-content-evaluate.js",
    "content": "import { isInTabOrder } from '../../commons/dom';\n\nexport default function frameFocusableContentEvaluate(\n  node,\n  options,\n  virtualNode\n) {\n  if (!virtualNode.children) {\n    return undefined;\n  }\n\n  try {\n    return !virtualNode.children.some(child => {\n      return focusableDescendants(child);\n    });\n  } catch {\n    return undefined;\n  }\n}\n\nfunction focusableDescendants(vNode) {\n  if (isInTabOrder(vNode)) {\n    return true;\n  }\n\n  if (!vNode.children) {\n    if (vNode.props.nodeType === 1) {\n      throw new Error('Cannot determine children');\n    }\n\n    return false;\n  }\n\n  return vNode.children.some(child => {\n    return focusableDescendants(child);\n  });\n}\n"
  },
  {
    "path": "lib/checks/keyboard/frame-focusable-content.json",
    "content": "{\n  \"id\": \"frame-focusable-content\",\n  \"evaluate\": \"frame-focusable-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element does not have focusable descendants\",\n      \"fail\": \"Element has focusable descendants\",\n      \"incomplete\": \"Could not determine if element has descendants\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/landmark-is-top-level-evaluate.js",
    "content": "import { getRole, implicitRole, getExplicitRole } from '../../commons/aria';\nimport { getAriaRolesByType } from '../../commons/standards';\nimport { getComposedParent } from '../../commons/dom';\n\nfunction landmarkIsTopLevelEvaluate(node) {\n  const landmarks = getAriaRolesByType('landmark');\n  let parent = getComposedParent(node);\n  const nodeRole = getRole(node);\n\n  this.data({ role: nodeRole });\n\n  while (parent) {\n    let role = getExplicitRole(parent);\n    if (!role && parent.nodeName.toUpperCase() !== 'FORM') {\n      role = implicitRole(parent);\n    }\n    // allow aside inside main\n    // @see https://github.com/dequelabs/axe-core/issues/2651\n    if (\n      role &&\n      landmarks.includes(role) &&\n      !(role === 'main' && nodeRole === 'complementary')\n    ) {\n      return false;\n    }\n    parent = getComposedParent(parent);\n  }\n  return true;\n}\n\nexport default landmarkIsTopLevelEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/landmark-is-top-level.json",
    "content": "{\n  \"id\": \"landmark-is-top-level\",\n  \"evaluate\": \"landmark-is-top-level-evaluate\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"The ${data.role} landmark is at the top level.\",\n      \"fail\": \"The ${data.role} landmark is contained in another landmark.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/no-focusable-content-evaluate.js",
    "content": "import isFocusable from '../../commons/dom/is-focusable';\nimport { getRoleType } from '../../commons/aria';\nimport { parseTabindex } from '../../core/utils';\n\nexport default function noFocusableContentEvaluate(node, options, virtualNode) {\n  if (!virtualNode.children) {\n    return undefined;\n  }\n\n  try {\n    const focusableDescendants = getFocusableDescendants(virtualNode);\n\n    if (!focusableDescendants.length) {\n      return true;\n    }\n\n    const notHiddenElements = focusableDescendants.filter(\n      usesUnreliableHidingStrategy\n    );\n\n    if (notHiddenElements.length > 0) {\n      this.data({ messageKey: 'notHidden' });\n      this.relatedNodes(notHiddenElements);\n    } else {\n      this.relatedNodes(focusableDescendants);\n    }\n\n    return false;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction getFocusableDescendants(vNode) {\n  if (!vNode.children) {\n    if (vNode.props.nodeType === 1) {\n      throw new Error('Cannot determine children');\n    }\n\n    return [];\n  }\n\n  const retVal = [];\n  vNode.children.forEach(child => {\n    if (getRoleType(child) === 'widget' && isFocusable(child)) {\n      retVal.push(child);\n    } else {\n      retVal.push(...getFocusableDescendants(child));\n    }\n  });\n  return retVal;\n}\n\nfunction usesUnreliableHidingStrategy(vNode) {\n  const tabIndex = parseTabindex(vNode.attr('tabindex'));\n  return tabIndex !== null && tabIndex < 0;\n}\n"
  },
  {
    "path": "lib/checks/keyboard/no-focusable-content.json",
    "content": "{\n  \"id\": \"no-focusable-content\",\n  \"evaluate\": \"no-focusable-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element does not have focusable descendants\",\n      \"fail\": {\n        \"default\": \"Element has focusable descendants\",\n        \"notHidden\": \"Using a negative tabindex on an element inside an interactive control does not prevent assistive technologies from focusing the element (even with aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Could not determine if element has descendants\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/page-has-heading-one.json",
    "content": "{\n  \"id\": \"page-has-heading-one\",\n  \"evaluate\": \"has-descendant-evaluate\",\n  \"after\": \"has-descendant-after\",\n  \"options\": {\n    \"selector\": \"h1:not([role], [aria-level]), :is(h1, h2, h3, h4, h5, h6):not([role])[aria-level=1], [role=heading][aria-level=1]\",\n    \"passForModal\": true\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Page has at least one level-one heading\",\n      \"fail\": \"Page must have a level-one heading\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/page-has-main.json",
    "content": "{\n  \"id\": \"page-has-main\",\n  \"evaluate\": \"has-descendant-evaluate\",\n  \"after\": \"has-descendant-after\",\n  \"options\": {\n    \"selector\": \"main:not([role]), [role='main']\",\n    \"passForModal\": true\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Document has at least one main landmark\",\n      \"fail\": \"Document does not have a main landmark\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/page-no-duplicate-banner.json",
    "content": "{\n  \"id\": \"page-no-duplicate-banner\",\n  \"evaluate\": \"page-no-duplicate-evaluate\",\n  \"after\": \"page-no-duplicate-after\",\n  \"options\": {\n    \"selector\": \"header:not([role]), [role=banner]\",\n    \"role\": \"banner\"\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Document does not have more than one banner landmark\",\n      \"fail\": \"Document has more than one banner landmark\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/page-no-duplicate-contentinfo.json",
    "content": "{\n  \"id\": \"page-no-duplicate-contentinfo\",\n  \"evaluate\": \"page-no-duplicate-evaluate\",\n  \"after\": \"page-no-duplicate-after\",\n  \"options\": {\n    \"selector\": \"footer:not([role]), [role=contentinfo]\",\n    \"role\": \"contentinfo\"\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Document does not have more than one contentinfo landmark\",\n      \"fail\": \"Document has more than one contentinfo landmark\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/page-no-duplicate-main.json",
    "content": "{\n  \"id\": \"page-no-duplicate-main\",\n  \"evaluate\": \"page-no-duplicate-evaluate\",\n  \"after\": \"page-no-duplicate-after\",\n  \"options\": {\n    \"selector\": \"main:not([role]), [role='main']\"\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Document does not have more than one main landmark\",\n      \"fail\": \"Document has more than one main landmark\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/keyboard/tabindex-evaluate.js",
    "content": "import { parseTabindex } from '../../core/utils';\n\nfunction tabindexEvaluate(node, options, virtualNode) {\n  const tabIndex = parseTabindex(virtualNode.attr('tabindex'));\n\n  // an invalid tabindex will either return 0 or -1 (based on the element) so\n  // will never be above 0\n  // @see https://www.w3.org/TR/html51/editing.html#the-tabindex-attribute\n  return tabIndex === null || tabIndex <= 0;\n}\n\nexport default tabindexEvaluate;\n"
  },
  {
    "path": "lib/checks/keyboard/tabindex.json",
    "content": "{\n  \"id\": \"tabindex\",\n  \"evaluate\": \"tabindex-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element does not have a tabindex greater than 0\",\n      \"fail\": \"Element has a tabindex greater than 0\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/alt-space-value-evaluate.js",
    "content": "function altSpaceValueEvaluate(node, options, virtualNode) {\n  const alt = virtualNode.attr('alt');\n  const isOnlySpace = /^\\s+$/;\n  return typeof alt === 'string' && isOnlySpace.test(alt);\n}\n\nexport default altSpaceValueEvaluate;\n"
  },
  {
    "path": "lib/checks/label/alt-space-value.json",
    "content": "{\n  \"id\": \"alt-space-value\",\n  \"evaluate\": \"alt-space-value-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has a valid alt attribute value\",\n      \"fail\": \"Element has an alt attribute containing only a space character, which is not ignored by all screen readers\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/duplicate-img-label-evaluate.js",
    "content": "import { getRole } from '../../commons/aria';\nimport { visibleVirtual, accessibleTextVirtual } from '../../commons/text';\nimport { closest } from '../../core/utils';\n\nfunction duplicateImgLabelEvaluate(node, options, virtualNode) {\n  if (['none', 'presentation'].includes(getRole(virtualNode))) {\n    return false;\n  }\n  const parentVNode = closest(virtualNode, options.parentSelector);\n\n  if (!parentVNode) {\n    return false;\n  }\n\n  const visibleText = visibleVirtual(parentVNode, true).toLowerCase();\n  if (visibleText === '') {\n    return false;\n  }\n\n  return visibleText === accessibleTextVirtual(virtualNode).toLowerCase();\n}\n\nexport default duplicateImgLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/label/duplicate-img-label.json",
    "content": "{\n  \"id\": \"duplicate-img-label\",\n  \"evaluate\": \"duplicate-img-label-evaluate\",\n  \"options\": {\n    \"parentSelector\": \"button, [role=button], a[href], p, li, td, th\"\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element does not duplicate existing text in <img> alt text\",\n      \"fail\": \"Element contains <img> element with alt text that duplicates existing text\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/explicit-evaluate.js",
    "content": "import { getRootNode, isVisibleOnScreen } from '../../commons/dom';\nimport { accessibleText, sanitize } from '../../commons/text';\nimport { escapeSelector } from '../../core/utils';\n\nfunction explicitEvaluate(node, options, virtualNode) {\n  if (!virtualNode.attr('id')) {\n    return false;\n  }\n  if (!virtualNode.actualNode) {\n    return undefined;\n  }\n\n  const root = getRootNode(virtualNode.actualNode);\n  const id = escapeSelector(virtualNode.attr('id'));\n  const labels = Array.from(root.querySelectorAll(`label[for=\"${id}\"]`));\n  this.relatedNodes(labels);\n\n  if (!labels.length) {\n    return false;\n  }\n\n  try {\n    return labels.some(label => {\n      // defer to hidden-explicit-label check for better messaging\n      if (!isVisibleOnScreen(label)) {\n        return true;\n      } else {\n        const explicitLabel = sanitize(\n          accessibleText(label, {\n            inControlContext: true,\n            startNode: virtualNode\n          })\n        );\n        this.data({ explicitLabel });\n        return !!explicitLabel;\n      }\n    });\n  } catch {\n    return undefined;\n  }\n}\n\nexport default explicitEvaluate;\n"
  },
  {
    "path": "lib/checks/label/explicit.json",
    "content": "{\n  \"id\": \"explicit-label\",\n  \"evaluate\": \"explicit-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has an explicit <label>\",\n      \"fail\": \"Element does not have an explicit <label>\",\n      \"incomplete\": \"Unable to determine if form element has an explicit <label>\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/help-same-as-label-evaluate.js",
    "content": "import { labelVirtual, accessibleText, sanitize } from '../../commons/text';\nimport { idrefs } from '../../commons/dom';\n\nfunction helpSameAsLabelEvaluate(node, options, virtualNode) {\n  const labelText = labelVirtual(virtualNode);\n  let check = node.getAttribute('title');\n\n  if (!labelText) {\n    return false;\n  }\n\n  if (!check) {\n    check = '';\n\n    if (node.getAttribute('aria-describedby')) {\n      const ref = idrefs(node, 'aria-describedby');\n      check = ref\n        .map(thing => {\n          return thing ? accessibleText(thing) : '';\n        })\n        .join('');\n    }\n  }\n\n  return sanitize(check) === sanitize(labelText);\n}\n\nexport default helpSameAsLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/label/help-same-as-label.json",
    "content": "{\n  \"id\": \"help-same-as-label\",\n  \"evaluate\": \"help-same-as-label-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Help text (title or aria-describedby) does not duplicate label text\",\n      \"fail\": \"Help text (title or aria-describedby) text is the same as the label text\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/hidden-explicit-label-evaluate.js",
    "content": "import { getRootNode, isVisibleToScreenReaders } from '../../commons/dom';\nimport { accessibleTextVirtual } from '../../commons/text';\nimport { escapeSelector } from '../../core/utils';\n\nfunction hiddenExplicitLabelEvaluate(node, options, virtualNode) {\n  if (virtualNode.hasAttr('id')) {\n    if (!virtualNode.actualNode) {\n      return undefined;\n    }\n\n    const root = getRootNode(node);\n    const id = escapeSelector(node.getAttribute('id'));\n    const label = root.querySelector(`label[for=\"${id}\"]`);\n\n    if (label && !isVisibleToScreenReaders(label)) {\n      let name;\n      try {\n        name = accessibleTextVirtual(virtualNode).trim();\n      } catch {\n        return undefined;\n      }\n\n      const isNameEmpty = name === '';\n      return isNameEmpty;\n    }\n  }\n  return false;\n}\n\nexport default hiddenExplicitLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/label/hidden-explicit-label.json",
    "content": "{\n  \"id\": \"hidden-explicit-label\",\n  \"evaluate\": \"hidden-explicit-label-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Form element has a visible explicit <label>\",\n      \"fail\": \"Form element has explicit <label> that is hidden\",\n      \"incomplete\": \"Unable to determine if form element has explicit <label> that is hidden\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/implicit-evaluate.js",
    "content": "import { closest } from '../../core/utils';\nimport { accessibleTextVirtual, sanitize } from '../../commons/text';\n\nfunction implicitEvaluate(node, options, virtualNode) {\n  try {\n    const label = closest(virtualNode, 'label');\n    if (label) {\n      const implicitLabel = sanitize(\n        accessibleTextVirtual(label, {\n          inControlContext: true,\n          startNode: virtualNode\n        })\n      );\n      if (label.actualNode) {\n        this.relatedNodes([label.actualNode]);\n      }\n      this.data({ implicitLabel });\n      return !!implicitLabel;\n    }\n    return false;\n  } catch {\n    return undefined;\n  }\n}\n\nexport default implicitEvaluate;\n"
  },
  {
    "path": "lib/checks/label/implicit.json",
    "content": "{\n  \"id\": \"implicit-label\",\n  \"evaluate\": \"implicit-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has an implicit (wrapped) <label>\",\n      \"fail\": \"Element does not have an implicit (wrapped) <label>\",\n      \"incomplete\": \"Unable to determine if form element has an implicit (wrapped) <label>\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/label-content-name-mismatch-evaluate.js",
    "content": "import {\n  accessibleText,\n  isHumanInterpretable,\n  subtreeText,\n  sanitize,\n  removeUnicode\n} from '../../commons/text';\n\n/**\n * Check if a given text exists in another\n *\n * @param {String} compare given text to check\n * @param {String} compareWith text against which to be compared\n * @returns {Boolean}\n */\nfunction isStringContained(compare, compareWith) {\n  const curatedCompareWith = curateString(compareWith);\n  const curatedCompare = curateString(compare);\n  if (!curatedCompareWith || !curatedCompare) {\n    return false;\n  }\n  return curatedCompareWith.includes(curatedCompare);\n}\n\n/**\n * Curate given text, by removing emoji's, punctuations, unicode and trim whitespace.\n *\n * @param {String} str given text to curate\n * @returns {String}\n */\nfunction curateString(str) {\n  const noUnicodeStr = removeUnicode(str, {\n    emoji: true,\n    nonBmp: true,\n    punctuations: true\n  });\n  return sanitize(noUnicodeStr);\n}\n\nfunction labelContentNameMismatchEvaluate(node, options, virtualNode) {\n  const pixelThreshold = options?.pixelThreshold;\n  const occurrenceThreshold =\n    options?.occurrenceThreshold ?? options?.occuranceThreshold;\n  const accText = accessibleText(node).toLowerCase();\n  const visibleText = sanitize(\n    subtreeText(virtualNode, {\n      subtreeDescendant: true,\n      ignoreIconLigature: true,\n      pixelThreshold,\n      occurrenceThreshold\n    })\n  ).toLowerCase();\n\n  if (!visibleText) {\n    return true;\n  }\n\n  if (\n    isHumanInterpretable(accText) < 1 ||\n    isHumanInterpretable(visibleText) < 1\n  ) {\n    return undefined;\n  }\n\n  return isStringContained(visibleText, accText);\n}\n\nexport default labelContentNameMismatchEvaluate;\n"
  },
  {
    "path": "lib/checks/label/label-content-name-mismatch.json",
    "content": "{\n  \"id\": \"label-content-name-mismatch\",\n  \"evaluate\": \"label-content-name-mismatch-evaluate\",\n  \"options\": {\n    \"pixelThreshold\": 0.1,\n    \"occurrenceThreshold\": 3\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element contains visible text as part of it's accessible name\",\n      \"fail\": \"Text inside the element is not included in the accessible name\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/multiple-label-evaluate.js",
    "content": "import {\n  getRootNode,\n  isHiddenForEveryone,\n  isVisibleToScreenReaders,\n  idrefs\n} from '../../commons/dom';\nimport { escapeSelector } from '../../core/utils';\n\nfunction multipleLabelEvaluate(node) {\n  const id = escapeSelector(node.getAttribute('id'));\n  let parent = node.parentNode;\n  let root = getRootNode(node);\n  root = root.documentElement || root;\n  let labels = Array.from(root.querySelectorAll(`label[for=\"${id}\"]`));\n\n  if (labels.length) {\n    // filter out CSS hidden labels because they're fine\n    labels = labels.filter(label => !isHiddenForEveryone(label));\n  }\n\n  while (parent) {\n    if (\n      parent.nodeName.toUpperCase() === 'LABEL' &&\n      labels.indexOf(parent) === -1\n    ) {\n      labels.push(parent);\n    }\n    parent = parent.parentNode;\n  }\n\n  this.relatedNodes(labels);\n\n  // more than 1 CSS visible label\n  if (labels.length > 1) {\n    const ATVisibleLabels = labels.filter(label =>\n      isVisibleToScreenReaders(label)\n    );\n    // more than 1 AT visible label will fail IOS/Safari/VO even with aria-labelledby\n    if (ATVisibleLabels.length > 1) {\n      return undefined;\n    }\n    // make sure the ONE AT visible label is in the list of idRefs of aria-labelledby\n    const labelledby = idrefs(node, 'aria-labelledby');\n    return !labelledby.includes(ATVisibleLabels[0]) ? undefined : false;\n  }\n\n  // only 1 CSS visible label\n  return false;\n}\n\nexport default multipleLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/label/multiple-label.json",
    "content": "{\n  \"id\": \"multiple-label\",\n  \"evaluate\": \"multiple-label-evaluate\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Form field does not have multiple label elements\",\n      \"incomplete\": \"Multiple label elements is not widely supported in assistive technologies. Ensure the first label contains all necessary information.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/label/title-only-evaluate.js",
    "content": "import { labelVirtual, titleText } from '../../commons/text';\n\nfunction titleOnlyEvaluate(node, options, virtualNode) {\n  const labelText = labelVirtual(virtualNode);\n  const title = titleText(virtualNode);\n  const ariaDescribedBy = virtualNode.attr('aria-describedby');\n\n  return !labelText && !!(title || ariaDescribedBy);\n}\n\nexport default titleOnlyEvaluate;\n"
  },
  {
    "path": "lib/checks/label/title-only.json",
    "content": "{\n  \"id\": \"title-only\",\n  \"evaluate\": \"title-only-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Form element does not solely use title attribute for its label\",\n      \"fail\": \"Only title used to generate label for form element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/landmarks/landmark-is-unique-after.js",
    "content": "function landmarkIsUniqueAfter(results) {\n  const uniqueLandmarks = [];\n\n  // filter out landmark elements that share the same role and accessible text\n  // so every non-unique landmark isn't reported as a failure (just the first)\n  return results.filter(currentResult => {\n    const findMatch = someResult => {\n      return (\n        currentResult.data.role === someResult.data.role &&\n        currentResult.data.accessibleText === someResult.data.accessibleText\n      );\n    };\n\n    const matchedResult = uniqueLandmarks.find(findMatch);\n    if (matchedResult) {\n      matchedResult.result = false;\n      matchedResult.relatedNodes.push(currentResult.relatedNodes[0]);\n      return false;\n    }\n\n    uniqueLandmarks.push(currentResult);\n    currentResult.relatedNodes = [];\n    return true;\n  });\n}\n\nexport default landmarkIsUniqueAfter;\n"
  },
  {
    "path": "lib/checks/landmarks/landmark-is-unique-evaluate.js",
    "content": "import { getRole } from '../../commons/aria';\nimport { accessibleTextVirtual } from '../../commons/text';\n\nfunction landmarkIsUniqueEvaluate(node, options, virtualNode) {\n  const role = getRole(node);\n  let accessibleText = accessibleTextVirtual(virtualNode);\n  accessibleText = accessibleText ? accessibleText.toLowerCase() : null;\n  this.data({ role: role, accessibleText: accessibleText });\n  this.relatedNodes([node]);\n\n  return true;\n}\n\nexport default landmarkIsUniqueEvaluate;\n"
  },
  {
    "path": "lib/checks/landmarks/landmark-is-unique.json",
    "content": "{\n  \"id\": \"landmark-is-unique\",\n  \"evaluate\": \"landmark-is-unique-evaluate\",\n  \"after\": \"landmark-is-unique-after\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Landmarks must have a unique role or role/label/title (i.e. accessible name) combination\",\n      \"fail\": \"The landmark must have a unique aria-label, aria-labelledby, or title to make landmarks distinguishable\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/language/has-lang-evaluate.js",
    "content": "import { isXHTML } from '../../core/utils';\n\nfunction hasValue(value) {\n  return (value || '').trim() !== '';\n}\n\nfunction hasLangEvaluate(node, options, virtualNode) {\n  // special case when xml:lang has a value and lang does not\n  // but the document is not XHTML\n  const xhtml = typeof document !== 'undefined' ? isXHTML(document) : false;\n\n  if (\n    options.attributes.includes('xml:lang') &&\n    options.attributes.includes('lang') &&\n    hasValue(virtualNode.attr('xml:lang')) &&\n    !hasValue(virtualNode.attr('lang')) &&\n    !xhtml\n  ) {\n    this.data({\n      messageKey: 'noXHTML'\n    });\n    return false;\n  }\n\n  const hasLang = options.attributes.some(name => {\n    return hasValue(virtualNode.attr(name));\n  });\n\n  if (!hasLang) {\n    this.data({\n      messageKey: 'noLang'\n    });\n    return false;\n  }\n\n  return true;\n}\n\nexport default hasLangEvaluate;\n"
  },
  {
    "path": "lib/checks/language/has-lang.json",
    "content": "{\n  \"id\": \"has-lang\",\n  \"evaluate\": \"has-lang-evaluate\",\n  \"options\": {\n    \"attributes\": [\"lang\", \"xml:lang\"]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"The <html> element has a lang attribute\",\n      \"fail\": {\n        \"noXHTML\": \"The xml:lang attribute is not valid on HTML pages, use the lang attribute.\",\n        \"noLang\": \"The <html> element does not have a lang attribute\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/language/valid-lang-evaluate.js",
    "content": "import { isValidLang, getBaseLang } from '../../core/utils';\nimport { sanitize } from '../../commons/text';\nimport { hasLangText } from '../../commons/dom';\n\nfunction validLangEvaluate(node, options, virtualNode) {\n  const invalid = [];\n  options.attributes.forEach(langAttr => {\n    const langVal = virtualNode.attr(langAttr);\n    if (typeof langVal !== 'string') {\n      return;\n    }\n\n    const baselangVal = getBaseLang(langVal);\n    const invalidLang = options.value\n      ? !options.value.map(getBaseLang).includes(baselangVal)\n      : !isValidLang(baselangVal);\n\n    // Edge sets lang to an empty string when xml:lang is set\n    // so we need to ignore empty strings here\n    if (\n      (baselangVal !== '' && invalidLang) ||\n      // whitespace only lang value is invalid\n      (langVal !== '' && !sanitize(langVal))\n    ) {\n      invalid.push(langAttr + '=\"' + virtualNode.attr(langAttr) + '\"');\n    }\n  });\n\n  if (!invalid.length) {\n    return false;\n  }\n  if (\n    // Except for `html`, ignore elements with no text\n    virtualNode.props.nodeName !== 'html' &&\n    !hasLangText(virtualNode)\n  ) {\n    return false;\n  }\n  this.data(invalid);\n  return true;\n}\n\nexport default validLangEvaluate;\n"
  },
  {
    "path": "lib/checks/language/valid-lang.json",
    "content": "{\n  \"id\": \"valid-lang\",\n  \"evaluate\": \"valid-lang-evaluate\",\n  \"options\": {\n    \"attributes\": [\"lang\", \"xml:lang\"]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Value of lang attribute is included in the list of valid languages\",\n      \"fail\": \"Value of lang attribute not included in the list of valid languages\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/language/xml-lang-mismatch-evaluate.js",
    "content": "import { getBaseLang } from '../../core/utils';\n\nfunction xmlLangMismatchEvaluate(node, options, vNode) {\n  const primaryLangValue = getBaseLang(vNode.attr('lang'));\n  const primaryXmlLangValue = getBaseLang(vNode.attr('xml:lang'));\n\n  return primaryLangValue === primaryXmlLangValue;\n}\n\nexport default xmlLangMismatchEvaluate;\n"
  },
  {
    "path": "lib/checks/language/xml-lang-mismatch.json",
    "content": "{\n  \"id\": \"xml-lang-mismatch\",\n  \"evaluate\": \"xml-lang-mismatch-evaluate\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Lang and xml:lang attributes have the same base language\",\n      \"fail\": \"Lang and xml:lang attributes do not have the same base language\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/lists/dlitem-evaluate.js",
    "content": "import { getComposedParent } from '../../commons/dom';\nimport { getExplicitRole } from '../../commons/aria';\n\nfunction dlitemEvaluate(node) {\n  let parent = getComposedParent(node);\n  let parentTagName = parent.nodeName.toUpperCase();\n  let parentRole = getExplicitRole(parent);\n\n  if (\n    parentTagName === 'DIV' &&\n    ['presentation', 'none', null].includes(parentRole)\n  ) {\n    parent = getComposedParent(parent);\n    parentTagName = parent.nodeName.toUpperCase();\n    parentRole = getExplicitRole(parent);\n  }\n\n  // Unlike with UL|OL+LI, DT|DD must be in a DL\n  if (parentTagName !== 'DL') {\n    return false;\n  }\n\n  if (!parentRole || ['presentation', 'none', 'list'].includes(parentRole)) {\n    return true;\n  }\n\n  return false;\n}\n\nexport default dlitemEvaluate;\n"
  },
  {
    "path": "lib/checks/lists/dlitem.json",
    "content": "{\n  \"id\": \"dlitem\",\n  \"evaluate\": \"dlitem-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Description list item has a <dl> parent element\",\n      \"fail\": \"Description list item does not have a <dl> parent element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/lists/invalid-children-evaluate.js",
    "content": "import { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getExplicitRole } from '../../commons/aria';\n\nexport default function invalidChildrenEvaluate(\n  node,\n  options = {},\n  virtualNode\n) {\n  const relatedNodes = [];\n  const issues = [];\n  if (!virtualNode.children) {\n    return undefined;\n  }\n\n  const vChildren = mapWithNested(virtualNode.children);\n  while (vChildren.length) {\n    const { vChild, nested } = vChildren.shift();\n    if (options.divGroups && !nested && isDivGroup(vChild)) {\n      if (!vChild.children) {\n        return undefined;\n      }\n      const vGrandChildren = mapWithNested(vChild.children, true);\n      vChildren.push(...vGrandChildren);\n      continue;\n    }\n\n    const issue = getInvalidSelector(vChild, nested, options);\n    if (!issue) {\n      continue;\n    }\n    if (!issues.includes(issue)) {\n      issues.push(issue);\n    }\n    if (vChild?.actualNode?.nodeType === 1) {\n      relatedNodes.push(vChild.actualNode);\n    }\n  }\n  if (issues.length === 0) {\n    return false;\n  }\n\n  this.data({ values: issues.join(', ') });\n  this.relatedNodes(relatedNodes);\n  return true;\n}\n\nfunction getInvalidSelector(\n  vChild,\n  nested,\n  { validRoles = [], validNodeNames = [] }\n) {\n  const { nodeName, nodeType, nodeValue } = vChild.props;\n  const selector = nested ? 'div > ' : '';\n  if (nodeType === 3 && nodeValue.trim() !== '') {\n    return selector + `#text`;\n  }\n  if (nodeType !== 1 || !isVisibleToScreenReaders(vChild)) {\n    return false;\n  }\n\n  const role = getExplicitRole(vChild);\n  if (role) {\n    return validRoles.includes(role) ? false : selector + `[role=${role}]`;\n  } else {\n    return validNodeNames.includes(nodeName) ? false : selector + nodeName;\n  }\n}\n\nfunction isDivGroup(vNode) {\n  return vNode.props.nodeName === 'div' && getExplicitRole(vNode) === null;\n}\n\nfunction mapWithNested(vNodes, nested = false) {\n  return vNodes.map(vChild => ({ vChild, nested }));\n}\n"
  },
  {
    "path": "lib/checks/lists/listitem-evaluate.js",
    "content": "import { isValidRole, getExplicitRole } from '../../commons/aria';\n\nexport default function listitemEvaluate(node, options, virtualNode) {\n  const { parent } = virtualNode;\n  if (!parent) {\n    // Can only happen with detached DOM nodes and roots:\n    return undefined;\n  }\n\n  const parentNodeName = parent.props.nodeName;\n  const parentRole = getExplicitRole(parent);\n\n  if (['presentation', 'none', 'list'].includes(parentRole)) {\n    return true;\n  }\n\n  if (parentRole && isValidRole(parentRole)) {\n    this.data({\n      messageKey: 'roleNotValid'\n    });\n    return false;\n  }\n  return ['ul', 'ol', 'menu'].includes(parentNodeName);\n}\n"
  },
  {
    "path": "lib/checks/lists/listitem.json",
    "content": "{\n  \"id\": \"listitem\",\n  \"evaluate\": \"listitem-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"List item has a <ul>, <ol> or role=\\\"list\\\" parent element\",\n      \"fail\": {\n        \"default\": \"List item does not have a <ul>, <ol> parent element\",\n        \"roleNotValid\": \"List item parent element has a role that is not role=\\\"list\\\"\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/lists/only-dlitems-evaluate.js",
    "content": "import { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getRole, getExplicitRole } from '../../commons/aria';\n\n/**\n * @deprecated\n */\nexport default function onlyDlitemsEvaluate(node, options, virtualNode) {\n  const ALLOWED_ROLES = ['definition', 'term', 'list'];\n  const base = {\n    badNodes: [],\n    hasNonEmptyTextNode: false\n  };\n\n  const content = virtualNode.children.reduce((vNodes, child) => {\n    const { actualNode } = child;\n    if (\n      actualNode.nodeName.toUpperCase() === 'DIV' &&\n      getRole(actualNode) === null\n    ) {\n      return vNodes.concat(child.children);\n    }\n    return vNodes.concat(child);\n  }, []);\n\n  const result = content.reduce((out, childNode) => {\n    const { actualNode } = childNode;\n    const tagName = actualNode.nodeName.toUpperCase();\n\n    if (actualNode.nodeType === 1 && isVisibleToScreenReaders(actualNode)) {\n      const explicitRole = getExplicitRole(actualNode);\n\n      if ((tagName !== 'DT' && tagName !== 'DD') || explicitRole) {\n        if (!ALLOWED_ROLES.includes(explicitRole)) {\n          // handle comment - https://github.com/dequelabs/axe-core/pull/518/files#r139284668\n          out.badNodes.push(actualNode);\n        }\n      }\n    } else if (\n      actualNode.nodeType === 3 &&\n      actualNode.nodeValue.trim() !== ''\n    ) {\n      out.hasNonEmptyTextNode = true;\n    }\n    return out;\n  }, base);\n\n  if (result.badNodes.length) {\n    this.relatedNodes(result.badNodes);\n  }\n\n  return !!result.badNodes.length || result.hasNonEmptyTextNode;\n}\n"
  },
  {
    "path": "lib/checks/lists/only-dlitems.json",
    "content": "{\n  \"id\": \"only-dlitems\",\n  \"evaluate\": \"invalid-children-evaluate\",\n  \"options\": {\n    \"validRoles\": [\"definition\", \"term\", \"listitem\"],\n    \"validNodeNames\": [\"dt\", \"dd\"],\n    \"divGroups\": true\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"dl element only has direct children that are allowed inside; <dt>, <dd>, or <div> elements\",\n      \"fail\": \"dl element has direct children that are not allowed: ${data.values}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/lists/only-listitems-evaluate.js",
    "content": "import { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getRole } from '../../commons/aria';\n\n/**\n * @deprecated\n */\nfunction onlyListitemsEvaluate(node, options, virtualNode) {\n  let hasNonEmptyTextNode = false;\n  let atLeastOneListitem = false;\n  let isEmpty = true;\n  const badNodes = [];\n  const badRoleNodes = [];\n  const badRoles = [];\n\n  virtualNode.children.forEach(vNode => {\n    const { actualNode } = vNode;\n\n    if (actualNode.nodeType === 3 && actualNode.nodeValue.trim() !== '') {\n      hasNonEmptyTextNode = true;\n      return;\n    }\n\n    if (actualNode.nodeType !== 1 || !isVisibleToScreenReaders(actualNode)) {\n      return;\n    }\n\n    isEmpty = false;\n    const isLi = actualNode.nodeName.toUpperCase() === 'LI';\n    const role = getRole(vNode);\n    const isListItemRole = role === 'listitem';\n\n    if (!isLi && !isListItemRole) {\n      badNodes.push(actualNode);\n    }\n\n    if (isLi && !isListItemRole) {\n      badRoleNodes.push(actualNode);\n\n      if (!badRoles.includes(role)) {\n        badRoles.push(role);\n      }\n    }\n\n    if (isListItemRole) {\n      atLeastOneListitem = true;\n    }\n  });\n\n  if (hasNonEmptyTextNode || badNodes.length) {\n    this.relatedNodes(badNodes);\n    return true;\n  }\n\n  if (isEmpty || atLeastOneListitem) {\n    return false;\n  }\n\n  this.relatedNodes(badRoleNodes);\n  this.data({\n    messageKey: 'roleNotValid',\n    roles: badRoles.join(', ')\n  });\n  return true;\n}\n\nexport default onlyListitemsEvaluate;\n"
  },
  {
    "path": "lib/checks/lists/only-listitems.json",
    "content": "{\n  \"id\": \"only-listitems\",\n  \"evaluate\": \"invalid-children-evaluate\",\n  \"options\": {\n    \"validRoles\": [\"listitem\"],\n    \"validNodeNames\": [\"li\"]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"List element only has direct children that are allowed inside <li> elements\",\n      \"fail\": \"List element has direct children that are not allowed: ${data.values}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/lists/structured-dlitems-evaluate.js",
    "content": "function structuredDlitemsEvaluate(node, options, virtualNode) {\n  const children = virtualNode.children;\n  if (!children || !children.length) {\n    return false;\n  }\n\n  let hasDt = false,\n    hasDd = false,\n    nodeName;\n  for (let i = 0; i < children.length; i++) {\n    nodeName = children[i].props.nodeName.toUpperCase();\n    if (nodeName === 'DT') {\n      hasDt = true;\n    }\n    if (hasDt && nodeName === 'DD') {\n      return false;\n    }\n    if (nodeName === 'DD') {\n      hasDd = true;\n    }\n  }\n\n  return hasDt || hasDd;\n}\n\nexport default structuredDlitemsEvaluate;\n"
  },
  {
    "path": "lib/checks/lists/structured-dlitems.json",
    "content": "{\n  \"id\": \"structured-dlitems\",\n  \"evaluate\": \"structured-dlitems-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"When not empty, element has both <dt> and <dd> elements\",\n      \"fail\": \"When not empty, element does not have at least one <dt> element followed by at least one <dd> element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/media/caption-evaluate.js",
    "content": "import { querySelectorAll } from '../../core/utils';\n\nfunction captionEvaluate(node, options, virtualNode) {\n  const tracks = querySelectorAll(virtualNode, 'track');\n  const hasCaptions = tracks.some(vNode => {\n    return (vNode.attr('kind') || '').toLowerCase() === 'captions';\n  });\n\n  // Undefined if there are no tracks - media may use another caption method\n  return hasCaptions ? false : undefined;\n}\n\nexport default captionEvaluate;\n"
  },
  {
    "path": "lib/checks/media/caption.json",
    "content": "{\n  \"id\": \"caption\",\n  \"evaluate\": \"caption-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"The multimedia element has a captions track\",\n      \"incomplete\": \"Check that captions are available for the element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/media/frame-tested-after.js",
    "content": "const joinStr = ' > ';\n\nfunction frameTestedAfter(results) {\n  const iframes = {};\n\n  return results.filter(result => {\n    const frameResult =\n      result.node.ancestry[result.node.ancestry.length - 1] !== 'html';\n\n    if (frameResult) {\n      const ancestry = result.node.ancestry.flat(Infinity).join(joinStr);\n      iframes[ancestry] = result;\n      return true;\n    }\n\n    // remove the `html` from the path to get the iframe path\n    const ancestry = result.node.ancestry\n      .slice(0, result.node.ancestry.length - 1)\n      .flat(Infinity)\n      .join(joinStr);\n\n    // pass for each iframe that has an html result\n    if (iframes[ancestry]) {\n      iframes[ancestry].result = true;\n    }\n\n    return false;\n  });\n}\n\nexport default frameTestedAfter;\n"
  },
  {
    "path": "lib/checks/media/frame-tested-evaluate.js",
    "content": "function frameTestedEvaluate(node, options) {\n  // assume iframe is not tested\n  return options.isViolation ? false : undefined;\n}\n\nexport default frameTestedEvaluate;\n"
  },
  {
    "path": "lib/checks/media/frame-tested.json",
    "content": "{\n  \"id\": \"frame-tested\",\n  \"evaluate\": \"frame-tested-evaluate\",\n  \"after\": \"frame-tested-after\",\n  \"options\": {\n    \"isViolation\": false\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"The iframe was tested with axe-core\",\n      \"fail\": \"The iframe could not be tested with axe-core\",\n      \"incomplete\": \"The iframe still has to be tested with axe-core\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/media/no-autoplay-audio-evaluate.js",
    "content": "function noAutoplayAudioEvaluate(node, options) {\n  const hasControls = node.hasAttribute('controls');\n\n  /**\n   * if the media loops then we only need to know if it has controls, regardless\n   * of the duration\n   */\n  if (node.hasAttribute('loop')) {\n    return hasControls;\n  }\n\n  /**\n   * if duration cannot be read, this means `preloadMedia` has failed\n   */\n  if (!node.duration) {\n    console.warn(`axe.utils.preloadMedia did not load metadata`);\n    return undefined;\n  }\n\n  /**\n   * Compute playable duration and verify if it within allowed duration\n   */\n  const { allowedDuration = 3 } = options;\n  const playableDuration = getPlayableDuration(node);\n  if (playableDuration <= allowedDuration) {\n    return true;\n  }\n\n  /**\n   * if media element does not provide controls mechanism\n   * -> fail\n   */\n  if (!hasControls) {\n    return false;\n  }\n\n  return true;\n\n  /**\n   * Compute playback duration\n   * @param {HTMLMediaElement} elm media element\n   */\n  function getPlayableDuration(elm) {\n    if (!elm.currentSrc) {\n      return 0;\n    }\n\n    const playbackRange = getPlaybackRange(elm.currentSrc);\n    if (!playbackRange) {\n      return Math.abs(elm.duration - (elm.currentTime || 0));\n    }\n\n    if (playbackRange.length === 1) {\n      return Math.abs(elm.duration - playbackRange[0]);\n    }\n\n    return Math.abs(playbackRange[1] - playbackRange[0]);\n  }\n\n  /**\n   * Get playback range from a media elements source, if specified\n   * See - https://developer.mozilla.org/de/docs/Web/HTML/Using_HTML5_audio_and_video#Specifying_playback_range\n   *\n   * Eg:\n   * src='....someMedia.mp3#t=8'\n   * \t-> should yeild [8]\n   * src='....someMedia.mp3#t=10,12'\n   *  -> should yeild [10,12]\n   * @param {String} src media src\n   * @returns {Array|undefined}\n   */\n  function getPlaybackRange(src) {\n    const match = src.match(/#t=(.*)/);\n    if (!match) {\n      return;\n    }\n    const [, value] = match;\n    const ranges = value.split(',');\n\n    return ranges.map(range => {\n      // range is denoted in HH:MM:SS -> convert to seconds\n      if (/:/.test(range)) {\n        return convertHourMinSecToSeconds(range);\n      }\n      return parseFloat(range);\n    });\n  }\n\n  /**\n   * Add HH, MM, SS to seconds\n   * @param {String} hhMmSs time expressed in HH:MM:SS\n   */\n  function convertHourMinSecToSeconds(hhMmSs) {\n    const parts = hhMmSs.split(':');\n    let secs = 0;\n    let mins = 1;\n\n    while (parts.length > 0) {\n      secs += mins * parseInt(parts.pop(), 10);\n      mins *= 60;\n    }\n\n    return parseFloat(secs);\n  }\n}\n\nexport default noAutoplayAudioEvaluate;\n"
  },
  {
    "path": "lib/checks/media/no-autoplay-audio.json",
    "content": "{\n  \"id\": \"no-autoplay-audio\",\n  \"evaluate\": \"no-autoplay-audio-evaluate\",\n  \"options\": {\n    \"allowedDuration\": 3\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"<video> or <audio> does not output audio for more than allowed duration or has controls mechanism\",\n      \"fail\": \"<video> or <audio> outputs audio for more than allowed duration and does not have a controls mechanism\",\n      \"incomplete\": \"Check that the <video> or <audio> does not output audio for more than allowed duration or provides a controls mechanism\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/mobile/css-orientation-lock-evaluate.js",
    "content": "function cssOrientationLockEvaluate(node, options, virtualNode, context) {\n  const { cssom = undefined } = context || {};\n  const { degreeThreshold = 0 } = options || {};\n  if (!cssom || !cssom.length) {\n    return undefined;\n  }\n\n  let isLocked = false;\n  let relatedElements = [];\n  const rulesGroupByDocumentFragment = groupCssomByDocument(cssom);\n\n  for (const key of Object.keys(rulesGroupByDocumentFragment)) {\n    const { root, rules } = rulesGroupByDocumentFragment[key];\n    const orientationRules = rules.filter(isMediaRuleWithOrientation);\n    if (!orientationRules.length) {\n      continue;\n    }\n\n    orientationRules.forEach(({ cssRules }) => {\n      Array.from(cssRules).forEach(cssRule => {\n        const locked = getIsOrientationLocked(cssRule);\n\n        // if locked and not root HTML, preserve as relatedNodes\n        if (locked && cssRule.selectorText.toUpperCase() !== 'HTML') {\n          const elms =\n            Array.from(root.querySelectorAll(cssRule.selectorText)) || [];\n          relatedElements = relatedElements.concat(elms);\n        }\n\n        isLocked = isLocked || locked;\n      });\n    });\n  }\n\n  if (!isLocked) {\n    return true;\n  }\n  if (relatedElements.length) {\n    this.relatedNodes(relatedElements);\n  }\n  return false;\n\n  /**\n   * Group given cssom by document/ document fragment\n   * @param {Array<Object>} allCssom cssom\n   * @return {Object}\n   */\n  function groupCssomByDocument(cssObjectModel) {\n    return cssObjectModel.reduce((out, { sheet, root, shadowId }) => {\n      const key = shadowId ? shadowId : 'topDocument';\n\n      if (!out[key]) {\n        out[key] = { root, rules: [] };\n      }\n\n      if (!sheet || !sheet.cssRules) {\n        return out;\n      }\n\n      const rules = Array.from(sheet.cssRules);\n      out[key].rules = out[key].rules.concat(rules);\n\n      return out;\n    }, {});\n  }\n\n  /**\n   * Filter CSS Rules that target Orientation CSS Media Features\n   * @param {Array<Object>} cssRules\n   * @returns {Array<Object>}\n   */\n  function isMediaRuleWithOrientation({ type, cssText }) {\n    /**\n     * Filter:\n     * CSSRule.MEDIA_Rule\n     * -> https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule\n     */\n    if (type !== 4) {\n      return false;\n    }\n\n    /**\n     * Filter:\n     * CSSRule with conditionText of `orientation`\n     */\n    return (\n      /orientation:\\s*landscape/i.test(cssText) ||\n      /orientation:\\s*portrait/i.test(cssText)\n    );\n  }\n\n  /**\n   * Interpolate a given CSS Rule to ascertain if orientation is locked by use of any transformation functions that affect rotation along the Z Axis\n   * @param {Object} cssRule given CSS Rule\n   * @property {String} cssRule.selectorText selector text targetted by given cssRule\n   * @property {Object} cssRule.style style\n   * @return {Boolean}\n   */\n  function getIsOrientationLocked({ selectorText, style }) {\n    if (!selectorText || style.length <= 0) {\n      return false;\n    }\n\n    const transformStyle =\n      style.transform || style.webkitTransform || style.msTransform || false;\n    if (!transformStyle && !style.rotate) {\n      return false;\n    }\n\n    const transformDegrees = getTransformDegrees(transformStyle);\n    const rotateDegrees = getRotationInDegrees('rotate', style.rotate);\n\n    // `transform: rotate` and `rotate` are additive\n    let degrees = transformDegrees + rotateDegrees;\n    if (!degrees) {\n      return false;\n    }\n    degrees = Math.abs(degrees);\n\n    /**\n     * When degree is a multiple of 180, it is not considered an orientation lock\n     */\n    if (Math.abs(degrees - 180) % 180 <= degreeThreshold) {\n      return false;\n    }\n\n    return Math.abs(degrees - 90) % 90 <= degreeThreshold;\n  }\n\n  /**\n   * Get the degree value of a transform.\n   * @property {Object} cssRule.style style\n   * @return {Number}\n   */\n  function getTransformDegrees(transformStyle) {\n    if (!transformStyle) {\n      return 0;\n    }\n\n    /**\n     * get last match/occurrence of a transformation function that can affect rotation along Z axis\n     */\n    const matches = transformStyle.match(\n      /(rotate|rotateZ|rotate3d|matrix|matrix3d)\\(([^)]+)\\)(?!.*(rotate|rotateZ|rotate3d|matrix|matrix3d))/\n    );\n    if (!matches) {\n      return 0;\n    }\n\n    const [, transformFn, transformFnValue] = matches;\n    return getRotationInDegrees(transformFn, transformFnValue);\n  }\n\n  /**\n   * Interpolate rotation along the z axis from a given value to a transform function\n   * @param {String} transformFunction CSS transformation function\n   * @param {String} transformFnValue value applied to a transform function (contains a unit)\n   * @returns {Number}\n   */\n  function getRotationInDegrees(transformFunction, transformFnValue) {\n    switch (transformFunction) {\n      case 'rotate':\n      case 'rotateZ':\n        return getAngleInDegrees(transformFnValue);\n      case 'rotate3d':\n        const [, , z, angleWithUnit] = transformFnValue\n          .split(',')\n          .map(value => value.trim());\n        if (parseInt(z) === 0) {\n          // no transform is applied along z axis -> ignore\n          return;\n        }\n        return getAngleInDegrees(angleWithUnit);\n      case 'matrix':\n      case 'matrix3d':\n        return getAngleInDegreesFromMatrixTransform(transformFnValue);\n      default:\n        return 0;\n    }\n  }\n\n  /**\n   * Get angle in degrees from a transform value by interpolating the unit of measure\n   * @param {String} angleWithUnit value applied to a transform function (Eg: 1turn)\n   * @returns{Number|undefined}\n   */\n  function getAngleInDegrees(angleWithUnit) {\n    const [unit] = angleWithUnit.match(/(deg|grad|rad|turn)/) || [];\n    if (!unit) {\n      return 0;\n    }\n\n    const angle = parseFloat(angleWithUnit.replace(unit, ``));\n    switch (unit) {\n      case 'rad':\n        return convertRadToDeg(angle);\n      case 'grad':\n        return convertGradToDeg(angle);\n      case 'turn':\n        return convertTurnToDeg(angle);\n      case 'deg':\n      default:\n        return parseInt(angle);\n    }\n  }\n\n  /**\n   * Get angle in degrees from a transform value applied to `matrix` or `matrix3d` transform functions\n   * @param {String} transformFnValue value applied to a transform function (contains a unit)\n   * @returns {Number}\n   */\n  function getAngleInDegreesFromMatrixTransform(transformFnValue) {\n    const values = transformFnValue.split(',');\n\n    /**\n     * Matrix 2D\n     * Notes: https://css-tricks.com/get-value-of-css-rotation-through-javascript/\n     */\n    if (values.length <= 6) {\n      const [a, b] = values;\n      const radians = Math.atan2(parseFloat(b), parseFloat(a));\n      return convertRadToDeg(radians);\n    }\n\n    /**\n     * Matrix 3D\n     * Notes: https://drafts.csswg.org/css-transforms-2/#decomposing-a-3d-matrix\n     */\n    const sinB = parseFloat(values[8]);\n    const b = Math.asin(sinB);\n    const cosB = Math.cos(b);\n    const rotateZRadians = Math.acos(parseFloat(values[0]) / cosB);\n    return convertRadToDeg(rotateZRadians);\n  }\n\n  /**\n   * Convert angle specified in unit radians to degrees\n   * See - https://drafts.csswg.org/css-values-3/#rad\n   * @param {Number} radians radians\n   * @return {Number}\n   */\n  function convertRadToDeg(radians) {\n    return Math.round(radians * (180 / Math.PI));\n  }\n\n  /**\n   * Convert angle specified in unit grad to degrees\n   * See - https://drafts.csswg.org/css-values-3/#grad\n   * @param {Number} grad grad\n   * @return {Number}\n   */\n  function convertGradToDeg(grad) {\n    grad = grad % 400;\n    if (grad < 0) {\n      grad += 400;\n    }\n    return Math.round((grad / 400) * 360);\n  }\n\n  /**\n   * Convert angle specifed in unit turn to degrees\n   * See - https://drafts.csswg.org/css-values-3/#turn\n   * @param {Number} turn\n   * @returns {Number}\n   */\n  function convertTurnToDeg(turn) {\n    return Math.round(360 / (1 / turn));\n  }\n}\n\nexport default cssOrientationLockEvaluate;\n"
  },
  {
    "path": "lib/checks/mobile/css-orientation-lock.json",
    "content": "{\n  \"id\": \"css-orientation-lock\",\n  \"evaluate\": \"css-orientation-lock-evaluate\",\n  \"options\": {\n    \"degreeThreshold\": 2\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Display is operable, and orientation lock does not exist\",\n      \"fail\": \"CSS Orientation lock is applied, and makes display inoperable\",\n      \"incomplete\": \"CSS Orientation lock cannot be determined\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/mobile/meta-viewport-large.json",
    "content": "{\n  \"id\": \"meta-viewport-large\",\n  \"evaluate\": \"meta-viewport-scale-evaluate\",\n  \"options\": {\n    \"scaleMinimum\": 5,\n    \"lowerBound\": 2\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"<meta> tag does not prevent significant zooming on mobile devices\",\n      \"fail\": \"<meta> tag limits zooming on mobile devices\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/mobile/meta-viewport-scale-evaluate.js",
    "content": "function metaViewportScaleEvaluate(node, options, virtualNode) {\n  const { scaleMinimum = 2, lowerBound = false } = options || {};\n\n  const content = virtualNode.attr('content') || '';\n  if (!content) {\n    return true;\n  }\n\n  const result = content.split(/[;,]/).reduce((out, item) => {\n    const contentValue = item.trim();\n    if (!contentValue) {\n      return out;\n    }\n\n    const [key, value] = contentValue.split('=');\n    if (!key || !value) {\n      return out;\n    }\n    const curatedKey = key.toLowerCase().trim();\n    let curatedValue = value.toLowerCase().trim();\n\n    // convert `yes` to `1`\n    if (curatedKey === 'maximum-scale' && curatedValue === 'yes') {\n      curatedValue = 1;\n    }\n    // when negative ignore key\n    if (curatedKey === 'maximum-scale' && parseFloat(curatedValue) < 0) {\n      return out;\n    }\n\n    out[curatedKey] = curatedValue;\n    return out;\n  }, {});\n\n  if (\n    lowerBound &&\n    result['maximum-scale'] &&\n    parseFloat(result['maximum-scale']) < lowerBound\n  ) {\n    return true;\n  }\n\n  if (!lowerBound && result['user-scalable'] === 'no') {\n    this.data('user-scalable=no');\n    return false;\n  }\n\n  const userScalableAsFloat = parseFloat(result['user-scalable']);\n  if (\n    !lowerBound &&\n    result['user-scalable'] &&\n    (userScalableAsFloat || userScalableAsFloat === 0) &&\n    userScalableAsFloat > -1 &&\n    userScalableAsFloat < 1\n  ) {\n    this.data('user-scalable');\n    return false;\n  }\n\n  if (\n    result['maximum-scale'] &&\n    parseFloat(result['maximum-scale']) < scaleMinimum\n  ) {\n    this.data('maximum-scale');\n    return false;\n  }\n\n  return true;\n}\n\nexport default metaViewportScaleEvaluate;\n"
  },
  {
    "path": "lib/checks/mobile/meta-viewport.json",
    "content": "{\n  \"id\": \"meta-viewport\",\n  \"evaluate\": \"meta-viewport-scale-evaluate\",\n  \"options\": {\n    \"scaleMinimum\": 2\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"<meta> tag does not disable zooming on mobile devices\",\n      \"fail\": \"${data} on <meta> tag disables zooming on mobile devices\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/mobile/target-offset-evaluate.js",
    "content": "import { findNearbyElms, isFocusable, isInTabOrder } from '../../commons/dom';\nimport { getRoleType } from '../../commons/aria';\nimport { getOffset, rectHasMinimumSize } from '../../commons/math';\n\nconst roundingMargin = 0.05;\n\nexport default function targetOffsetEvaluate(node, options, vNode) {\n  const minOffset = options?.minOffset || 24;\n  // Bail early to avoid hitting very expensive calculations.\n  // Targets are so large they are unlikely to fail.\n  if (rectHasMinimumSize(minOffset * 10, vNode.boundingClientRect)) {\n    this.data({ messageKey: 'large', minOffset });\n    return true;\n  }\n\n  const closeNeighbors = [];\n  let closestOffset = minOffset;\n  for (const vNeighbor of findNearbyElms(vNode, minOffset)) {\n    if (getRoleType(vNeighbor) !== 'widget' || !isFocusable(vNeighbor)) {\n      continue;\n    }\n    // the offset code works off radius but we want our messaging to reflect diameter\n    let offset = null;\n    try {\n      offset = getOffset(vNode, vNeighbor, minOffset / 2);\n    } catch (err) {\n      if (err.message.startsWith('splitRects')) {\n        this.data({\n          messageKey: 'tooManyRects',\n          closestOffset: 0,\n          minOffset\n        });\n        return undefined;\n      }\n\n      throw err;\n    }\n\n    if (offset === null) {\n      continue;\n    }\n\n    offset = roundToSingleDecimal(offset) * 2;\n    if (offset + roundingMargin >= minOffset) {\n      continue;\n    }\n    closestOffset = Math.min(closestOffset, offset);\n    closeNeighbors.push(vNeighbor);\n  }\n\n  if (closeNeighbors.length === 0) {\n    this.data({ closestOffset, minOffset });\n    return true;\n  }\n\n  this.relatedNodes(closeNeighbors.map(({ actualNode }) => actualNode));\n\n  if (!closeNeighbors.some(isInTabOrder)) {\n    this.data({\n      messageKey: 'nonTabbableNeighbor',\n      closestOffset,\n      minOffset\n    });\n    return undefined;\n  }\n\n  this.data({ closestOffset, minOffset });\n  return isInTabOrder(vNode) ? false : undefined;\n}\n\nfunction roundToSingleDecimal(num) {\n  return Math.round(num * 10) / 10;\n}\n"
  },
  {
    "path": "lib/checks/mobile/target-offset.json",
    "content": "{\n  \"id\": \"target-offset\",\n  \"evaluate\": \"target-offset-evaluate\",\n  \"options\": {\n    \"minOffset\": 24\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": {\n        \"default\": \"Target has sufficient space from its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px which is at least ${data.minOffset}px.\",\n        \"large\": \"Target far exceeds the minimum size of ${data.minOffset}px.\"\n      },\n      \"fail\": \"Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is this a target?\",\n        \"nonTabbableNeighbor\": \"Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?\",\n        \"tooManyRects\": \"Could not get the target size because there are too many overlapping elements\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/mobile/target-size-evaluate.js",
    "content": "import { findNearbyElms, isFocusable, isInTabOrder } from '../../commons/dom';\nimport { getRoleType } from '../../commons/aria';\nimport {\n  splitRects,\n  rectHasMinimumSize,\n  hasVisualOverlap\n} from '../../commons/math';\nimport { contains } from '../../core/utils';\n\n/**\n * Determine if an element has a minimum size, taking into account\n * any elements that may obscure it.\n */\nexport default function targetSizeEvaluate(node, options, vNode) {\n  const minSize = options?.minSize || 24;\n  const nodeRect = vNode.boundingClientRect;\n  // Bail early to avoid hitting very expensive calculations.\n  // Targets are so large they are unlikely to fail.\n  if (rectHasMinimumSize(minSize * 10, nodeRect)) {\n    this.data({ messageKey: 'large', minSize });\n    return true;\n  }\n\n  const hasMinimumSize = rectHasMinimumSize.bind(null, minSize);\n  const nearbyElms = findNearbyElms(vNode);\n  const overflowingContent = filterOverflowingContent(vNode, nearbyElms);\n  const { fullyObscuringElms, partialObscuringElms } = filterByElmsOverlap(\n    vNode,\n    nearbyElms\n  );\n\n  // Target has overflowing content;\n  //   and is either not fully obscured (so may not pass),\n  //   or has insufficient space (and so may not fail)\n  if (\n    overflowingContent.length &&\n    (fullyObscuringElms.length || !hasMinimumSize(nodeRect))\n  ) {\n    this.data({ minSize, messageKey: 'contentOverflow' });\n    this.relatedNodes(mapActualNodes(overflowingContent));\n    return undefined;\n  }\n\n  // Target is fully obscured and no overflowing content (which may not be obscured)\n  if (fullyObscuringElms.length) {\n    this.relatedNodes(mapActualNodes(fullyObscuringElms));\n    this.data({ messageKey: 'obscured' });\n    return true;\n  }\n\n  // Check cannot fail if the target is not in the tab order\n  const negativeOutcome = isInTabOrder(vNode) ? false : undefined;\n\n  // Target is too small, and has no overflowing content that increases the size\n  if (!hasMinimumSize(nodeRect)) {\n    this.data({ minSize, ...toDecimalSize(nodeRect) });\n    return negativeOutcome;\n  }\n\n  // Figure out the largest space on the target, not obscured by other widgets\n  const obscuredWidgets = filterFocusableWidgets(partialObscuringElms);\n\n  // Target not obscured and has sufficient space\n  if (!obscuredWidgets.length) {\n    this.data({ minSize, ...toDecimalSize(nodeRect) });\n    return true;\n  }\n\n  const largestInnerRect = getLargestUnobscuredArea(\n    vNode,\n    obscuredWidgets,\n    minSize\n  );\n  if (!largestInnerRect) {\n    this.data({ minSize, messageKey: 'tooManyRects' });\n    return undefined;\n  }\n\n  // Target is obscured, and insufficient space is left\n  if (!hasMinimumSize(largestInnerRect)) {\n    if (overflowingContent.length) {\n      this.data({ minSize, messageKey: 'contentOverflow' });\n      this.relatedNodes(mapActualNodes(overflowingContent));\n      return undefined;\n    }\n\n    const allTabbable = obscuredWidgets.every(isInTabOrder);\n    const messageKey = `partiallyObscured${allTabbable ? '' : 'NonTabbable'}`;\n\n    this.data({ messageKey, minSize, ...toDecimalSize(largestInnerRect) });\n    this.relatedNodes(mapActualNodes(obscuredWidgets));\n    return allTabbable ? negativeOutcome : undefined;\n  }\n\n  // Target has sufficient space\n  this.data({ minSize, ...toDecimalSize(largestInnerRect || nodeRect) });\n  this.relatedNodes(mapActualNodes(obscuredWidgets));\n  return true;\n}\n\n// Filter nearby elements based on if they are overflowing content of vNode\n// Except if the overflowing content is a descendant in the tab order\nfunction filterOverflowingContent(vNode, nearbyElms) {\n  return nearbyElms.filter(\n    nearbyElm =>\n      !isEnclosedRect(nearbyElm, vNode) &&\n      isDescendantNotInTabOrder(vNode, nearbyElm)\n  );\n}\n\n// Return fully and partially obscuring nodes from nearby nodes\nfunction filterByElmsOverlap(vNode, nearbyElms) {\n  const fullyObscuringElms = [];\n  const partialObscuringElms = [];\n  for (const vNeighbor of nearbyElms) {\n    // Determine if the element is obscured\n    if (\n      !isDescendantNotInTabOrder(vNode, vNeighbor) &&\n      hasVisualOverlap(vNode, vNeighbor) &&\n      getCssPointerEvents(vNeighbor) !== 'none'\n    ) {\n      // Group by fully or partially obscured\n      if (isEnclosedRect(vNode, vNeighbor)) {\n        fullyObscuringElms.push(vNeighbor);\n      } else {\n        partialObscuringElms.push(vNeighbor);\n      }\n    }\n  }\n  return { fullyObscuringElms, partialObscuringElms };\n}\n\n// Find areas of the target that are not obscured\nfunction getLargestUnobscuredArea(vNode, obscuredNodes, minSize) {\n  const nodeRect = vNode.boundingClientRect;\n  const obscuringRects = obscuredNodes\n    .map(obscuredNode => {\n      const display = obscuredNode.getComputedStylePropertyValue('display');\n      return display === 'inline'\n        ? obscuredNode.clientRects\n        : obscuredNode.boundingClientRect;\n    })\n    .flat(Infinity);\n  let unobscuredRects;\n  try {\n    unobscuredRects = splitRects(nodeRect, obscuringRects);\n  } catch {\n    return null;\n  }\n\n  // Of the unobscured inner rects, work out the largest\n  return getLargestRect(unobscuredRects, minSize);\n}\n\n// Find the largest rectangle in the array, prioritize ones that meet a minimum size\nfunction getLargestRect(rects, minSize) {\n  return rects.reduce((rectA, rectB) => {\n    const rectAisMinimum = rectHasMinimumSize(minSize, rectA);\n    const rectBisMinimum = rectHasMinimumSize(minSize, rectB);\n    // Prioritize rects that pass the minimum\n    if (rectAisMinimum !== rectBisMinimum) {\n      return rectAisMinimum ? rectA : rectB;\n    }\n    const areaA = rectA.width * rectA.height;\n    const areaB = rectB.width * rectB.height;\n    return areaA > areaB ? rectA : rectB;\n  });\n}\n\n// Filter only focusable widgets\nfunction filterFocusableWidgets(vNodes) {\n  return vNodes.filter(\n    vNode => getRoleType(vNode) === 'widget' && isFocusable(vNode)\n  );\n}\n\nfunction isEnclosedRect(vNodeA, vNodeB) {\n  const rectA = vNodeA.boundingClientRect;\n  const rectB = vNodeB.boundingClientRect;\n  return (\n    rectA.top >= rectB.top &&\n    rectA.left >= rectB.left &&\n    rectA.bottom <= rectB.bottom &&\n    rectA.right <= rectB.right\n  );\n}\n\nfunction getCssPointerEvents(vNode) {\n  return vNode.getComputedStylePropertyValue('pointer-events');\n}\n\nfunction toDecimalSize(rect) {\n  return {\n    width: Math.round(rect.width * 10) / 10,\n    height: Math.round(rect.height * 10) / 10\n  };\n}\n\nfunction isDescendantNotInTabOrder(vAncestor, vNode) {\n  return contains(vAncestor, vNode) && !isInTabOrder(vNode);\n}\n\nfunction mapActualNodes(vNodes) {\n  return vNodes.map(({ actualNode }) => actualNode);\n}\n"
  },
  {
    "path": "lib/checks/mobile/target-size.json",
    "content": "{\n  \"id\": \"target-size\",\n  \"evaluate\": \"target-size-evaluate\",\n  \"options\": {\n    \"minSize\": 24\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": {\n        \"default\": \"Control has sufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\",\n        \"obscured\": \"Control is ignored because it is fully obscured and thus not clickable\",\n        \"large\": \"Target far exceeds the minimum size of ${data.minSize}px.\"\n      },\n      \"fail\": {\n        \"default\": \"Target has insufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\",\n        \"partiallyObscured\": \"Target has insufficient size because it is partially obscured (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element with negative tabindex has insufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is this a target?\",\n        \"contentOverflow\": \"Element size could not be accurately determined due to overflow content\",\n        \"partiallyObscured\": \"Element with negative tabindex has insufficient size because it is partially obscured (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is this a target?\",\n        \"partiallyObscuredNonTabbable\": \"Target has insufficient size because it is partially obscured by a neighbor with negative tabindex (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is the neighbor a target?\",\n        \"tooManyRects\": \"Could not get the target size because there are too many overlapping elements\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/header-present.json",
    "content": "{\n  \"id\": \"header-present\",\n  \"evaluate\": \"has-descendant-evaluate\",\n  \"after\": \"has-descendant-after\",\n  \"options\": {\n    \"selector\": \":is(h1, h2, h3, h4, h5, h6):not([role]), [role=heading]\"\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Page has a heading\",\n      \"fail\": \"Page does not have a heading\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/heading-order-after.js",
    "content": "import { matchAncestry } from '../../core/utils';\n\nexport default function headingOrderAfter(results) {\n  // Construct a map of all headings on the page\n  const headingOrder = getHeadingOrder(results);\n  results.forEach(result => {\n    result.result = getHeadingOrderOutcome(result, headingOrder);\n  });\n  return results;\n}\n\n/**\n * Determine check outcome, based on the position of the result in the headingOrder\n */\nfunction getHeadingOrderOutcome(result, headingOrder) {\n  const index = findHeadingOrderIndex(headingOrder, result.node.ancestry);\n  const currLevel = headingOrder[index]?.level ?? -1;\n  const prevLevel = headingOrder[index - 1]?.level ?? -1;\n\n  // First heading always passes\n  if (index === 0) {\n    return true;\n  }\n  // Heading not in the map\n  if (currLevel === -1) {\n    return undefined;\n  }\n  // Check if a heading is skipped\n  return currLevel - prevLevel <= 1;\n}\n\n/**\n * Generate a flattened heading order map, from the data property\n * of heading-order results\n */\nfunction getHeadingOrder(results) {\n  // Ensure parent frames are handled first\n  results = [...results];\n  results.sort(({ node: nodeA }, { node: nodeB }) => {\n    return nodeA.ancestry.length - nodeB.ancestry.length;\n  });\n  // push or splice result.data into headingOrder\n  const headingOrder = results.reduce(mergeHeadingOrder, []);\n  // Remove all frame placeholders\n  return headingOrder.filter(({ level }) => level !== -1);\n}\n\n/**\n * Add the data of a heading-order result to the headingOrder map\n */\nfunction mergeHeadingOrder(mergedHeadingOrder, result) {\n  const frameHeadingOrder = result.data?.headingOrder;\n  const frameAncestry = shortenArray(result.node.ancestry, 1);\n\n  // Only the first result in each frame has a headingOrder. Ignore the rest\n  if (!frameHeadingOrder) {\n    return mergedHeadingOrder;\n  }\n\n  // Prepend node ancestry to each heading.ancestry\n  const normalizedHeadingOrder = frameHeadingOrder.map(heading => {\n    return addFrameToHeadingAncestry(heading, frameAncestry);\n  });\n\n  // Find if the result is from a frame previously processed\n  const index = getFrameIndex(mergedHeadingOrder, frameAncestry);\n  // heading is not in a frame, stick 'm in at the end.\n  if (index === -1) {\n    mergedHeadingOrder.push(...normalizedHeadingOrder);\n  } else {\n    mergedHeadingOrder.splice(index, 0, ...normalizedHeadingOrder);\n  }\n  return mergedHeadingOrder;\n}\n\n/**\n * Determine where the iframe results fit into the top-level heading order\n *\n * If a frame has no headings, but it does have iframes we might not have a result.\n * We can account for this by finding the closest ancestor we do know about.\n */\nfunction getFrameIndex(headingOrder, frameAncestry) {\n  while (frameAncestry.length) {\n    const index = findHeadingOrderIndex(headingOrder, frameAncestry);\n    if (index !== -1) {\n      return index;\n    }\n    frameAncestry = shortenArray(frameAncestry, 1);\n  }\n  return -1;\n}\n\n/**\n * Find the index of a heading in the headingOrder by matching ancestries\n */\nfunction findHeadingOrderIndex(headingOrder, ancestry) {\n  return headingOrder.findIndex(heading => {\n    return matchAncestry(heading.ancestry, ancestry);\n  });\n}\n\n/**\n * Prepend the frame ancestry of a node to heading.ancestry\n */\nfunction addFrameToHeadingAncestry(heading, frameAncestry) {\n  const ancestry = frameAncestry.concat(heading.ancestry);\n  return { ...heading, ancestry };\n}\n\n/**\n * Shorten an array by some number of items\n */\nfunction shortenArray(arr, spliceLength) {\n  return arr.slice(0, arr.length - spliceLength);\n}\n"
  },
  {
    "path": "lib/checks/navigation/heading-order-evaluate.js",
    "content": "import cache from '../../core/base/cache';\nimport { querySelectorAllFilter, getAncestry } from '../../core/utils';\nimport { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getRole } from '../../commons/aria';\n\nfunction getLevel(vNode) {\n  const role = getRole(vNode);\n  const headingRole = role && role.includes('heading');\n  const ariaHeadingLevel = vNode.attr('aria-level');\n  const ariaLevel = parseInt(ariaHeadingLevel, 10);\n\n  const [, headingLevel] = vNode.props.nodeName.match(/h(\\d)/) || [];\n\n  if (!headingRole) {\n    return -1;\n  }\n\n  if (headingLevel && !ariaHeadingLevel) {\n    return parseInt(headingLevel, 10);\n  }\n\n  /*\n   * default aria-level for a role=heading is 2 if it is\n   * not set or set to an incorrect value.\n   * default aria-level for a heading element is the\n   * heading level.\n   * note that NVDA and VO allow any positive level\n   * @see https://www.w3.org/TR/wai-aria-1.1/#heading\n   * @see https://codepen.io/straker/pen/jOBjNNe\n   */\n  if (isNaN(ariaLevel) || ariaLevel < 1) {\n    if (headingLevel) {\n      return parseInt(headingLevel, 10);\n    }\n    return 2;\n  }\n\n  if (ariaLevel) {\n    return ariaLevel;\n  }\n\n  return -1;\n}\n\nfunction headingOrderEvaluate() {\n  let headingOrder = cache.get('headingOrder');\n  if (headingOrder) {\n    return true;\n  }\n\n  // find all headings, even ones that are outside the current\n  // context. also need to know where iframes are so we can insert\n  // the results of any in-context iframes into their proper order\n  // @see https://github.com/dequelabs/axe-core/issues/728\n  const selector = 'h1, h2, h3, h4, h5, h6, [role=heading], iframe, frame';\n  // TODO: es-modules_tree\n  const vNodes = querySelectorAllFilter(\n    axe._tree[0],\n    selector,\n    isVisibleToScreenReaders\n  );\n\n  headingOrder = vNodes.map(vNode => {\n    // save the path so we can reconstruct the heading order\n    return {\n      ancestry: [getAncestry(vNode.actualNode)],\n      level: getLevel(vNode)\n    };\n  });\n  this.data({ headingOrder });\n  cache.set('headingOrder', vNodes);\n  return true;\n}\n\nexport default headingOrderEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/heading-order.json",
    "content": "{\n  \"id\": \"heading-order\",\n  \"evaluate\": \"heading-order-evaluate\",\n  \"after\": \"heading-order-after\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Heading order valid\",\n      \"fail\": \"Heading order invalid\",\n      \"incomplete\": \"Unable to determine previous heading\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/identical-links-same-purpose-after.js",
    "content": "/**\n * Check if two given objects are the same (Note: this fn is not extensive in terms of depth equality)\n * @param {Object} a object a, to compare\n * @param {*} b object b, to compare\n * @returns {Boolean}\n */\nfunction isIdenticalObject(a, b) {\n  if (!a || !b) {\n    return false;\n  }\n\n  const aProps = Object.getOwnPropertyNames(a);\n  const bProps = Object.getOwnPropertyNames(b);\n\n  if (aProps.length !== bProps.length) {\n    return false;\n  }\n\n  const result = aProps.every(propName => {\n    const aValue = a[propName];\n    const bValue = b[propName];\n\n    if (typeof aValue !== typeof bValue) {\n      return false;\n    }\n\n    if (typeof aValue === `object` || typeof bValue === `object`) {\n      return isIdenticalObject(aValue, bValue);\n    }\n\n    return aValue === bValue;\n  });\n\n  return result;\n}\n\nfunction identicalLinksSamePurposeAfter(results) {\n  /**\n   * Skip, as no results to curate\n   */\n  if (results.length < 2) {\n    return results;\n  }\n\n  /**\n   * Filter results for which `result` is undefined & thus `data`, `relatedNodes` are undefined\n   */\n  const incompleteResults = results.filter(\n    ({ result }) => result !== undefined\n  );\n\n  /**\n   * for each result\n   * - get other results with matching accessible name\n   * - check if same purpose is served\n   * - if not change `result` to `undefined`\n   * - construct a list of unique results with relatedNodes to return\n   */\n  const uniqueResults = [];\n  const nameMap = {};\n\n  for (let index = 0; index < incompleteResults.length; index++) {\n    const currentResult = incompleteResults[index];\n\n    const { name, urlProps } = currentResult.data;\n    /**\n     * This is to avoid duplications in the `nodeMap`\n     */\n    if (nameMap[name]) {\n      continue;\n    }\n\n    const sameNameResults = incompleteResults.filter(\n      ({ data }, resultNum) => data.name === name && resultNum !== index\n    );\n    const isSameUrl = sameNameResults.every(({ data }) =>\n      isIdenticalObject(data.urlProps, urlProps)\n    );\n\n    /**\n     * when identical nodes exists but do not resolve to same url, flag result as `incomplete`\n     */\n    if (sameNameResults.length && !isSameUrl) {\n      currentResult.result = undefined;\n    }\n\n    /**\n     *  -> deduplicate results (for both `pass` or `incomplete`) and add `relatedNodes` if any\n     */\n    currentResult.relatedNodes = [];\n    currentResult.relatedNodes.push(\n      ...sameNameResults.map(node => node.relatedNodes[0])\n    );\n\n    /**\n     * Update `nodeMap` with `sameNameResults`\n     */\n    nameMap[name] = sameNameResults;\n\n    uniqueResults.push(currentResult);\n  }\n\n  return uniqueResults;\n}\n\nexport default identicalLinksSamePurposeAfter;\n"
  },
  {
    "path": "lib/checks/navigation/identical-links-same-purpose-evaluate.js",
    "content": "import { dom, text } from '../../commons';\n\n/**\n * Note: `identical-links-same-purpose-after` fn, helps reconcile the results\n */\nfunction identicalLinksSamePurposeEvaluate(node, options, virtualNode) {\n  const accText = text.accessibleTextVirtual(virtualNode);\n  const name = text\n    .sanitize(\n      text.removeUnicode(accText, {\n        emoji: true,\n        nonBmp: true,\n        punctuations: true\n      })\n    )\n    .toLowerCase();\n\n  if (!name) {\n    return undefined;\n  }\n\n  /**\n   * Set `data` and `relatedNodes` for use in `after` fn\n   */\n  const afterData = {\n    name,\n    urlProps: dom.urlPropsFromAttribute(node, 'href')\n  };\n  this.data(afterData);\n  this.relatedNodes([node]);\n\n  return true;\n}\n\nexport default identicalLinksSamePurposeEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/identical-links-same-purpose.json",
    "content": "{\n  \"id\": \"identical-links-same-purpose\",\n  \"evaluate\": \"identical-links-same-purpose-evaluate\",\n  \"after\": \"identical-links-same-purpose-after\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"There are no other links with the same name, that go to a different URL\",\n      \"incomplete\": \"Check that links have the same purpose, or are intentionally ambiguous.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/internal-link-present-evaluate.js",
    "content": "import { querySelectorAll } from '../../core/utils';\n\nfunction internalLinkPresentEvaluate(node, options, virtualNode) {\n  const links = querySelectorAll(virtualNode, 'a[href]');\n  return links.some(vLink => {\n    return /^#[^/!]/.test(vLink.attr('href'));\n  });\n}\n\nexport default internalLinkPresentEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/internal-link-present.json",
    "content": "{\n  \"id\": \"internal-link-present\",\n  \"evaluate\": \"internal-link-present-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Valid skip link found\",\n      \"fail\": \"No valid skip link found\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/landmark.json",
    "content": "{\n  \"id\": \"landmark\",\n  \"evaluate\": \"has-descendant-evaluate\",\n  \"options\": {\n    \"selector\": \"main, [role=main]\"\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Page has a landmark region\",\n      \"fail\": \"Page does not have a landmark region\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/meta-refresh-evaluate.js",
    "content": "const separatorRegex = /[;,\\s]/;\nconst validRedirectNumRegex = /^[0-9.]+$/;\n\nexport default function metaRefreshEvaluate(node, options, virtualNode) {\n  const { minDelay, maxDelay } = options || {};\n  const content = (virtualNode.attr('content') || '').trim();\n  const [redirectStr] = content.split(separatorRegex);\n  if (!redirectStr.match(validRedirectNumRegex)) {\n    return true;\n  }\n\n  const redirectDelay = parseFloat(redirectStr);\n  this.data({ redirectDelay });\n  if (typeof minDelay === 'number' && redirectDelay <= options.minDelay) {\n    return true;\n  }\n  if (typeof maxDelay === 'number' && redirectDelay > options.maxDelay) {\n    return true;\n  }\n  return false;\n}\n"
  },
  {
    "path": "lib/checks/navigation/meta-refresh-no-exceptions.json",
    "content": "{\n  \"id\": \"meta-refresh-no-exceptions\",\n  \"evaluate\": \"meta-refresh-evaluate\",\n  \"options\": {\n    \"minDelay\": 72000,\n    \"maxDelay\": false\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"<meta> tag does not immediately refresh the page\",\n      \"fail\": \"<meta> tag forces timed refresh of page\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/meta-refresh.json",
    "content": "{\n  \"id\": \"meta-refresh\",\n  \"evaluate\": \"meta-refresh-evaluate\",\n  \"options\": {\n    \"minDelay\": 0,\n    \"maxDelay\": 72000\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"<meta> tag does not immediately refresh the page\",\n      \"fail\": \"<meta> tag forces timed refresh of page (less than 20 hours)\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/p-as-heading-evaluate.js",
    "content": "import { findUpVirtual } from '../../commons/dom';\n\nfunction normalizeFontWeight(weight) {\n  switch (weight) {\n    case 'lighter':\n      return 100;\n    case 'normal':\n      return 400;\n    case 'bold':\n      return 700;\n    case 'bolder':\n      return 900;\n  }\n  weight = parseInt(weight);\n  return !isNaN(weight) ? weight : 400;\n}\n\nfunction getTextContainer(elm) {\n  let nextNode = elm;\n  const outerText = elm.textContent.trim();\n  let innerText = outerText;\n\n  while (innerText === outerText && nextNode !== undefined) {\n    let i = -1;\n    elm = nextNode;\n    if (elm.children.length === 0) {\n      return elm;\n    }\n\n    do {\n      // find the first non-empty child\n      i++;\n      innerText = elm.children[i].textContent.trim();\n    } while (innerText === '' && i + 1 < elm.children.length);\n    nextNode = elm.children[i];\n  }\n\n  return elm;\n}\n\nfunction getStyleValues(node) {\n  const style = window.getComputedStyle(getTextContainer(node));\n  return {\n    fontWeight: normalizeFontWeight(style.getPropertyValue('font-weight')),\n    fontSize: parseInt(style.getPropertyValue('font-size')),\n    isItalic: style.getPropertyValue('font-style') === 'italic'\n  };\n}\n\nfunction isHeaderStyle(styleA, styleB, margins) {\n  return margins.reduce((out, margin) => {\n    return (\n      out ||\n      ((!margin.size || styleA.fontSize / margin.size > styleB.fontSize) &&\n        (!margin.weight ||\n          styleA.fontWeight - margin.weight > styleB.fontWeight) &&\n        (!margin.italic || (styleA.isItalic && !styleB.isItalic)))\n    );\n  }, false);\n}\n\nfunction pAsHeadingEvaluate(node, options, virtualNode) {\n  const siblings = Array.from(node.parentNode.children);\n  const currentIndex = siblings.indexOf(node);\n\n  options = options || {};\n  const margins = options.margins || [];\n\n  const nextSibling = siblings\n    .slice(currentIndex + 1)\n    .find(elm => elm.nodeName.toUpperCase() === 'P');\n\n  const prevSibling = siblings\n    .slice(0, currentIndex)\n    .reverse()\n    .find(elm => elm.nodeName.toUpperCase() === 'P');\n\n  const currStyle = getStyleValues(node);\n  const nextStyle = nextSibling ? getStyleValues(nextSibling) : null;\n  const prevStyle = prevSibling ? getStyleValues(prevSibling) : null;\n\n  const optionsPassLength = options.passLength;\n  const optionsFailLength = options.failLength;\n\n  const headingLength = node.textContent.trim().length;\n  const paragraphLength = nextSibling?.textContent.trim().length;\n\n  if (headingLength > paragraphLength * optionsPassLength) {\n    return true;\n  }\n\n  if (!nextStyle || !isHeaderStyle(currStyle, nextStyle, margins)) {\n    return true;\n  }\n\n  const blockquote = findUpVirtual(virtualNode, 'blockquote');\n  if (blockquote && blockquote.nodeName.toUpperCase() === 'BLOCKQUOTE') {\n    return undefined;\n  }\n\n  if (prevStyle && !isHeaderStyle(currStyle, prevStyle, margins)) {\n    return undefined;\n  }\n\n  if (headingLength > paragraphLength * optionsFailLength) {\n    return undefined;\n  }\n  return false;\n}\n\nexport default pAsHeadingEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/p-as-heading.json",
    "content": "{\n  \"id\": \"p-as-heading\",\n  \"evaluate\": \"p-as-heading-evaluate\",\n  \"options\": {\n    \"margins\": [\n      {\n        \"weight\": 150,\n        \"italic\": true\n      },\n      {\n        \"weight\": 150,\n        \"size\": 1.15\n      },\n      {\n        \"italic\": true,\n        \"size\": 1.15\n      },\n      {\n        \"size\": 1.4\n      }\n    ],\n    \"passLength\": 1,\n    \"failLength\": 0.5\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"<p> elements are not styled as headings\",\n      \"fail\": \"Heading elements should be used instead of styled <p> elements\",\n      \"incomplete\": \"Unable to determine if <p> elements are styled as headings\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/region-after.js",
    "content": "import { matchAncestry } from '../../core/utils';\n\nfunction regionAfter(results) {\n  const iframeResults = results.filter(r => r.data.isIframe);\n\n  results.forEach(r => {\n    // continue if the element passed the check or is not in a frame\n    if (r.result || r.node.ancestry.length === 1) {\n      return;\n    }\n\n    const frameAncestry = r.node.ancestry.slice(0, -1);\n    for (const iframeResult of iframeResults) {\n      // if the container frame passed the check, this element should also pass\n      if (matchAncestry(frameAncestry, iframeResult.node.ancestry)) {\n        r.result = iframeResult.result;\n        break;\n      }\n    }\n  });\n\n  // iframe elements should always pass\n  iframeResults.forEach(r => {\n    if (!r.result) {\n      r.result = true;\n    }\n  });\n  return results;\n}\n\nexport default regionAfter;\n"
  },
  {
    "path": "lib/checks/navigation/region-evaluate.js",
    "content": "import * as dom from '../../commons/dom';\nimport { hasChildTextNodes } from '../../commons/dom/has-content-virtual';\nimport { getRole } from '../../commons/aria';\nimport * as standards from '../../commons/standards';\nimport matches from '../../commons/matches';\nimport cache from '../../core/base/cache';\n\nconst implicitAriaLiveRoles = ['alert', 'log', 'status'];\n\nexport default function regionEvaluate(node, options, virtualNode) {\n  this.data({\n    isIframe: ['iframe', 'frame'].includes(virtualNode.props.nodeName)\n  });\n\n  const regionlessNodes = cache.get('regionlessNodes', () =>\n    getRegionlessNodes(options)\n  );\n\n  return !regionlessNodes.includes(virtualNode);\n}\n\nfunction getRegionlessNodes(options) {\n  const regionlessNodes = findRegionlessElms(axe._tree[0], options)\n    // Find first parent marked as having region descendant (or body) and\n    // return the node right before it as the \"outer\" element\n    .map(vNode => {\n      while (\n        vNode.parent &&\n        !vNode.parent._hasRegionDescendant &&\n        vNode.parent.actualNode !== document.body\n      ) {\n        vNode = vNode.parent;\n      }\n\n      return vNode;\n    })\n    // Remove duplicate containers\n    .filter((vNode, index, array) => {\n      return array.indexOf(vNode) === index;\n    });\n  return regionlessNodes;\n}\n\n/**\n * Find all visible elements not wrapped inside a landmark or skiplink\n */\nfunction findRegionlessElms(virtualNode, options) {\n  const node = virtualNode.actualNode;\n  // End recursion if the element is...\n  // - a landmark\n  // - a skiplink\n  // - hidden content\n  if (\n    getRole(virtualNode) === 'button' ||\n    isRegion(virtualNode, options) ||\n    ['iframe', 'frame'].includes(virtualNode.props.nodeName) ||\n    (dom.isSkipLink(virtualNode.actualNode) &&\n      dom.getElementByReference(virtualNode.actualNode, 'href')) ||\n    !dom.isVisibleToScreenReaders(node)\n  ) {\n    // Mark each parent node as having region descendant\n    let vNode = virtualNode;\n    while (vNode) {\n      vNode._hasRegionDescendant = true;\n      vNode = vNode.parent;\n    }\n    // iframes are counted as regionless here, but parent tree should treat them as having content\n    // after method makes iframes count as regions\n    // this means siblings to iframes will fail, not the parent that contains an iframe and other children\n    if (['iframe', 'frame'].includes(virtualNode.props.nodeName)) {\n      return [virtualNode];\n    }\n    return [];\n\n    // Return the node is a content element. Ignore any direct text children\n    // of body so we don't report body as being outside of a landmark.\n    // @see https://github.com/dequelabs/axe-core/issues/2049\n  } else if (\n    node !== document.body &&\n    dom.hasContent(node, /* noRecursion: */ true) &&\n    !isShallowlyHidden(virtualNode)\n  ) {\n    return [virtualNode];\n\n    // Recursively look at all child elements\n  } else {\n    return virtualNode.children\n      .filter(({ actualNode }) => actualNode.nodeType === 1)\n      .map(vNode => findRegionlessElms(vNode, options))\n      .reduce((a, b) => a.concat(b), []); // flatten the results\n  }\n}\n\nfunction isShallowlyHidden(virtualNode) {\n  // The element itself is not visible to screen readers, but its descendants might be\n  return (\n    ['none', 'presentation'].includes(getRole(virtualNode)) &&\n    !hasChildTextNodes(virtualNode)\n  );\n}\n\n// Check if the current element is a landmark\nfunction isRegion(virtualNode, options) {\n  const node = virtualNode.actualNode;\n  const role = getRole(virtualNode);\n  const ariaLive = (node.getAttribute('aria-live') || '').toLowerCase().trim();\n  const landmarkRoles = standards.getAriaRolesByType('landmark');\n\n  // Ignore content inside of aria-live\n  if (\n    ['assertive', 'polite'].includes(ariaLive) ||\n    implicitAriaLiveRoles.includes(role)\n  ) {\n    return true;\n  }\n\n  // Check if the node matches a landmark role\n  if (landmarkRoles.includes(role)) {\n    return true;\n  }\n\n  // Check if node matches an option\n  if (options.regionMatcher && matches(virtualNode, options.regionMatcher)) {\n    return true;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "lib/checks/navigation/region.json",
    "content": "{\n  \"id\": \"region\",\n  \"evaluate\": \"region-evaluate\",\n  \"after\": \"region-after\",\n  \"options\": {\n    \"regionMatcher\": \"dialog, [role=dialog], [role=alertdialog], svg\"\n  },\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"All page content is contained by landmarks\",\n      \"fail\": \"Some page content is not contained by landmarks\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/skip-link-evaluate.js",
    "content": "import {\n  getElementByReference,\n  isVisibleToScreenReaders\n} from '../../commons/dom';\n\nfunction skipLinkEvaluate(node) {\n  const target = getElementByReference(node, 'href');\n  if (target) {\n    return isVisibleToScreenReaders(target) || undefined;\n  }\n  return false;\n}\n\nexport default skipLinkEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/skip-link.json",
    "content": "{\n  \"id\": \"skip-link\",\n  \"evaluate\": \"skip-link-evaluate\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Skip link target exists\",\n      \"incomplete\": \"Skip link target should become visible on activation\",\n      \"fail\": \"No skip link target\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/navigation/unique-frame-title-after.js",
    "content": "function uniqueFrameTitleAfter(results) {\n  const titles = {};\n  results.forEach(r => {\n    titles[r.data] = titles[r.data] !== undefined ? ++titles[r.data] : 0;\n  });\n  results.forEach(r => {\n    r.result = !!titles[r.data];\n  });\n\n  return results;\n}\n\nexport default uniqueFrameTitleAfter;\n"
  },
  {
    "path": "lib/checks/navigation/unique-frame-title-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\n\nfunction uniqueFrameTitleEvaluate(node, options, vNode) {\n  const title = sanitize(vNode.attr('title')).toLowerCase();\n  this.data(title);\n  return true;\n}\n\nexport default uniqueFrameTitleEvaluate;\n"
  },
  {
    "path": "lib/checks/navigation/unique-frame-title.json",
    "content": "{\n  \"id\": \"unique-frame-title\",\n  \"evaluate\": \"unique-frame-title-evaluate\",\n  \"after\": \"unique-frame-title-after\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element's title attribute is unique\",\n      \"fail\": \"Element's title attribute is not unique\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/parsing/duplicate-id-active.json",
    "content": "{\n  \"id\": \"duplicate-id-active\",\n  \"evaluate\": \"duplicate-id-evaluate\",\n  \"after\": \"duplicate-id-after\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Document has no active elements that share the same id attribute\",\n      \"fail\": \"Document has active elements with the same id attribute: ${data}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/parsing/duplicate-id-after.js",
    "content": "function duplicateIdAfter(results) {\n  const uniqueIds = [];\n  return results.filter(r => {\n    if (uniqueIds.indexOf(r.data) === -1) {\n      uniqueIds.push(r.data);\n      return true;\n    }\n    return false;\n  });\n}\n\nexport default duplicateIdAfter;\n"
  },
  {
    "path": "lib/checks/parsing/duplicate-id-aria.json",
    "content": "{\n  \"id\": \"duplicate-id-aria\",\n  \"evaluate\": \"duplicate-id-evaluate\",\n  \"after\": \"duplicate-id-after\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Document has no elements referenced with ARIA or labels that share the same id attribute\",\n      \"fail\": \"Document has multiple elements referenced with ARIA with the same id attribute: ${data}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/parsing/duplicate-id-evaluate.js",
    "content": "import { getRootNode } from '../../commons/dom';\nimport { escapeSelector } from '../../core/utils';\n\nfunction duplicateIdEvaluate(node) {\n  const id = node.getAttribute('id').trim();\n  // Since empty ID's are not meaningful and are ignored by Edge, we'll\n  // let those pass.\n  if (!id) {\n    return true;\n  }\n  const root = getRootNode(node);\n  const matchingNodes = Array.from(\n    root.querySelectorAll(`[id=\"${escapeSelector(id)}\"]`)\n  ).filter(foundNode => foundNode !== node);\n\n  if (matchingNodes.length) {\n    this.relatedNodes(matchingNodes);\n  }\n  this.data(id);\n\n  return matchingNodes.length === 0;\n}\n\nexport default duplicateIdEvaluate;\n"
  },
  {
    "path": "lib/checks/parsing/duplicate-id.json",
    "content": "{\n  \"id\": \"duplicate-id\",\n  \"evaluate\": \"duplicate-id-evaluate\",\n  \"after\": \"duplicate-id-after\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Document has no static elements that share the same id attribute\",\n      \"fail\": \"Document has multiple static elements with the same id attribute: ${data}\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/aria-label-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\nimport { arialabelText } from '../../commons/aria';\n\nfunction ariaLabelEvaluate(node, options, virtualNode) {\n  return !!sanitize(arialabelText(virtualNode));\n}\n\nexport default ariaLabelEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/aria-label.json",
    "content": "{\n  \"id\": \"aria-label\",\n  \"evaluate\": \"aria-label-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-label attribute exists and is not empty\",\n      \"fail\": \"aria-label attribute does not exist or is empty\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/aria-labelledby-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\nimport { arialabelledbyText } from '../../commons/aria';\n\nfunction ariaLabelledbyEvaluate(node, options, virtualNode) {\n  try {\n    return !!sanitize(arialabelledbyText(virtualNode));\n  } catch {\n    return undefined;\n  }\n}\n\nexport default ariaLabelledbyEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/aria-labelledby.json",
    "content": "{\n  \"id\": \"aria-labelledby\",\n  \"evaluate\": \"aria-labelledby-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"aria-labelledby attribute exists and references elements that are visible to screen readers\",\n      \"fail\": \"aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty\",\n      \"incomplete\": \"Ensure aria-labelledby references an existing element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/avoid-inline-spacing-evaluate.js",
    "content": "function avoidInlineSpacingEvaluate(node, options) {\n  const overriddenProperties = options.cssProperties.filter(property => {\n    if (node.style.getPropertyPriority(property) === `important`) {\n      return property;\n    }\n  });\n\n  if (overriddenProperties.length > 0) {\n    this.data(overriddenProperties);\n    return false;\n  }\n\n  return true;\n}\n\nexport default avoidInlineSpacingEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/avoid-inline-spacing.json",
    "content": "{\n  \"id\": \"avoid-inline-spacing\",\n  \"evaluate\": \"avoid-inline-spacing-evaluate\",\n  \"options\": {\n    \"cssProperties\": [\"line-height\", \"letter-spacing\", \"word-spacing\"]\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"No inline styles with '!important' that affect text spacing has been specified\",\n      \"fail\": {\n        \"singular\": \"Remove '!important' from inline style ${data.values}, as overriding this is not supported by most browsers\",\n        \"plural\": \"Remove '!important' from inline styles ${data.values}, as overriding this is not supported by most browsers\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/button-has-visible-text.json",
    "content": "{\n  \"id\": \"button-has-visible-text\",\n  \"evaluate\": \"has-text-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has inner text that is visible to screen readers\",\n      \"fail\": \"Element does not have inner text that is visible to screen readers\",\n      \"incomplete\": \"Unable to determine if element has children\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/doc-has-title-evaluate.js",
    "content": "import { sanitize } from '../../commons/text';\n\nfunction docHasTitleEvaluate() {\n  const title = document.title;\n  return !!sanitize(title);\n}\n\nexport default docHasTitleEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/doc-has-title.json",
    "content": "{\n  \"id\": \"doc-has-title\",\n  \"evaluate\": \"doc-has-title-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Document has a non-empty <title> element\",\n      \"fail\": \"Document does not have a non-empty <title> element\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/error-occurred.json",
    "content": "{\n  \"id\": \"error-occurred\",\n  \"evaluate\": \"exists-evaluate\",\n  \"metadata\": {\n    \"messages\": {\n      \"pass\": \"\",\n      \"incomplete\": \"Axe encountered an error; test the page for this type of problem manually\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/exists-evaluate.js",
    "content": "function existsEvaluate() {\n  return undefined;\n}\n\nexport default existsEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/exists.json",
    "content": "{\n  \"id\": \"exists\",\n  \"evaluate\": \"exists-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element does not exist\",\n      \"incomplete\": \"Element exists\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/has-alt-evaluate.js",
    "content": "function hasAltEvaluate(node, options, virtualNode) {\n  const { nodeName } = virtualNode.props;\n  if (!['img', 'input', 'area'].includes(nodeName)) {\n    return false;\n  }\n\n  return virtualNode.hasAttr('alt');\n}\n\nexport default hasAltEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/has-alt.json",
    "content": "{\n  \"id\": \"has-alt\",\n  \"evaluate\": \"has-alt-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has an alt attribute\",\n      \"fail\": \"Element does not have an alt attribute\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/has-visible-text.json",
    "content": "{\n  \"id\": \"has-visible-text\",\n  \"evaluate\": \"has-text-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element has text that is visible to screen readers\",\n      \"fail\": \"Element does not have text that is visible to screen readers\",\n      \"incomplete\": \"Unable to determine if element has children\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/important-letter-spacing.json",
    "content": "{\n  \"id\": \"important-letter-spacing\",\n  \"evaluate\": \"inline-style-property-evaluate\",\n  \"options\": {\n    \"cssProperty\": \"letter-spacing\",\n    \"minValue\": 0.12\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Letter-spacing in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"letter-spacing in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/important-line-height.json",
    "content": "{\n  \"id\": \"important-line-height\",\n  \"evaluate\": \"inline-style-property-evaluate\",\n  \"options\": {\n    \"multiLineOnly\": true,\n    \"cssProperty\": \"line-height\",\n    \"minValue\": 1.5,\n    \"normalValue\": 1\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"line-height in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"line-height in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/important-word-spacing.json",
    "content": "{\n  \"id\": \"important-word-spacing\",\n  \"evaluate\": \"inline-style-property-evaluate\",\n  \"options\": {\n    \"cssProperty\": \"word-spacing\",\n    \"minValue\": 0.16\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"word-spacing in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"word-spacing in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/inline-style-property-evaluate.js",
    "content": "import { isMultiline } from '../../commons/dom';\n\n/**\n * Check if a CSS property, !important or not is within an allowed range\n */\nexport default function inlineStyleProperty(node, options) {\n  const {\n    cssProperty,\n    absoluteValues,\n    minValue,\n    maxValue,\n    normalValue = 0,\n    noImportant,\n    multiLineOnly\n  } = options;\n  if (\n    (!noImportant &&\n      node.style.getPropertyPriority(cssProperty) !== `important`) ||\n    (multiLineOnly && !isMultiline(node))\n  ) {\n    return true;\n  }\n\n  const data = {};\n  if (typeof minValue === 'number') {\n    data.minValue = minValue;\n  }\n  if (typeof maxValue === 'number') {\n    data.maxValue = maxValue;\n  }\n\n  // These do not set the actual value to important, instead they\n  // say that it is important to use the inherited / root value.\n  // The actual value can still be modified\n  const declaredPropValue = node.style.getPropertyValue(cssProperty);\n  if (\n    ['inherit', 'unset', 'revert', 'revert-layer'].includes(declaredPropValue)\n  ) {\n    this.data({ value: declaredPropValue, ...data });\n    return true;\n  }\n\n  const value = getNumberValue(node, {\n    absoluteValues,\n    cssProperty,\n    normalValue\n  });\n  this.data({ value, ...data });\n  if (typeof value !== 'number') {\n    return undefined; // Renderer did something it shouldn't\n  }\n\n  if (\n    (typeof minValue !== 'number' || value >= minValue) &&\n    (typeof maxValue !== 'number' || value <= maxValue)\n  ) {\n    return true;\n  }\n  return false;\n}\n\nfunction getNumberValue(domNode, { cssProperty, absoluteValues, normalValue }) {\n  const computedStyle = window.getComputedStyle(domNode);\n  const cssPropValue = computedStyle.getPropertyValue(cssProperty);\n  if (cssPropValue === 'normal') {\n    return normalValue;\n  }\n  const parsedValue = parseFloat(cssPropValue);\n  if (absoluteValues) {\n    return parsedValue;\n  }\n\n  const fontSize = parseFloat(computedStyle.getPropertyValue('font-size'));\n  // Make the value relative to the font-size\n  const value = Math.round((parsedValue / fontSize) * 100) / 100;\n  if (isNaN(value)) {\n    return cssPropValue; // Something went wrong, return the string instead\n  }\n  return value;\n}\n"
  },
  {
    "path": "lib/checks/shared/is-on-screen-evaluate.js",
    "content": "import { isVisibleOnScreen } from '../../commons/dom';\n\nfunction isOnScreenEvaluate(node) {\n  // From a visual perspective\n  return isVisibleOnScreen(node);\n}\n\nexport default isOnScreenEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/is-on-screen.json",
    "content": "{\n  \"id\": \"is-on-screen\",\n  \"evaluate\": \"is-on-screen-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element is not visible\",\n      \"fail\": \"Element is visible\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/non-empty-alt.json",
    "content": "{\n  \"id\": \"non-empty-alt\",\n  \"evaluate\": \"attr-non-space-content-evaluate\",\n  \"options\": {\n    \"attribute\": \"alt\"\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has a non-empty alt attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no alt attribute\",\n        \"emptyAttr\": \"Element has an empty alt attribute\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/non-empty-if-present-evaluate.js",
    "content": "function nonEmptyIfPresentEvaluate(node, options, virtualNode) {\n  // Check for 'default' names, which are given to reset and submit buttons\n  const nodeName = virtualNode.props.nodeName;\n  const type = (virtualNode.attr('type') || '').toLowerCase();\n  const label = virtualNode.attr('value');\n\n  if (label) {\n    this.data({\n      messageKey: 'has-label'\n    });\n  }\n\n  if (nodeName === 'input' && ['submit', 'reset'].includes(type)) {\n    return label === null;\n  }\n  return false;\n}\n\nexport default nonEmptyIfPresentEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/non-empty-if-present.json",
    "content": "{\n  \"id\": \"non-empty-if-present\",\n  \"evaluate\": \"non-empty-if-present-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": {\n        \"default\": \"Element does not have a value attribute\",\n        \"has-label\": \"Element has a non-empty value attribute\"\n      },\n      \"fail\": \"Element has a value attribute and the value attribute is empty\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/non-empty-placeholder.json",
    "content": "{\n  \"id\": \"non-empty-placeholder\",\n  \"evaluate\": \"attr-non-space-content-evaluate\",\n  \"options\": {\n    \"attribute\": \"placeholder\"\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element has a placeholder attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no placeholder attribute\",\n        \"emptyAttr\": \"Element has an empty placeholder attribute\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/non-empty-title.json",
    "content": "{\n  \"id\": \"non-empty-title\",\n  \"evaluate\": \"attr-non-space-content-evaluate\",\n  \"options\": {\n    \"attribute\": \"title\"\n  },\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element has a title attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no title attribute\",\n        \"emptyAttr\": \"Element has an empty title attribute\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/non-empty-value.json",
    "content": "{\n  \"id\": \"non-empty-value\",\n  \"evaluate\": \"attr-non-space-content-evaluate\",\n  \"options\": {\n    \"attribute\": \"value\"\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Element has a non-empty value attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no value attribute\",\n        \"emptyAttr\": \"Element has an empty value attribute\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/presentational-role-evaluate.js",
    "content": "import { getExplicitRole, getRole } from '../../commons/aria';\nimport { getGlobalAriaAttrs } from '../../commons/standards';\nimport { isFocusable } from '../../commons/dom';\n\nexport default function presentationalRoleEvaluate(node, options, virtualNode) {\n  const explicitRole = getExplicitRole(virtualNode);\n\n  // in JAWS, an iframe or frame with an accessible name and a presentational role gets announced\n  // as \"group\" rather than being ignored. aria-label is handled by role-conflict resolution and\n  // aria-labelledby only triggers the group role when it's valid (exists and has content)\n  if (\n    ['presentation', 'none'].includes(explicitRole) &&\n    ['iframe', 'frame'].includes(virtualNode.props.nodeName) &&\n    virtualNode.hasAttr('title')\n  ) {\n    this.data({\n      messageKey: 'iframe',\n      nodeName: virtualNode.props.nodeName\n    });\n    return false;\n  }\n\n  const role = getRole(virtualNode);\n\n  if (['presentation', 'none'].includes(role)) {\n    this.data({ role });\n    return true;\n  }\n\n  // if the user didn't intended to make this presentational we fail\n  if (!['presentation', 'none'].includes(explicitRole)) {\n    return false;\n  }\n\n  // user intended to make this presentational so inform them of\n  // problems caused by role conflict resolution\n  const hasGlobalAria = getGlobalAriaAttrs().some(attr =>\n    virtualNode.hasAttr(attr)\n  );\n  const focusable = isFocusable(virtualNode);\n  let messageKey;\n\n  if (hasGlobalAria && !focusable) {\n    messageKey = 'globalAria';\n  } else if (!hasGlobalAria && focusable) {\n    messageKey = 'focusable';\n  } else {\n    messageKey = 'both';\n  }\n\n  this.data({\n    messageKey,\n    role\n  });\n  return false;\n}\n"
  },
  {
    "path": "lib/checks/shared/presentational-role.json",
    "content": "{\n  \"id\": \"presentational-role\",\n  \"evaluate\": \"presentational-role-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"Element's default semantics were not overridden with role=\\\"none\\\" or role=\\\"presentation\\\"\",\n        \"globalAria\": \"Element's role is not presentational because it has a global ARIA attribute\",\n        \"focusable\": \"Element's role is not presentational because it is focusable\",\n        \"both\": \"Element's role is not presentational because it has a global ARIA attribute and is focusable\",\n        \"iframe\": \"Using the \\\"title\\\" attribute on an ${data.nodeName} element with a presentational role behaves inconsistently between screen readers\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/role-none.json",
    "content": "{\n  \"id\": \"role-none\",\n  \"evaluate\": \"matches-definition-evaluate\",\n  \"deprecated\": true,\n  \"options\": {\n    \"matcher\": {\n      \"attributes\": {\n        \"role\": \"none\"\n      }\n    }\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"none\\\"\",\n      \"fail\": \"Element's default semantics were not overridden with role=\\\"none\\\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/role-presentation.json",
    "content": "{\n  \"id\": \"role-presentation\",\n  \"evaluate\": \"matches-definition-evaluate\",\n  \"deprecated\": true,\n  \"options\": {\n    \"matcher\": {\n      \"attributes\": {\n        \"role\": \"presentation\"\n      }\n    }\n  },\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"presentation\\\"\",\n      \"fail\": \"Element's default semantics were not overridden with role=\\\"presentation\\\"\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/shared/svg-non-empty-title-evaluate.js",
    "content": "import { subtreeText } from '../../commons/text';\n\nfunction svgNonEmptyTitleEvaluate(node, options, virtualNode) {\n  if (!virtualNode.children) {\n    return undefined;\n  }\n\n  const titleNode = virtualNode.children.find(({ props }) => {\n    return props.nodeName === 'title';\n  });\n\n  if (!titleNode) {\n    this.data({\n      messageKey: 'noTitle'\n    });\n    return false;\n  }\n\n  try {\n    const titleText = subtreeText(titleNode, { includeHidden: true }).trim();\n    if (titleText === '') {\n      this.data({\n        messageKey: 'emptyTitle'\n      });\n      return false;\n    }\n  } catch {\n    return undefined;\n  }\n\n  return true;\n}\n\nexport default svgNonEmptyTitleEvaluate;\n"
  },
  {
    "path": "lib/checks/shared/svg-non-empty-title.json",
    "content": "{\n  \"id\": \"svg-non-empty-title\",\n  \"evaluate\": \"svg-non-empty-title-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"Element has a child that is a title\",\n      \"fail\": {\n        \"noTitle\": \"Element has no child that is a title\",\n        \"emptyTitle\": \"Element child title is empty\"\n      },\n      \"incomplete\": \"Unable to determine element has a child that is a title\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/caption-faked-evaluate.js",
    "content": "import { toGrid } from '../../commons/table';\n\nfunction captionFakedEvaluate(node) {\n  const table = toGrid(node);\n  const firstRow = table[0];\n\n  if (table.length <= 1 || firstRow.length <= 1 || node.rows.length <= 1) {\n    return true;\n  }\n\n  return firstRow.reduce((out, curr, i) => {\n    return out || (curr !== firstRow[i + 1] && firstRow[i + 1] !== undefined);\n  }, false);\n}\n\nexport default captionFakedEvaluate;\n"
  },
  {
    "path": "lib/checks/tables/caption-faked.json",
    "content": "{\n  \"id\": \"caption-faked\",\n  \"evaluate\": \"caption-faked-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"The first row of a table is not used as a caption\",\n      \"fail\": \"The first child of the table should be a caption instead of a table cell\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/html5-scope-evaluate.js",
    "content": "import { isHTML5 } from '../../commons/dom';\n\nfunction html5ScopeEvaluate(node) {\n  if (!isHTML5(document)) {\n    return true;\n  }\n\n  return node.nodeName.toUpperCase() === 'TH';\n}\n\nexport default html5ScopeEvaluate;\n"
  },
  {
    "path": "lib/checks/tables/html5-scope.json",
    "content": "{\n  \"id\": \"html5-scope\",\n  \"evaluate\": \"html5-scope-evaluate\",\n  \"metadata\": {\n    \"impact\": \"moderate\",\n    \"messages\": {\n      \"pass\": \"Scope attribute is only used on table header elements (<th>)\",\n      \"fail\": \"In HTML 5, scope attributes may only be used on table header elements (<th>)\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/same-caption-summary-evaluate.js",
    "content": "import { sanitize, subtreeText } from '../../commons/text';\n\nexport default sameCaptionSummaryEvaluate;\n\nfunction sameCaptionSummaryEvaluate(node, options, virtualNode) {\n  if (virtualNode.children === undefined) {\n    return undefined;\n  }\n\n  const summary = virtualNode.attr('summary');\n  const captionNode = virtualNode.children.find(isCaptionNode);\n  const caption = captionNode ? sanitize(subtreeText(captionNode)) : false;\n\n  if (!caption || !summary) {\n    return false;\n  }\n\n  return sanitize(summary).toLowerCase() === sanitize(caption).toLowerCase();\n}\n\nfunction isCaptionNode(virtualNode) {\n  return virtualNode.props.nodeName === 'caption';\n}\n"
  },
  {
    "path": "lib/checks/tables/same-caption-summary.json",
    "content": "{\n  \"id\": \"same-caption-summary\",\n  \"evaluate\": \"same-caption-summary-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"Content of summary attribute and <caption> are not duplicated\",\n      \"fail\": \"Content of summary attribute and <caption> element are identical\",\n      \"incomplete\": \"Unable to determine if <table> element has a caption\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/scope-value-evaluate.js",
    "content": "function scopeValueEvaluate(node, options) {\n  const value = node.getAttribute('scope').toLowerCase();\n\n  return options.values.indexOf(value) !== -1;\n}\n\nexport default scopeValueEvaluate;\n"
  },
  {
    "path": "lib/checks/tables/scope-value.json",
    "content": "{\n  \"id\": \"scope-value\",\n  \"evaluate\": \"scope-value-evaluate\",\n  \"options\": {\n    \"values\": [\"row\", \"col\", \"rowgroup\", \"colgroup\"]\n  },\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"Scope attribute is used correctly\",\n      \"fail\": \"The value of the scope attribute may only be 'row' or 'col'\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/td-has-header-evaluate.js",
    "content": "import * as tableUtils from '../../commons/table';\nimport { hasContent } from '../../commons/dom';\nimport { label } from '../../commons/aria';\n\nfunction tdHasHeaderEvaluate(node) {\n  const badCells = [];\n  const cells = tableUtils.getAllCells(node);\n  const tableGrid = tableUtils.toGrid(node);\n\n  cells.forEach(cell => {\n    // For each non-empty data cell that doesn't have an aria label\n    if (hasContent(cell) && tableUtils.isDataCell(cell) && !label(cell)) {\n      // Check if it has any headers\n      const hasHeaders = tableUtils.getHeaders(cell, tableGrid).some(header => {\n        return header !== null && !!hasContent(header);\n      });\n\n      // If no headers, put it on the naughty list\n      if (!hasHeaders) {\n        badCells.push(cell);\n      }\n    }\n  });\n\n  if (badCells.length) {\n    this.relatedNodes(badCells);\n    return false;\n  }\n\n  return true;\n}\n\nexport default tdHasHeaderEvaluate;\n"
  },
  {
    "path": "lib/checks/tables/td-has-header.json",
    "content": "{\n  \"id\": \"td-has-header\",\n  \"evaluate\": \"td-has-header-evaluate\",\n  \"metadata\": {\n    \"impact\": \"critical\",\n    \"messages\": {\n      \"pass\": \"All non-empty data cells have table headers\",\n      \"fail\": \"Some non-empty data cells do not have table headers\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/td-headers-attr-evaluate.js",
    "content": "import { tokenList } from '../../core/utils';\nimport { isVisibleToScreenReaders } from '../../commons/dom';\nimport { getRole } from '../../commons/aria';\n\n// Order determines the priority of reporting\n// Only if 0 of higher issues exists will the next be reported\nconst messageKeys = [\n  'cell-header-not-in-table',\n  'cell-header-not-th',\n  'header-refs-self',\n  'empty-hdrs' // incomplete\n];\nconst [notInTable, notTh, selfRef, emptyHdrs] = messageKeys;\n\nexport default function tdHeadersAttrEvaluate(node) {\n  const cells = [];\n  const cellRoleById = {};\n  for (let rowIndex = 0; rowIndex < node.rows.length; rowIndex++) {\n    const row = node.rows[rowIndex];\n\n    for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {\n      const cell = row.cells[cellIndex];\n      cells.push(cell);\n\n      // Save header id to set if it's th or td with roles columnheader/rowheader\n      const cellId = cell.getAttribute('id');\n      if (cellId) {\n        cellRoleById[cellId] = getRole(cell);\n      }\n    }\n  }\n\n  const badCells = {\n    [selfRef]: new Set(),\n    [notInTable]: new Set(),\n    [notTh]: new Set(),\n    [emptyHdrs]: new Set()\n  };\n  cells.forEach(cell => {\n    if (!cell.hasAttribute('headers') || !isVisibleToScreenReaders(cell)) {\n      return;\n    }\n    const headersAttr = cell.getAttribute('headers').trim();\n    if (!headersAttr) {\n      badCells[emptyHdrs].add(cell);\n      return;\n    }\n\n    const cellId = cell.getAttribute('id');\n    // Get a list all the values of the headers attribute\n    const headers = tokenList(headersAttr);\n    headers.forEach(headerId => {\n      if (cellId && headerId === cellId) {\n        // Header references its own cell\n        badCells[selfRef].add(cell);\n      } else if (!cellRoleById[headerId]) {\n        // Header references a cell that is not in the table\n        badCells[notInTable].add(cell);\n      } else if (\n        !['columnheader', 'rowheader'].includes(cellRoleById[headerId])\n      ) {\n        // Header references a cell that is not a row or column header\n        badCells[notTh].add(cell);\n      }\n    });\n  });\n\n  for (const messageKey of messageKeys) {\n    if (badCells[messageKey].size > 0) {\n      this.relatedNodes([...badCells[messageKey]]);\n      if (messageKey === emptyHdrs) {\n        return undefined;\n      }\n      this.data({ messageKey });\n      return false;\n    }\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/checks/tables/td-headers-attr.json",
    "content": "{\n  \"id\": \"td-headers-attr\",\n  \"evaluate\": \"td-headers-attr-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"The headers attribute is exclusively used to refer to other header cells in the table\",\n      \"incomplete\": \"The headers attribute is empty\",\n      \"fail\": {\n        \"cell-header-not-in-table\": \"The headers attribute is not exclusively used to refer to other header cells in the table\",\n        \"cell-header-not-th\": \"The headers attribute must refer to header cells, not data cells\",\n        \"header-refs-self\": \"The element with headers attribute refers to itself\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/tables/th-has-data-cells-evaluate.js",
    "content": "import * as tableUtils from '../../commons/table';\nimport { sanitize } from '../../commons/text';\nimport { getExplicitRole } from '../../commons/aria';\n\nfunction thHasDataCellsEvaluate(node) {\n  const cells = tableUtils.getAllCells(node);\n  const checkResult = this;\n\n  // Get a list of all headers reffed to in this rule\n  let reffedHeaders = [];\n  cells.forEach(cell => {\n    const headers = cell.getAttribute('headers');\n    if (headers) {\n      reffedHeaders = reffedHeaders.concat(headers.split(/\\s+/));\n    }\n\n    const ariaLabel = cell.getAttribute('aria-labelledby');\n    if (ariaLabel) {\n      reffedHeaders = reffedHeaders.concat(ariaLabel.split(/\\s+/));\n    }\n  });\n\n  // Get all the headers\n  const headers = cells.filter(cell => {\n    if (sanitize(cell.textContent) === '') {\n      return false;\n    }\n    return (\n      cell.nodeName.toUpperCase() === 'TH' ||\n      ['rowheader', 'columnheader'].indexOf(getExplicitRole(cell)) !== -1\n    );\n  });\n\n  const tableGrid = tableUtils.toGrid(node);\n\n  let out = true;\n  headers.forEach(header => {\n    if (\n      header.getAttribute('id') &&\n      reffedHeaders.includes(header.getAttribute('id'))\n    ) {\n      return;\n    }\n\n    const pos = tableUtils.getCellPosition(header, tableGrid);\n\n    // ensure column header has at least 1 non-header cell and that the cell is\n    // not pointing to a different header\n    let hasCell = false;\n    if (tableUtils.isColumnHeader(header)) {\n      hasCell = tableUtils\n        .traverse('down', pos, tableGrid)\n        .find(\n          cell =>\n            !tableUtils.isColumnHeader(cell) &&\n            tableUtils.getHeaders(cell, tableGrid).includes(header)\n        );\n    }\n\n    // ensure row header has at least 1 non-header cell and that the cell is not\n    // pointing to a different header\n    if (!hasCell && tableUtils.isRowHeader(header)) {\n      hasCell = tableUtils\n        .traverse('right', pos, tableGrid)\n        .find(\n          cell =>\n            !tableUtils.isRowHeader(cell) &&\n            tableUtils.getHeaders(cell, tableGrid).includes(header)\n        );\n    }\n\n    // report the node as having failed\n    if (!hasCell) {\n      checkResult.relatedNodes(header);\n    }\n\n    out = out && hasCell;\n  });\n\n  return out ? true : undefined;\n}\n\nexport default thHasDataCellsEvaluate;\n"
  },
  {
    "path": "lib/checks/tables/th-has-data-cells.json",
    "content": "{\n  \"id\": \"th-has-data-cells\",\n  \"evaluate\": \"th-has-data-cells-evaluate\",\n  \"metadata\": {\n    \"impact\": \"serious\",\n    \"messages\": {\n      \"pass\": \"All table header cells refer to data cells\",\n      \"fail\": \"Not all table header cells refer to data cells\",\n      \"incomplete\": \"Table data cells are missing or empty\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/checks/visibility/hidden-content-evaluate.js",
    "content": "import { hasContentVirtual, getComposedParent } from '../../commons/dom';\n\nfunction hiddenContentEvaluate(node, options, virtualNode) {\n  const allowlist = [\n    'SCRIPT',\n    'HEAD',\n    'TITLE',\n    'NOSCRIPT',\n    'STYLE',\n    'TEMPLATE'\n  ];\n  if (\n    !allowlist.includes(node.nodeName.toUpperCase()) &&\n    hasContentVirtual(virtualNode)\n  ) {\n    const styles = window.getComputedStyle(node);\n    if (styles.getPropertyValue('display') === 'none') {\n      return undefined;\n    } else if (styles.getPropertyValue('visibility') === 'hidden') {\n      // Check if visibility isn't inherited\n      const parent = getComposedParent(node);\n      const parentStyle = parent && window.getComputedStyle(parent);\n      if (\n        !parentStyle ||\n        parentStyle.getPropertyValue('visibility') !== 'hidden'\n      ) {\n        return undefined;\n      }\n    }\n  }\n  return true;\n}\n\nexport default hiddenContentEvaluate;\n"
  },
  {
    "path": "lib/checks/visibility/hidden-content.json",
    "content": "{\n  \"id\": \"hidden-content\",\n  \"evaluate\": \"hidden-content-evaluate\",\n  \"metadata\": {\n    \"impact\": \"minor\",\n    \"messages\": {\n      \"pass\": \"All content on the page has been analyzed.\",\n      \"fail\": \"There were problems analyzing the content on this page.\",\n      \"incomplete\": \"There is hidden content on the page that was not analyzed. You will need to trigger the display of this content in order to analyze it.\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/commons/aria/allowed-attr.js",
    "content": "import standards from '../../standards';\nimport getGlobalAriaAttrs from '../standards/get-global-aria-attrs';\n\n/**\n * Get allowed attributes for a given role\n * @method allowedAttr\n * @memberof axe.commons.aria\n * @instance\n * @param  {String} role The role to check\n * @return {Array}\n */\nfunction allowedAttr(role) {\n  const roleDef = standards.ariaRoles[role];\n  const attrs = [...getGlobalAriaAttrs()];\n\n  if (!roleDef) {\n    return attrs;\n  }\n\n  if (roleDef.allowedAttrs) {\n    attrs.push(...roleDef.allowedAttrs);\n  }\n\n  if (roleDef.requiredAttrs) {\n    attrs.push(...roleDef.requiredAttrs);\n  }\n\n  return attrs;\n}\n\nexport default allowedAttr;\n"
  },
  {
    "path": "lib/commons/aria/arialabel-text.js",
    "content": "import { nodeLookup } from '../../core/utils';\n\n/**\n * Get the text value of aria-label, if any\n *\n * @deprecated Do not use Element directly. Pass VirtualNode instead\n * @param {VirtualNode|Element} element\n * @return {string} ARIA label\n */\nexport default function arialabelText(element) {\n  const { vNode } = nodeLookup(element);\n  if (vNode?.props.nodeType !== 1) {\n    return '';\n  }\n\n  return vNode.attr('aria-label') || '';\n}\n"
  },
  {
    "path": "lib/commons/aria/arialabelledby-text.js",
    "content": "import idrefs from '../dom/idrefs';\nimport accessibleText from '../text/accessible-text';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Get the accessible name based on aria-labelledby\n *\n * @deprecated Do not use Element directly. Pass VirtualNode instead\n * @param {VirtualNode|Element} element\n * @param {Object} context\n * @property {Bool} inLabelledByContext Whether or not the lookup is part of aria-labelledby reference\n * @property {Bool} inControlContext Whether or not the lookup is part of a native label reference\n * @property {Element} startNode First node in accessible name computation\n * @property {Bool} debug Enable logging for formControlValue\n * @return {string} Concatenated text value for referenced elements\n */\nfunction arialabelledbyText(element, context = {}) {\n  const { vNode } = nodeLookup(element);\n  if (vNode?.props.nodeType !== 1) {\n    return '';\n  }\n\n  /**\n   * Note: The there are significant difference in how many \"leads\" browsers follow.\n   * - Firefox stops after the first IDREF, so it\n   * \t\tdoesn't follow aria-labelledby after a for:>ID ref.\n   * - Chrome seems to just keep iterating no matter how many levels deep.\n   * - AccName-AAM 1.1 suggests going one level deep, but to treat\n   * \t\teach ref type separately.\n   *\n   * Axe-core's implementation behaves most closely like Firefox as it seems\n   *  to be the common denominator. Main difference is that Firefox\n   *  includes the value of form controls in addition to aria-label(s),\n   *  something no other browser seems to do. Axe doesn't do that.\n   */\n  if (\n    vNode.props.nodeType !== 1 ||\n    context.inLabelledByContext ||\n    context.inControlContext ||\n    !vNode.attr('aria-labelledby')\n  ) {\n    return '';\n  }\n\n  const refs = idrefs(vNode, 'aria-labelledby').filter(elm => elm);\n  return refs.reduce((accessibleName, elm) => {\n    const accessibleNameAdd = accessibleText(elm, {\n      // Prevent the infinite reference loop:\n      inLabelledByContext: true,\n      startNode: context.startNode || vNode,\n      ...context\n    });\n\n    if (!accessibleName) {\n      return accessibleNameAdd;\n    } else {\n      return `${accessibleName} ${accessibleNameAdd}`;\n    }\n  }, '');\n}\n\nexport default arialabelledbyText;\n"
  },
  {
    "path": "lib/commons/aria/get-accessible-refs.js",
    "content": "import getRootNode from '../dom/get-root-node';\nimport cache from '../../core/base/cache';\nimport { tokenList } from '../../core/utils';\nimport standards from '../../standards';\nimport { sanitize } from '../text/';\n\nconst idRefsRegex = /^idrefs?$/;\n\n/**\n * Cache all ID references of a node and its children\n */\nfunction cacheIdRefs(node, idRefs, refAttrs) {\n  if (node.hasAttribute) {\n    if (node.nodeName.toUpperCase() === 'LABEL' && node.hasAttribute('for')) {\n      const id = node.getAttribute('for');\n      if (!idRefs.has(id)) {\n        idRefs.set(id, [node]);\n      } else {\n        idRefs.get(id).push(node);\n      }\n    }\n\n    for (let i = 0; i < refAttrs.length; ++i) {\n      const attr = refAttrs[i];\n      const attrValue = sanitize(node.getAttribute(attr) || '');\n      if (!attrValue) {\n        continue;\n      }\n\n      for (const token of tokenList(attrValue)) {\n        if (!idRefs.has(token)) {\n          idRefs.set(token, [node]);\n        } else {\n          idRefs.get(token).push(node);\n        }\n      }\n    }\n  }\n\n  for (let i = 0; i < node.childNodes.length; i++) {\n    if (node.childNodes[i].nodeType === 1) {\n      cacheIdRefs(node.childNodes[i], idRefs, refAttrs);\n    }\n  }\n}\n\n/**\n * Return all DOM nodes that use the nodes ID in the accessibility tree.\n * @param {Element} node\n * @returns {Element[]}\n */\nfunction getAccessibleRefs(node) {\n  node = node.actualNode || node;\n  let root = getRootNode(node);\n  root = root.documentElement || root; // account for shadow roots\n\n  const idRefsByRoot = cache.get('idRefsByRoot', () => new Map());\n\n  let idRefs = idRefsByRoot.get(root);\n  if (!idRefs) {\n    idRefs = new Map();\n    idRefsByRoot.set(root, idRefs);\n\n    const refAttrs = Object.keys(standards.ariaAttrs).filter(attr => {\n      const { type } = standards.ariaAttrs[attr];\n      return idRefsRegex.test(type);\n    });\n    cacheIdRefs(root, idRefs, refAttrs);\n  }\n\n  return idRefs.get(node.id) ?? [];\n}\n\nexport default getAccessibleRefs;\n"
  },
  {
    "path": "lib/commons/aria/get-element-unallowed-roles.js",
    "content": "import isValidRole from './is-valid-role';\nimport getImplicitRole from './implicit-role';\nimport getRoleType from './get-role-type';\nimport isAriaRoleAllowedOnElement from './is-aria-role-allowed-on-element';\nimport { tokenList, isHtmlElement, nodeLookup } from '../../core/utils';\n\n// dpub roles which are subclassing roles that are implicit on some native\n// HTML elements (img, link, etc.)\nconst dpubRoles = [\n  'doc-backlink',\n  'doc-biblioentry',\n  'doc-biblioref',\n  'doc-cover',\n  'doc-endnote',\n  'doc-glossref',\n  'doc-noteref'\n];\n\nconst landmarkRoles = {\n  header: 'banner',\n  footer: 'contentinfo'\n};\n\n/**\n * Returns all roles applicable to element in a list\n *\n * @method getRoleSegments\n * @private\n * @param {Element} node\n * @returns {Array} Roles list or empty list\n */\n\nfunction getRoleSegments(vNode) {\n  let roles = [];\n  if (!vNode) {\n    return roles;\n  }\n\n  if (vNode.hasAttr('role')) {\n    const nodeRoles = tokenList(vNode.attr('role').toLowerCase());\n    roles = roles.concat(nodeRoles);\n  }\n\n  // filter invalid roles\n  return roles.filter(role => isValidRole(role));\n}\n\n/**\n * gets all unallowed roles for a given node\n * @method getElementUnallowedRoles\n * @param {Element|VirtualNode} node HTMLElement to validate\n * @param {String} allowImplicit option to allow implicit roles, defaults to true\n * @return {Array<String>} retruns an array of roles that are not allowed on the given node\n */\nfunction getElementUnallowedRoles(node, allowImplicit = true) {\n  const { vNode } = nodeLookup(node);\n  // by pass custom elements\n  if (!isHtmlElement(vNode)) {\n    return [];\n  }\n  // allow landmark roles to use their implicit role inside another landmark\n  // @see https://github.com/dequelabs/axe-core/pull/3142\n  const { nodeName } = vNode.props;\n  const implicitRole = getImplicitRole(vNode) || landmarkRoles[nodeName];\n\n  const roleSegments = getRoleSegments(vNode);\n  return roleSegments.filter(role => {\n    return !roleIsAllowed(role, vNode, allowImplicit, implicitRole);\n  });\n}\n\nfunction roleIsAllowed(role, vNode, allowImplicit, implicitRole) {\n  if (allowImplicit && role === implicitRole) {\n    return true;\n  }\n  // if role is a dpub role make sure it's used on an element with a valid\n  // implicit role fallback\n  if (dpubRoles.includes(role) && getRoleType(role) !== implicitRole) {\n    return false;\n  }\n  // check if role is allowed on element\n  return isAriaRoleAllowedOnElement(vNode, role);\n}\n\nexport default getElementUnallowedRoles;\n"
  },
  {
    "path": "lib/commons/aria/get-explicit-role.js",
    "content": "import isValidRole from './is-valid-role';\nimport { getNodeFromTree, tokenList } from '../../core/utils';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\n\nfunction getExplicitRole(vNode, { fallback, abstracts, dpub } = {}) {\n  vNode = vNode instanceof AbstractVirtuaNode ? vNode : getNodeFromTree(vNode);\n\n  if (vNode.props.nodeType !== 1) {\n    return null;\n  }\n\n  const roleAttr = (vNode.attr('role') || '').trim().toLowerCase();\n  const roleList = fallback ? tokenList(roleAttr) : [roleAttr];\n\n  // Get the first valid role:\n  const firstValidRole = roleList.find(role => {\n    if (!dpub && role.substr(0, 4) === 'doc-') {\n      return false;\n    }\n    return isValidRole(role, { allowAbstract: abstracts });\n  });\n\n  return firstValidRole || null;\n}\n\nexport default getExplicitRole;\n"
  },
  {
    "path": "lib/commons/aria/get-owned-virtual.js",
    "content": "import idrefs from '../dom/idrefs';\n\n/**\n * Get an element's owned elements\n *\n * @param {VirtualNode} element\n * @return {VirtualNode[]} Owned elements\n */\nfunction getOwnedVirtual(virtualNode) {\n  const { actualNode, children } = virtualNode;\n  if (!children) {\n    throw new Error('getOwnedVirtual requires a virtual node');\n  }\n  // TODO: Check that the element has a role\n  // TODO: Descend into children with role=presentation|none\n  // TODO: Exclude descendents owned by other elements\n  if (virtualNode.hasAttr('aria-owns')) {\n    const owns = idrefs(actualNode, 'aria-owns')\n      .filter(element => !!element)\n      .map(element => axe.utils.getNodeFromTree(element));\n\n    // Deduplicates by first occurrence to match browser accessibility tree behavior\n    // See: https://github.com/dequelabs/axe-core/pull/4987\n    const uniqueOwns = owns.filter((own, index) => owns.indexOf(own) === index);\n    const nativeChildren = children.filter(\n      child => !uniqueOwns.includes(child)\n    );\n\n    return [...nativeChildren, ...uniqueOwns];\n  }\n\n  return [...children];\n}\n\nexport default getOwnedVirtual;\n"
  },
  {
    "path": "lib/commons/aria/get-role-type.js",
    "content": "import standards from '../../standards';\nimport AbstractVirtualNode from '../../core/base/virtual-node/abstract-virtual-node';\n\n/**\n * Get the \"type\" of role; either widget, composite, abstract, landmark or `null`\n * @method getRoleType\n * @memberof axe.commons.aria\n * @instance\n * @param {String|Null|Node|Element} role The role to check, or element to check the role of\n * @return {Mixed} String if a matching role and its type are found, otherwise `null`\n */\nfunction getRoleType(role) {\n  if (\n    role instanceof AbstractVirtualNode ||\n    (window?.Node && role instanceof window.Node)\n  ) {\n    role = axe.commons.aria.getRole(role);\n  }\n  const roleDef = standards.ariaRoles[role];\n  return roleDef?.type || null;\n}\n\nexport default getRoleType;\n"
  },
  {
    "path": "lib/commons/aria/get-role.js",
    "content": "import getExplicitRole from './get-explicit-role';\nimport getImplicitRole from './implicit-role';\nimport getGlobalAriaAttrs from '../standards/get-global-aria-attrs';\nimport isFocusable from '../dom/is-focusable';\nimport { nodeLookup } from '../../core/utils';\n\n// when an element inherits the presentational role from a parent\n// is not defined in the spec, but through testing it seems to be\n// when a specific HTML parent relationship is required and that\n// parent has `role=presentation`, then the child inherits the\n// role (i.e. table, ul, dl). Further testing has shown that\n// intermediate elements (such as divs) break this chain only in\n// Chrome.\n//\n// Also, any nested structure chains reset the role (so two nested\n// lists with the topmost list role=none will not cause the nested\n// list to inherit the role=none).\n//\n// from Scott O'Hara:\n//\n// \"the expectation for me, in standard html is that element\n// structures that require specific parent/child relationships,\n// if the parent is set to presentational that should set the\n// children to presentational.  ala, tables and lists.\"\n// \"but outside of those specific constructs, i would not expect\n// role=presentation to do anything to child element roles\"\nconst inheritsPresentationChain = {\n  // valid parent elements, any other element will prevent any\n  // children from inheriting a presentational role from a valid\n  // ancestor\n  td: ['tr'],\n  th: ['tr'],\n  tr: ['thead', 'tbody', 'tfoot', 'table'],\n  thead: ['table'],\n  tbody: ['table'],\n  tfoot: ['table'],\n  li: ['ol', 'ul'],\n  // dts and dds can be wrapped in divs and the div will pass through\n  // the presentation role\n  dt: ['dl', 'div'],\n  dd: ['dl', 'div'],\n  div: ['dl']\n};\n\n// role presentation inheritance.\n// Source: https://www.w3.org/TR/wai-aria-1.1/#conflict_resolution_presentation_none\nfunction getInheritedRole(vNode, explicitRoleOptions) {\n  const parentNodeNames = inheritsPresentationChain[vNode.props.nodeName];\n  if (!parentNodeNames) {\n    return null;\n  }\n\n  // if we can't look at the parent then we can't know if the node\n  // inherits the presentational role or not\n  if (!vNode.parent) {\n    if (!vNode.actualNode) {\n      return null;\n    }\n\n    throw new ReferenceError(\n      'Cannot determine role presentational inheritance of a required parent outside the current scope.'\n    );\n  }\n\n  // parent is not a valid ancestor that can inherit presentation\n  if (!parentNodeNames.includes(vNode.parent.props.nodeName)) {\n    return null;\n  }\n\n  const parentRole = getExplicitRole(vNode.parent, explicitRoleOptions);\n  if (\n    ['none', 'presentation'].includes(parentRole) &&\n    !hasConflictResolution(vNode.parent)\n  ) {\n    return parentRole;\n  }\n\n  // an explicit role of anything other than presentational will\n  // prevent any children from inheriting a presentational role\n  // from a valid ancestor\n  if (parentRole) {\n    return null;\n  }\n\n  return getInheritedRole(vNode.parent, explicitRoleOptions);\n}\n\nfunction resolveImplicitRole(vNode, { chromium, ...explicitRoleOptions }) {\n  const implicitRole = getImplicitRole(vNode, {\n    chromium\n  });\n\n  if (!implicitRole) {\n    return null;\n  }\n\n  const presentationalRole = getInheritedRole(vNode, explicitRoleOptions);\n  if (presentationalRole) {\n    return presentationalRole;\n  }\n\n  return implicitRole;\n}\n\n// role conflict resolution\n// note: Chrome returns a list with resolved role as \"generic\"\n// instead of as a list\n// (e.g. <ul role=\"none\" aria-label><li>hello</li></ul>)\n// we will return it as a list as that is the best option.\n// Source: https://www.w3.org/TR/wai-aria-1.1/#conflict_resolution_presentation_none\n// See also: https://github.com/w3c/aria/issues/1270\nfunction hasConflictResolution(vNode) {\n  const hasGlobalAria = getGlobalAriaAttrs().some(attr => vNode.hasAttr(attr));\n  return hasGlobalAria || isFocusable(vNode);\n}\n\n/**\n *\n * @method resolveRole\n * @param {Element|VirtualNode} node\n * @param {Object} options\n * @see getRole for option details\n * @returns {string|null} Role or null\n * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead.\n */\nfunction resolveRole(node, { noImplicit, ...roleOptions } = {}) {\n  const { vNode } = nodeLookup(node);\n  if (vNode.props.nodeType !== 1) {\n    return null;\n  }\n\n  const explicitRole = getExplicitRole(vNode, roleOptions);\n\n  if (!explicitRole) {\n    return noImplicit ? null : resolveImplicitRole(vNode, roleOptions);\n  }\n\n  if (!['presentation', 'none'].includes(explicitRole)) {\n    return explicitRole;\n  }\n\n  if (hasConflictResolution(vNode)) {\n    // return null if there is a conflict resolution but no implicit\n    // has been set as the explicit role is not the true role\n    return noImplicit ? null : resolveImplicitRole(vNode, roleOptions);\n  }\n\n  // role presentation or none and no conflict resolution\n  return explicitRole;\n}\n\n/**\n * Return the semantic role of an element.\n *\n * @method getRole\n * @memberof axe.commons.aria\n * @instance\n * @param {Element|VirtualNode} node\n * @param {Object} options\n * @param {boolean} options.noImplicit  Do not return the implicit role // @deprecated\n * @param {boolean} options.fallback  Allow fallback roles\n * @param {boolean} options.abstracts  Allow role to be abstract\n * @param {boolean} options.dpub  Allow role to be any (valid) doc-* roles\n * @param {boolean} options.noPresentational return null if role is presentation or none\n * @param {boolean} options.chromium Include implicit roles from chromium-based browsers in role result\n * @returns {string|null} Role or null\n *\n * @deprecated noImplicit option is deprecated. Use aria.getExplicitRole instead.\n */\nfunction getRole(node, { noPresentational, ...options } = {}) {\n  const role = resolveRole(node, options);\n\n  if (noPresentational && ['presentation', 'none'].includes(role)) {\n    return null;\n  }\n\n  return role;\n}\n\nexport default getRole;\n"
  },
  {
    "path": "lib/commons/aria/get-roles-by-type.js",
    "content": "import getAriaRolesByType from '../standards/get-aria-roles-by-type';\n\n/**\n * Get the roles that have a certain \"type\"\n * @method getRolesByType\n * @memberof axe.commons.aria\n * @deprecated use standards/get-aria-roles-by-type\n * @instance\n * @param {String} roleType The roletype to check\n * @return {Array} Array of roles that match the type\n */\nfunction getRolesByType(roleType) {\n  return getAriaRolesByType(roleType);\n}\n\nexport default getRolesByType;\n"
  },
  {
    "path": "lib/commons/aria/get-roles-with-name-from-contents.js",
    "content": "import getAriaRolesSupportingNameFromContent from '../standards/get-aria-roles-supporting-name-from-content';\n\n/**\n * Get the roles that get name from the element's contents\n * @method getRolesWithNameFromContents\n * @memberof axe.commons.aria\n * @instance\n * @deprecated use standards/get-aria-roles-supporting-name-from-content\n * @return {Array} Array of roles that match the type\n */\nfunction getRolesWithNameFromContents() {\n  return getAriaRolesSupportingNameFromContent();\n}\n\nexport default getRolesWithNameFromContents;\n"
  },
  {
    "path": "lib/commons/aria/implicit-nodes.js",
    "content": "import { clone } from '../../core/utils';\nimport lookupTable from './lookup-table';\n\n/**\n * Get a list of CSS selectors of nodes that have an implicit role\n * @method implicitNodes\n * @memberof axe.commons.aria\n * @deprecated\n * @instance\n * @param {String} role The role to check\n * @return {Mixed} Either an Array of CSS selectors or `null` if there are none\n */\nfunction implicitNodes(role) {\n  let implicit = null;\n  const roles = lookupTable.role[role];\n\n  if (roles && roles.implicit) {\n    implicit = clone(roles.implicit);\n  }\n  return implicit;\n}\n\nexport default implicitNodes;\n"
  },
  {
    "path": "lib/commons/aria/implicit-role.js",
    "content": "import implicitHtmlRoles from '../standards/implicit-html-roles';\nimport { getNodeFromTree } from '../../core/utils';\nimport getElementSpec from '../standards/get-element-spec';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\n\n/**\n * Get the implicit role for a given node\n * @method implicitRole\n * @memberof axe.commons.aria\n * @instance\n * @param {HTMLElement|VirtualNode} node The node to test\n * @return {Mixed} Either the role or `null` if there is none\n */\nfunction implicitRole(node, { chromium } = {}) {\n  const vNode =\n    node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node);\n  node = vNode.actualNode;\n\n  // this error is only thrown if the virtual tree is not a\n  // complete tree, which only happens in linting and if a\n  // user used `getFlattenedTree` manually on a subset of the\n  // DOM tree\n  if (!vNode) {\n    throw new ReferenceError(\n      'Cannot get implicit role of a node outside the current scope.'\n    );\n  }\n\n  const nodeName = vNode.props.nodeName;\n  const role = implicitHtmlRoles[nodeName];\n\n  if (!role && chromium) {\n    const { chromiumRole } = getElementSpec(vNode);\n    return chromiumRole || null;\n  }\n\n  if (typeof role === 'function') {\n    return role(vNode);\n  }\n\n  return role || null;\n}\n\nexport default implicitRole;\n"
  },
  {
    "path": "lib/commons/aria/index.js",
    "content": "/**\n * Namespace for aria-related utilities.\n * @namespace commons.aria\n * @memberof axe\n */\nexport { default as allowedAttr } from './allowed-attr';\nexport { default as arialabelText } from './arialabel-text';\nexport { default as arialabelledbyText } from './arialabelledby-text';\nexport { default as getAccessibleRefs } from './get-accessible-refs';\nexport { default as getElementUnallowedRoles } from './get-element-unallowed-roles';\nexport { default as getExplicitRole } from './get-explicit-role';\nexport { default as getImplicitRole } from './implicit-role';\nexport { default as getOwnedVirtual } from './get-owned-virtual';\nexport { default as getRoleType } from './get-role-type';\nexport { default as getRole } from './get-role';\nexport { default as getRolesByType } from './get-roles-by-type';\nexport { default as getRolesWithNameFromContents } from './get-roles-with-name-from-contents';\nexport { default as implicitNodes } from './implicit-nodes';\nexport { default as implicitRole } from './implicit-role';\nexport { default as isAccessibleRef } from './is-accessible-ref';\nexport { default as isAriaRoleAllowedOnElement } from './is-aria-role-allowed-on-element';\nexport { default as isComboboxPopup } from './is-combobox-popup';\nexport { default as isUnsupportedRole } from './is-unsupported-role';\nexport { default as isValidRole } from './is-valid-role';\nexport { default as labelVirtual } from './label-virtual';\nexport { default as label } from './label';\nexport { default as lookupTable } from './lookup-table';\nexport { default as namedFromContents } from './named-from-contents';\nexport { default as requiredAttr } from './required-attr';\nexport { default as requiredContext } from './required-context';\nexport { default as requiredOwned } from './required-owned';\nexport { default as validateAttrValue } from './validate-attr-value';\nexport { default as validateAttr } from './validate-attr';\n"
  },
  {
    "path": "lib/commons/aria/is-accessible-ref.js",
    "content": "import getAccessibleRefs from './get-accessible-refs';\n\n/**\n * Check that a DOM node is a reference in the accessibility tree\n * @param {Element} node\n * @returns {Boolean}\n */\nfunction isAccessibleRef(node) {\n  return !!getAccessibleRefs(node).length;\n}\n\nexport default isAccessibleRef;\n"
  },
  {
    "path": "lib/commons/aria/is-aria-role-allowed-on-element.js",
    "content": "import { getNodeFromTree } from '../../core/utils';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\nimport getImplicitRole from './implicit-role';\nimport getElementSpec from '../standards/get-element-spec';\n\n/**\n * @description validate if a given role is an allowed ARIA role for the supplied node\n * @method isAriaRoleAllowedOnElement\n * @param {HTMLElement} node the node to verify\n * @param {String} role aria role to check\n * @return {Boolean} retruns true/false\n */\nfunction isAriaRoleAllowedOnElement(node, role) {\n  const vNode =\n    node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node);\n  const implicitRole = getImplicitRole(vNode);\n\n  const spec = getElementSpec(vNode);\n\n  if (Array.isArray(spec.allowedRoles)) {\n    return spec.allowedRoles.includes(role);\n  }\n\n  // By default, ARIA in HTML does not allow implicit roles to be the same as explicit ones\n  // aria-allowed-roles has an `allowedImplicit` option to bypass this.\n  if (role === implicitRole) {\n    return false;\n  }\n  return !!spec.allowedRoles;\n}\n\nexport default isAriaRoleAllowedOnElement;\n"
  },
  {
    "path": "lib/commons/aria/is-combobox-popup.js",
    "content": "import getRole from './get-role';\nimport ariaAttrs from '../../standards/aria-attrs';\nimport { getRootNode } from '../../core/utils';\n\n/**\n * Whether an element is the popup for a combobox\n * @method isComboboxPopup\n * @memberof axe.commons.aria\n * @instance\n * @param {VirtualNode} virtualNode\n * @param {Object} options\n * @property {String[]} popupRoles Overrides which roles can be popup. Defaults to aria-haspopup values\n * @returns {boolean}\n */\nexport default function isComboboxPopup(virtualNode, { popupRoles } = {}) {\n  const role = getRole(virtualNode);\n  popupRoles ??= ariaAttrs['aria-haspopup'].values;\n  if (!popupRoles.includes(role)) {\n    return false;\n  }\n\n  // in ARIA 1.1 the container has role=combobox\n  const vParent = nearestParentWithRole(virtualNode);\n  if (isCombobox(vParent)) {\n    return true;\n  }\n\n  const { id } = virtualNode.props;\n  if (!id) {\n    return false;\n  }\n\n  if (!virtualNode.actualNode) {\n    throw new Error('Unable to determine combobox popup without an actualNode');\n  }\n  const root = getRootNode(virtualNode.actualNode);\n  const ownedCombobox = root.querySelectorAll(\n    // aria-owns was from ARIA 1.0, aria-controls was from ARIA 1.2\n    `[aria-owns~=\"${id}\"][role~=\"combobox\"]:not(select),\n     [aria-controls~=\"${id}\"][role~=\"combobox\"]:not(select)`\n  );\n\n  return Array.from(ownedCombobox).some(isCombobox);\n}\n\nconst isCombobox = node => node && getRole(node) === 'combobox';\n\nfunction nearestParentWithRole(vNode) {\n  while ((vNode = vNode.parent)) {\n    if (getRole(vNode, { noPresentational: true }) !== null) {\n      return vNode;\n    }\n  }\n  return null;\n}\n"
  },
  {
    "path": "lib/commons/aria/is-unsupported-role.js",
    "content": "import standards from '../../standards';\n\n/**\n * Check if a given role is unsupported\n * @method isUnsupportedRole\n * @memberof axe.commons.aria\n * @instance\n * @param {String} role The role to check\n * @return {Boolean}\n */\nfunction isUnsupportedRole(role) {\n  const roleDefinition = standards.ariaRoles[role];\n  return roleDefinition ? !!roleDefinition.unsupported : false;\n}\n\nexport default isUnsupportedRole;\n"
  },
  {
    "path": "lib/commons/aria/is-valid-role.js",
    "content": "import standards from '../../standards';\nimport isUnsupportedRole from './is-unsupported-role';\n\n/**\n * Check if a given role is valid\n * @method isValidRole\n * @memberof axe.commons.aria\n * @instance\n * @param {String} role The role to check\n * @param {Object} options Use `allowAbstract` if you want abstracts, and `flagUnsupported: true` to report unsupported roles\n * @return {Boolean}\n */\nfunction isValidRole(role, { allowAbstract, flagUnsupported = false } = {}) {\n  const roleDefinition = standards.ariaRoles[role];\n  const isRoleUnsupported = isUnsupportedRole(role);\n  if (!roleDefinition || (flagUnsupported && isRoleUnsupported)) {\n    return false;\n  }\n  return allowAbstract ? true : roleDefinition.type !== 'abstract';\n}\n\nexport default isValidRole;\n"
  },
  {
    "path": "lib/commons/aria/label-virtual.js",
    "content": "import idrefs from '../dom/idrefs';\nimport visibleVirtual from '../text/visible-virtual';\nimport sanitize from '../text/sanitize';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Gets the accessible ARIA label text of a given element\n * @see http://www.w3.org/WAI/PF/aria/roles#namecalculation\n * @method labelVirtual\n * @memberof axe.commons.aria\n * @instance\n * @param  {VirtualNode} virtualNode The virtualNode to test\n * @return {Mixed}  String of visible text, or `null` if no label is found\n */\nfunction labelVirtual(virtualNode) {\n  let ref, candidate;\n\n  if (virtualNode.attr('aria-labelledby')) {\n    // aria-labelledby\n    ref = idrefs(virtualNode.actualNode, 'aria-labelledby');\n    candidate = ref\n      .map(thing => {\n        const vNode = getNodeFromTree(thing);\n        return vNode ? visibleVirtual(vNode) : '';\n      })\n      .join(' ')\n      .trim();\n\n    if (candidate) {\n      return candidate;\n    }\n  }\n\n  // aria-label\n  candidate = virtualNode.attr('aria-label');\n  if (candidate) {\n    candidate = sanitize(candidate);\n    if (candidate) {\n      return candidate;\n    }\n  }\n\n  return null;\n}\n\nexport default labelVirtual;\n"
  },
  {
    "path": "lib/commons/aria/label.js",
    "content": "import labelVirtual from './label-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Gets the aria label for a given node\n * @method label\n * @memberof axe.commons.aria\n * @instance\n * @param  {HTMLElement} node The element to check\n * @return {Mixed} String of visible text, or `null` if no label is found\n */\nfunction label(node) {\n  node = getNodeFromTree(node);\n  return labelVirtual(node);\n}\n\nexport default label;\n"
  },
  {
    "path": "lib/commons/aria/lookup-table.js",
    "content": "// @deprecated use standards object instead\nimport implicitHtmlRoles from '../standards/implicit-html-roles';\n\nconst isNull = value => value === null;\nconst isNotNull = value => value !== null;\n\nconst lookupTable = {};\n\nlookupTable.attributes = {\n  'aria-activedescendant': {\n    type: 'idref',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-atomic': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-autocomplete': {\n    type: 'nmtoken',\n    values: ['inline', 'list', 'both', 'none'],\n    unsupported: false\n  },\n  'aria-busy': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-checked': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'mixed', 'undefined'],\n    unsupported: false\n  },\n  'aria-colcount': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-colindex': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-colspan': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-controls': {\n    type: 'idrefs',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-current': {\n    type: 'nmtoken',\n    allowEmpty: true,\n    values: ['page', 'step', 'location', 'date', 'time', 'true', 'false'],\n    unsupported: false\n  },\n  'aria-describedby': {\n    type: 'idrefs',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-describedat': {\n    unsupported: true,\n    unstandardized: true\n  },\n  'aria-details': {\n    type: 'idref',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-disabled': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-dropeffect': {\n    type: 'nmtokens',\n    values: ['copy', 'move', 'reference', 'execute', 'popup', 'none'],\n    unsupported: false\n  },\n  'aria-errormessage': {\n    type: 'idref',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-expanded': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined'],\n    unsupported: false\n  },\n  'aria-flowto': {\n    type: 'idrefs',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-grabbed': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined'],\n    unsupported: false\n  },\n  'aria-haspopup': {\n    type: 'nmtoken',\n    allowEmpty: true,\n    values: ['true', 'false', 'menu', 'listbox', 'tree', 'grid', 'dialog'],\n    unsupported: false\n  },\n  'aria-hidden': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-invalid': {\n    type: 'nmtoken',\n    allowEmpty: true,\n    values: ['true', 'false', 'spelling', 'grammar'],\n    unsupported: false\n  },\n  'aria-keyshortcuts': {\n    type: 'string',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-label': {\n    type: 'string',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-labelledby': {\n    type: 'idrefs',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-level': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-live': {\n    type: 'nmtoken',\n    values: ['off', 'polite', 'assertive'],\n    unsupported: false\n  },\n  'aria-modal': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-multiline': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-multiselectable': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-orientation': {\n    type: 'nmtoken',\n    values: ['horizontal', 'vertical'],\n    unsupported: false\n  },\n  'aria-owns': {\n    type: 'idrefs',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-placeholder': {\n    type: 'string',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-posinset': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-pressed': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'mixed', 'undefined'],\n    unsupported: false\n  },\n  'aria-readonly': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-relevant': {\n    type: 'nmtokens',\n    values: ['additions', 'removals', 'text', 'all'],\n    unsupported: false\n  },\n  'aria-required': {\n    type: 'boolean',\n    values: ['true', 'false'],\n    unsupported: false\n  },\n  'aria-roledescription': {\n    type: 'string',\n    allowEmpty: true,\n    unsupported: false\n  },\n  'aria-rowcount': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-rowindex': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-rowspan': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-selected': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined'],\n    unsupported: false\n  },\n  'aria-setsize': {\n    type: 'int',\n    unsupported: false\n  },\n  'aria-sort': {\n    type: 'nmtoken',\n    values: ['ascending', 'descending', 'other', 'none'],\n    unsupported: false\n  },\n  'aria-valuemax': {\n    type: 'decimal',\n    unsupported: false\n  },\n  'aria-valuemin': {\n    type: 'decimal',\n    unsupported: false\n  },\n  'aria-valuenow': {\n    type: 'decimal',\n    unsupported: false\n  },\n  'aria-valuetext': {\n    type: 'string',\n    unsupported: false\n  }\n};\n\nlookupTable.globalAttributes = [\n  'aria-atomic',\n  'aria-busy',\n  'aria-controls',\n  'aria-current',\n  'aria-describedby',\n  'aria-details',\n  'aria-disabled',\n  'aria-dropeffect',\n  'aria-flowto',\n  'aria-grabbed',\n  'aria-haspopup',\n  'aria-hidden',\n  'aria-invalid',\n  'aria-keyshortcuts',\n  'aria-label',\n  'aria-labelledby',\n  'aria-live',\n  'aria-owns',\n  'aria-relevant',\n  'aria-roledescription'\n];\n\nlookupTable.role = {\n  // valid roles below\n  alert: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  alertdialog: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-modal', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['dialog', 'section']\n  },\n  application: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage', 'aria-activedescendant']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      'article',\n      'audio',\n      'embed',\n      'iframe',\n      'object',\n      'section',\n      'svg',\n      'video'\n    ]\n  },\n  article: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-expanded',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['article'],\n    unsupported: false\n  },\n  banner: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['header'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  button: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-pressed', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: [\n      'button',\n      'input[type=\"button\"]',\n      'input[type=\"image\"]',\n      'input[type=\"reset\"]',\n      'input[type=\"submit\"]',\n      'summary'\n    ],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  cell: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-colindex',\n        'aria-colspan',\n        'aria-rowindex',\n        'aria-rowspan',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['row'],\n    implicit: ['td', 'th'],\n    unsupported: false\n  },\n  checkbox: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-checked',\n        'aria-required',\n        'aria-readonly',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['input[type=\"checkbox\"]'],\n    unsupported: false,\n    allowedElements: ['button']\n  },\n  columnheader: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-colindex',\n        'aria-colspan',\n        'aria-expanded',\n        'aria-rowindex',\n        'aria-rowspan',\n        'aria-required',\n        'aria-readonly',\n        'aria-selected',\n        'aria-sort',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['row'],\n    implicit: ['th'],\n    unsupported: false\n  },\n  combobox: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-autocomplete',\n        'aria-required',\n        'aria-activedescendant',\n        'aria-orientation',\n        'aria-errormessage'\n      ],\n      required: ['aria-expanded']\n    },\n    owned: {\n      all: ['listbox', 'tree', 'grid', 'dialog', 'textbox']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['text', 'search', 'tel', 'url', 'email']\n        }\n      }\n    ]\n  },\n  command: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  },\n  complementary: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['aside'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  composite: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  },\n  contentinfo: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['footer'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  definition: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['dd', 'dfn'],\n    unsupported: false\n  },\n  dialog: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-modal', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['dialog'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  directory: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  document: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['body'],\n    unsupported: false,\n    allowedElements: ['article', 'embed', 'iframe', 'object', 'section', 'svg']\n  },\n  'doc-abstract': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-acknowledgments': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-afterword': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-appendix': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-backlink': {\n    type: 'link',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  'doc-biblioentry': {\n    type: 'listitem',\n    attributes: {\n      allowed: [\n        'aria-expanded',\n        'aria-level',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: ['doc-bibliography'],\n    unsupported: false,\n    allowedElements: ['li']\n  },\n  'doc-bibliography': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: {\n      one: ['doc-biblioentry']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-biblioref': {\n    type: 'link',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  'doc-chapter': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-colophon': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-conclusion': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-cover': {\n    type: 'img',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false\n  },\n  'doc-credit': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-credits': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-dedication': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-endnote': {\n    type: 'listitem',\n    attributes: {\n      allowed: [\n        'aria-expanded',\n        'aria-level',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: ['doc-endnotes'],\n    unsupported: false,\n    allowedElements: ['li']\n  },\n  'doc-endnotes': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: {\n      one: ['doc-endnote']\n    },\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-epigraph': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false\n  },\n  'doc-epilogue': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-errata': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-example': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['aside', 'section']\n  },\n  'doc-footnote': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['aside', 'footer', 'header']\n  },\n  'doc-foreword': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-glossary': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: ['term', 'definition'],\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['dl']\n  },\n  'doc-glossref': {\n    type: 'link',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  'doc-index': {\n    type: 'navigation',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['nav', 'section']\n  },\n  'doc-introduction': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-noteref': {\n    type: 'link',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  'doc-notice': {\n    type: 'note',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-pagebreak': {\n    type: 'separator',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['hr']\n  },\n  'doc-pagelist': {\n    type: 'navigation',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['nav', 'section']\n  },\n  'doc-part': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-preface': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-prologue': {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-pullquote': {\n    type: 'none',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['aside', 'section']\n  },\n  'doc-qna': {\n    type: 'section',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  'doc-subtitle': {\n    type: 'sectionhead',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: {\n      nodeName: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']\n    }\n  },\n  'doc-tip': {\n    type: 'note',\n    attributes: {\n      allowed: ['aria-expanded']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['aside']\n  },\n  'doc-toc': {\n    type: 'navigation',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    namefrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['nav', 'section']\n  },\n  feed: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: {\n      one: ['article']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['article', 'aside', 'section']\n  },\n  figure: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['figure'],\n    unsupported: false\n  },\n  form: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['form'],\n    unsupported: false\n  },\n  grid: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-expanded',\n        'aria-colcount',\n        'aria-level',\n        'aria-multiselectable',\n        'aria-readonly',\n        'aria-rowcount',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      one: ['rowgroup', 'row']\n    },\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['table'],\n    unsupported: false\n  },\n  gridcell: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-colindex',\n        'aria-colspan',\n        'aria-expanded',\n        'aria-rowindex',\n        'aria-rowspan',\n        'aria-selected',\n        'aria-readonly',\n        'aria-required',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['row'],\n    implicit: ['td', 'th'],\n    unsupported: false\n  },\n  group: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-activedescendant', 'aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['details', 'optgroup'],\n    unsupported: false,\n    allowedElements: [\n      'dl',\n      'figcaption',\n      'fieldset',\n      'figure',\n      'footer',\n      'header',\n      'ol',\n      'ul'\n    ]\n  },\n  heading: {\n    type: 'structure',\n    attributes: {\n      required: ['aria-level'],\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],\n    unsupported: false\n  },\n  img: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['img'],\n    unsupported: false,\n    allowedElements: ['embed', 'iframe', 'object', 'svg']\n  },\n  input: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  },\n  landmark: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  },\n  link: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['a[href]', 'area[href]'],\n    unsupported: false,\n    allowedElements: [\n      'button',\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['image', 'button']\n        }\n      }\n    ]\n  },\n  list: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: {\n      all: ['listitem']\n    },\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['ol', 'ul', 'dl'],\n    unsupported: false\n  },\n  listbox: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-multiselectable',\n        'aria-readonly',\n        'aria-required',\n        'aria-expanded',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      all: ['option']\n    },\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['select'],\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  listitem: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-level',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-expanded',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['list'],\n    implicit: ['li', 'dt'],\n    unsupported: false\n  },\n  log: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  main: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['main'],\n    unsupported: false,\n    allowedElements: ['article', 'section']\n  },\n  marquee: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  math: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['math'],\n    unsupported: false\n  },\n  menu: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-expanded',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      one: ['menuitem', 'menuitemradio', 'menuitemcheckbox']\n    },\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['menu[type=\"context\"]'],\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  menubar: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-expanded',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      one: ['menuitem', 'menuitemradio', 'menuitemcheckbox']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  menuitem: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-posinset',\n        'aria-setsize',\n        'aria-expanded',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['menu', 'menubar'],\n    implicit: ['menuitem[type=\"command\"]'],\n    unsupported: false,\n    allowedElements: [\n      'button',\n      'li',\n      {\n        nodeName: 'iput',\n        properties: {\n          type: ['image', 'button']\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  menuitemcheckbox: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-checked',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['menu', 'menubar'],\n    implicit: ['menuitem[type=\"checkbox\"]'],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: ['button', 'li']\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['checkbox', 'image', 'button']\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  menuitemradio: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-checked',\n        'aria-selected',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['menu', 'menubar'],\n    implicit: ['menuitem[type=\"radio\"]'],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: ['button', 'li']\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['image', 'button', 'radio']\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  navigation: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['nav'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  none: {\n    type: 'structure',\n    attributes: null,\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: [\n          'article',\n          'aside',\n          'dl',\n          'embed',\n          'figcaption',\n          'fieldset',\n          'figure',\n          'footer',\n          'form',\n          'h1',\n          'h2',\n          'h3',\n          'h4',\n          'h5',\n          'h6',\n          'header',\n          'hr',\n          'iframe',\n          'li',\n          'ol',\n          'section',\n          'ul'\n        ]\n      },\n      {\n        nodeName: 'img',\n        attributes: {\n          alt: isNotNull\n        }\n      }\n    ]\n  },\n  note: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['aside']\n  },\n  option: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-selected',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-checked',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['listbox'],\n    implicit: ['option'],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: ['button', 'li']\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['checkbox', 'button']\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  presentation: {\n    type: 'structure',\n    attributes: null,\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: [\n          'article',\n          'aside',\n          'dl',\n          'embed',\n          'figcaption',\n          'fieldset',\n          'figure',\n          'footer',\n          'form',\n          'h1',\n          'h2',\n          'h3',\n          'h4',\n          'h5',\n          'h6',\n          'header',\n          'hr',\n          'iframe',\n          'li',\n          'ol',\n          'section',\n          'ul'\n        ]\n      },\n      {\n        nodeName: 'img',\n        attributes: {\n          alt: isNotNull\n        }\n      }\n    ]\n  },\n  progressbar: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-valuetext',\n        'aria-valuenow',\n        'aria-valuemax',\n        'aria-valuemin',\n        'aria-expanded',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['progress'],\n    unsupported: false\n  },\n  radio: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-selected',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-required',\n        'aria-errormessage',\n        'aria-checked'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['input[type=\"radio\"]'],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: ['button', 'li']\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['image', 'button']\n        }\n      }\n    ]\n  },\n  radiogroup: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-required',\n        'aria-expanded',\n        'aria-readonly',\n        'aria-errormessage',\n        'aria-orientation'\n      ]\n    },\n    owned: {\n      all: ['radio']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: {\n      nodeName: ['ol', 'ul', 'fieldset']\n    }\n  },\n  range: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    // @marcysutton, @wilco\n    // - there is no unsupported here (noticed when resolving conflicts) from PR - https://github.com/dequelabs/axe-core/pull/1064\n    // - https://github.com/dequelabs/axe-core/pull/1064/files#diff-ec67bb6113bfd9a900ee27ecef942f74R1229\n    // - adding unsupported flag (false)\n    unsupported: false\n  },\n  region: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: [\n      'section[aria-label]',\n      'section[aria-labelledby]',\n      'section[title]'\n    ],\n    unsupported: false,\n    allowedElements: {\n      nodeName: ['article', 'aside']\n    }\n  },\n  roletype: {\n    type: 'abstract',\n    unsupported: false\n  },\n  row: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-colindex',\n        'aria-expanded',\n        'aria-level',\n        'aria-selected',\n        'aria-rowindex',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      one: ['cell', 'columnheader', 'rowheader', 'gridcell']\n    },\n    nameFrom: ['author', 'contents'],\n    context: ['rowgroup', 'grid', 'treegrid', 'table'],\n    implicit: ['tr'],\n    unsupported: false\n  },\n  rowgroup: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-activedescendant', 'aria-expanded', 'aria-errormessage']\n    },\n    owned: {\n      all: ['row']\n    },\n    nameFrom: ['author', 'contents'],\n    context: ['grid', 'table', 'treegrid'],\n    implicit: ['tbody', 'thead', 'tfoot'],\n    unsupported: false\n  },\n  rowheader: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-colindex',\n        'aria-colspan',\n        'aria-expanded',\n        'aria-rowindex',\n        'aria-rowspan',\n        'aria-required',\n        'aria-readonly',\n        'aria-selected',\n        'aria-sort',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['row'],\n    implicit: ['th'],\n    unsupported: false\n  },\n  scrollbar: {\n    type: 'widget',\n    attributes: {\n      required: ['aria-controls', 'aria-valuenow'],\n      allowed: [\n        'aria-valuetext',\n        'aria-orientation',\n        'aria-errormessage',\n        'aria-valuemax',\n        'aria-valuemin'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false\n  },\n  search: {\n    type: 'landmark',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: {\n      nodeName: ['aside', 'form', 'section']\n    }\n  },\n  searchbox: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-autocomplete',\n        'aria-multiline',\n        'aria-readonly',\n        'aria-required',\n        'aria-placeholder',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['input[type=\"search\"]'],\n    unsupported: false,\n    allowedElements: {\n      nodeName: 'input',\n      properties: {\n        type: 'text'\n      }\n    }\n  },\n  section: {\n    nameFrom: ['author', 'contents'],\n    type: 'abstract',\n    unsupported: false\n  },\n  sectionhead: {\n    nameFrom: ['author', 'contents'],\n    type: 'abstract',\n    unsupported: false\n  },\n  select: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  },\n  separator: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-expanded',\n        'aria-orientation',\n        'aria-valuenow',\n        'aria-valuemax',\n        'aria-valuemin',\n        'aria-valuetext',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['hr'],\n    unsupported: false,\n    allowedElements: ['li']\n  },\n  slider: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-valuetext',\n        'aria-orientation',\n        'aria-readonly',\n        'aria-errormessage',\n        'aria-valuemax',\n        'aria-valuemin'\n      ],\n      required: ['aria-valuenow']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['input[type=\"range\"]'],\n    unsupported: false\n  },\n  spinbutton: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-valuetext',\n        'aria-required',\n        'aria-readonly',\n        'aria-errormessage',\n        'aria-valuemax',\n        'aria-valuemin'\n      ],\n      required: ['aria-valuenow']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['input[type=\"number\"]'],\n    unsupported: false,\n    allowedElements: {\n      nodeName: 'input',\n      properties: {\n        type: ['text', 'tel']\n      }\n    }\n  },\n  status: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['output'],\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  structure: {\n    type: 'abstract',\n    unsupported: false\n  },\n  switch: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-errormessage'],\n      required: ['aria-checked']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    unsupported: false,\n    allowedElements: [\n      'button',\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['checkbox', 'image', 'button']\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  tab: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-selected',\n        'aria-expanded',\n        'aria-setsize',\n        'aria-posinset',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['tablist'],\n    unsupported: false,\n    allowedElements: [\n      {\n        nodeName: ['button', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li']\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: 'button'\n        }\n      },\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  table: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-colcount', 'aria-rowcount', 'aria-errormessage']\n    },\n    owned: {\n      one: ['rowgroup', 'row']\n    },\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['table'],\n    unsupported: false\n  },\n  tablist: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-expanded',\n        'aria-level',\n        'aria-multiselectable',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      all: ['tab']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  tabpanel: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['section']\n  },\n  term: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    implicit: ['dt'],\n    unsupported: false\n  },\n  textbox: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-autocomplete',\n        'aria-multiline',\n        'aria-readonly',\n        'aria-required',\n        'aria-placeholder',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: [\n      'input[type=\"text\"]',\n      'input[type=\"email\"]',\n      'input[type=\"password\"]',\n      'input[type=\"tel\"]',\n      'input[type=\"url\"]',\n      'input:not([type])',\n      'textarea'\n    ],\n    unsupported: false\n  },\n  timer: {\n    type: 'widget',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false\n  },\n  toolbar: {\n    type: 'structure',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-expanded',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author'],\n    context: null,\n    implicit: ['menu[type=\"toolbar\"]'],\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  tooltip: {\n    type: 'structure',\n    attributes: {\n      allowed: ['aria-expanded', 'aria-errormessage']\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: null,\n    unsupported: false\n  },\n  tree: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-multiselectable',\n        'aria-required',\n        'aria-expanded',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      all: ['treeitem']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false,\n    allowedElements: ['ol', 'ul']\n  },\n  treegrid: {\n    type: 'composite',\n    attributes: {\n      allowed: [\n        'aria-activedescendant',\n        'aria-colcount',\n        'aria-expanded',\n        'aria-level',\n        'aria-multiselectable',\n        'aria-readonly',\n        'aria-required',\n        'aria-rowcount',\n        'aria-orientation',\n        'aria-errormessage'\n      ]\n    },\n    owned: {\n      one: ['rowgroup', 'row']\n    },\n    nameFrom: ['author'],\n    context: null,\n    unsupported: false\n  },\n  treeitem: {\n    type: 'widget',\n    attributes: {\n      allowed: [\n        'aria-checked',\n        'aria-selected',\n        'aria-expanded',\n        'aria-level',\n        'aria-posinset',\n        'aria-setsize',\n        'aria-errormessage'\n      ]\n    },\n    owned: null,\n    nameFrom: ['author', 'contents'],\n    context: ['group', 'tree'],\n    unsupported: false,\n    allowedElements: [\n      'li',\n      {\n        nodeName: 'a',\n        attributes: {\n          href: isNotNull\n        }\n      }\n    ]\n  },\n  widget: {\n    type: 'abstract',\n    unsupported: false\n  },\n  window: {\n    nameFrom: ['author'],\n    type: 'abstract',\n    unsupported: false\n  }\n};\n\nlookupTable.implicitHtmlRole = implicitHtmlRoles;\n\n// Source: https://www.w3.org/TR/html-aria/\nlookupTable.elementsAllowedNoRole = [\n  {\n    // Plain HTML nodes\n    nodeName: [\n      'base',\n      'body',\n      'caption',\n      'col',\n      'colgroup',\n      'datalist',\n      'dd',\n      'details',\n      'dt',\n      'head',\n      'html',\n      'keygen',\n      'label',\n      'legend',\n      'main',\n      'map',\n      'math',\n      'meta',\n      'meter',\n      'noscript',\n      'optgroup',\n      'param',\n      'picture',\n      'progress',\n      'script',\n      'source',\n      'style',\n      'template',\n      'textarea',\n      'title',\n      'track'\n    ]\n  },\n  {\n    nodeName: 'area',\n    attributes: {\n      href: isNotNull\n    }\n  },\n  {\n    nodeName: 'input',\n    properties: {\n      type: [\n        'color',\n        'data',\n        'datatime',\n        'file',\n        'hidden',\n        'month',\n        'number',\n        'password',\n        'range',\n        'reset',\n        'submit',\n        'time',\n        'week'\n      ]\n    }\n  },\n  {\n    nodeName: 'link',\n    attributes: {\n      href: isNotNull\n    }\n  },\n  {\n    nodeName: 'menu',\n    attributes: {\n      type: 'context'\n    }\n  },\n  {\n    nodeName: 'menuitem',\n    attributes: {\n      type: ['command', 'checkbox', 'radio']\n    }\n  },\n  {\n    nodeName: 'select',\n    condition: vNode => {\n      if (!(vNode instanceof axe.AbstractVirtualNode)) {\n        vNode = axe.utils.getNodeFromTree(vNode);\n      }\n\n      return Number(vNode.attr('size')) > 1;\n    },\n    properties: {\n      multiple: true\n    }\n  },\n  // svg elements (below)\n  {\n    nodeName: [\n      'clippath',\n      'cursor',\n      'defs',\n      'desc',\n      'feblend',\n      'fecolormatrix',\n      'fecomponenttransfer',\n      'fecomposite',\n      'feconvolvematrix',\n      'fediffuselighting',\n      'fedisplacementmap',\n      'fedistantlight',\n      'fedropshadow',\n      'feflood',\n      'fefunca',\n      'fefuncb',\n      'fefuncg',\n      'fefuncr',\n      'fegaussianblur',\n      'feimage',\n      'femerge',\n      'femergenode',\n      'femorphology',\n      'feoffset',\n      'fepointlight',\n      'fespecularlighting',\n      'fespotlight',\n      'fetile',\n      'feturbulence',\n      'filter',\n      'hatch',\n      'hatchpath',\n      'lineargradient',\n      'marker',\n      'mask',\n      'meshgradient',\n      'meshpatch',\n      'meshrow',\n      'metadata',\n      'mpath',\n      'pattern',\n      'radialgradient',\n      'solidcolor',\n      'stop',\n      'switch',\n      'view'\n    ]\n  }\n];\n\n// Source: https://www.w3.org/TR/html-aria/\nlookupTable.elementsAllowedAnyRole = [\n  {\n    nodeName: 'a',\n    attributes: {\n      href: isNull\n    }\n  },\n  {\n    nodeName: 'img',\n    attributes: {\n      alt: isNull\n    }\n  },\n  {\n    nodeName: [\n      'abbr',\n      'address',\n      'canvas',\n      'div',\n      'p',\n      'pre',\n      'blockquote',\n      'ins',\n      'del',\n      'output',\n      'span',\n      'table',\n      'tbody',\n      'thead',\n      'tfoot',\n      'td',\n      'em',\n      'strong',\n      'small',\n      's',\n      'cite',\n      'q',\n      'dfn',\n      'abbr',\n      'time',\n      'code',\n      'var',\n      'samp',\n      'kbd',\n      'sub',\n      'sup',\n      'i',\n      'b',\n      'u',\n      'mark',\n      'ruby',\n      'rt',\n      'rp',\n      'bdi',\n      'bdo',\n      'br',\n      'wbr',\n      'th',\n      'tr'\n    ]\n  }\n];\n\nlookupTable.evaluateRoleForElement = {\n  A: ({ node, out }) => {\n    if (node.namespaceURI === 'http://www.w3.org/2000/svg') {\n      return true;\n    }\n    if (node.href.length) {\n      return out;\n    }\n    return true;\n  },\n  AREA: ({ node }) => !node.href,\n  BUTTON: ({ node, role, out }) => {\n    if (node.getAttribute('type') === 'menu') {\n      return role === 'menuitem';\n    }\n    return out;\n  },\n  IMG: ({ node, role, out }) => {\n    switch (node.alt) {\n      case null:\n        return out;\n      case '':\n        return role === 'presentation' || role === 'none';\n      default:\n        return role !== 'presentation' && role !== 'none';\n    }\n  },\n  INPUT: ({ node, role, out }) => {\n    switch (node.type) {\n      case 'button':\n      case 'image':\n        return out;\n      case 'checkbox':\n        if (role === 'button' && node.hasAttribute('aria-pressed')) {\n          return true;\n        }\n        return out;\n      case 'radio':\n        return role === 'menuitemradio';\n      case 'text':\n        return (\n          role === 'combobox' || role === 'searchbox' || role === 'spinbutton'\n        );\n      case 'tel':\n        return role === 'combobox' || role === 'spinbutton';\n      case 'url':\n      case 'search':\n      case 'email':\n        return role === 'combobox';\n      default:\n        return false;\n    }\n  },\n  LI: ({ node, out }) => {\n    const hasImplicitListitemRole = axe.utils.matchesSelector(\n      node,\n      'ol li, ul li'\n    );\n    if (hasImplicitListitemRole) {\n      return out;\n    }\n    return true;\n  },\n  MENU: ({ node }) => {\n    if (node.getAttribute('type') === 'context') {\n      return false;\n    }\n    return true;\n  },\n  OPTION: ({ node }) => {\n    const withinOptionList = axe.utils.matchesSelector(\n      node,\n      'select > option, datalist > option, optgroup > option'\n    );\n    return !withinOptionList;\n  },\n  SELECT: ({ node, role }) =>\n    !node.multiple && node.size <= 1 && role === 'menu',\n  SVG: ({ node, out }) => {\n    // if in svg context it all roles may be used\n    if (\n      node.parentNode &&\n      node.parentNode.namespaceURI === 'http://www.w3.org/2000/svg'\n    ) {\n      return true;\n    }\n    return out;\n  }\n};\n\n/**\n * Note:\n * \tUsage of `rolesOfType` is deprecated within the source code.\n * \tLeaving this here for now, to keep support for custom rules.\n */\nlookupTable.rolesOfType = {\n  widget: [\n    'button',\n    'checkbox',\n    'dialog',\n    'gridcell',\n    'link',\n    'log',\n    'marquee',\n    'menuitem',\n    'menuitemcheckbox',\n    'menuitemradio',\n    'option',\n    'progressbar',\n    'radio',\n    'scrollbar',\n    'searchbox',\n    'slider',\n    'spinbutton',\n    'status',\n    'switch',\n    'tab',\n    'tabpanel',\n    'textbox',\n    'timer',\n    'tooltip',\n    'tree',\n    'treeitem'\n  ]\n};\n\nexport default lookupTable;\n"
  },
  {
    "path": "lib/commons/aria/named-from-contents.js",
    "content": "import getRole from './get-role';\nimport standards from '../../standards';\nimport { getNodeFromTree } from '../../core/utils';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\n\n/**\n * Check if an element is named from contents\n *\n * @param {Node|VirtualNode} element\n * @param {Object} options\n * @property {Bool} strict Whether or not to follow the spects strictly\n * @return {Bool}\n */\nfunction namedFromContents(vNode, { strict } = {}) {\n  vNode = vNode instanceof AbstractVirtuaNode ? vNode : getNodeFromTree(vNode);\n  if (vNode.props.nodeType !== 1) {\n    return false;\n  }\n\n  const role = getRole(vNode);\n  const roleDef = standards.ariaRoles[role];\n\n  if (roleDef && roleDef.nameFromContent) {\n    return true;\n  }\n\n  /**\n   * Note: Strictly speaking if the role is null, presentation, or none, the element\n   * isn't named from contents. Axe-core often needs to know if an element\n   * has content anyway, so we're allowing it here.\n   * Use { strict: true } to disable this behavior.\n   */\n  if (strict) {\n    return false;\n  }\n  return !roleDef || ['presentation', 'none'].includes(role);\n}\n\nexport default namedFromContents;\n"
  },
  {
    "path": "lib/commons/aria/required-attr.js",
    "content": "import standards from '../../standards';\n\n/**\n * Get required attributes for a given role\n * @method requiredAttr\n * @memberof axe.commons.aria\n * @instance\n * @param  {String} role The role to check\n * @return {Array}\n */\nfunction requiredAttr(role) {\n  const roleDef = standards.ariaRoles[role];\n\n  if (!roleDef || !Array.isArray(roleDef.requiredAttrs)) {\n    return [];\n  }\n\n  return [...roleDef.requiredAttrs];\n}\n\nexport default requiredAttr;\n"
  },
  {
    "path": "lib/commons/aria/required-context.js",
    "content": "import standards from '../../standards';\n\n/**\n * Get the required context (parent) roles for a given role\n * @method requiredContext\n * @memberof axe.commons.aria\n * @instance\n * @param {String} role The role to check\n * @return {Mixed} Either an Array of required context elements or `null` if there are none\n */\nfunction requiredContext(role) {\n  const roleDef = standards.ariaRoles[role];\n\n  if (!roleDef || !Array.isArray(roleDef.requiredContext)) {\n    return null;\n  }\n\n  return [...roleDef.requiredContext];\n}\n\nexport default requiredContext;\n"
  },
  {
    "path": "lib/commons/aria/required-owned.js",
    "content": "import standards from '../../standards';\n\n/**\n * Get the required owned (children) roles for a given role\n * @method requiredOwned\n * @memberof axe.commons.aria\n * @instance\n * @param {String} role The role to check\n * @return {Mixed} Either an Array of required owned elements or `null` if there are none\n */\nfunction requiredOwned(role) {\n  const roleDef = standards.ariaRoles[role];\n\n  if (!roleDef || !Array.isArray(roleDef.requiredOwned)) {\n    return null;\n  }\n\n  return [...roleDef.requiredOwned];\n}\n\nexport default requiredOwned;\n"
  },
  {
    "path": "lib/commons/aria/validate-attr-value.js",
    "content": "import standards from '../../standards';\nimport getRootNode from '../dom/get-root-node';\nimport idrefs from '../dom/idrefs';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\nimport { tokenList, getNodeFromTree } from '../../core/utils';\n\n/**\n * Validate the value of an ARIA attribute\n * @method validateAttrValue\n * @memberof axe.commons.aria\n * @instance\n * @param  {HTMLElement} node The element to check\n * @param  {String} attr The name of the attribute\n * @return {Boolean}\n */\nfunction validateAttrValue(vNode, attr) {\n  vNode = vNode instanceof AbstractVirtuaNode ? vNode : getNodeFromTree(vNode);\n\n  let matches;\n  let list;\n  const value = vNode.attr(attr);\n  const attrInfo = standards.ariaAttrs[attr];\n\n  if (!attrInfo) {\n    return true;\n  }\n  if (attrInfo.allowEmpty && (!value || value.trim() === '')) {\n    return true;\n  }\n\n  switch (attrInfo.type) {\n    case 'boolean':\n      return ['true', 'false'].includes(value.toLowerCase());\n\n    case 'nmtoken':\n      return (\n        typeof value === 'string' &&\n        attrInfo.values.includes(value.toLowerCase())\n      );\n\n    case 'nmtokens':\n      list = tokenList(value);\n      // Check if any value isn't in the list of values\n      return list.reduce((result, token) => {\n        return result && attrInfo.values.includes(token);\n        // Initial state, fail if the list is empty\n      }, list.length !== 0);\n\n    case 'idref':\n      try {\n        const doc = getRootNode(vNode.actualNode);\n        return !!(value && doc.getElementById(value));\n      } catch {\n        throw new TypeError('Cannot resolve id references for partial DOM');\n      }\n\n    case 'idrefs':\n      return idrefs(vNode, attr).some(node => !!node);\n\n    case 'string':\n      // Not allowed empty except with allowEmpty: true\n      return value.trim() !== '';\n\n    case 'decimal':\n      matches = value.match(/^[-+]?([0-9]*)\\.?([0-9]*)$/);\n      return !!(matches && (matches[1] || matches[2]));\n\n    case 'int':\n      const minValue =\n        typeof attrInfo.minValue !== 'undefined'\n          ? attrInfo.minValue\n          : -Infinity;\n      return /^[-+]?[0-9]+$/.test(value) && parseInt(value) >= minValue;\n  }\n}\n\nexport default validateAttrValue;\n"
  },
  {
    "path": "lib/commons/aria/validate-attr.js",
    "content": "import standards from '../../standards';\n\n/**\n * Check if an aria- attribute name is valid\n * @method validateAttr\n * @memberof axe.commons.aria\n * @instance\n * @param  {String} att The attribute name\n * @return {Boolean}\n */\nfunction validateAttr(att) {\n  const attrDefinition = standards.ariaAttrs[att];\n  return !!attrDefinition;\n}\n\nexport default validateAttr;\n"
  },
  {
    "path": "lib/commons/color/center-point-of-rect.js",
    "content": "/**\n * Get coordinates for an element's client rects or bounding client rect\n *\n * @method centerPointOfRect\n * @memberof axe.commons.color\n * @param {DOMRect} rect\n * @deprecated\n * @returns {Object | undefined}\n */\nfunction centerPointOfRect(rect) {\n  if (rect.left > window.innerWidth) {\n    return undefined;\n  }\n\n  if (rect.top > window.innerHeight) {\n    return undefined;\n  }\n\n  const x = Math.min(\n    Math.ceil(rect.left + rect.width / 2),\n    window.innerWidth - 1\n  );\n  const y = Math.min(\n    Math.ceil(rect.top + rect.height / 2),\n    window.innerHeight - 1\n  );\n\n  return { x, y };\n}\n\nexport default centerPointOfRect;\n"
  },
  {
    "path": "lib/commons/color/color.js",
    "content": "import { Colorjs, ArrayFrom } from '../../core/imports';\nimport incompleteData from './incomplete-data';\n\nconst hexRegex = /^#[0-9a-f]{3,8}$/i;\nconst hslRegex = /hsl\\(\\s*([-\\d.]+)(rad|turn)/;\n\n/**\n * @class Color\n * @memberof axe.commons.color\n * @param {number} red\n * @param {number} green\n * @param {number} blue\n * @param {number} alpha\n */\nexport default class Color {\n  // color channel values typically in the range of 0-1 (can go below or above)\n  #r;\n  #g;\n  #b;\n  // color component values resolved to the sRGB color space (0-255)\n  #red;\n  #green;\n  #blue;\n\n  constructor(red, green, blue, alpha = 1) {\n    if (red instanceof Color) {\n      // preserve out of gamut values\n      const { r, g, b } = red;\n      this.r = r;\n      this.g = g;\n      this.b = b;\n      this.alpha = red.alpha;\n      return;\n    }\n\n    /** @type {number} */\n    this.red = red;\n\n    /** @type {number} */\n    this.green = green;\n\n    /** @type {number} */\n    this.blue = blue;\n\n    /** @type {number} */\n    this.alpha = alpha;\n  }\n\n  get r() {\n    return this.#r;\n  }\n\n  set r(value) {\n    this.#r = value;\n    this.#red = Math.round(clamp(value, 0, 1) * 255);\n  }\n\n  get g() {\n    return this.#g;\n  }\n\n  set g(value) {\n    this.#g = value;\n    this.#green = Math.round(clamp(value, 0, 1) * 255);\n  }\n\n  get b() {\n    return this.#b;\n  }\n\n  set b(value) {\n    this.#b = value;\n    this.#blue = Math.round(clamp(value, 0, 1) * 255);\n  }\n\n  get red() {\n    return this.#red;\n  }\n\n  set red(value) {\n    this.#r = value / 255;\n    this.#red = clamp(value, 0, 255);\n  }\n\n  get green() {\n    return this.#green;\n  }\n\n  set green(value) {\n    this.#g = value / 255;\n    this.#green = clamp(value, 0, 255);\n  }\n\n  get blue() {\n    return this.#blue;\n  }\n\n  set blue(value) {\n    this.#b = value / 255;\n    this.#blue = clamp(value, 0, 255);\n  }\n\n  /**\n   * Provide the hex string value for the color\n   * @method toHexString\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @return {string}\n   */\n  toHexString() {\n    const redString = Math.round(this.red).toString(16);\n    const greenString = Math.round(this.green).toString(16);\n    const blueString = Math.round(this.blue).toString(16);\n    return (\n      '#' +\n      (this.red > 15.5 ? redString : '0' + redString) +\n      (this.green > 15.5 ? greenString : '0' + greenString) +\n      (this.blue > 15.5 ? blueString : '0' + blueString)\n    );\n  }\n\n  toJSON() {\n    const { red, green, blue, alpha } = this;\n    return { red, green, blue, alpha };\n  }\n\n  /**\n   * Parse any valid color string and assign its values to \"this\"\n   * @method parseString\n   * @memberof axe.commons.color.Color\n   * @instance\n   */\n  parseString(colorString) {\n    // Colorjs <v0.5.0 does not support rad or turn angle values\n    // @see https://github.com/LeaVerou/color.js/issues/311\n    colorString = colorString.replace(hslRegex, (match, angle, unit) => {\n      const value = angle + unit;\n\n      switch (unit) {\n        case 'rad':\n          return match.replace(value, radToDeg(angle));\n        case 'turn':\n          return match.replace(value, turnToDeg(angle));\n      }\n    });\n\n    try {\n      // revert prototype.js override of Array.from\n      // in order to get color-contrast working\n      // @see https://github.com/dequelabs/axe-core/issues/4428\n      let prototypeArrayFrom;\n      if ('Prototype' in window && 'Version' in window.Prototype) {\n        prototypeArrayFrom = Array.from;\n        Array.from = ArrayFrom;\n      }\n\n      // srgb values are between 0 and 1\n      const color = new Colorjs(colorString)\n        .toGamut({\n          space: 'srgb',\n          method: 'clip'\n        })\n        .to('srgb');\n\n      if (prototypeArrayFrom) {\n        Array.from = prototypeArrayFrom;\n        prototypeArrayFrom = null;\n      }\n\n      this.r = color.r;\n      this.g = color.g;\n      this.b = color.b;\n      // color.alpha is a Number object so convert it to a number\n      this.alpha = +color.alpha;\n    } catch {\n      incompleteData.set('colorParse', colorString);\n      throw new Error(`Unable to parse color \"${colorString}\"`);\n    }\n\n    return this;\n  }\n\n  /**\n   * Set the color value based on a CSS RGB/RGBA string\n   * @method parseRgbString\n   * @deprecated\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @param  {string}  rgb  The string value\n   */\n  parseRgbString(colorString) {\n    this.parseString(colorString);\n  }\n\n  /**\n   * Set the color value based on a CSS RGB/RGBA string\n   * @method parseHexString\n   * @deprecated\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @param  {string}  rgb  The string value\n   */\n  parseHexString(colorString) {\n    if (!colorString.match(hexRegex) || [6, 8].includes(colorString.length)) {\n      return;\n    }\n\n    this.parseString(colorString);\n  }\n\n  /**\n   * Set the color value based on a CSS RGB/RGBA string\n   * @method parseColorFnString\n   * @deprecated\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @param  {string}  rgb  The string value\n   */\n  parseColorFnString(colorString) {\n    this.parseString(colorString);\n  }\n\n  /**\n   * Get the relative luminance value\n   * using algorithm from http://www.w3.org/WAI/GL/wiki/Relative_luminance\n   * @method getRelativeLuminance\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @return {number} The luminance value, ranges from 0 to 1\n   */\n  getRelativeLuminance() {\n    const { r: rSRGB, g: gSRGB, b: bSRGB } = this;\n\n    const r =\n      rSRGB <= 0.04045 ? rSRGB / 12.92 : Math.pow((rSRGB + 0.055) / 1.055, 2.4);\n    const g =\n      gSRGB <= 0.04045 ? gSRGB / 12.92 : Math.pow((gSRGB + 0.055) / 1.055, 2.4);\n    const b =\n      bSRGB <= 0.04045 ? bSRGB / 12.92 : Math.pow((bSRGB + 0.055) / 1.055, 2.4);\n\n    return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n  }\n\n  /**\n   * Add a value to the color channels\n   * @private\n   * @param  {number}  value  The value to add\n   * @return {Color} A new color instance\n   */\n  #add(value) {\n    const C = new Color(this);\n    C.r += value;\n    C.g += value;\n    C.b += value;\n    return C;\n  }\n\n  /**\n   * Get the luminosity of a color\n   * using algorithm from https://www.w3.org/TR/compositing-1/#blendingnonseparable\n   * @method getLuminosity\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @return {number} The luminosity of the color\n   */\n  getLuminosity() {\n    return 0.3 * this.r + 0.59 * this.g + 0.11 * this.b;\n  }\n\n  /**\n   * Set the luminosity of a color\n   * using algorithm from https://www.w3.org/TR/compositing-1/#blendingnonseparable\n   * @method setLuminosity\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @param  {number}  L  The luminosity\n   * @return {Color} A new color instance\n   */\n  setLuminosity(L) {\n    const d = L - this.getLuminosity();\n    return this.#add(d).clip();\n  }\n\n  /**\n   * Get the saturation of a color\n   * using algorithm from https://www.w3.org/TR/compositing-1/#blendingnonseparable\n   * @method getSaturation\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @return {number} The saturation of the color\n   */\n  getSaturation() {\n    return Math.max(this.r, this.g, this.b) - Math.min(this.r, this.g, this.b);\n  }\n\n  /**\n   * Set the saturation of a color\n   * using algorithm from https://www.w3.org/TR/compositing-1/#blendingnonseparable\n   * @method setSaturation\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @param  {number}  s  The saturation\n   * @return {Color} A new color instance\n   */\n  setSaturation(s) {\n    const C = new Color(this);\n    const colorEntires = [\n      { name: 'r', value: C.r },\n      { name: 'g', value: C.g },\n      { name: 'b', value: C.b }\n    ];\n\n    // find the min, mid, and max values of the color components\n    const [Cmin, Cmid, Cmax] = colorEntires.sort((a, b) => {\n      return a.value - b.value;\n    });\n\n    if (Cmax.value > Cmin.value) {\n      Cmid.value = ((Cmid.value - Cmin.value) * s) / (Cmax.value - Cmin.value);\n      Cmax.value = s;\n    } else {\n      Cmid.value = Cmax.value = 0;\n    }\n\n    Cmin.value = 0;\n\n    C[Cmax.name] = Cmax.value;\n    C[Cmin.name] = Cmin.value;\n    C[Cmid.name] = Cmid.value;\n    return C;\n  }\n\n  /**\n   * Clip the color between RGB 0-1 accounting for the luminosity of the color. Color must be normalized before calling.\n   * using algorithm from https://www.w3.org/TR/compositing-1/#blendingnonseparable\n   * @method clip\n   * @memberof axe.commons.color.Color\n   * @instance\n   * @return {Color} A new color instance clipped between 0-1\n   */\n  clip() {\n    const C = new Color(this);\n    const L = C.getLuminosity();\n    const n = Math.min(C.r, C.g, C.b);\n    const x = Math.max(C.r, C.g, C.b);\n\n    if (n < 0) {\n      C.r = L + ((C.r - L) * L) / (L - n);\n      C.g = L + ((C.g - L) * L) / (L - n);\n      C.b = L + ((C.b - L) * L) / (L - n);\n    }\n\n    if (x > 1) {\n      C.r = L + ((C.r - L) * (1 - L)) / (x - L);\n      C.g = L + ((C.g - L) * (1 - L)) / (x - L);\n      C.b = L + ((C.b - L) * (1 - L)) / (x - L);\n    }\n\n    return C;\n  }\n}\n\n// clamp a value between two numbers (inclusive)\nfunction clamp(value, min, max) {\n  return Math.min(Math.max(min, value), max);\n}\n\n// convert radians to degrees\nfunction radToDeg(rad) {\n  return (rad * 180) / Math.PI;\n}\n\n// convert turn to degrees\nfunction turnToDeg(turn) {\n  return turn * 360;\n}\n"
  },
  {
    "path": "lib/commons/color/element-has-image.js",
    "content": "import incompleteData from './incomplete-data';\n\n/**\n * Reports if an element has a background image or gradient\n *\n * @method elementHasImage\n * @memberof axe.commons.color\n * @private\n * @param {Element} elm\n * @param {Object|null} style\n * @return {Boolean}\n */\nfunction elementHasImage(elm, style) {\n  const graphicNodes = ['IMG', 'CANVAS', 'OBJECT', 'IFRAME', 'VIDEO', 'SVG'];\n  const nodeName = elm.nodeName.toUpperCase();\n\n  if (graphicNodes.includes(nodeName)) {\n    incompleteData.set('bgColor', 'imgNode');\n    return true;\n  }\n\n  style = style || window.getComputedStyle(elm);\n\n  const bgImageStyle = style.getPropertyValue('background-image');\n  const hasBgImage = bgImageStyle !== 'none';\n\n  if (hasBgImage) {\n    const hasGradient = /gradient/.test(bgImageStyle);\n    incompleteData.set('bgColor', hasGradient ? 'bgGradient' : 'bgImage');\n  }\n\n  return hasBgImage;\n}\n\nexport default elementHasImage;\n"
  },
  {
    "path": "lib/commons/color/element-is-distinct.js",
    "content": "import Color from './color';\n\n/**\n * Creates a string array of fonts for given CSSStyleDeclaration object\n * @private\n * @param {Object} style CSSStyleDeclaration object\n * @return {Array}\n */\nfunction _getFonts(style) {\n  return style\n    .getPropertyValue('font-family')\n    .split(/[,;]/g)\n    .map(font => {\n      return font.trim().toLowerCase();\n    });\n}\n\n/**\n * Determine if the text content of two nodes is styled in a way that they can be distinguished without relying on color\n * @method elementIsDistinct\n * @memberof axe.commons.color\n * @instance\n * @param  {HTMLElement} node The element to check\n * @param  {HTMLElement} ancestorNode The ancestor node element to check\n * @return {Boolean}\n */\nfunction elementIsDistinct(node, ancestorNode) {\n  const nodeStyle = window.getComputedStyle(node);\n\n  // Check if the link has a background\n  if (nodeStyle.getPropertyValue('background-image') !== 'none') {\n    return true;\n  }\n\n  // Check if the link has a border or outline\n  const hasBorder = ['border-bottom', 'border-top', 'outline'].reduce(\n    (result, edge) => {\n      const borderClr = new Color();\n      borderClr.parseString(nodeStyle.getPropertyValue(edge + '-color'));\n\n      // Check if a border/outline was specified\n      return (\n        result ||\n        // or if the current border edge / outline\n        (nodeStyle.getPropertyValue(edge + '-style') !== 'none' &&\n          parseFloat(nodeStyle.getPropertyValue(edge + '-width')) > 0 &&\n          borderClr.alpha !== 0)\n      );\n    },\n    false\n  );\n\n  if (hasBorder) {\n    return true;\n  }\n\n  const parentStyle = window.getComputedStyle(ancestorNode);\n  // Compare fonts\n  if (_getFonts(nodeStyle)[0] !== _getFonts(parentStyle)[0]) {\n    return true;\n  }\n\n  let hasStyle = [\n    'text-decoration-line',\n    'text-decoration-style',\n    'font-weight',\n    'font-style',\n    'font-size'\n  ].reduce((result, cssProp) => {\n    return (\n      result ||\n      nodeStyle.getPropertyValue(cssProp) !==\n        parentStyle.getPropertyValue(cssProp)\n    );\n  }, false);\n\n  const tDec = nodeStyle.getPropertyValue('text-decoration');\n  if (tDec.split(' ').length < 3) {\n    // old style CSS text decoration\n    hasStyle =\n      hasStyle || tDec !== parentStyle.getPropertyValue('text-decoration');\n  }\n\n  return hasStyle;\n}\n\nexport default elementIsDistinct;\n"
  },
  {
    "path": "lib/commons/color/filtered-rect-stack.js",
    "content": "import getRectStack from './get-rect-stack';\nimport incompleteData from './incomplete-data';\n\n/**\n * Get filtered stack of block and inline elements, excluding line breaks\n * @deprecated use color.getBackgroundStack instead\n * @method filteredRectStack\n * @memberof axe.commons.color\n * @param {Element} elm\n * @return {Array}\n */\nfunction filteredRectStack(elm) {\n  const rectStack = getRectStack(elm);\n\n  if (rectStack && rectStack.length === 1) {\n    return rectStack[0];\n  }\n\n  if (rectStack && rectStack.length > 1) {\n    const boundingStack = rectStack.shift();\n    let isSame;\n\n    // iterating over arrays of DOMRects\n    rectStack.forEach((rectList, index) => {\n      if (index === 0) {\n        return;\n      }\n      // if the stacks are the same, use the first one. otherwise, return null.\n      const rectA = rectStack[index - 1],\n        rectB = rectStack[index];\n\n      // if elements in clientRects are the same\n      // or the boundingClientRect contains the differing element, pass it\n      isSame =\n        rectA.every(\n          (element, elementIndex) => element === rectB[elementIndex]\n        ) || boundingStack.includes(elm);\n    });\n    if (!isSame) {\n      incompleteData.set('bgColor', 'elmPartiallyObscuring');\n      return null;\n    }\n    // pass the first stack if it wasn't partially covered\n    return rectStack[0];\n  }\n\n  // rect outside of viewport\n  incompleteData.set('bgColor', 'outsideViewport');\n  return null;\n}\n\nexport default filteredRectStack;\n"
  },
  {
    "path": "lib/commons/color/flatten-colors.js",
    "content": "import Color from './color';\n\n// @see https://www.w3.org/TR/compositing-1/#blendingnonseparable\nconst nonSeparableBlendModes = ['hue', 'saturation', 'color', 'luminosity'];\n\n// how to combine background and foreground colors together when using\n// the CSS property `mix-blend-mode`. Defaults to `normal`\n// @see https://www.w3.org/TR/compositing-1/#blendingseparable\nconst blendFunctions = {\n  normal(Cb, Cs) {\n    return Cs;\n  },\n  multiply(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingmultiply\n    return Cs * Cb;\n  },\n  screen(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingscreen\n    return Cb + Cs - Cb * Cs;\n  },\n  overlay(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingoverlay\n    return this['hard-light'](Cs, Cb);\n  },\n  darken(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingdarken\n    return Math.min(Cb, Cs);\n  },\n  lighten(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendinglighten\n    return Math.max(Cb, Cs);\n  },\n  'color-dodge'(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingcolordodge\n    return Cb === 0 ? 0 : Cs === 1 ? 1 : Math.min(1, Cb / (1 - Cs));\n  },\n  'color-burn'(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingcolorburn\n    return Cb === 1 ? 1 : Cs === 0 ? 0 : 1 - Math.min(1, (1 - Cb) / Cs);\n  },\n  'hard-light'(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendinghardlight\n\n    return Cs <= 0.5 ? this.multiply(Cb, 2 * Cs) : this.screen(Cb, 2 * Cs - 1);\n  },\n  'soft-light'(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingsoftlight\n    if (Cs <= 0.5) {\n      return Cb - (1 - 2 * Cs) * Cb * (1 - Cb);\n    } else {\n      const D = Cb <= 0.25 ? ((16 * Cb - 12) * Cb + 4) * Cb : Math.sqrt(Cb);\n      return Cb + (2 * Cs - 1) * (D - Cb);\n    }\n  },\n  difference(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingdifference\n    return Math.abs(Cb - Cs);\n  },\n  exclusion(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingexclusion\n    return Cb + Cs - 2 * Cb * Cs;\n  },\n\n  // non-separate color function take the entire color object\n  // and not individual color components (red, green, blue)\n  hue(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendinghue\n    return Cs.setSaturation(Cb.getSaturation()).setLuminosity(\n      Cb.getLuminosity()\n    );\n  },\n  saturation(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingsaturation\n    return Cb.setSaturation(Cs.getSaturation()).setLuminosity(\n      Cb.getLuminosity()\n    );\n  },\n  color(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingcolor\n    return Cs.setLuminosity(Cb.getLuminosity());\n  },\n  luminosity(Cb, Cs) {\n    // @see https://www.w3.org/TR/compositing-1/#blendingluminosity\n    return Cb.setLuminosity(Cs.getLuminosity());\n  }\n};\n\n/**\n * Combine the two given color according to alpha blending.\n * @method flattenColors\n * @memberof axe.commons.color.Color\n * @instance\n * @param {Color} sourceColor Foreground color\n * @param {Color} backdrop Background color\n * @return {Color} Blended color\n */\nexport default function flattenColors(\n  sourceColor,\n  backdrop,\n  blendMode = 'normal'\n) {\n  const blendingResult = blend(backdrop, sourceColor, blendMode);\n\n  // foreground is the \"source\" color and background is the \"backdrop\" color\n  const r = simpleAlphaCompositing(\n    sourceColor.red,\n    sourceColor.alpha,\n    backdrop.red,\n    backdrop.alpha,\n    // we don't want to round the blended value\n    blendingResult.r * 255\n  );\n  const g = simpleAlphaCompositing(\n    sourceColor.green,\n    sourceColor.alpha,\n    backdrop.green,\n    backdrop.alpha,\n    blendingResult.g * 255\n  );\n  const b = simpleAlphaCompositing(\n    sourceColor.blue,\n    sourceColor.alpha,\n    backdrop.blue,\n    backdrop.alpha,\n    blendingResult.b * 255\n  );\n\n  // formula: αo = αs + αb x (1 - αs)\n  // clamp alpha between 0 and 1\n  const αo = clamp(\n    sourceColor.alpha + backdrop.alpha * (1 - sourceColor.alpha),\n    0,\n    1\n  );\n  if (αo === 0) {\n    return new Color(r, g, b, αo);\n  }\n\n  // simple alpha compositing gives premultiplied values, but our Color\n  // constructor takes unpremultiplied values. So we need to divide the\n  // final color values by the final alpha\n  // formula: Co = co / αo\n  // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing\n  // @see https://github.com/w3c/fxtf-drafts/issues/440#issuecomment-956418953\n  //\n  // RGB color space doesn't have decimal values so we will follow what browsers do and round\n  // e.g. rgb(255.2, 127.5, 127.8) === rgb(255, 128, 128)\n  const Cr = Math.round(r / αo);\n  const Cg = Math.round(g / αo);\n  const Cb = Math.round(b / αo);\n\n  return new Color(Cr, Cg, Cb, αo);\n}\n\n// Simple Alpha Compositing written as non-premultiplied.\n// formula: Rrgb × Ra = Srgb × Sa + Drgb × Da × (1 − Sa)\n// Cs: the source color\n// αs: the source alpha\n// Cb: the backdrop color\n// αb: the backdrop alpha\n// @see https://www.w3.org/TR/compositing-1/#simplealphacompositing\n// @see https://www.w3.org/TR/compositing-1/#blending\n// @see https://ciechanow.ski/alpha-compositing/\nfunction simpleAlphaCompositing(Cs, αs, Cb, αb, blendingResult) {\n  return αs * (1 - αb) * Cs + αs * αb * blendingResult + (1 - αs) * αb * Cb;\n}\n\n// clamp a value between two numbers (inclusive)\nfunction clamp(value, min, max) {\n  return Math.min(Math.max(min, value), max);\n}\n\nfunction blend(Cb, Cs, blendMode) {\n  if (nonSeparableBlendModes.includes(blendMode)) {\n    return blendFunctions[blendMode](Cb, Cs);\n  }\n\n  const C = new Color();\n  ['r', 'g', 'b'].forEach(channel => {\n    C[channel] = blendFunctions[blendMode](Cb[channel], Cs[channel]);\n  });\n  return C;\n}\n"
  },
  {
    "path": "lib/commons/color/flatten-shadow-colors.js",
    "content": "import Color from './color';\n\n/**\n * Combine the two given shadow colors according to alpha blending.\n * @deprecated Use axe.commons.color.flattenColors instead\n * @method flattenColors\n * @memberof axe.commons.color.Color\n * @instance\n * @param {Color} fgColor Foreground color\n * @param {Color} bgColor Background color\n * @return {Color} Blended color\n */\nexport default function flattenShadowColors(fgColor, bgColor) {\n  const alpha = fgColor.alpha;\n  const r = (1 - alpha) * bgColor.red + alpha * fgColor.red;\n  const g = (1 - alpha) * bgColor.green + alpha * fgColor.green;\n  const b = (1 - alpha) * bgColor.blue + alpha * fgColor.blue;\n  const a = fgColor.alpha + bgColor.alpha * (1 - fgColor.alpha);\n\n  return new Color(r, g, b, a);\n}\n"
  },
  {
    "path": "lib/commons/color/get-background-color.js",
    "content": "import incompleteData from './incomplete-data';\nimport getBackgroundStack from './get-background-stack';\nimport getOwnBackgroundColor from './get-own-background-color';\nimport elementHasImage from './element-has-image';\nimport Color from './color';\nimport flattenColors from './flatten-colors';\nimport flattenShadowColors from './flatten-shadow-colors';\nimport getTextShadowColors from './get-text-shadow-colors';\nimport getVisibleChildTextRects from '../dom/get-visible-child-text-rects';\nimport { getNodeFromTree } from '../../core/utils';\nimport { getStackingContext, stackingContextToColor } from './stacking-context';\n\n/**\n * Returns background color for element\n * Uses getBackgroundStack() to get all elements rendered underneath the current element,\n * to help determine the composite background color.\n *\n * @method getBackgroundColor\n * @memberof axe.commons.color\n * @param  {Element} elm Element to determine background color\n * @param  {Array}   [bgElms=[]] elements to inspect\n * @param  {Number}  shadowOutlineEmMax Thickness of `text-shadow` at which it becomes a background color\n * @returns {Color}\n */\nexport default function getBackgroundColor(\n  elm,\n  bgElms = [],\n  shadowOutlineEmMax = 0.1\n) {\n  const vNode = getNodeFromTree(elm);\n  const bgColorCache = vNode._cache.getBackgroundColor;\n\n  if (bgColorCache) {\n    bgElms.push(...bgColorCache.bgElms);\n    incompleteData.set('bgColor', bgColorCache.incompleteData);\n    return bgColorCache.bgColor;\n  }\n\n  const bgColor = _getBackgroundColor(elm, bgElms, shadowOutlineEmMax);\n\n  vNode._cache.getBackgroundColor = {\n    bgColor,\n    bgElms,\n    incompleteData: incompleteData.get('bgColor')\n  };\n\n  return bgColor;\n}\n\nfunction _getBackgroundColor(elm, bgElms, shadowOutlineEmMax) {\n  const elmStack = getBackgroundStack(elm);\n  if (!elmStack) {\n    return null;\n  }\n\n  const textRects = getVisibleChildTextRects(elm);\n  let bgColors =\n    getTextShadowColors(elm, {\n      minRatio: shadowOutlineEmMax,\n      ignoreEdgeCount: true\n    }) ?? []; // Empty object shouldn't be necessary. Just being safe.\n  if (bgColors.length) {\n    bgColors = [{ color: bgColors.reduce(flattenShadowColors) }];\n  }\n\n  // Search the stack until we have an alpha === 1 background\n  for (let i = 0; i < elmStack.length; i++) {\n    const bgElm = elmStack[i];\n    const bgElmStyle = window.getComputedStyle(bgElm);\n\n    if (elementHasImage(bgElm, bgElmStyle)) {\n      bgElms.push(bgElm);\n\n      return null;\n    }\n\n    // Get the background color\n    let bgColor;\n    try {\n      bgColor = getOwnBackgroundColor(bgElmStyle);\n      if (bgColor.alpha === 0) {\n        continue;\n      }\n    } catch (error) {\n      if (error && incompleteData.get('colorParse')) {\n        return null;\n      }\n      throw error;\n    }\n\n    // abort if a node is partially obscured and obscuring element has a background\n    if (\n      bgElmStyle.getPropertyValue('display') !== 'inline' &&\n      !fullyEncompasses(bgElm, textRects)\n    ) {\n      bgElms.push(bgElm);\n      incompleteData.set('bgColor', 'elmPartiallyObscured');\n\n      return null;\n    }\n\n    // store elements contributing to the bg color.\n    bgElms.push(bgElm);\n\n    // Exit if the background is opaque\n    if (bgColor.alpha === 1) {\n      break;\n    }\n  }\n\n  const stackingContext = getStackingContext(elm, elmStack);\n  bgColors = stackingContext.map(stackingContextToColor).concat(bgColors);\n\n  const pageBgs = getPageBackgroundColors(\n    elm,\n    elmStack.includes(document.body)\n  );\n  bgColors.unshift(...pageBgs);\n\n  // default to white if bgColors is empty\n  if (bgColors.length === 0) {\n    return new Color(255, 255, 255, 1);\n  }\n  // Mix the colors together. Colors must be mixed in bottom up\n  // order (background to foreground order) to produce the correct\n  // result.\n  // @see https://github.com/dequelabs/axe-core/issues/2924\n  const blendedColor = bgColors.reduce((bgColor, fgColor) => {\n    return flattenColors(\n      fgColor.color,\n      bgColor.color instanceof Color ? bgColor.color : bgColor,\n      fgColor.blendMode\n    );\n  });\n\n  // default page background is white which must be mixed last\n  // @see https://www.w3.org/TR/compositing-1/#pagebackdrop\n  return flattenColors(\n    blendedColor.color instanceof Color ? blendedColor.color : blendedColor,\n    new Color(255, 255, 255, 1)\n  );\n}\n\n/**\n * Checks whether a node fully encompasses a set of rects.\n * @private\n * @param {Element} node\n * @param {NodeRect[]} rects\n * @return {Boolean}\n */\nfunction fullyEncompasses(node, rects) {\n  rects = Array.isArray(rects) ? rects : [rects];\n\n  const nodeRect = node.getBoundingClientRect();\n  let { right, bottom } = nodeRect;\n  const style = window.getComputedStyle(node);\n  const overflow = style.getPropertyValue('overflow');\n  const paddingLeft = parseInt(style.getPropertyValue('padding-left'), 10);\n  const paddingRight = parseInt(style.getPropertyValue('padding-right'), 10);\n  const paddingTop = parseInt(style.getPropertyValue('padding-top'), 10);\n  const paddingBottom = parseInt(style.getPropertyValue('padding-bottom'), 10);\n\n  if (\n    ['scroll', 'auto'].includes(overflow) ||\n    node instanceof window.HTMLHtmlElement\n  ) {\n    right = nodeRect.left + node.scrollWidth + paddingLeft + paddingRight;\n    bottom = nodeRect.top + node.scrollHeight + paddingTop + paddingBottom;\n  }\n\n  return rects.every(rect => {\n    return (\n      rect.top >= nodeRect.top &&\n      rect.bottom <= bottom &&\n      rect.left >= nodeRect.left &&\n      rect.right <= right\n    );\n  });\n}\n\nfunction normalizeBlendMode(blendmode) {\n  return !!blendmode ? blendmode : undefined;\n}\n\n/**\n * Get the page background color.\n * @private\n * @param {Element} elm\n * @param {Boolean} stackContainsBody\n * @return {Colors[]}\n */\nfunction getPageBackgroundColors(elm, stackContainsBody) {\n  const pageColors = [];\n\n  // Body can sometimes appear out of order in the stack:\n  //   1) Body is not the first element due to negative z-index elements\n  //   2) Elements are positioned outside of body's rect coordinates\n  //      (see https://github.com/dequelabs/axe-core/issues/1456)\n  // In those instances we need to determine if we should use the\n  // body background or the html background color\n  if (!stackContainsBody) {\n    // if the html element defines a bgColor and body defines a\n    // bgColor but body's height is not the full viewport, then the\n    // html bgColor fills the full viewport and body bgColor only\n    // fills to its size. however, if the html element does not\n    // define a bgColor, then the body bgColor fills the full\n    // viewport. so if the body wasn't added to the elmStack, we\n    // need to know which bgColor to get (html or body)\n    const html = document.documentElement;\n    const body = document.body;\n    const htmlStyle = window.getComputedStyle(html);\n    const bodyStyle = window.getComputedStyle(body);\n    const htmlBgColor = getOwnBackgroundColor(htmlStyle);\n    const bodyBgColor = getOwnBackgroundColor(bodyStyle);\n    const bodyBgColorApplies =\n      bodyBgColor.alpha !== 0 &&\n      fullyEncompasses(body, elm.getBoundingClientRect());\n    if (\n      (bodyBgColor.alpha !== 0 && htmlBgColor.alpha === 0) ||\n      (bodyBgColorApplies && bodyBgColor.alpha !== 1)\n    ) {\n      pageColors.unshift({\n        color: bodyBgColor,\n        blendMode: normalizeBlendMode(\n          bodyStyle.getPropertyValue('mix-blend-mode')\n        )\n      });\n    }\n\n    if (\n      htmlBgColor.alpha !== 0 &&\n      (!bodyBgColorApplies || (bodyBgColorApplies && bodyBgColor.alpha !== 1))\n    ) {\n      pageColors.unshift({\n        color: htmlBgColor,\n        blendMode: normalizeBlendMode(\n          htmlStyle.getPropertyValue('mix-blend-mode')\n        )\n      });\n    }\n  }\n\n  return pageColors;\n}\n"
  },
  {
    "path": "lib/commons/color/get-background-stack.js",
    "content": "import getTextElementStack from '../dom/get-text-element-stack';\nimport elementHasImage from './element-has-image';\nimport getOwnBackgroundColor from './get-own-background-color';\nimport incompleteData from './incomplete-data';\nimport reduceToElementsBelowFloating from '../dom/reduce-to-elements-below-floating';\n\n/**\n * Get all elements rendered underneath the current element,\n * In the order they are displayed (front to back)\n *\n * @method getBackgroundStack\n * @memberof axe.commons.color\n * @param {Element} node\n * @return {Array}\n */\nexport default function getBackgroundStack(node) {\n  const stacks = getTextElementStack(node).map(stack => {\n    stack = reduceToElementsBelowFloating(stack, node);\n    stack = sortPageBackground(stack);\n    return stack;\n  });\n\n  for (let index = 0; index < stacks.length; index++) {\n    const stack = stacks[index];\n\n    if (stack[0] !== node) {\n      incompleteData.set('bgColor', 'bgOverlap');\n      return null;\n    }\n\n    // verify stacks are the same\n    if (index !== 0 && !shallowArraysEqual(stack, stacks[0])) {\n      incompleteData.set('bgColor', 'elmPartiallyObscuring');\n      return null;\n    }\n  }\n\n  return stacks[0] || null;\n}\n\n/**\n * Look at document and body elements for relevant background information\n * @method sortPageBackground\n * @private\n * @param {Array} elmStack\n * @returns {Array}\n */\nfunction sortPageBackground(elmStack) {\n  const bodyIndex = elmStack.indexOf(document.body);\n  const bgNodes = elmStack;\n\n  // body can sometimes appear out of order in the stack when it\n  // is not the first element due to negative z-index elements.\n  // however, we only want to change order if the html element\n  // does not define a background color (ya, it's a strange edge\n  // case. it turns out that if html defines a background it treats\n  // body as a normal element, but if it doesn't then body is treated\n  // as the \"html\" element)\n  const htmlBgColor = getOwnBackgroundColor(\n    window.getComputedStyle(document.documentElement)\n  );\n  if (\n    bodyIndex > 1 &&\n    htmlBgColor.alpha === 0 &&\n    !elementHasImage(document.documentElement)\n  ) {\n    // Only remove document.body if it was originally contained within the element stack\n    if (bodyIndex > 1) {\n      bgNodes.splice(bodyIndex, 1);\n\n      // Put the body background as the lowest element\n      bgNodes.push(document.body);\n    }\n\n    const htmlIndex = bgNodes.indexOf(document.documentElement);\n    if (htmlIndex > 0) {\n      bgNodes.splice(htmlIndex, 1);\n\n      // Put the html background as the lowest element\n      bgNodes.push(document.documentElement);\n    }\n  }\n  return bgNodes;\n}\n\n/**\n * Check to see if two arrays are equal\n * @see https://stackoverflow.com/a/16436975/2124254\n */\nfunction shallowArraysEqual(a, b) {\n  if (a === b) {\n    return true;\n  }\n  if (a === null || b === null) {\n    return false;\n  }\n  if (a.length !== b.length) {\n    return false;\n  }\n\n  for (let i = 0; i < a.length; ++i) {\n    if (a[i] !== b[i]) {\n      return false;\n    }\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/commons/color/get-contrast.js",
    "content": "import flattenColors from './flatten-colors';\n\n/**\n * Get the contrast of two colors\n * @method getContrast\n * @memberof axe.commons.color.Color\n * @instance\n * @param  {Color}  bgcolor  Background color\n * @param  {Color}  fgcolor  Foreground color\n * @return {number} The contrast ratio\n */\nfunction getContrast(bgColor, fgColor) {\n  if (!fgColor || !bgColor) {\n    return null;\n  }\n\n  if (fgColor.alpha < 1) {\n    fgColor = flattenColors(fgColor, bgColor);\n  }\n\n  const bL = bgColor.getRelativeLuminance();\n  const fL = fgColor.getRelativeLuminance();\n\n  return (Math.max(fL, bL) + 0.05) / (Math.min(fL, bL) + 0.05);\n}\n\nexport default getContrast;\n"
  },
  {
    "path": "lib/commons/color/get-foreground-color.js",
    "content": "import Color from './color';\nimport getBackgroundColor from './get-background-color';\nimport incompleteData from './incomplete-data';\nimport flattenColors from './flatten-colors';\nimport getTextShadowColors from './get-text-shadow-colors';\nimport { getStackingContext, stackingContextToColor } from './stacking-context';\n\n/**\n * Returns the flattened foreground color of an element, or null if it can't be determined because\n * of transparency\n * @method getForegroundColor\n * @memberof axe.commons.color\n * @instance\n * @param {Element} node\n * @param {Boolean} noScroll (default false)\n * @param {Color} bgColor\n * @param {Object} Options\n * @return {Color|null}\n *\n * @deprecated noScroll parameter\n */\nexport default function getForegroundColor(node, _, bgColor, options = {}) {\n  const nodeStyle = window.getComputedStyle(node);\n\n  const colorStack = [\n    // Start with -webkit-text-stroke, it is rendered on top\n    () => getStrokeColor(nodeStyle, options),\n    // Next color / -webkit-text-fill-color\n    () => getTextColor(nodeStyle),\n    // If text is (semi-)transparent shadows are visible through it\n    () => getTextShadowColors(node, { minRatio: 0 })\n  ];\n  let fgColors = [];\n\n  try {\n    for (const colorFn of colorStack) {\n      const color = colorFn();\n      if (!color) {\n        continue;\n      }\n\n      fgColors = fgColors.concat(color);\n\n      if (color.alpha === 1) {\n        break;\n      }\n    }\n  } catch (error) {\n    if (error && incompleteData.get('colorParse')) {\n      return null;\n    }\n    throw error;\n  }\n\n  const fgColor = fgColors.reduce((source, backdrop) => {\n    return flattenColors(source, backdrop);\n  });\n\n  // Lastly blend the background\n  bgColor ??= getBackgroundColor(node, []);\n  if (bgColor === null) {\n    const reason = incompleteData.get('bgColor');\n    incompleteData.set('fgColor', reason);\n    return null;\n  }\n\n  const stackingContexts = getStackingContext(node);\n  const context = findNodeInContexts(stackingContexts, node);\n  return flattenColors(\n    calculateBlendedForegroundColor(fgColor, context, stackingContexts),\n    // default page background\n    new Color(255, 255, 255, 1)\n  );\n}\n\nfunction getTextColor(nodeStyle) {\n  return new Color().parseString(\n    nodeStyle.getPropertyValue('-webkit-text-fill-color') ||\n      nodeStyle.getPropertyValue('color')\n  );\n}\n\nfunction getStrokeColor(nodeStyle, { textStrokeEmMin = 0 }) {\n  const strokeWidth = parseFloat(\n    nodeStyle.getPropertyValue('-webkit-text-stroke-width')\n  );\n  if (strokeWidth === 0) {\n    return null;\n  }\n  const fontSize = nodeStyle.getPropertyValue('font-size');\n  const relativeStrokeWidth = strokeWidth / parseFloat(fontSize);\n  if (isNaN(relativeStrokeWidth) || relativeStrokeWidth < textStrokeEmMin) {\n    return null;\n  }\n\n  const strokeColor = nodeStyle.getPropertyValue('-webkit-text-stroke-color');\n  return new Color().parseString(strokeColor);\n}\n\n/**\n * Blend a foreground color into the background stacking context, taking into account opacity at each step.\n * @param {Color} fgColor\n * @param {Object} context - The nodes stacking context\n * @param {Object[]} stackingContexts - Array of all stacking contexts\n * @return {Color}\n */\nfunction calculateBlendedForegroundColor(fgColor, context, stackingContexts) {\n  while (context) {\n    // find the nearest ancestor that has opacity < 1\n    if (context.opacity === 1 && context.ancestor) {\n      context = context.ancestor;\n      continue;\n    }\n\n    fgColor.alpha *= context.opacity;\n\n    // when blending the foreground color to a background color with opacity,\n    // we ignore the background color of the node itself and instead blend\n    // with the stack behind it\n    let stack = context.ancestor?.descendants || stackingContexts;\n    if (context.opacity !== 1) {\n      stack = stack.slice(0, stack.indexOf(context));\n    }\n\n    const bgColors = stack.map(stackingContextToColor);\n\n    if (!bgColors.length) {\n      context = context.ancestor;\n      continue;\n    }\n\n    const bgColor = bgColors.reduce(\n      (backdrop, source) => {\n        return flattenColors(\n          source.color,\n          backdrop.color instanceof Color ? backdrop.color : backdrop\n        );\n      },\n      {\n        color: new Color(0, 0, 0, 0),\n        blendMode: 'normal'\n      }\n    );\n\n    fgColor = flattenColors(fgColor, bgColor);\n    context = context.ancestor;\n  }\n\n  return fgColor;\n}\n\n/**\n * Find the stacking context that belongs to the passed in node\n * @param {Object} contexts - Array of stacking contexts\n * @param {Element} node\n * @returns {Object}\n */\nfunction findNodeInContexts(contexts, node) {\n  for (const context of contexts) {\n    if (context.vNode?.actualNode === node) {\n      return context;\n    }\n\n    const found = findNodeInContexts(context.descendants, node);\n    if (found) {\n      return found;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/commons/color/get-own-background-color.js",
    "content": "import Color from './color';\n\n/**\n * Returns the non-alpha-blended background color of an element\n *\n * @method getOwnBackgroundColor\n * @memberof axe.commons.color\n *\n * @param {Object} elmStyle style of the element\n * @return {Color}\n */\nfunction getOwnBackgroundColor(elmStyle) {\n  const bgColor = new Color();\n  bgColor.parseString(elmStyle.getPropertyValue('background-color'));\n\n  if (bgColor.alpha !== 0) {\n    const opacity = elmStyle.getPropertyValue('opacity');\n    bgColor.alpha = bgColor.alpha * opacity;\n  }\n\n  return bgColor;\n}\n\nexport default getOwnBackgroundColor;\n"
  },
  {
    "path": "lib/commons/color/get-rect-stack.js",
    "content": "import getElementStack from '../dom/get-element-stack';\nimport getTextElementStack from '../dom/get-text-element-stack';\n\n/**\n * Get relevant stacks of block and inline elements, excluding line breaks\n * @deprecated use color.getBackgroundStack instead\n * @method getRectStack\n * @memberof axe.commons.color\n * @param {Element} elm\n * @return {Array}\n */\nfunction getRectStack(elm) {\n  const boundingStack = getElementStack(elm);\n\n  // Handle inline elements spanning multiple lines to be evaluated\n  const filteredArr = getTextElementStack(elm);\n\n  // If the element does not have multiple rects, like for display:block, return a single stack\n  if (!filteredArr || filteredArr.length <= 1) {\n    return [boundingStack];\n  }\n\n  if (filteredArr.some(stack => stack === undefined)) {\n    // Can be happen when one or more of the rects sits outside the viewport\n    return null;\n  }\n\n  // add bounding client rect stack for comparison later\n  filteredArr.splice(0, 0, boundingStack);\n  return filteredArr;\n}\n\nexport default getRectStack;\n"
  },
  {
    "path": "lib/commons/color/get-stroke-colors-from-shadows.js",
    "content": "import Color from './color';\n\n/** Magic numbers **/\n// Alpha value to use when text shadows are offset between .5px and 1.5px\nconst SHADOW_STROKE_ALPHA = 0.54;\n// Shadows offset by less than this are not visible enough no matter how much you stack them\nconst VISIBLE_SHADOW_MIN_PX = 0.5;\n// Shadows offset by more than this have full opacity\nconst OPAQUE_STROKE_OFFSET_MIN_PX = 1.5;\n\nconst edges = ['top', 'right', 'bottom', 'left'];\n\n/**\n * Work out which color(s) of an array of text shadows form a stroke around the text.\n * @param {Array[]} testShadows Parsed test shadows (see color.parseTestShadow())\n * @param {Object} options (optional)\n * @property {Bool} ignoreEdgeCount Do not return null when if shadows cover 2 or 3 edges, ignore those instead\n * @returns {Array|null} Array of colors or null if text-shadow was too complex to measure\n */\nexport default function getStrokeColorsFromShadows(\n  parsedShadows,\n  { ignoreEdgeCount = false } = {}\n) {\n  const shadowMap = getShadowColorsMap(parsedShadows);\n  const shadowsByColor = Object.entries(shadowMap).map(([colorStr, sides]) => {\n    const edgeCount = edges.filter(side => sides[side].length !== 0).length;\n    return { colorStr, sides, edgeCount };\n  });\n\n  // Bail immediately if any shadow group covers too much of the text to be ignored, but not enough to be tested\n  if (\n    !ignoreEdgeCount &&\n    shadowsByColor.some(({ edgeCount }) => edgeCount > 1 && edgeCount < 4)\n  ) {\n    return null;\n  }\n\n  return shadowsByColor\n    .map(shadowGroupToColor)\n    .filter(shadow => shadow !== null);\n}\n\n/**\n * Create a map of colors to the sides they are on\n */\nfunction getShadowColorsMap(parsedShadows) {\n  const colorMap = {};\n  for (const { colorStr, pixels } of parsedShadows) {\n    colorMap[colorStr] ??= { top: [], right: [], bottom: [], left: [] };\n    const borders = colorMap[colorStr];\n    const [offsetX, offsetY] = pixels;\n\n    if (offsetX > VISIBLE_SHADOW_MIN_PX) {\n      borders.right.push(offsetX);\n    } else if (-offsetX > VISIBLE_SHADOW_MIN_PX) {\n      borders.left.push(-offsetX);\n    }\n    if (offsetY > VISIBLE_SHADOW_MIN_PX) {\n      borders.bottom.push(offsetY);\n    } else if (-offsetY > VISIBLE_SHADOW_MIN_PX) {\n      borders.top.push(-offsetY);\n    }\n  }\n  return colorMap;\n}\n\n/**\n * Using colorStr and thickness of sides, create a color object\n */\nfunction shadowGroupToColor({ colorStr, sides, edgeCount }) {\n  if (edgeCount !== 4) {\n    return null; // ignore thin shadows and shadows on one side of the text\n  }\n  const strokeColor = new Color();\n  strokeColor.parseString(colorStr);\n\n  // Detect whether any sides' shadows are thin enough to be considered\n  // translucent, and if so, calculate an alpha value to apply on top of\n  // the parsed color.\n  let density = 0;\n  let isSolid = true;\n  edges.forEach(edge => {\n    // Decimal values are ignored. a .6px shadow is treated as 1px\n    // because it is not rendered evenly around the text.\n    // I.e. .6 ends up as 70% alpha on one side and 16% on the other.\n    density += sides[edge].length / 4;\n    isSolid &&= sides[edge].every(\n      offset => offset > OPAQUE_STROKE_OFFSET_MIN_PX\n    );\n  });\n\n  if (!isSolid) {\n    // As more shadows surround the text, the opacity increases\n    strokeColor.alpha = 1 - Math.pow(SHADOW_STROKE_ALPHA, density);\n  }\n  return strokeColor;\n}\n"
  },
  {
    "path": "lib/commons/color/get-text-shadow-colors.js",
    "content": "import Color from './color';\nimport assert from '../../core/utils/assert';\nimport getStrokeColorsFromShadows from './get-stroke-colors-from-shadows';\nimport parseTextShadows from './parse-text-shadows';\n\n/**\n * Get text-shadow colors that can impact the color contrast of the text, including from:\n * - Shadows which are individually thick enough (minRatio <= thickness <= maxRatio) to distinguish as characters\n * - Groups of \"thin\" shadows (thickness < minRatio) that collectively act as a pseudo-text-stroke (see #4064)\n * @param {Element} node  DOM Element\n * @param {Object} options (optional)\n * @property {Bool} minRatio Treat shadows smaller than this as \"thin\", ratio shadow size divided by font size\n * @property {Bool} maxRatio Ignore shadows equal or larger than this, ratio shadow size divided by font size\n * @property {Bool} ignoreEdgeCount Do not return null when if shadows cover 2 or 3 edges, ignore those instead\n * @returns {Array|null} Array of colors or null if text-shadow was too complex to measure\n */\nexport default function getTextShadowColors(\n  node,\n  { minRatio, maxRatio, ignoreEdgeCount } = {}\n) {\n  const shadowColors = [];\n  const style = window.getComputedStyle(node);\n  const textShadow = style.getPropertyValue('text-shadow');\n  if (textShadow === 'none') {\n    return shadowColors;\n  }\n\n  const fontSizeStr = style.getPropertyValue('font-size');\n  const fontSize = parseInt(fontSizeStr);\n  assert(\n    isNaN(fontSize) === false,\n    `Unable to determine font-size value ${fontSizeStr}`\n  );\n\n  const thinShadows = [];\n  const shadows = parseTextShadows(textShadow);\n  for (const shadow of shadows) {\n    // Defaults only necessary for IE\n    const colorStr = shadow.colorStr || style.getPropertyValue('color');\n    const [offsetX, offsetY, blurRadius = 0] = shadow.pixels;\n    if (maxRatio && blurRadius >= fontSize * maxRatio) {\n      continue;\n    }\n    if (minRatio && blurRadius < fontSize * minRatio) {\n      thinShadows.push({ colorStr, pixels: shadow.pixels });\n      continue;\n    }\n    if (thinShadows.length > 0) {\n      // Inset any stroke colors before this shadow\n      const strokeColors = getStrokeColorsFromShadows(thinShadows, {\n        ignoreEdgeCount\n      });\n      if (strokeColors === null) {\n        return null; // Exit early if text-shadow is too complex\n      }\n      shadowColors.push(...strokeColors);\n      thinShadows.splice(0, thinShadows.length); // empty\n    }\n\n    const color = textShadowColor({\n      colorStr,\n      offsetX,\n      offsetY,\n      blurRadius,\n      fontSize\n    });\n    shadowColors.push(color);\n  }\n\n  if (thinShadows.length > 0) {\n    // Append any remaining stroke colors\n    const strokeColors = getStrokeColorsFromShadows(thinShadows, {\n      ignoreEdgeCount\n    });\n    if (strokeColors === null) {\n      return null; // Exit early if text-shadow is too complex\n    }\n    shadowColors.push(...strokeColors);\n  }\n\n  return shadowColors;\n}\n\nfunction textShadowColor({ colorStr, offsetX, offsetY, blurRadius, fontSize }) {\n  if (offsetX > blurRadius || offsetY > blurRadius) {\n    // Shadow is too far removed from the text to impact contrast\n    return new Color(0, 0, 0, 0);\n  }\n\n  const shadowColor = new Color();\n  shadowColor.parseString(colorStr);\n  shadowColor.alpha *= blurRadiusToAlpha(blurRadius, fontSize);\n\n  return shadowColor;\n}\n\nfunction blurRadiusToAlpha(blurRadius, fontSize) {\n  if (blurRadius === 0) {\n    return 1;\n  }\n\n  // This formula is an estimate based on various tests.\n  // Different people test this differently, so opinions may vary.\n  const relativeBlur = blurRadius / fontSize;\n  return 0.185 / (relativeBlur + 0.4);\n}\n"
  },
  {
    "path": "lib/commons/color/has-valid-contrast-ratio.js",
    "content": "import getContrast from './get-contrast';\n\n/**\n * Check whether certain text properties meet WCAG contrast rules\n * @method hasValidContrastRatio\n * @memberof axe.commons.color.Color\n * @instance\n * @param  {Color}  bgcolor  Background color\n * @param  {Color}  fgcolor  Foreground color\n * @param  {number}  fontSize  Font size of text, in pixels\n * @param  {boolean}  isBold  Whether the text is bold\n * @return {{isValid: boolean, contrastRatio: number, expectedContrastRatio: number}}\n *\n * @deprecated\n */\nfunction hasValidContrastRatio(bg, fg, fontSize, isBold) {\n  const contrast = getContrast(bg, fg);\n  const isSmallFont =\n    (isBold && Math.ceil(fontSize * 72) / 96 < 14) ||\n    (!isBold && Math.ceil(fontSize * 72) / 96 < 18);\n  const expectedContrastRatio = isSmallFont ? 4.5 : 3;\n\n  return {\n    isValid: contrast > expectedContrastRatio,\n    contrastRatio: contrast,\n    expectedContrastRatio: expectedContrastRatio\n  };\n}\n\nexport default hasValidContrastRatio;\n"
  },
  {
    "path": "lib/commons/color/incomplete-data.js",
    "content": "import cache from '../../core/base/cache';\n\nconst cacheKey = 'color.incompleteData';\n\n/**\n * API for handling incomplete color data\n * @namespace axe.commons.color.incompleteData\n * @inner\n */\nconst incompleteData = {\n  /**\n   * Store incomplete data by key with a string value\n   * @method set\n   * @memberof axe.commons.color.incompleteData\n   * @instance\n   * @param {String} key Identifier for missing data point (fgColor, bgColor, etc.)\n   * @param {String} reason Missing data reason to match message template\n   */\n  set: function (key, reason) {\n    if (typeof key !== 'string') {\n      throw new Error('Incomplete data: key must be a string');\n    }\n    const data = cache.get(cacheKey, () => ({}));\n    if (reason) {\n      data[key] = reason;\n    }\n    return data[key];\n  },\n  /**\n   * Get incomplete data by key\n   * @method get\n   * @memberof axe.commons.color.incompleteData\n   * @instance\n   * @param {String} key \tIdentifier for missing data point (fgColor, bgColor, etc.)\n   * @return {String} String for reason we couldn't tell\n   */\n  get: function (key) {\n    const data = cache.get(cacheKey);\n    return data?.[key];\n  },\n  /**\n   * Clear incomplete data on demand\n   * @method clear\n   * @memberof axe.commons.color.incompleteData\n   * @instance\n   */\n  clear: function () {\n    cache.set(cacheKey, {});\n  }\n};\n\nexport default incompleteData;\n"
  },
  {
    "path": "lib/commons/color/index.js",
    "content": "/**\n * Namespace for color-related utilities.\n * @namespace commons.color\n * @memberof axe\n */\nexport { default as centerPointOfRect } from './center-point-of-rect';\nexport { default as Color } from './color';\nexport { default as elementHasImage } from './element-has-image';\nexport { default as elementIsDistinct } from './element-is-distinct';\nexport { default as filteredRectStack } from './filtered-rect-stack';\nexport { default as flattenColors } from './flatten-colors';\nexport { default as flattenShadowColors } from './flatten-shadow-colors';\nexport { default as getBackgroundColor } from './get-background-color';\nexport { default as getBackgroundStack } from './get-background-stack';\nexport { default as getContrast } from './get-contrast';\nexport { default as getForegroundColor } from './get-foreground-color';\nexport { default as getOwnBackgroundColor } from './get-own-background-color';\nexport { default as getRectStack } from './get-rect-stack';\nexport { default as getStrokeColorsFromShadows } from './get-stroke-colors-from-shadows';\nexport { default as getTextShadowColors } from './get-text-shadow-colors';\nexport { default as hasValidContrastRatio } from './has-valid-contrast-ratio';\nexport { default as incompleteData } from './incomplete-data';\nexport { default as parseTextShadows } from './parse-text-shadows';\nexport { getStackingContext, stackingContextToColor } from './stacking-context';\n"
  },
  {
    "path": "lib/commons/color/parse-text-shadows.js",
    "content": "import assert from '../../core/utils/assert';\n\n/**\n * Parse text-shadow property value. Required for IE, which can return the color\n * either at the start or the end, and either in rgb(a) or as a named color\n * @param {String} textShadow\n * @returns {Array} Array of objects with `pixels` and `colorStr` properties\n */\nexport default function parseTextShadows(textShadow) {\n  let current = { pixels: [] };\n  let str = textShadow.trim();\n  const shadows = [current];\n  if (!str) {\n    return [];\n  }\n\n  while (str) {\n    const colorMatch =\n      // match a color name or function (e.g. `oklch(39.2% 0.4 0 / 0.5)`) or a hex value\n      str.match(/^[a-z]+(\\([^)]+\\))?/i) || str.match(/^#[0-9a-f]+/i);\n    const pixelMatch = str.match(/^([0-9.-]+)px/i) || str.match(/^(0)/);\n\n    if (colorMatch) {\n      assert(\n        !current.colorStr,\n        `Multiple colors identified in text-shadow: ${textShadow}`\n      );\n      str = str.replace(colorMatch[0], '').trim();\n      current.colorStr = colorMatch[0];\n    } else if (pixelMatch) {\n      assert(\n        current.pixels.length < 3,\n        `Too many pixel units in text-shadow: ${textShadow}`\n      );\n      str = str.replace(pixelMatch[0], '').trim();\n      const pixelUnit = parseFloat(\n        (pixelMatch[1][0] === '.' ? '0' : '') + pixelMatch[1]\n      );\n      current.pixels.push(pixelUnit);\n    } else if (str[0] === ',') {\n      // multiple text-shadows in a single string (e.g. `text-shadow: 1px 1px 1px #000, 3px 3px 5px blue;`\n      assert(\n        current.pixels.length >= 2,\n        `Missing pixel value in text-shadow: ${textShadow}`\n      );\n      current = { pixels: [] };\n      shadows.push(current);\n      str = str.substr(1).trim();\n    } else {\n      throw new Error(`Unable to process text-shadows: ${str}`);\n    }\n  }\n\n  shadows.forEach(({ pixels }) => {\n    if (pixels.length === 2) {\n      pixels.push(0); // Append default blur\n    }\n  });\n\n  return shadows;\n}\n"
  },
  {
    "path": "lib/commons/color/stacking-context.js",
    "content": "import { getNodeFromTree } from '../../core/utils';\nimport getBackgroundStack from './get-background-stack';\nimport Color from './color';\nimport flattenColors from './flatten-colors';\n\n/**\n * Create a stacking context hierarchy tree for an element. This structure closely mimics the painting order of a page.\n * @see https://www.w3.org/TR/CSS22/zindex.html#painting-order\n *\n * @example\n * Given the following HTML structure:\n *\n * <div id=\"parent\" style=\"background-color: red; opacity: 0.8\">\n *   <div id=\"target\" style=\"background-color: rgba(0,255,0,0.5)\">Text</div>\n * <div>\n *\n * Produces the following stacking context tree. Since the #parent element creates a stacking context due to `opacity`, the #target element's stacking context belongs under the #parent's context.\n *\n * [\n *   {\n *     vNode: #parent,\n *     opacity: 0.8,\n *     blendMode: 'normal',\n *     bgColor: Color(255,0,0,1),\n *     descendants: [\n *       {\n *         vNode: #target,\n *         opacity: 1,\n *         blendMode: 'normal',\n *         bgColor: Color(0,255,0,0.5),\n *         descendants: []\n *       }\n *     ]\n *   }\n * ]\n *\n * The stacking context hierarchy tree does not mimic the HTML structure. That is, elements that are on the same context level are siblings in the stacking context tree even if they have a parent/child HTML relationship.\n *\n * For example, given the following HTML structure:\n *\n * <main>\n *  <header style=\"position: relative; z-index: 1;\">\n *    <h1><span>Hello World</span></h1>\n *  </header>\n * </main>\n * <p>Lorium ipsum <a href=\"#\">dolores</a>...</p>\n *\n * Produces the following tree structure:\n *\n * body\n * - main\n * - header\n *   - h1\n *   - span\n * - p\n * - a\n *\n * @param {Node} elm\n * @param {Node[]} [elmStack] - Optional element stack array to save on computing it again.\n * @return {Object}\n */\nexport function getStackingContext(elm, elmStack) {\n  const virtualNode = getNodeFromTree(elm);\n  if (virtualNode._stackingContext) {\n    return virtualNode._stackingContext;\n  }\n\n  const stackingContext = [];\n  const contextMap = new Map();\n  elmStack = elmStack ?? getBackgroundStack(elm);\n\n  elmStack.forEach(bgElm => {\n    const bgVNode = getNodeFromTree(bgElm);\n    const bgColor = getOwnBackgroundColor(bgVNode);\n\n    /*\n      remove the ROOT_ORDER element to treat all root stacks and first-order\n      stacks at the same level (instead of nesting the first-order stack inside\n      the root stack)\n\n      e.g. an element that creates a non-positioned stacking context at the\n      root level should be a sibling to root level elements that do not create\n      a stacking context\n     */\n    const stackingOrder = bgVNode._stackingOrder.filter(({ vNode }) => !!vNode);\n\n    // create a stacking context for each node in the stacking order\n    stackingOrder.forEach(({ vNode }, index) => {\n      const ancestorVNode = stackingOrder[index - 1]?.vNode;\n      const context = addToStackingContext(contextMap, vNode, ancestorVNode);\n\n      if (index === 0 && !contextMap.get(vNode)) {\n        stackingContext.unshift(context);\n      }\n      contextMap.set(vNode, context);\n    });\n\n    // create a stacking context for the current node\n    const ancestorVNode = stackingOrder[stackingOrder.length - 1]?.vNode;\n    const context = addToStackingContext(contextMap, bgVNode, ancestorVNode);\n    if (!stackingOrder.length) {\n      stackingContext.unshift(context);\n    }\n\n    // only assign the color to the current node so we don't apply any\n    // background colors from ancestor nodes that are not part of the element\n    // stack\n    context.bgColor = bgColor;\n  });\n\n  virtualNode._stackingContext = stackingContext;\n  return stackingContext;\n}\n\n/**\n * Transform a stacking context object into a Color.\n * @param {Object} context\n * @return {Object}\n */\nexport function stackingContextToColor(context) {\n  if (!context.descendants?.length) {\n    const color = context.bgColor;\n    color.alpha *= context.opacity;\n\n    return {\n      color,\n      blendMode: context.blendMode\n    };\n  }\n\n  const sourceColor = context.descendants.reduce(\n    reduceToColor,\n    // ensure an array with a single context is reduced to a color by passing\n    // in an empty stacking context\n    createStackingContext()\n  );\n  const color = flattenColors(\n    sourceColor,\n    context.bgColor,\n    context.descendants[0].blendMode\n  );\n  color.alpha *= context.opacity;\n\n  // carry forward the mix-blind-mode property so background color algorithm\n  // can use it to flatten multiple contexts together\n  return {\n    color,\n    blendMode: context.blendMode\n  };\n}\n\n/**\n * Reduce two context objects into a Color by blending them together\n * @param {Object} backdropContext\n * @param {Object} sourceContext\n * @return {Color}\n */\nfunction reduceToColor(backdropContext, sourceContext) {\n  let backdrop;\n  if (backdropContext instanceof Color) {\n    backdrop = backdropContext;\n  } else {\n    backdrop = stackingContextToColor(backdropContext).color;\n  }\n\n  const sourceColor = stackingContextToColor(sourceContext).color;\n  return flattenColors(sourceColor, backdrop, sourceContext.blendMode);\n}\n\n/**\n * Create a stacking context object for a virtual node.\n * @param {VirtualNode} vNode\n * @param {Object} ancestorContext\n * @return {Object}\n */\nfunction createStackingContext(vNode, ancestorContext) {\n  return {\n    vNode: vNode,\n    ancestor: ancestorContext,\n    opacity: parseFloat(vNode?.getComputedStylePropertyValue('opacity') ?? 1),\n    bgColor: new Color(0, 0, 0, 0),\n    blendMode: normalizeBlendMode(\n      vNode?.getComputedStylePropertyValue('mix-blend-mode')\n    ),\n    descendants: []\n  };\n}\n\n/**\n * Normalize a mix-blend-mode CSS value\n * @param {String} blendmode\n * @return {String|undefined}\n */\nfunction normalizeBlendMode(blendmode) {\n  return !!blendmode ? blendmode : undefined;\n}\n\n/**\n * Create a stacking context for a virtual node and add it as a descendant of an ancestor's context.\n * @param {Map} contextMap\n * @param {VirtualNode} vNode\n * @param {VirtualNode} ancestorVNode\n * @return {Object}\n */\nfunction addToStackingContext(contextMap, vNode, ancestorVNode) {\n  const ancestorContext = contextMap.get(ancestorVNode);\n  const context =\n    contextMap.get(vNode) ?? createStackingContext(vNode, ancestorContext);\n  if (\n    ancestorContext &&\n    ancestorVNode !== vNode &&\n    !ancestorContext.descendants.includes(context)\n  ) {\n    ancestorContext.descendants.unshift(context);\n  }\n\n  return context;\n}\n\n/**\n * Get the background color for a virtual node\n * @param {VirtualNode} vNode\n * @return {Color}\n */\nfunction getOwnBackgroundColor(vNode) {\n  const bgColor = new Color();\n  bgColor.parseString(vNode.getComputedStylePropertyValue('background-color'));\n\n  return bgColor;\n}\n"
  },
  {
    "path": "lib/commons/dom/create-grid.js",
    "content": "import isVisibleOnScreen from './is-visible-on-screen';\nimport { getBoundingRect } from '../math/get-bounding-rect';\nimport { isPointInRect } from '../math/is-point-in-rect';\nimport VirtualNode from '../../core/base/virtual-node/virtual-node';\nimport { getNodeFromTree, getScroll, isShadowRoot } from '../../core/utils';\nimport constants from '../../core/constants';\nimport cache from '../../core/base/cache';\nimport assert from '../../core/utils/assert';\nimport getOverflowHiddenAncestors from './get-overflow-hidden-ancestors';\nimport { getIntersectionRect } from '../math';\n\nconst ROOT_LEVEL = 0;\nconst DEFAULT_LEVEL = 0.1;\nconst FLOAT_LEVEL = 0.2;\nconst POSITION_LEVEL = 0.3;\nlet nodeIndex = 0;\n\n/**\n * Setup the 2d grid and add every element to it, even elements not\n * included in the flat tree\n * @returns gridSize\n */\nexport default function createGrid(\n  root = document.body,\n  rootGrid,\n  parentVNode = null\n) {\n  // Prevent multiple calls per run\n  if (cache.get('gridCreated') && !parentVNode) {\n    return constants.gridSize;\n  }\n  cache.set('gridCreated', true);\n\n  // by not starting at the htmlElement we don't have to pass a custom\n  // filter function into the treeWalker to filter out head elements,\n  // which would be called for every node\n  if (!parentVNode) {\n    let vNode = getNodeFromTree(document.documentElement);\n    if (!vNode) {\n      vNode = new VirtualNode(document.documentElement);\n    }\n\n    nodeIndex = 0;\n    vNode._stackingOrder = [\n      createStackingContext(ROOT_LEVEL, nodeIndex++, null)\n    ];\n    rootGrid ??= new Grid();\n    addNodeToGrid(rootGrid, vNode);\n\n    if (getScroll(vNode.actualNode)) {\n      const subGrid = new Grid(vNode);\n      vNode._subGrid = subGrid;\n    }\n  }\n\n  // IE11 requires the first 3 parameters\n  // @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker\n  const treeWalker = document.createTreeWalker(\n    root,\n    window.NodeFilter.SHOW_ELEMENT,\n    null,\n    false\n  );\n  let node = parentVNode ? treeWalker.nextNode() : treeWalker.currentNode;\n  while (node) {\n    let vNode = getNodeFromTree(node);\n\n    if (vNode && vNode.parent) {\n      parentVNode = vNode.parent;\n    }\n    // Elements with an assigned slot need to be a child of the slot element\n    else if (node.assignedSlot) {\n      parentVNode = getNodeFromTree(node.assignedSlot);\n    }\n    // An SVG in IE11 does not have a parentElement but instead has a\n    // parentNode. but parentNode could be a shadow root so we need to\n    // verify it's in the tree first\n    else if (node.parentElement) {\n      parentVNode = getNodeFromTree(node.parentElement);\n    } else if (node.parentNode && getNodeFromTree(node.parentNode)) {\n      parentVNode = getNodeFromTree(node.parentNode);\n    }\n\n    if (!vNode) {\n      vNode = new axe.VirtualNode(node, parentVNode);\n    }\n\n    vNode._stackingOrder = createStackingOrder(vNode, parentVNode, nodeIndex++);\n\n    const scrollRegionParent = findScrollRegionParent(vNode, parentVNode);\n    const grid = scrollRegionParent ? scrollRegionParent._subGrid : rootGrid;\n\n    if (getScroll(vNode.actualNode)) {\n      const subGrid = new Grid(vNode);\n      vNode._subGrid = subGrid;\n    }\n\n    // filter out any elements with 0 width or height\n    // (we don't do this before so we can calculate stacking context\n    // of parents with 0 width/height)\n    const rect = vNode.boundingClientRect;\n    if (rect.width !== 0 && rect.height !== 0 && isVisibleOnScreen(node)) {\n      addNodeToGrid(grid, vNode);\n    }\n\n    // add shadow root elements to the grid\n    if (isShadowRoot(node)) {\n      createGrid(node.shadowRoot, grid, vNode);\n    }\n\n    node = treeWalker.nextNode();\n  }\n  return constants.gridSize;\n}\n\n/**\n * Determine if node produces a stacking context.\n * References:\n * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context\n * https://github.com/gwwar/z-context/blob/master/devtools/index.js\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nfunction isStackingContext(vNode, parentVNode) {\n  const position = vNode.getComputedStylePropertyValue('position');\n  const zIndex = vNode.getComputedStylePropertyValue('z-index');\n\n  // the root element (HTML) is skipped since we always start with a\n  // stacking order of [0]\n\n  // position: fixed or sticky\n  if (position === 'fixed' || position === 'sticky') {\n    return true;\n  }\n\n  // positioned (absolutely or relatively) with a z-index value other than \"auto\",\n  if (zIndex !== 'auto' && position !== 'static') {\n    return true;\n  }\n\n  // elements with an opacity value less than 1.\n  if (vNode.getComputedStylePropertyValue('opacity') !== '1') {\n    return true;\n  }\n\n  // elements with a transform value other than \"none\"\n  const transform =\n    vNode.getComputedStylePropertyValue('-webkit-transform') ||\n    vNode.getComputedStylePropertyValue('-ms-transform') ||\n    vNode.getComputedStylePropertyValue('transform') ||\n    'none';\n\n  if (transform !== 'none') {\n    return true;\n  }\n\n  // elements with a mix-blend-mode value other than \"normal\"\n  const mixBlendMode = vNode.getComputedStylePropertyValue('mix-blend-mode');\n  if (mixBlendMode && mixBlendMode !== 'normal') {\n    return true;\n  }\n\n  // elements with a filter value other than \"none\"\n  const filter = vNode.getComputedStylePropertyValue('filter');\n  if (filter && filter !== 'none') {\n    return true;\n  }\n\n  // elements with a perspective value other than \"none\"\n  const perspective = vNode.getComputedStylePropertyValue('perspective');\n  if (perspective && perspective !== 'none') {\n    return true;\n  }\n\n  // element with a clip-path value other than \"none\"\n  const clipPath = vNode.getComputedStylePropertyValue('clip-path');\n  if (clipPath && clipPath !== 'none') {\n    return true;\n  }\n\n  // element with a mask value other than \"none\"\n  const mask =\n    vNode.getComputedStylePropertyValue('-webkit-mask') ||\n    vNode.getComputedStylePropertyValue('mask') ||\n    'none';\n  if (mask !== 'none') {\n    return true;\n  }\n\n  // element with a mask-image value other than \"none\"\n  const maskImage =\n    vNode.getComputedStylePropertyValue('-webkit-mask-image') ||\n    vNode.getComputedStylePropertyValue('mask-image') ||\n    'none';\n  if (maskImage !== 'none') {\n    return true;\n  }\n\n  // element with a mask-border value other than \"none\"\n  const maskBorder =\n    vNode.getComputedStylePropertyValue('-webkit-mask-border') ||\n    vNode.getComputedStylePropertyValue('mask-border') ||\n    'none';\n  if (maskBorder !== 'none') {\n    return true;\n  }\n\n  // elements with isolation set to \"isolate\"\n  if (vNode.getComputedStylePropertyValue('isolation') === 'isolate') {\n    return true;\n  }\n\n  // transform or opacity in will-change even if you don't specify values for these attributes directly\n  const willChange = vNode.getComputedStylePropertyValue('will-change');\n  if (willChange === 'transform' || willChange === 'opacity') {\n    return true;\n  }\n\n  // elements with -webkit-overflow-scrolling set to \"touch\"\n  if (\n    vNode.getComputedStylePropertyValue('-webkit-overflow-scrolling') ===\n    'touch'\n  ) {\n    return true;\n  }\n\n  // element with a contain value of \"layout\" or \"paint\" or a composite value\n  // that includes either of them (i.e. contain: strict, contain: content).\n  const contain = vNode.getComputedStylePropertyValue('contain');\n  if (['layout', 'paint', 'strict', 'content'].includes(contain)) {\n    return true;\n  }\n\n  // a flex item or gird item with a z-index value other than \"auto\", that is the parent element display: flex|inline-flex|grid|inline-grid,\n  if (zIndex !== 'auto' && isFlexOrGridContainer(parentVNode)) {\n    return true;\n  }\n\n  return false;\n}\n\n/**\n * Determine if element is a flex or grid container.\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nfunction isFlexOrGridContainer(vNode) {\n  if (!vNode) {\n    return false;\n  }\n\n  const display = vNode.getComputedStylePropertyValue('display');\n  return ['flex', 'inline-flex', 'grid', 'inline-grid'].includes(display);\n}\n\n/**\n * Determine the stacking order of an element. The stacking order is an array of\n * stacking contexts in ancestor order.\n * @param {VirtualNode} vNode\n * @param {VirtualNode} parentVNode\n * @param {Number} treeOrder\n * @return {Number[]}\n */\nfunction createStackingOrder(vNode, parentVNode, treeOrder) {\n  const stackingOrder = parentVNode._stackingOrder.slice();\n\n  // if an element creates a stacking context, find the first\n  // true stack (not a \"fake\" stack created from positioned or\n  // floated elements without a z-index) and create a new stack at\n  // that point (step #5 and step #8)\n  // @see https://www.w3.org/Style/css2-updates/css2/zindex.html\n  if (isStackingContext(vNode, parentVNode)) {\n    const index = stackingOrder.findIndex(({ stackLevel }) =>\n      [ROOT_LEVEL, FLOAT_LEVEL, POSITION_LEVEL].includes(stackLevel)\n    );\n    if (index !== -1) {\n      stackingOrder.splice(index, stackingOrder.length - index);\n    }\n  }\n\n  const stackLevel = getStackLevel(vNode, parentVNode);\n  if (stackLevel !== null) {\n    stackingOrder.push(createStackingContext(stackLevel, treeOrder, vNode));\n  }\n  return stackingOrder;\n}\n\n/**\n * Create a stacking context, keeping track of the stack level, tree order, and virtual\n * node container.\n * @see https://www.w3.org/Style/css2-updates/css2/zindex.html\n * @see https://www.w3.org/Style/css2-updates/css2/visuren.html#layers\n * @param {Number} stackLevel - The stack level of the stacking context\n * @param {Number} treeOrder - The elements depth-first traversal order\n * @param {VirtualNode} vNode - The virtual node that is the container for the stacking context\n */\nfunction createStackingContext(stackLevel, treeOrder, vNode) {\n  return {\n    stackLevel,\n    treeOrder,\n    vNode\n  };\n}\n\n/**\n * Calculate the level of the stacking context.\n * @param {VirtualNode} vNode - The virtual node container of the stacking context\n * @param {VirtualNode} parentVNode - The parent virtual node of the vNode\n * @return {Number|null}\n */\nfunction getStackLevel(vNode, parentVNode) {\n  const zIndex = getRealZIndex(vNode, parentVNode);\n  if (!['auto', '0'].includes(zIndex)) {\n    return parseInt(zIndex);\n  }\n\n  // if a positioned element has z-index: auto or 0 (step #8), or if\n  // a non-positioned floating element (step #5), treat it as its\n  // own stacking context\n  // @see https://www.w3.org/Style/css2-updates/css2/zindex.html\n\n  // Put positioned elements above floated elements\n  if (vNode.getComputedStylePropertyValue('position') !== 'static') {\n    return POSITION_LEVEL;\n  }\n\n  // Put floated elements above z-index: 0\n  // (step #5 floating get sorted below step #8 positioned)\n  if (vNode.getComputedStylePropertyValue('float') !== 'none') {\n    return FLOAT_LEVEL;\n  }\n\n  if (isStackingContext(vNode, parentVNode)) {\n    return DEFAULT_LEVEL;\n  }\n\n  return null;\n}\n\n/**\n * Calculate the z-index value of a node taking into account when doesn't apply.\n * @param {VirtualNode} vNode - The virtual node to get z-index of\n * @param {VirtualNode} parentVNode - The parent virtual node of the vNode\n * @return {Number|'auto'}\n */\nfunction getRealZIndex(vNode, parentVNode) {\n  const position = vNode.getComputedStylePropertyValue('position');\n  if (position === 'static' && !isFlexOrGridContainer(parentVNode)) {\n    // z-index is ignored on position:static, except if on a flex or grid\n    // @see https://www.w3.org/TR/css-flexbox-1/#painting\n    // @see https://www.w3.org/TR/css-grid-1/#z-order\n    return 'auto';\n  }\n  return vNode.getComputedStylePropertyValue('z-index');\n}\n\n/**\n * Return the parent node that is a scroll region.\n * @param {VirtualNode}\n * @return {VirtualNode|null}\n */\nfunction findScrollRegionParent(vNode, parentVNode) {\n  let scrollRegionParent = null;\n  const checkedNodes = [vNode];\n\n  while (parentVNode) {\n    if (getScroll(parentVNode.actualNode)) {\n      scrollRegionParent = parentVNode;\n      break;\n    }\n\n    if (parentVNode._scrollRegionParent) {\n      scrollRegionParent = parentVNode._scrollRegionParent;\n      break;\n    }\n\n    checkedNodes.push(parentVNode);\n    parentVNode = getNodeFromTree(\n      parentVNode.actualNode.parentElement || parentVNode.actualNode.parentNode\n    );\n  }\n\n  // cache result of parent scroll region so we don't have to look up the entire\n  // tree again for a child node\n  checkedNodes.forEach(\n    virtualNode => (virtualNode._scrollRegionParent = scrollRegionParent)\n  );\n  return scrollRegionParent;\n}\n\n/**\n * Add a node to every cell of the grid it intersects with.\n * @param {Grid}\n * @param {VirtualNode}\n */\nfunction addNodeToGrid(grid, vNode) {\n  const overflowHiddenNodes = getOverflowHiddenAncestors(vNode);\n\n  vNode.clientRects.forEach(clientRect => {\n    // ignore any rects that are outside the bounds of overflow hidden ancestors\n    const visibleRect = overflowHiddenNodes.reduce((rect, overflowNode) => {\n      return rect && getIntersectionRect(rect, overflowNode.boundingClientRect);\n    }, clientRect);\n\n    if (!visibleRect) {\n      return;\n    }\n\n    // save a reference to where this element is in the grid so we\n    // can find it even if it's in a subgrid\n    vNode._grid ??= grid;\n    const gridRect = grid.getGridPositionOfRect(visibleRect);\n    grid.loopGridPosition(gridRect, gridCell => {\n      if (!gridCell.includes(vNode)) {\n        gridCell.push(vNode);\n      }\n    });\n  });\n}\n\nclass Grid {\n  constructor(container = null) {\n    this.container = container;\n    this.cells = [];\n  }\n\n  /**\n   * Convert x or y coordinate from rect, to a position in the grid\n   * @param {number}\n   * @returns {number}\n   */\n  toGridIndex(num) {\n    return Math.floor(num / constants.gridSize);\n  }\n\n  /**\n   * Return an an array of nodes available at a particular grid coordinate\n   * @param {DOMPoint} gridPosition\n   * @returns {Array<AbstractVirtualNode>}\n   */\n  getCellFromPoint({ x, y }) {\n    assert(this.boundaries, 'Grid does not have cells added');\n    const rowIndex = this.toGridIndex(y);\n    const colIndex = this.toGridIndex(x);\n    assert(\n      isPointInRect({ y: rowIndex, x: colIndex }, this.boundaries),\n      'Element midpoint exceeds the grid bounds'\n    );\n    const row = this.cells[rowIndex - this.cells._negativeIndex] ?? [];\n    return row[colIndex - row._negativeIndex] ?? [];\n  }\n\n  /**\n   * Loop over all cells within the gridPosition rect\n   * @param {DOMRect} gridPosition\n   * @param {Function} callback\n   */\n  loopGridPosition(gridPosition, callback) {\n    const { left, right, top, bottom } = gridPosition;\n    if (this.boundaries) {\n      gridPosition = getBoundingRect(this.boundaries, gridPosition);\n    }\n    this.boundaries = gridPosition;\n\n    loopNegativeIndexMatrix(this.cells, top, bottom, (gridRow, row) => {\n      loopNegativeIndexMatrix(gridRow, left, right, (gridCell, col) => {\n        callback(gridCell, { row, col });\n      });\n    });\n  }\n\n  /**\n   * Scale the rect to the position within the grid\n   * @param {DOMRect} clientOrBoundingRect\n   * @param {number} margin Offset outside the rect, default 0\n   * @returns {DOMRect} gridPosition\n   */\n  getGridPositionOfRect({ top, right, bottom, left }, margin = 0) {\n    top = this.toGridIndex(top - margin);\n    right = this.toGridIndex(right + margin - 1);\n    bottom = this.toGridIndex(bottom + margin - 1);\n    left = this.toGridIndex(left - margin);\n    return new window.DOMRect(left, top, right - left, bottom - top);\n  }\n}\n\n// handle negative row/col values\nfunction loopNegativeIndexMatrix(matrix, start, end, callback) {\n  matrix._negativeIndex ??= 0;\n  // Shift the array when start is negative\n  if (start < matrix._negativeIndex) {\n    for (let i = 0; i < matrix._negativeIndex - start; i++) {\n      matrix.splice(0, 0, []);\n    }\n    matrix._negativeIndex = start;\n  }\n\n  const startOffset = start - matrix._negativeIndex;\n  const endOffset = end - matrix._negativeIndex;\n  for (let index = startOffset; index <= endOffset; index++) {\n    matrix[index] ??= [];\n    callback(matrix[index], index + matrix._negativeIndex);\n  }\n}\n"
  },
  {
    "path": "lib/commons/dom/find-elms-in-context.js",
    "content": "import getRootNode from './get-root-node';\nimport { escapeSelector } from '../../core/utils';\n\n/**\n * Find elements referenced from a given context\n * @method findElmsInContext\n * @memberof axe.commons.dom\n * @instance\n * @param {Object} element\n * @param {String} element.context Element in the same context\n * @param {String} element.value Attribute value to search for\n * @param {String} element.attr Attribute name to search for\n * @param {String} element.elm NodeName to search for (optional)\n * @return {Array<Node>}\n */\nfunction findElmsInContext({ context, value, attr, elm = '' }) {\n  let root;\n  const escapedValue = escapeSelector(value);\n\n  if (context.nodeType === 9 || context.nodeType === 11) {\n    // It's already root\n    root = context;\n  } else {\n    root = getRootNode(context);\n  }\n  return Array.from(\n    root.querySelectorAll(elm + '[' + attr + '=' + escapedValue + ']')\n  );\n}\n\nexport default findElmsInContext;\n"
  },
  {
    "path": "lib/commons/dom/find-nearby-elms.js",
    "content": "import getNodeGrid from './get-node-grid';\nimport { memoize } from '../../core/utils';\n\nexport default function findNearbyElms(vNode, margin = 0) {\n  const grid = getNodeGrid(vNode);\n  if (!grid?.cells?.length) {\n    return []; // Elements not in the grid don't have ._grid\n  }\n  const rect = vNode.boundingClientRect;\n  const selfIsFixed = hasFixedPosition(vNode);\n  const gridPosition = grid.getGridPositionOfRect(rect, margin);\n\n  const neighbors = [];\n  grid.loopGridPosition(gridPosition, vNeighbors => {\n    for (const vNeighbor of vNeighbors) {\n      if (\n        vNeighbor &&\n        vNeighbor !== vNode &&\n        !neighbors.includes(vNeighbor) &&\n        selfIsFixed === hasFixedPosition(vNeighbor)\n      ) {\n        neighbors.push(vNeighbor);\n      }\n    }\n  });\n\n  return neighbors;\n}\n\nconst hasFixedPosition = memoize(vNode => {\n  if (!vNode) {\n    return false;\n  }\n  if (vNode.getComputedStylePropertyValue('position') === 'fixed') {\n    return true;\n  }\n  return hasFixedPosition(vNode.parent);\n});\n"
  },
  {
    "path": "lib/commons/dom/find-up-virtual.js",
    "content": "import { matchesSelector } from '../../core/utils';\n\n/**\n * recusively walk up the DOM, checking for a node which matches a selector\n *\n * **WARNING:** this should be used sparingly, as it's not even close to being performant\n * @method findUpVirtual\n * @memberof axe.commons.dom\n * @instance\n * @deprecated use axe.utils.closest\n * @param {VirtualNode} element The starting virtualNode\n * @param {String} target The selector for the HTMLElement\n * @return {HTMLElement|null} Either the matching HTMLElement or `null` if there was no match\n */\nfunction findUpVirtual(element, target) {\n  let parent;\n\n  parent = element.actualNode;\n  // virtualNode will have a shadowId if the element lives inside a shadow DOM or is\n  // slotted into a shadow DOM\n  if (!element.shadowId && typeof element.actualNode.closest === 'function') {\n    // non-shadow DOM elements\n    const match = element.actualNode.closest(target);\n    if (match) {\n      return match;\n    }\n    return null;\n  }\n  // handle shadow DOM elements and older browsers\n  do {\n    // recursively walk up the DOM, checking each parent node\n    parent = parent.assignedSlot ? parent.assignedSlot : parent.parentNode;\n    if (parent && parent.nodeType === 11) {\n      parent = parent.host;\n    }\n  } while (\n    parent &&\n    !matchesSelector(parent, target) &&\n    parent !== document.documentElement\n  );\n\n  if (!parent) {\n    return null;\n  }\n\n  if (!matchesSelector(parent, target)) {\n    return null;\n  }\n  return parent;\n}\n\nexport default findUpVirtual;\n"
  },
  {
    "path": "lib/commons/dom/find-up.js",
    "content": "import findUpVirtual from './find-up-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Find the virtual node and call dom.fundUpVirtual\n *\n * **WARNING:** this should be used sparingly, as it's not even close to being performant\n * @method findUp\n * @memberof axe.commons.dom\n * @instance\n * @deprecated use axe.utils.closest\n * @param {HTMLElement} element The starting HTMLElement\n * @param {String} target The selector for the HTMLElement\n * @return {HTMLElement|null} Either the matching HTMLElement or `null` if there was no match\n */\nfunction findUp(element, target) {\n  return findUpVirtual(getNodeFromTree(element), target);\n}\n\nexport default findUp;\n"
  },
  {
    "path": "lib/commons/dom/focus-disabled.js",
    "content": "import { nodeLookup } from '../../core/utils';\nimport isHiddenForEveryone from './is-hidden-for-everyone';\nimport isInert from './is-inert';\n\n// Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled\nconst allowedDisabledNodeNames = [\n  'button',\n  'command',\n  'fieldset',\n  'keygen',\n  'optgroup',\n  'option',\n  'select',\n  'textarea',\n  'input'\n];\n\nfunction isDisabledAttrAllowed(nodeName) {\n  return allowedDisabledNodeNames.includes(nodeName);\n}\n\n/**\n * Determines if focusing has been disabled on an element.\n * @param {HTMLElement|VirtualNode} el The HTMLElement\n * @return {Boolean} Whether focusing has been disabled on an element.\n */\nfunction focusDisabled(el) {\n  const { vNode } = nodeLookup(el);\n\n  if (\n    (isDisabledAttrAllowed(vNode.props.nodeName) &&\n      vNode.hasAttr('disabled')) ||\n    isInert(vNode)\n  ) {\n    return true;\n  }\n\n  // if a form element is in a legend, that element will not be disabled even if the fieldset is\n  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset\n  let parentNode = vNode.parent;\n  const ancestors = [];\n  let fieldsetDisabled = false;\n  while (\n    parentNode &&\n    parentNode.shadowId === vNode.shadowId &&\n    !fieldsetDisabled\n  ) {\n    ancestors.push(parentNode);\n    if (parentNode.props.nodeName === 'legend') {\n      break;\n    }\n\n    // use the cached value if one exists and it's from the same shadow tree\n    if (parentNode._inDisabledFieldset !== undefined) {\n      fieldsetDisabled = parentNode._inDisabledFieldset;\n      break;\n    }\n\n    if (\n      parentNode.props.nodeName === 'fieldset' &&\n      parentNode.hasAttr('disabled')\n    ) {\n      fieldsetDisabled = true;\n    }\n    parentNode = parentNode.parent;\n  }\n\n  // cache whether each element turned out to be in a disabled fieldset so we only have to look at each element once\n  ancestors.forEach(\n    ancestor => (ancestor._inDisabledFieldset = fieldsetDisabled)\n  );\n  if (fieldsetDisabled) {\n    return true;\n  }\n\n  if (vNode.props.nodeName !== 'area') {\n    // if the virtual node does not have an actual node, treat it\n    // as not hidden\n    if (!vNode.actualNode) {\n      return false;\n    }\n    return isHiddenForEveryone(vNode);\n  }\n\n  return false;\n}\n\nexport default focusDisabled;\n"
  },
  {
    "path": "lib/commons/dom/get-composed-parent.js",
    "content": "/**\n * Get an element's parent in the flattened tree\n * @method getComposedParent\n * @memberof axe.commons.dom\n * @instance\n * @param {Node} element\n * @return {Node|null} Parent element or Null for root node\n */\nfunction getComposedParent(element) {\n  if (element.assignedSlot) {\n    // NOTE: If the display of a slot element isn't 'contents',\n    // the slot shouldn't be ignored. Chrome does not support this (yet) so,\n    // we'll skip this part for now.\n    return getComposedParent(element.assignedSlot); // parent of a shadow DOM slot\n  } else if (element.parentNode) {\n    const parentNode = element.parentNode;\n    if (parentNode.nodeType === 1) {\n      return parentNode; // Regular node\n    } else if (parentNode.host) {\n      return parentNode.host; // Shadow root\n    }\n  }\n  return null; // Root node\n}\n\nexport default getComposedParent;\n"
  },
  {
    "path": "lib/commons/dom/get-element-by-reference.js",
    "content": "import isCurrentPageLink from './is-current-page-link';\n\n/**\n * Returns a reference to the element matching the attr URL fragment value\n * @method getElementByReference\n * @memberof axe.commons.dom\n * @instance\n * @param {Element} node\n * @param {String} attr Attribute name (href)\n * @return {Element}\n */\nfunction getElementByReference(node, attr) {\n  let fragment = node.getAttribute(attr);\n  if (!fragment) {\n    return null;\n  }\n\n  if (attr === 'href' && !isCurrentPageLink(node)) {\n    return null;\n  }\n\n  if (fragment.indexOf('#') !== -1) {\n    fragment = decodeURIComponent(fragment.substr(fragment.indexOf('#') + 1));\n  }\n\n  let candidate = document.getElementById(fragment);\n  if (candidate) {\n    return candidate;\n  }\n\n  candidate = document.getElementsByName(fragment);\n  if (candidate.length) {\n    return candidate[0];\n  }\n  return null;\n}\n\nexport default getElementByReference;\n"
  },
  {
    "path": "lib/commons/dom/get-element-coordinates.js",
    "content": "import getScrollOffset from './get-scroll-offset';\n\n/**\n * Get the coordinates of the element passed into the function relative to the document\n * @method getElementCoordinates\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} element The HTMLElement\n * @return {elementObj} elementObj Returns a `Object` with the following properties, which\n * each hold a value representing the pixels for each of the\n */\n/**\n * @typedef elementObj\n * @type {Object}\n * @property {Number} top The top coordinate of the element\n * @property {Number} right The right coordinate of the element\n * @property {Number} bottom The bottom coordinate of the element\n * @property {Number} left The left coordinate of the element\n * @property {Number} width The width of the element\n * @property {Number} height The height of the element\n */\nfunction getElementCoordinates(element) {\n  const scrollOffset = getScrollOffset(document),\n    xOffset = scrollOffset.left,\n    yOffset = scrollOffset.top,\n    coords = element.getBoundingClientRect();\n\n  return {\n    top: coords.top + yOffset,\n    right: coords.right + xOffset,\n    bottom: coords.bottom + yOffset,\n    left: coords.left + xOffset,\n    width: coords.right - coords.left,\n    height: coords.bottom - coords.top\n  };\n}\n\nexport default getElementCoordinates;\n"
  },
  {
    "path": "lib/commons/dom/get-element-stack.js",
    "content": "import { getRectStack } from './get-rect-stack';\nimport { getNodeFromTree } from '../../core/utils';\nimport getNodeGrid from './get-node-grid';\n\n/**\n * Return all elements that are at the center bounding rect of the passed in node.\n * @method getElementStack\n * @memberof axe.commons.dom\n * @param {Node} node\n * @return {Node[]}\n */\nfunction getElementStack(node) {\n  const grid = getNodeGrid(node);\n  if (!grid) {\n    return [];\n  }\n  const rect = getNodeFromTree(node).boundingClientRect;\n  return getRectStack(grid, rect);\n}\n\nexport default getElementStack;\n"
  },
  {
    "path": "lib/commons/dom/get-modal-dialog.js",
    "content": "import memoize from '../../core/utils/memoize';\nimport { querySelectorAllFilter } from '../../core/utils';\nimport isVisibleOnScreen from './is-visible-on-screen';\nimport createGrid from './create-grid';\nimport getIntersectionRect from '../math/get-intersection-rect';\n\n/**\n * Determine if a dialog element is opened as a modal. Currently there are no APIs to determine this so we'll use a bit of a hacky solution that has known issues.\n * This can tell us that a dialog element is open but it cannot tell us which one is the top layer, nor which one is visually on top. Nested dialogs that are opened using both `.show` and`.showModal` can cause issues as well.\n * @see https://github.com/dequelabs/axe-core/issues/3463\n * @return {VirtualNode|Null} The modal dialog virtual node or null if none are found\n */\nconst getModalDialog = memoize(function getModalDialogMemoized() {\n  // this is here for tests so we don't have\n  // to set up the virtual tree when code\n  // isn't testing this bit\n  if (!axe._tree) {\n    return null;\n  }\n\n  const dialogs = querySelectorAllFilter(\n    // TODO: es-module-_tree\n    axe._tree[0],\n    'dialog[open]',\n    vNode => {\n      const rect = vNode.boundingClientRect;\n      const stack = document.elementsFromPoint(rect.left + 1, rect.top + 1);\n      return stack.includes(vNode.actualNode) && isVisibleOnScreen(vNode);\n    }\n  );\n\n  if (!dialogs.length) {\n    return null;\n  }\n\n  // for Chrome and Firefox, look to see if\n  // elementsFromPoint returns the dialog\n  // when checking outside its bounds\n  const modalDialog = dialogs.find(dialog => {\n    const rect = dialog.boundingClientRect;\n    const stack = document.elementsFromPoint(rect.left - 10, rect.top - 10);\n\n    return stack.includes(dialog.actualNode);\n  });\n\n  if (modalDialog) {\n    return modalDialog;\n  }\n\n  // fallback for Safari, look at the grid to\n  // find a node to check as elementsFromPoint\n  // does not return inert nodes\n  return (\n    dialogs.find(dialog => {\n      const { vNode, rect } = getNodeFromGrid(dialog) ?? {};\n      if (!vNode) {\n        return false;\n      }\n\n      const stack = document.elementsFromPoint(rect.left + 1, rect.top + 1);\n      return !stack.includes(vNode.actualNode);\n    }) ?? null\n  );\n});\nexport default getModalDialog;\n\n/**\n * Find the first non-html from the grid to use as a test for elementsFromPoint\n * @return {Object}\n */\nfunction getNodeFromGrid(dialog) {\n  createGrid();\n  // TODO: es-module-_tree\n  const grid = axe._tree[0]._grid;\n  const viewRect = new window.DOMRect(\n    0,\n    0,\n    window.innerWidth,\n    window.innerHeight\n  );\n\n  if (!grid) {\n    return;\n  }\n\n  for (let row = 0; row < grid.cells.length; row++) {\n    const cols = grid.cells[row];\n    if (!cols) {\n      continue;\n    }\n\n    for (let col = 0; col < cols.length; col++) {\n      const cells = cols[col];\n      if (!cells) {\n        continue;\n      }\n\n      for (let i = 0; i < cells.length; i++) {\n        const vNode = cells[i];\n        const rect = vNode.boundingClientRect;\n        const intersection = getIntersectionRect(rect, viewRect);\n\n        if (\n          // html is always returned from\n          // elementsFromPoint\n          vNode.props.nodeName !== 'html' &&\n          vNode !== dialog &&\n          vNode.getComputedStylePropertyValue('pointer-events') !== 'none' &&\n          // ensure the element is visible in\n          // the current viewport for\n          // elementsFromPoint so we don't have\n          // to scroll\n          intersection\n        ) {\n          return { vNode, rect: intersection };\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/commons/dom/get-node-grid.js",
    "content": "import createGrid from './create-grid';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Get the grid an element exists in\n * @param {Node|VirtualNode} node\n * @returns {Grid}\n */\nexport default function getNodeGrid(node) {\n  createGrid(); // Ensure the grid exists\n  const { vNode } = nodeLookup(node);\n  return vNode._grid;\n}\n"
  },
  {
    "path": "lib/commons/dom/get-overflow-hidden-ancestors.js",
    "content": "import memoize from '../../core/utils/memoize';\n\n/**\n * Get all ancestor nodes (including the passed in node) that have overflow:hidden\n * @method getOverflowHiddenAncestors\n * @memberof axe.commons.dom\n * @param {VirtualNode} vNode\n * @returns {VirtualNode[]}\n */\nconst getOverflowHiddenAncestors = memoize(\n  function getOverflowHiddenAncestorsMemoized(vNode) {\n    const ancestors = [];\n\n    if (!vNode) {\n      return ancestors;\n    }\n\n    const overflow = vNode.getComputedStylePropertyValue('overflow');\n\n    if (overflow === 'hidden') {\n      ancestors.push(vNode);\n    }\n\n    return ancestors.concat(getOverflowHiddenAncestors(vNode.parent));\n  }\n);\n\nexport default getOverflowHiddenAncestors;\n"
  },
  {
    "path": "lib/commons/dom/get-rect-stack.js",
    "content": "import visuallySort from './visually-sort';\nimport { getRectCenter } from '../math';\n\nexport function getRectStack(grid, rect, recursed = false) {\n  const center = getRectCenter(rect);\n  const gridCell = grid.getCellFromPoint(center) || [];\n\n  const floorX = Math.floor(center.x);\n  const floorY = Math.floor(center.y);\n  let stack = gridCell.filter(gridCellNode => {\n    return gridCellNode.clientRects.some(clientRect => {\n      const rectX = clientRect.left;\n      const rectY = clientRect.top;\n\n      // perform an AABB (axis-aligned bounding box) collision check for the\n      // point inside the rect\n      // account for differences in how browsers handle floating point\n      // precision of bounding rects\n      return (\n        floorX < Math.floor(rectX + clientRect.width) &&\n        floorX >= Math.floor(rectX) &&\n        floorY < Math.floor(rectY + clientRect.height) &&\n        floorY >= Math.floor(rectY)\n      );\n    });\n  });\n\n  const gridContainer = grid.container;\n  if (gridContainer) {\n    stack = getRectStack(\n      gridContainer._grid,\n      gridContainer.boundingClientRect,\n      true\n    ).concat(stack);\n  }\n\n  if (!recursed) {\n    stack = stack\n      .sort(visuallySort)\n      .map(vNode => vNode.actualNode)\n      // always make sure html is the last element\n      .concat(document.documentElement)\n      // remove duplicates caused by adding client rects of the same node\n      .filter((node, index, array) => array.indexOf(node) === index);\n  }\n\n  return stack;\n}\n"
  },
  {
    "path": "lib/commons/dom/get-root-node.js",
    "content": "import getRootNode from '../../core/utils/get-root-node';\n\n/**\n * Return the document or document fragment (shadow DOM)\n * @method getRootNode\n * @memberof axe.commons.dom\n * @instance\n * @param {Element} node\n * @returns {DocumentFragment|Document}\n * @deprecated use axe.utils.getRootNode\n */\nexport default getRootNode;\n"
  },
  {
    "path": "lib/commons/dom/get-scroll-offset.js",
    "content": "/**\n * Get the scroll offset of the document passed in\n * @method getScrollOffset\n * @memberof axe.commons.dom\n * @instance\n * @param {Document} element The element to evaluate, defaults to document\n * @return {Object} Contains the attributes `x` and `y` which contain the scroll offsets\n */\nfunction getScrollOffset(element) {\n  if (!element.nodeType && element.document) {\n    element = element.document;\n  }\n\n  // 9 === Node.DOCUMENT_NODE\n  if (element.nodeType === 9) {\n    const docElement = element.documentElement,\n      body = element.body;\n\n    return {\n      left:\n        (docElement && docElement.scrollLeft) || (body && body.scrollLeft) || 0,\n      top: (docElement && docElement.scrollTop) || (body && body.scrollTop) || 0\n    };\n  }\n\n  return {\n    left: element.scrollLeft,\n    top: element.scrollTop\n  };\n}\n\nexport default getScrollOffset;\n"
  },
  {
    "path": "lib/commons/dom/get-tabbable-elements.js",
    "content": "import { querySelectorAll } from '../../core/utils';\nimport { parseTabindex } from '../../core/utils';\n\n/**\n * Get all elements (including given node) that are part of the tab order\n * @method getTabbableElements\n * @memberof axe.commons.dom\n * @instance\n * @param  {Object} virtualNode The virtualNode to assess\n * @return {Boolean}\n */\nfunction getTabbableElements(virtualNode) {\n  const nodeAndDescendents = querySelectorAll(virtualNode, '*');\n\n  const tabbableElements = nodeAndDescendents.filter(vNode => {\n    const isFocusable = vNode.isFocusable;\n    const tabIndex = parseTabindex(vNode.actualNode.getAttribute('tabindex'));\n\n    return tabIndex !== null ? isFocusable && tabIndex >= 0 : isFocusable;\n  });\n\n  return tabbableElements;\n}\n\nexport default getTabbableElements;\n"
  },
  {
    "path": "lib/commons/dom/get-target-rects.js",
    "content": "import findNearbyElms from './find-nearby-elms';\nimport isInTabOrder from './is-in-tab-order';\nimport { splitRects, hasVisualOverlap } from '../math';\nimport memoize from '../../core/utils/memoize';\nimport { contains } from '../../core/utils';\n\nexport default memoize(getTargetRects);\n\n/**\n * Return all unobscured rects of a target.\n * @see https://www.w3.org/TR/WCAG22/#dfn-bounding-boxes\n * @param {VitualNode} vNode\n * @return {DOMRect[]}\n */\nfunction getTargetRects(vNode) {\n  const display = vNode.getComputedStylePropertyValue('display');\n  const nodeRects =\n    display === 'inline' ? vNode.clientRects : [vNode.boundingClientRect];\n  const overlappingVNodes = findNearbyElms(vNode).filter(vNeighbor => {\n    return (\n      hasVisualOverlap(vNode, vNeighbor) &&\n      vNeighbor.getComputedStylePropertyValue('pointer-events') !== 'none' &&\n      !isDescendantNotInTabOrder(vNode, vNeighbor)\n    );\n  });\n\n  if (!overlappingVNodes.length) {\n    return nodeRects;\n  }\n\n  const obscuringRects = overlappingVNodes\n    .map(overlappingVNode => {\n      const overlappingDisplay =\n        overlappingVNode.getComputedStylePropertyValue('display');\n      return overlappingDisplay === 'inline'\n        ? overlappingVNode.clientRects\n        : overlappingVNode.boundingClientRect;\n    })\n    .flat(Infinity);\n  return splitRects(nodeRects, obscuringRects);\n}\n\nfunction isDescendantNotInTabOrder(vAncestor, vNode) {\n  return contains(vAncestor, vNode) && !isInTabOrder(vNode);\n}\n"
  },
  {
    "path": "lib/commons/dom/get-target-size.js",
    "content": "import getTargetRects from './get-target-rects';\nimport { rectHasMinimumSize } from '../math';\nimport memoize from '../../core/utils/memoize';\n\nexport default memoize(getTargetSize);\n\n/**\n * Compute the target size of an element.\n * @see https://www.w3.org/TR/WCAG22/#dfn-targets\n */\nfunction getTargetSize(vNode, minSize) {\n  const rects = getTargetRects(vNode);\n  return getLargestRect(rects, minSize);\n}\n\n// Find the largest rectangle in the array, prioritize ones that meet a minimum size\nfunction getLargestRect(rects, minSize) {\n  return rects.reduce((rectA, rectB) => {\n    const rectAisMinimum = rectHasMinimumSize(minSize, rectA);\n    const rectBisMinimum = rectHasMinimumSize(minSize, rectB);\n    // Prioritize rects that pass the minimum\n    if (rectAisMinimum !== rectBisMinimum) {\n      return rectAisMinimum ? rectA : rectB;\n    }\n    const areaA = rectA.width * rectA.height;\n    const areaB = rectB.width * rectB.height;\n    return areaA > areaB ? rectA : rectB;\n  });\n}\n"
  },
  {
    "path": "lib/commons/dom/get-text-element-stack.js",
    "content": "import { getRectStack } from './get-rect-stack';\nimport getNodeGrid from './get-node-grid';\nimport getVisibleChildTextRects from './get-visible-child-text-rects';\n\n/**\n * Return all elements that are at the center of each text client rect of the passed in node.\n * @method getTextElementStack\n * @memberof axe.commons.dom\n * @param {Node} node\n * @return {Array<Node[]>}\n */\nfunction getTextElementStack(node) {\n  const grid = getNodeGrid(node);\n  if (!grid) {\n    return [];\n  }\n\n  const clientRects = getVisibleChildTextRects(node);\n  return clientRects.map(rect => getRectStack(grid, rect));\n}\n\nexport default getTextElementStack;\n"
  },
  {
    "path": "lib/commons/dom/get-viewport-size.js",
    "content": "/**\n * Gets the width and height of the viewport; used to calculate the right and bottom boundaries of the viewable area.\n * @method getViewportSize\n * @memberof axe.commons.dom\n * @instance\n * @param  {Object}  win The `window` object that should be measured\n * @return {Object}  Object with the `width` and `height` of the viewport\n */\nfunction getViewportSize(win) {\n  const doc = win.document;\n  const docElement = doc.documentElement;\n\n  if (win.innerWidth) {\n    return {\n      width: win.innerWidth,\n      height: win.innerHeight\n    };\n  }\n\n  if (docElement) {\n    return {\n      width: docElement.clientWidth,\n      height: docElement.clientHeight\n    };\n  }\n\n  const body = doc.body;\n\n  return {\n    width: body.clientWidth,\n    height: body.clientHeight\n  };\n}\n\nexport default getViewportSize;\n"
  },
  {
    "path": "lib/commons/dom/get-visible-child-text-rects.js",
    "content": "import { getNodeFromTree, memoize } from '../../core/utils';\nimport { sanitize } from '../text';\nimport { getRectCenter, isPointInRect, getIntersectionRect } from '../math';\nimport getOverflowHiddenAncestors from './get-overflow-hidden-ancestors';\n\n/**\n * Get the visible text client rects of a node.\n * @method getVisibleChildTextRects\n * @memberof axe.commons.dom\n * @instance\n * @param {Element} node\n */\nconst getVisibleChildTextRects = memoize(\n  function getVisibleChildTextRectsMemoized(node) {\n    const vNode = getNodeFromTree(node);\n    const nodeRect = vNode.boundingClientRect;\n    const clientRects = [];\n    const overflowHiddenNodes = getOverflowHiddenAncestors(vNode);\n\n    node.childNodes.forEach(textNode => {\n      if (textNode.nodeType !== 3 || sanitize(textNode.nodeValue) === '') {\n        return;\n      }\n\n      const contentRects = getContentRects(textNode);\n      if (isOutsideNodeBounds(contentRects, nodeRect)) {\n        return;\n      }\n\n      clientRects.push(...filterHiddenRects(contentRects, overflowHiddenNodes));\n    });\n\n    /**\n     * if all text rects are larger than the bounds of the node,\n     * or goes outside of the bounds of the node, we need to use\n     * the nodes bounding rect so we stay within the bounds of the\n     * element.\n     *\n     * @see https://github.com/dequelabs/axe-core/issues/2178\n     * @see https://github.com/dequelabs/axe-core/issues/2483\n     * @see https://github.com/dequelabs/axe-core/issues/2681\n     *\n     * also need to resize the nodeRect to fit within the bounds of any overflow: hidden ancestors.\n     *\n     * @see https://github.com/dequelabs/axe-core/issues/4253\n     */\n    return clientRects.length\n      ? clientRects\n      : filterHiddenRects([nodeRect], overflowHiddenNodes);\n  }\n);\nexport default getVisibleChildTextRects;\n\nfunction getContentRects(node) {\n  const range = document.createRange();\n  range.selectNodeContents(node);\n  return Array.from(range.getClientRects());\n}\n\n/**\n * Check to see if the text rect size is outside the of the\n * nodes bounding rect. Since we use the midpoint of the element\n * when determining the rect stack we will also use the midpoint\n * of the text rect to determine out of bounds\n */\nfunction isOutsideNodeBounds(rects, nodeRect) {\n  return rects.some(rect => {\n    const centerPoint = getRectCenter(rect);\n    return !isPointInRect(centerPoint, nodeRect);\n  });\n}\n\n/**\n * Filter out 0 width and height rects (newline characters) and\n * any rects that are outside the bounds of overflow hidden\n * ancestors\n */\nfunction filterHiddenRects(contentRects, overflowHiddenNodes) {\n  const visibleRects = [];\n  contentRects.forEach(contentRect => {\n    // ie11 has newline characters return 0.00998, so we'll say if the\n    // line is < 1 it shouldn't be counted\n    if (contentRect.width < 1 || contentRect.height < 1) {\n      return;\n    }\n\n    // update the rect size to fit inside the bounds of all overflow\n    // hidden ancestors\n    const visibleRect = overflowHiddenNodes.reduce((rect, overflowNode) => {\n      return rect && getIntersectionRect(rect, overflowNode.boundingClientRect);\n    }, contentRect);\n\n    if (visibleRect) {\n      visibleRects.push(visibleRect);\n    }\n  });\n\n  return visibleRects;\n}\n"
  },
  {
    "path": "lib/commons/dom/has-content-virtual.js",
    "content": "import isVisualContent from './is-visual-content';\nimport labelVirtual from '../aria/label-virtual';\n\nconst hiddenTextElms = [\n  'head',\n  'title',\n  'template',\n  'script',\n  'style',\n  'iframe',\n  'object',\n  'video',\n  'audio',\n  'noscript'\n];\n\n/**\n * Test if the element has child nodes that are non-empty text nodes\n * @param {VirtualNode} elm\n * @returns boolean\n */\nexport function hasChildTextNodes(elm) {\n  if (hiddenTextElms.includes(elm.props.nodeName)) {\n    return false;\n  }\n  return elm.children.some(({ props }) => {\n    return props.nodeType === 3 && props.nodeValue.trim();\n  });\n}\n\n/**\n * Check that the element has visible content in the form of either text,\n * an aria-label or visual content such as image\n * @method hasContentVirtual\n * @memberof axe.commons.dom\n * @instance\n * @param\t{VirtualNode} elm Virtual Node to search\n * @param\t{Boolean} noRecursion If true, only the element is checked, otherwise it will search all child nodes\n * @param {Boolean} ignoreAria if true, ignores `aria label` computation for content deduction\n * @return {Boolean}\n */\nfunction hasContentVirtual(elm, noRecursion, ignoreAria) {\n  return (\n    // It has text\n    // or one of it's descendants does\n    hasChildTextNodes(elm) ||\n    // It is a graphical element\n    isVisualContent(elm.actualNode) ||\n    // It has an ARIA label\n    (!ignoreAria && !!labelVirtual(elm)) ||\n    (!noRecursion &&\n      elm.children.some(\n        child => child.actualNode.nodeType === 1 && hasContentVirtual(child)\n      ))\n  );\n}\n\nexport default hasContentVirtual;\n"
  },
  {
    "path": "lib/commons/dom/has-content.js",
    "content": "import hasContentVirtual from './has-content-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Find virtual node and call hasContentVirtual()\n * IMPORTANT: This method requires the composed tree at axe._tree\n * @see axe.commons.dom.hasContentVirtual\n * @method hasContent\n * @memberof axe.commons.dom\n * @instance\n * @param {DOMNode} elm DOMNode element to check\n * @param {Boolean} noRecursion If true, only the element is checked, otherwise it will search all child nodes\n * @param {Boolean} ignoreAria if true, ignores `aria label` computation for content deduction\n * @return {Boolean}\n */\nfunction hasContent(elm, noRecursion, ignoreAria) {\n  elm = getNodeFromTree(elm);\n  return hasContentVirtual(elm, noRecursion, ignoreAria);\n}\n\nexport default hasContent;\n"
  },
  {
    "path": "lib/commons/dom/has-lang-text.js",
    "content": "import { hasChildTextNodes } from './has-content-virtual';\nimport isVisualContent from './is-visual-content';\nimport isHiddenForEveryone from './is-hidden-for-everyone';\n\n/**\n * Check that a node has text, or an accessible name which language is defined by the\n * nearest ancestor's lang attribute.\n * @param {VirtualNode} virtualNode\n * @return boolean\n */\nexport default function hasLangText(virtualNode) {\n  if (\n    typeof virtualNode.children === 'undefined' ||\n    hasChildTextNodes(virtualNode)\n  ) {\n    return true;\n  }\n  if (virtualNode.props.nodeType === 1 && isVisualContent(virtualNode)) {\n    // See: https://github.com/dequelabs/axe-core/issues/3281\n    return !!axe.commons.text.accessibleTextVirtual(virtualNode);\n  }\n  return virtualNode.children.some(\n    child =>\n      !child.attr('lang') && // non-empty lang\n      hasLangText(child) && // has text\n      !isHiddenForEveryone(child) // Not hidden for anyone\n  );\n}\n"
  },
  {
    "path": "lib/commons/dom/idrefs.js",
    "content": "import getRootNode from './get-root-node';\nimport { tokenList } from '../../core/utils';\n\n/**\n * Get elements referenced via a space-separated token attribute;\n * it will insert `null` for any Element that is not found\n * @method idrefs\n * @memberof axe.commons.dom\n * @instance\n * @param  {HTMLElement} node\n * @param  {String} attr The name of attribute\n * @return {Array|null} Array of elements (or `null` if not found)\n *\n * NOTE: When in a shadow DOM environment: ID refs (even for slotted content)\n * refer to the document in which the element is considered to be in the\n * \"light DOM\". Therefore, we use getElementById on the root node and not QSA\n * on the flattened tree to dereference idrefs.\n *\n */\nfunction idrefs(node, attr) {\n  node = node.actualNode || node;\n\n  try {\n    const doc = getRootNode(node);\n    const result = [];\n    let attrValue = node.getAttribute(attr);\n\n    if (attrValue) {\n      attrValue = tokenList(attrValue);\n      for (let index = 0; index < attrValue.length; index++) {\n        result.push(doc.getElementById(attrValue[index]));\n      }\n    }\n\n    return result;\n  } catch {\n    throw new TypeError('Cannot resolve id references for non-DOM nodes');\n  }\n}\n\nexport default idrefs;\n"
  },
  {
    "path": "lib/commons/dom/index.js",
    "content": "/**\n * Namespace for dom-related utilities.\n * @namespace dom\n * @memberof axe.commons\n */\nexport { default as findElmsInContext } from './find-elms-in-context';\nexport { default as findUpVirtual } from './find-up-virtual';\nexport { default as findUp } from './find-up';\nexport { default as findNearbyElms } from './find-nearby-elms';\nexport { default as focusDisabled } from './focus-disabled';\nexport { default as getComposedParent } from './get-composed-parent';\nexport { default as getElementByReference } from './get-element-by-reference';\nexport { default as getElementCoordinates } from './get-element-coordinates';\nexport { default as getElementStack } from './get-element-stack';\nexport { default as getModalDialog } from './get-modal-dialog';\nexport { default as getOverflowHiddenAncestors } from './get-overflow-hidden-ancestors';\nexport { default as getRootNode } from './get-root-node';\nexport { default as getScrollOffset } from './get-scroll-offset';\nexport { default as getTabbableElements } from './get-tabbable-elements';\nexport { default as getTargetRects } from './get-target-rects';\nexport { default as getTargetSize } from './get-target-size';\nexport { default as getTextElementStack } from './get-text-element-stack';\nexport { default as getViewportSize } from './get-viewport-size';\nexport { default as getVisibleChildTextRects } from './get-visible-child-text-rects';\nexport { default as hasContentVirtual } from './has-content-virtual';\nexport { default as hasContent } from './has-content';\nexport { default as hasLangText } from './has-lang-text';\nexport { default as idrefs } from './idrefs';\nexport { default as insertedIntoFocusOrder } from './inserted-into-focus-order';\nexport { default as isCurrentPageLink } from './is-current-page-link';\nexport { default as isFocusable } from './is-focusable';\nexport { default as isHiddenWithCSS } from './is-hidden-with-css';\nexport { default as isHiddenForEveryone } from './is-hidden-for-everyone';\nexport { default as isHTML5 } from './is-html5';\nexport { default as isInTabOrder } from './is-in-tab-order';\nexport { default as isInTextBlock } from './is-in-text-block';\nexport { default as isInert } from './is-inert';\nexport { default as isModalOpen } from './is-modal-open';\nexport { default as isMultiline } from './is-multiline';\nexport { default as isNativelyFocusable } from './is-natively-focusable';\nexport { default as isNode } from './is-node';\nexport { default as isOffscreen } from './is-offscreen';\nexport { default as isOpaque } from './is-opaque';\nexport { default as isSkipLink } from './is-skip-link';\nexport { default as isVisibleToScreenReaders } from './is-visible-to-screenreader';\nexport { default as isVisibleOnScreen } from './is-visible-on-screen';\nexport { default as isVisible } from './is-visible';\nexport { default as isVisualContent } from './is-visual-content';\nexport { default as reduceToElementsBelowFloating } from './reduce-to-elements-below-floating';\nexport { default as shadowElementsFromPoint } from './shadow-elements-from-point';\nexport { default as urlPropsFromAttribute } from './url-props-from-attribute';\nexport { default as visuallyContains } from './visually-contains';\nexport { default as visuallyOverlaps } from './visually-overlaps';\nexport { default as visuallySort } from './visually-sort';\nexport { default as createGrid } from './create-grid';\nexport { default as getNodeGrid } from './get-node-grid';\n"
  },
  {
    "path": "lib/commons/dom/inserted-into-focus-order.js",
    "content": "import isFocusable from './is-focusable';\nimport isNativelyFocusable from './is-natively-focusable';\nimport { parseTabindex } from '../../core/utils';\n\n/**\n * Determines if an element is in the focus order, but would not be if its\n * tabindex were unspecified.\n * @method insertedIntoFocusOrder\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} el The HTMLElement\n * @return {Boolean} True if the element is in the focus order but wouldn't be\n * if its tabindex were removed. Else, false.\n */\nfunction insertedIntoFocusOrder(el) {\n  const tabIndex = parseTabindex(el.getAttribute('tabindex'));\n\n  // an element that has an invalid tabindex will return 0 or -1 based on\n  // if it is natively focusable or not, which will always be false for this\n  // check as NaN is not > 1\n  // @see https://www.w3.org/TR/html51/editing.html#the-tabindex-attribute\n  return tabIndex > -1 && isFocusable(el) && !isNativelyFocusable(el);\n}\n\nexport default insertedIntoFocusOrder;\n"
  },
  {
    "path": "lib/commons/dom/is-current-page-link.js",
    "content": "// angular skip links start with /#\nconst angularSkipLinkRegex = /^\\/\\#/;\n\n// angular router link uses #! or #/\nconst angularRouterLinkRegex = /^#[!/]/;\n\n/**\n * Determine if an anchor elements href attribute references the current page.\n * @method isCurrentPageLink\n * @memberof axe.commons.dom\n * @param {HTMLAnchorElement} anchor\n * @return {Boolean|null}\n */\nexport default function isCurrentPageLink(anchor) {\n  const href = anchor.getAttribute('href');\n  if (!href || href === '#') {\n    return false;\n  }\n\n  if (angularSkipLinkRegex.test(href)) {\n    return true;\n  }\n\n  const { hash, protocol, hostname, port, pathname } = anchor;\n  if (angularRouterLinkRegex.test(hash)) {\n    return false;\n  }\n\n  if (href.charAt(0) === '#') {\n    return true;\n  }\n\n  // jsdom can have window.location.origin set to \"null\" (the string)\n  // if the url option is not set when parsing the dom string\n  if (\n    typeof window.location?.origin !== 'string' ||\n    window.location.origin.indexOf('://') === -1\n  ) {\n    return null;\n  }\n\n  // ie11 does not support window.origin\n  const currentPageUrl = window.location.origin + window.location.pathname;\n\n  // ie11 does not have anchor.origin so we need to construct\n  // it ourselves\n  // also ie11 has empty protocol, hostname, and port when the\n  // link is relative, so use window.location.origin in these cases\n  let url;\n  if (!hostname) {\n    url = window.location.origin;\n  } else {\n    url = `${protocol}//${hostname}${port ? `:${port}` : ''}`;\n  }\n\n  // ie11 has empty pathname if link is just a hash, so use\n  // window.location.pathname in these cases\n  if (!pathname) {\n    url += window.location.pathname;\n  } else {\n    // ie11 pathname does not start with / but chrome and firefox do\n    url += (pathname[0] !== '/' ? '/' : '') + pathname;\n  }\n\n  return url === currentPageUrl;\n}\n"
  },
  {
    "path": "lib/commons/dom/is-focusable.js",
    "content": "import focusDisabled from './focus-disabled';\nimport isNativelyFocusable from './is-natively-focusable';\nimport { nodeLookup } from '../../core/utils';\nimport { parseTabindex } from '../../core/utils';\n\n/**\n * Determines if an element is keyboard or programmatically focusable.\n * @method isFocusable\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} el The HTMLElement\n * @return {Boolean} The element's focusability status\n */\nexport default function isFocusable(el) {\n  const { vNode } = nodeLookup(el);\n\n  if (vNode.props.nodeType !== 1) {\n    return false;\n  }\n\n  if (focusDisabled(vNode)) {\n    return false;\n  } else if (isNativelyFocusable(vNode)) {\n    return true;\n  }\n  // check if the tabindex is specified and a parseable number\n  const tabindex = parseTabindex(vNode.attr('tabindex'));\n  return tabindex !== null;\n}\n"
  },
  {
    "path": "lib/commons/dom/is-hidden-for-everyone.js",
    "content": "import { nodeLookup } from '../../core/utils';\nimport memoize from '../../core/utils/memoize';\nimport {\n  nativelyHidden,\n  displayHidden,\n  visibilityHidden,\n  contentVisibiltyHidden,\n  detailsHidden\n} from './visibility-methods';\n\nconst hiddenMethods = [\n  displayHidden,\n  visibilityHidden,\n  contentVisibiltyHidden,\n  detailsHidden\n];\n\n/**\n * Determine if an element is hidden from screenreaders and visual users\n * @method isHiddenForEveryone\n * @memberof axe.commons.dom\n * @param {VirtualNode} vNode The Virtual Node\n * @param {Object} [options]\n * @param {Boolean} [options.skipAncestors] If the ancestor tree should be not be used\n * @param {Boolean} [options.isAncestor] If this function is being called on an ancestor for the target node\n * @return {Boolean} The element's visibility state\n */\nexport default function isHiddenForEveryone(\n  vNode,\n  { skipAncestors, isAncestor = false } = {}\n) {\n  vNode = nodeLookup(vNode).vNode;\n\n  if (skipAncestors) {\n    return isHiddenSelf(vNode, isAncestor);\n  }\n\n  return isHiddenAncestors(vNode, isAncestor);\n}\n\n/**\n * Check the element for visibility state\n */\nconst isHiddenSelf = memoize(function isHiddenSelfMemoized(vNode, isAncestor) {\n  if (nativelyHidden(vNode)) {\n    return true;\n  }\n\n  if (!vNode.actualNode) {\n    return false;\n  }\n\n  if (hiddenMethods.some(method => method(vNode, { isAncestor }))) {\n    return true;\n  }\n\n  // detached node\n  if (!vNode.actualNode.isConnected) {\n    return true;\n  }\n\n  return false;\n});\n\n/**\n * Check the element and ancestors for visibility state\n */\nconst isHiddenAncestors = memoize(\n  function isHiddenAncestorsMemoized(vNode, isAncestor) {\n    if (isHiddenSelf(vNode, isAncestor)) {\n      return true;\n    }\n\n    if (!vNode.parent) {\n      return false;\n    }\n\n    return isHiddenAncestors(vNode.parent, true);\n  }\n);\n"
  },
  {
    "path": "lib/commons/dom/is-hidden-with-css.js",
    "content": "import getComposedParent from './get-composed-parent';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Determine whether an element is hidden based on css\n * @method isHiddenWithCSS\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} el The HTML Element\n * @param {Boolean} descendentVisibilityValue (Optional) immediate descendant visibility value used for recursive computation\n * @return {Boolean} the element's hidden status\n * @deprecated use isHiddenForEveryone\n */\nfunction isHiddenWithCSS(el, descendentVisibilityValue) {\n  const { vNode, domNode } = nodeLookup(el);\n\n  if (!vNode) {\n    return _isHiddenWithCSS(domNode, descendentVisibilityValue);\n  }\n\n  if (vNode._isHiddenWithCSS === void 0) {\n    vNode._isHiddenWithCSS = _isHiddenWithCSS(\n      domNode,\n      descendentVisibilityValue\n    );\n  }\n\n  return vNode._isHiddenWithCSS;\n}\n\nfunction _isHiddenWithCSS(el, descendentVisibilityValue) {\n  if (el.nodeType === 9) {\n    // 9 === Node.DOCUMENT\n    return false;\n  }\n\n  if (el.nodeType === 11) {\n    // 11 === Node.DOCUMENT_FRAGMENT_NODE\n    el = el.host; // swap to host node\n  }\n\n  if (['STYLE', 'SCRIPT'].includes(el.nodeName.toUpperCase())) {\n    return false;\n  }\n\n  const style = window.getComputedStyle(el, null);\n  if (!style) {\n    throw new Error('Style does not exist for the given element.');\n  }\n\n  const displayValue = style.getPropertyValue('display');\n  if (displayValue === 'none') {\n    return true;\n  }\n\n  const HIDDEN_VISIBILITY_VALUES = ['hidden', 'collapse'];\n  const visibilityValue = style.getPropertyValue('visibility');\n  if (\n    HIDDEN_VISIBILITY_VALUES.includes(visibilityValue) &&\n    !descendentVisibilityValue\n  ) {\n    return true;\n  }\n\n  if (\n    HIDDEN_VISIBILITY_VALUES.includes(visibilityValue) &&\n    descendentVisibilityValue &&\n    HIDDEN_VISIBILITY_VALUES.includes(descendentVisibilityValue)\n  ) {\n    return true;\n  }\n\n  const parent = getComposedParent(el);\n  if (parent && !HIDDEN_VISIBILITY_VALUES.includes(visibilityValue)) {\n    return isHiddenWithCSS(parent, visibilityValue);\n  }\n  return false;\n}\n\nexport default isHiddenWithCSS;\n"
  },
  {
    "path": "lib/commons/dom/is-html5.js",
    "content": "/**\n * Determines if a document node is HTML 5\n * @method isHTML5\n * @memberof axe.commons.dom\n * @instance\n * @param {Node} doc\n * @return {Boolean}\n */\nfunction isHTML5(doc) {\n  const node = doc.doctype;\n  if (node === null) {\n    return false;\n  }\n  return node.name === 'html' && !node.publicId && !node.systemId;\n}\n\nexport default isHTML5;\n"
  },
  {
    "path": "lib/commons/dom/is-in-tab-order.js",
    "content": "import { nodeLookup } from '../../core/utils';\nimport isFocusable from './is-focusable';\nimport { parseTabindex } from '../../core/utils';\n\n/**\n * Determines if an element is focusable and able to be tabbed to.\n * @method isInTabOrder\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} el The HTMLElement\n * @return {Boolean} The element's tabindex status\n */\nexport default function isInTabOrder(el) {\n  const { vNode } = nodeLookup(el);\n\n  if (vNode.props.nodeType !== 1) {\n    return false;\n  }\n\n  const tabindex = parseTabindex(vNode.attr('tabindex'));\n  if (tabindex <= -1) {\n    return false; // Elements with tabindex=-1 are never in the tab order\n  }\n\n  return isFocusable(vNode);\n}\n"
  },
  {
    "path": "lib/commons/dom/is-in-text-block.js",
    "content": "import getComposedParent from './get-composed-parent';\nimport sanitize from '../text/sanitize';\nimport { getNodeFromTree } from '../../core/utils';\nimport getRoleType from '../aria/get-role-type';\n\nfunction walkDomNode(node, functor) {\n  if (functor(node.actualNode) !== false) {\n    node.children.forEach(child => walkDomNode(child, functor));\n  }\n}\n\nconst blockLike = [\n  'block',\n  'list-item',\n  'table',\n  'flex',\n  'grid',\n  'inline-block'\n];\n\nfunction isBlock(elm) {\n  const display = window.getComputedStyle(elm).getPropertyValue('display');\n  return blockLike.includes(display) || display.substr(0, 6) === 'table-';\n}\n\nfunction getBlockParent(node) {\n  // Find the closest parent\n  let parentBlock = getComposedParent(node);\n  while (parentBlock && !isBlock(parentBlock)) {\n    parentBlock = getComposedParent(parentBlock);\n  }\n\n  return getNodeFromTree(parentBlock);\n}\n\n/**\n * Determines if an element is within a text block\n * With `noLengthCompare` true, will return if there is any non-space text outside\n * widgets. When false, compares the length of non-widget text to widget text\n *\n * @param  {Element} node [description]\n * @param  {Object} options Optional\n * @property {Bool} noLengthCompare\n * @return {Boolean}      [description]\n */\nfunction isInTextBlock(node, options) {\n  if (isBlock(node)) {\n    // Ignore if the link is a block\n    return false;\n  }\n\n  // Find all the text part of the parent block not in a link, and all the text in a link\n  const virtualParent = getBlockParent(node);\n  let parentText = '';\n  let widgetText = '';\n  let inBrBlock = 0;\n\n  // We want to ignore hidden text, and if br / hr is used, only use the section of the parent\n  // that has the link we're looking at\n  walkDomNode(virtualParent, currNode => {\n    // We're already passed it, skip everything else\n    if (inBrBlock === 2) {\n      return false;\n    }\n\n    if (currNode.nodeType === 3) {\n      // Add the text to the parent\n      parentText += currNode.nodeValue;\n    }\n    // Ignore any node that's not an element (or text as above)\n    if (currNode.nodeType !== 1) {\n      return;\n    }\n\n    const nodeName = (currNode.nodeName || '').toUpperCase();\n    if (currNode === node) {\n      inBrBlock = 1;\n    }\n    // BR and HR elements break the line\n    if (['BR', 'HR'].includes(nodeName)) {\n      if (inBrBlock === 0) {\n        parentText = '';\n        widgetText = '';\n      } else {\n        inBrBlock = 2;\n      }\n\n      // Don't walk nodes with content not displayed on screen.\n    } else if (\n      currNode.style.display === 'none' ||\n      currNode.style.overflow === 'hidden' ||\n      !['', null, 'none'].includes(currNode.style.float) ||\n      !['', null, 'relative'].includes(currNode.style.position)\n    ) {\n      return false;\n\n      // Don't walk widgets, we're only interested in what's not in them.\n    } else if (getRoleType(currNode) === 'widget') {\n      // Grab all the text from this element, but don't walk down it's children\n      widgetText += currNode.textContent;\n      return false;\n    }\n  });\n\n  parentText = sanitize(parentText);\n  if (options?.noLengthCompare) {\n    return parentText.length !== 0;\n  }\n\n  widgetText = sanitize(widgetText);\n  return parentText.length > widgetText.length;\n}\n\nexport default isInTextBlock;\n"
  },
  {
    "path": "lib/commons/dom/is-inert.js",
    "content": "import memoize from '../../core/utils/memoize';\nimport getModalDialog from './get-modal-dialog';\nimport { contains } from '../../core/utils';\n\n/**\n * Determines if an element is inside an inert subtree.\n * @param {VirtualNode} vNode\n * @param {Boolean} [options.skipAncestors] If the ancestor tree should not be used\n * @return {Boolean} The element's inert state\n */\nexport default function isInert(vNode, { skipAncestors, isAncestor } = {}) {\n  if (skipAncestors) {\n    return isInertSelf(vNode, isAncestor);\n  }\n\n  return isInertAncestors(vNode, isAncestor);\n}\n\n/**\n * Check the element for inert\n */\nconst isInertSelf = memoize(function isInertSelfMemoized(vNode, isAncestor) {\n  if (vNode.hasAttr('inert')) {\n    return true;\n  }\n\n  if (!isAncestor && vNode.actualNode) {\n    // elements outside of an opened modal\n    // dialog are treated as inert by the\n    // browser\n    const modalDialog = getModalDialog();\n    if (modalDialog && !contains(modalDialog, vNode)) {\n      return true;\n    }\n  }\n\n  return false;\n});\n\n/**\n * Check the element and ancestors for inert\n */\nconst isInertAncestors = memoize(\n  function isInertAncestorsMemoized(vNode, isAncestor) {\n    if (isInertSelf(vNode, isAncestor)) {\n      return true;\n    }\n\n    if (!vNode.parent) {\n      return false;\n    }\n\n    return isInertAncestors(vNode.parent, true);\n  }\n);\n"
  },
  {
    "path": "lib/commons/dom/is-modal-open.js",
    "content": "import isVisibleOnScreen from './is-visible-on-screen';\nimport getViewportSize from './get-viewport-size';\nimport cache from '../../core/base/cache';\nimport { querySelectorAllFilter } from '../../core/utils';\n\n/**\n * Determines if there is a modal currently open.\n * @method isModalOpen\n * @memberof axe.commons.dom\n * @instance\n * @return {Boolean|undefined} True if we know (or our best guess) that a modal is open, undefined if we can't tell (doesn't mean there isn't one open)\n */\nfunction isModalOpen(options) {\n  options = options || {};\n  const modalPercent = options.modalPercent || 0.75;\n\n  // there is no \"definitive\" way to code a modal so detecting when one is open\n  // is a bit of a guess. a modal won't always be accessible, so we can't rely\n  // on the `role` attribute, and relying on a class name as a convention is\n  // unreliable. we also cannot rely on the body/html not scrolling.\n  //\n  // because of this, we will look for two different types of modals:\n  // \"definitely a modal\" and \"could be a modal.\"\n  //\n  // \"definitely a modal\" is any visible element that is coded to be a modal\n  // by using one of the following criteria:\n  //\n  // - has the attribute `role=dialog`\n  // - has the attribute `aria-modal=true`\n  // - is the dialog element\n  //\n  // \"could be a modal\" is a visible element that takes up more than 75% of\n  // the screen (though typically full width/height) and is the top-most element\n  // in the viewport. since we aren't sure if it is or is not a modal this is\n  // just our best guess of being one based on convention.\n\n  if (cache.get('isModalOpen')) {\n    return cache.get('isModalOpen');\n  }\n\n  const definiteModals = querySelectorAllFilter(\n    // TODO: es-module-_tree\n    axe._tree[0],\n    'dialog, [role=dialog], [aria-modal=true]',\n    isVisibleOnScreen\n  );\n\n  if (definiteModals.length) {\n    cache.set('isModalOpen', true);\n    return true;\n  }\n\n  // to find a \"could be a modal\" we will take the element stack from each of\n  // four corners and one from the middle of the viewport (total of 5). if each\n  // stack contains an element whose width/height is >= 75% of the screen, we\n  // found a \"could be a modal\"\n  const viewport = getViewportSize(window);\n  const percentWidth = viewport.width * modalPercent;\n  const percentHeight = viewport.height * modalPercent;\n  const x = (viewport.width - percentWidth) / 2;\n  const y = (viewport.height - percentHeight) / 2;\n\n  const points = [\n    // top-left corner\n    { x, y },\n    // top-right corner\n    { x: viewport.width - x, y },\n    // center\n    { x: viewport.width / 2, y: viewport.height / 2 },\n    // bottom-left corner\n    { x, y: viewport.height - y },\n    // bottom-right corner\n    { x: viewport.width - x, y: viewport.height - y }\n  ];\n\n  const stacks = points.map(point => {\n    return Array.from(document.elementsFromPoint(point.x, point.y));\n  });\n\n  for (let i = 0; i < stacks.length; i++) {\n    // a modal isn't guaranteed to be the top most element so we'll have to\n    // find the first element in the stack that meets the modal criteria\n    // and make sure it's in the other stacks\n    const modalElement = stacks[i].find(elm => {\n      const style = window.getComputedStyle(elm);\n      return (\n        parseInt(style.width, 10) >= percentWidth &&\n        parseInt(style.height, 10) >= percentHeight &&\n        style.getPropertyValue('pointer-events') !== 'none' &&\n        (style.position === 'absolute' || style.position === 'fixed')\n      );\n    });\n\n    if (modalElement && stacks.every(stack => stack.includes(modalElement))) {\n      cache.set('isModalOpen', true);\n      return true;\n    }\n  }\n\n  cache.set('isModalOpen', undefined);\n  return undefined;\n}\n\nexport default isModalOpen;\n"
  },
  {
    "path": "lib/commons/dom/is-multiline.js",
    "content": "/**\n * Returns true if content has client rects that have no vertical overlap.\n * I.e. they are rendered on different \"lines\".\n * @param {Element} domNode\n * @param {number} margin (default: 2)\n * @returns {number}\n */\nexport default function isMultiline(domNode, margin = 2) {\n  const range = domNode.ownerDocument.createRange();\n  range.setStart(domNode, 0);\n  range.setEnd(domNode, domNode.childNodes.length);\n  let lastLineEnd = 0;\n  let lineCount = 0;\n  for (const rect of range.getClientRects()) {\n    if (rect.height <= margin) {\n      continue;\n    }\n    if (lastLineEnd > rect.top + margin) {\n      lastLineEnd = Math.max(lastLineEnd, rect.bottom);\n    } else if (lineCount === 0) {\n      lastLineEnd = rect.bottom;\n      lineCount++;\n    } else {\n      return true;\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "lib/commons/dom/is-natively-focusable.js",
    "content": "import { nodeLookup, querySelectorAll } from '../../core/utils';\nimport focusDisabled from './focus-disabled';\n\n/**\n * Determines if an element is focusable without considering its tabindex\n * @method isNativelyFocusable\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement|VirtualNode} el The HTMLElement\n * @return {Boolean} True if the element is in the focus order but wouldn't be\n * if its tabindex were removed. Else, false.\n */\nfunction isNativelyFocusable(el) {\n  const { vNode } = nodeLookup(el);\n\n  if (!vNode || focusDisabled(vNode)) {\n    return false;\n  }\n\n  switch (vNode.props.nodeName) {\n    case 'a':\n    case 'area':\n      if (vNode.hasAttr('href')) {\n        return true;\n      }\n      break;\n    case 'input':\n      return vNode.props.type !== 'hidden';\n    case 'textarea':\n    case 'select':\n    case 'summary':\n    case 'button':\n      return true;\n    case 'details':\n      return !querySelectorAll(vNode, 'summary').length;\n  }\n  return false;\n}\n\nexport default isNativelyFocusable;\n"
  },
  {
    "path": "lib/commons/dom/is-node.js",
    "content": "/**\n * Determines if element is an instance of Node\n * @method isNode\n * @memberof axe.commons.dom\n * @instance\n * @deprecated\n * @param  {Element} element\n * @return {Boolean}\n */\nfunction isNode(element) {\n  return element instanceof window.Node;\n}\n\nexport default isNode;\n"
  },
  {
    "path": "lib/commons/dom/is-offscreen.js",
    "content": "import getComposedParent from './get-composed-parent';\nimport getElementCoordinates from './get-element-coordinates';\nimport getViewportSize from './get-viewport-size';\nimport { nodeLookup } from '../../core/utils';\n\nfunction noParentScrolled(element, offset) {\n  element = getComposedParent(element);\n  while (element && element.nodeName.toLowerCase() !== 'html') {\n    if (element.scrollTop) {\n      offset += element.scrollTop;\n      if (offset >= 0) {\n        return false;\n      }\n    }\n    element = getComposedParent(element);\n  }\n  return true;\n}\n\n/**\n * Determines if element is off screen\n * @method isOffscreen\n * @memberof axe.commons.dom\n * @instance\n * @param  {Element} element\n * @param {Object} [options]\n * @param {Boolean} [options.isAncestor] If this function is being called on an ancestor of the target node\n * @return {Boolean|undefined}\n */\nfunction isOffscreen(element, { isAncestor } = {}) {\n  if (isAncestor) {\n    return false;\n  }\n\n  const { domNode } = nodeLookup(element);\n  if (!domNode) {\n    return undefined;\n  }\n\n  let leftBoundary;\n  const docElement = document.documentElement;\n  const styl = window.getComputedStyle(domNode);\n  const dir = window\n    .getComputedStyle(document.body || docElement)\n    .getPropertyValue('direction');\n  const coords = getElementCoordinates(domNode);\n\n  // bottom edge beyond\n  if (\n    coords.bottom < 0 &&\n    (noParentScrolled(domNode, coords.bottom) || styl.position === 'absolute')\n  ) {\n    return true;\n  }\n\n  if (coords.left === 0 && coords.right === 0) {\n    //This is an edge case, an empty (zero-width) element that isn't positioned 'off screen'.\n    return false;\n  }\n\n  if (dir === 'ltr') {\n    if (coords.right <= 0) {\n      return true;\n    }\n  } else {\n    leftBoundary = Math.max(\n      docElement.scrollWidth,\n      getViewportSize(window).width\n    );\n    if (coords.left >= leftBoundary) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nexport default isOffscreen;\n"
  },
  {
    "path": "lib/commons/dom/is-opaque.js",
    "content": "import elementHasImage from '../color/element-has-image';\nimport getOwnBackgroundColor from '../color/get-own-background-color';\n\n/**\n * Determines whether an element has a fully opaque background, whether solid color or an image\n * @param {Element} node\n * @return {Boolean} false if the background is transparent, true otherwise\n */\nfunction isOpaque(node) {\n  const style = window.getComputedStyle(node);\n  return (\n    elementHasImage(node, style) || getOwnBackgroundColor(style).alpha === 1\n  );\n}\n\nexport default isOpaque;\n"
  },
  {
    "path": "lib/commons/dom/is-skip-link.js",
    "content": "import cache from '../../core/base/cache';\nimport { querySelectorAll } from '../../core/utils';\nimport isCurrentPageLink from './is-current-page-link';\n\n/**\n * Determines if element is a skip link.\n *\n * Define a skip link as any anchor element whose resolved href\n * resolves to the current page and uses a fragment identifier (#)\n * and which precedes the first anchor element whose resolved href\n * does not resolve to the current page or that doesn't use a\n * fragment identifier.\n * @method isSkipLink\n * @memberof axe.commons.dom\n * @instance\n * @param  {Element} element\n * @return {Boolean}\n */\nexport default function isSkipLink(element) {\n  if (!element.href) {\n    return false;\n  }\n\n  const firstPageLink = cache.get('firstPageLink', generateFirstPageLink);\n\n  if (!firstPageLink) {\n    return true;\n  }\n\n  return (\n    element.compareDocumentPosition(firstPageLink.actualNode) ===\n    element.DOCUMENT_POSITION_FOLLOWING\n  );\n}\n\nfunction generateFirstPageLink() {\n  let firstPageLink;\n  if (!window.location.origin) {\n    firstPageLink = querySelectorAll(\n      // TODO: es-module-_tree\n      axe._tree,\n      'a:not([href^=\"#\"]):not([href^=\"/#\"]):not([href^=\"javascript:\"])'\n    )[0];\n  } else {\n    firstPageLink = querySelectorAll(\n      axe._tree,\n      'a[href]:not([href^=\"javascript:\"])'\n    ).find(link => !isCurrentPageLink(link.actualNode));\n  }\n\n  // null will signify no first page link\n  return firstPageLink || null;\n}\n"
  },
  {
    "path": "lib/commons/dom/is-visible-on-screen.js",
    "content": "import { nodeLookup } from '../../core/utils';\nimport memoize from '../../core/utils/memoize';\nimport isHiddenForEveryone from './is-hidden-for-everyone';\nimport {\n  opacityHidden,\n  scrollHidden,\n  overflowHidden,\n  clipHidden,\n  areaHidden\n} from './visibility-methods';\nimport isOffscreen from './is-offscreen';\n\nconst hiddenMethods = [\n  opacityHidden,\n  scrollHidden,\n  overflowHidden,\n  clipHidden,\n  isOffscreen\n];\n\n/**\n * Determine if an element is visible on screen\n * @method isVisibleOnScreen\n * @memberof axe.commons.dom\n * @param {VirtualNode} vNode The Virtual Node\n * @return {Boolean} True if the element is visible on screen\n */\nexport default function isVisibleOnScreen(vNode) {\n  vNode = nodeLookup(vNode).vNode;\n  return isVisibleOnScreenVirtual(vNode);\n}\n\nconst isVisibleOnScreenVirtual = memoize(\n  function isVisibleOnScreenMemoized(vNode, isAncestor) {\n    if (vNode.actualNode && vNode.props.nodeName === 'area') {\n      return !areaHidden(vNode, isVisibleOnScreenVirtual);\n    }\n\n    if (isHiddenForEveryone(vNode, { skipAncestors: true, isAncestor })) {\n      return false;\n    }\n\n    if (\n      vNode.actualNode &&\n      hiddenMethods.some(method => method(vNode, { isAncestor }))\n    ) {\n      return false;\n    }\n\n    if (!vNode.parent) {\n      return true;\n    }\n\n    return isVisibleOnScreenVirtual(vNode.parent, true);\n  }\n);\n"
  },
  {
    "path": "lib/commons/dom/is-visible-to-screenreader.js",
    "content": "import { nodeLookup } from '../../core/utils';\nimport memoize from '../../core/utils/memoize';\nimport isHiddenForEveryone from './is-hidden-for-everyone';\nimport { ariaHidden, areaHidden } from './visibility-methods';\nimport isInert from './is-inert';\n\n/**\n * Determine if an element is visible to a screen reader\n * @method isVisibleToScreenReaders\n * @memberof axe.commons.dom\n * @param {VirtualNode} vNode The Virtual Node\n * @return {Boolean} True if the element is visible to a screen reader\n */\nexport default function isVisibleToScreenReaders(vNode) {\n  vNode = nodeLookup(vNode).vNode;\n  return isVisibleToScreenReadersVirtual(vNode);\n}\n\n/**\n * Check the element and ancestors\n */\nconst isVisibleToScreenReadersVirtual = memoize(\n  function isVisibleToScreenReadersMemoized(vNode, isAncestor) {\n    if (\n      ariaHidden(vNode) ||\n      isInert(vNode, { skipAncestors: true, isAncestor })\n    ) {\n      return false;\n    }\n\n    if (vNode.actualNode && vNode.props.nodeName === 'area') {\n      return !areaHidden(vNode, isVisibleToScreenReadersVirtual);\n    }\n\n    if (isHiddenForEveryone(vNode, { skipAncestors: true, isAncestor })) {\n      return false;\n    }\n\n    if (!vNode.parent) {\n      return true;\n    }\n\n    return isVisibleToScreenReadersVirtual(vNode.parent, true);\n  }\n);\n"
  },
  {
    "path": "lib/commons/dom/is-visible.js",
    "content": "import getRootNode from './get-root-node';\nimport isOffscreen from './is-offscreen';\nimport findUp from './find-up';\nimport {\n  getScroll,\n  getNodeFromTree,\n  querySelectorAll,\n  escapeSelector\n} from '../../core/utils';\nimport AbstractVirtualNode from '../../core/base/virtual-node/abstract-virtual-node';\n\nconst clipRegex =\n  /rect\\s*\\(([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px\\s*\\)/;\nconst clipPathRegex = /(\\w+)\\((\\d+)/;\n\n/**\n * Determines if an element is hidden with a clip or clip-path technique\n * @method isClipped\n * @memberof axe.commons.dom\n * @private\n * @param  {CSSStyleDeclaration} style Computed style\n * @return {Boolean}\n */\nfunction isClipped(style) {\n  const matchesClip = style.getPropertyValue('clip').match(clipRegex);\n  const matchesClipPath = style\n    .getPropertyValue('clip-path')\n    .match(clipPathRegex);\n  if (matchesClip && matchesClip.length === 5) {\n    const position = style.getPropertyValue('position');\n    // clip is only applied to absolutely positioned elements\n    if (['fixed', 'absolute'].includes(position)) {\n      return (\n        matchesClip[3] - matchesClip[1] <= 0 &&\n        matchesClip[2] - matchesClip[4] <= 0\n      );\n    }\n  }\n  if (matchesClipPath) {\n    const type = matchesClipPath[1];\n    const value = parseInt(matchesClipPath[2], 10);\n\n    switch (type) {\n      case 'inset':\n        return value >= 50;\n      case 'circle':\n        return value === 0;\n      default:\n    }\n  }\n\n  return false;\n}\n\n/**\n * Check `AREA` element is visible\n * - validate if it is a child of `map`\n * - ensure `map` is referred by `img` using the `usemap` attribute\n * @param {Element} areaEl `AREA` element\n * @retruns {Boolean}\n */\nfunction isAreaVisible(el, screenReader, recursed) {\n  /**\n   * Note:\n   * - Verified that `map` element cannot refer to `area` elements across different document trees\n   * - Verified that `map` element does not get affected by altering `display` property\n   */\n  const mapEl = findUp(el, 'map');\n  if (!mapEl) {\n    return false;\n  }\n\n  const mapElName = mapEl.getAttribute('name');\n  if (!mapElName) {\n    return false;\n  }\n\n  /**\n   * `map` element has to be in light DOM\n   */\n  const mapElRootNode = getRootNode(el);\n  if (!mapElRootNode || mapElRootNode.nodeType !== 9) {\n    return false;\n  }\n\n  const refs = querySelectorAll(\n    // TODO: es-module-_tree\n    axe._tree,\n    `img[usemap=\"#${escapeSelector(mapElName)}\"]`\n  );\n  if (!refs || !refs.length) {\n    return false;\n  }\n\n  return refs.some(({ actualNode }) =>\n    isVisible(actualNode, screenReader, recursed)\n  );\n}\n\n/**\n * Determine whether an element is visible\n * @method isVisible\n * @memberof axe.commons.dom\n * @instance\n * @param {HTMLElement} el The HTMLElement\n * @param {Boolean} screenReader When provided, will evaluate visibility from the perspective of a screen reader\n * @param {Boolean} recursed\n * @return {Boolean} The element's visibilty status\n * @deprecated use isVisibleOnScreen or isVisibleToScreenReaders if `screenReader: true` was passed\n */\nfunction isVisible(el, screenReader, recursed) {\n  if (!el) {\n    throw new TypeError(\n      'Cannot determine if element is visible for non-DOM nodes'\n    );\n  }\n\n  const vNode = el instanceof AbstractVirtualNode ? el : getNodeFromTree(el);\n  el = vNode ? vNode.actualNode : el;\n\n  const cacheName = '_isVisible' + (screenReader ? 'ScreenReader' : '');\n  const { DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE } = window.Node ?? {};\n  const nodeType = vNode ? vNode.props.nodeType : el.nodeType;\n  const nodeName = vNode ? vNode.props.nodeName : el.nodeName.toLowerCase();\n\n  if (vNode && typeof vNode[cacheName] !== 'undefined') {\n    return vNode[cacheName];\n  }\n\n  if (nodeType === DOCUMENT_NODE) {\n    return true;\n  }\n\n  // always hidden\n  if (['style', 'script', 'noscript', 'template'].includes(nodeName)) {\n    return false;\n  }\n\n  if (el && nodeType === DOCUMENT_FRAGMENT_NODE) {\n    el = el.host; // grab the host Node\n  }\n\n  // hidden from screen readers\n  if (screenReader) {\n    const ariaHiddenValue = vNode\n      ? vNode.attr('aria-hidden')\n      : el.getAttribute('aria-hidden');\n\n    if (ariaHiddenValue === 'true') {\n      return false;\n    }\n  }\n\n  // detatched virtual node\n  if (!el) {\n    const parent = vNode.parent;\n    let visible = true;\n    if (parent) {\n      visible = isVisible(parent, screenReader, true);\n    }\n\n    if (vNode) {\n      vNode[cacheName] = visible;\n    }\n\n    return visible;\n  }\n\n  const style = window.getComputedStyle(el, null);\n  if (style === null) {\n    return false;\n  }\n\n  /**\n   * check visibility of `AREA`\n   * Note:\n   * Firefox's user-agent always sets `AREA` element to `display:none`\n   * hence excluding the edge case, for visibility computation\n   */\n  if (nodeName === 'area') {\n    return isAreaVisible(el, screenReader, recursed);\n  }\n\n  // always hidden\n  if (style.getPropertyValue('display') === 'none') {\n    return false;\n  }\n\n  // hidden from visual users\n  const elHeight = parseInt(style.getPropertyValue('height'));\n  const elWidth = parseInt(style.getPropertyValue('width'));\n\n  // ways to hide content visually\n  const scroll = getScroll(el);\n  const scrollableWithZeroHeight = scroll && elHeight === 0;\n  const scrollableWithZeroWidth = scroll && elWidth === 0;\n  const posAbsoluteOverflowHiddenAndSmall =\n    style.getPropertyValue('position') === 'absolute' &&\n    (elHeight < 2 || elWidth < 2) &&\n    style.getPropertyValue('overflow') === 'hidden';\n\n  if (\n    !screenReader &&\n    (isClipped(style) ||\n      style.getPropertyValue('opacity') === '0' ||\n      scrollableWithZeroHeight ||\n      scrollableWithZeroWidth ||\n      posAbsoluteOverflowHiddenAndSmall)\n  ) {\n    return false;\n  }\n\n  // visibility is only accurate on the first element and\n  // position does not matter if it was already calculated\n  if (\n    !recursed &&\n    (style.getPropertyValue('visibility') === 'hidden' ||\n      (!screenReader && isOffscreen(el)))\n  ) {\n    return false;\n  }\n\n  const parent = el.assignedSlot ? el.assignedSlot : el.parentNode;\n  let visible = false;\n  if (parent) {\n    visible = isVisible(parent, screenReader, true);\n  }\n\n  if (vNode) {\n    vNode[cacheName] = visible;\n  }\n\n  return visible;\n}\n\nexport default isVisible;\n"
  },
  {
    "path": "lib/commons/dom/is-visual-content.js",
    "content": "import { nodeLookup } from '../../core/utils';\n\nconst visualRoles = [\n  'checkbox',\n  'img',\n  'meter',\n  'progressbar',\n  'scrollbar',\n  'radio',\n  'slider',\n  'spinbutton',\n  'textbox'\n];\n\n/**\n * Check if an element is an inherently visual element\n * @method isVisualContent\n * @memberof axe.commons.dom\n * @instance\n * @param  {Element|VirtualNode} element The element to check\n * @return {Boolean}\n */\nfunction isVisualContent(el) {\n  const { vNode } = nodeLookup(el);\n  const role = axe.commons.aria.getExplicitRole(vNode);\n  if (role) {\n    return visualRoles.indexOf(role) !== -1;\n  }\n\n  switch (vNode.props.nodeName) {\n    case 'img':\n    case 'iframe':\n    case 'object':\n    case 'video':\n    case 'audio':\n    case 'canvas':\n    case 'svg':\n    case 'math':\n    case 'button':\n    case 'select':\n    case 'textarea':\n    case 'keygen':\n    case 'progress':\n    case 'meter':\n      return true;\n    case 'input':\n      return vNode.props.type !== 'hidden';\n    default:\n      return false;\n  }\n}\n\nexport default isVisualContent;\n"
  },
  {
    "path": "lib/commons/dom/reduce-to-elements-below-floating.js",
    "content": "/**\n * Reduce an array of elements to only those that are below a 'floating' element.\n * @method reduceToElementsBelowFloating\n * @memberof axe.commons.dom\n * @instance\n * @param {Array} elements\n * @param {Element} targetNode\n * @returns {Array}\n */\nfunction reduceToElementsBelowFloating(elements, targetNode) {\n  const floatingPositions = ['fixed', 'sticky'];\n  let finalElements = [];\n  let targetFound = false;\n\n  // Filter out elements that are temporarily floating above the target\n  for (let index = 0; index < elements.length; ++index) {\n    const currentNode = elements[index];\n    if (currentNode === targetNode) {\n      targetFound = true;\n    }\n\n    const style = window.getComputedStyle(currentNode);\n\n    if (!targetFound && floatingPositions.indexOf(style.position) !== -1) {\n      //Target was not found yet, so it must be under this floating thing (and will not always be under it)\n      finalElements = [];\n      continue;\n    }\n\n    finalElements.push(currentNode);\n  }\n\n  return finalElements;\n}\n\nexport default reduceToElementsBelowFloating;\n"
  },
  {
    "path": "lib/commons/dom/shadow-elements-from-point.js",
    "content": "import getRootNode from './get-root-node';\nimport visuallyContains from './visually-contains';\nimport { isShadowRoot } from '../../core/utils';\n\n/**\n * Get elements from point across shadow dom boundaries\n * @deprecated As of axe-core 4.4. May be removed in axe-core 5.0\n * @method shadowElementsFromPoint\n * @memberof axe.commons.dom\n * @instance\n * @param {Number} nodeX X coordinates of point\n * @param {Number} nodeY Y coordinates of point\n * @param {Object} [root] Shadow root or document root\n * @return {Array}\n */\nfunction shadowElementsFromPoint(nodeX, nodeY, root = document, i = 0) {\n  if (i > 999) {\n    throw new Error('Infinite loop detected');\n  }\n  return (\n    // IE can return null when there are no elements\n    Array.from(root.elementsFromPoint(nodeX, nodeY) || [])\n      // As of Chrome 66, elementFromPoint will return elements from parent trees.\n      // We only want to touch each tree once, so we're filtering out nodes on other trees.\n      .filter(nodes => getRootNode(nodes) === root)\n      .reduce((stack, elm) => {\n        if (isShadowRoot(elm)) {\n          const shadowStack = shadowElementsFromPoint(\n            nodeX,\n            nodeY,\n            elm.shadowRoot,\n            i + 1\n          );\n          stack = stack.concat(shadowStack);\n          // filter host nodes which get included regardless of overlap\n          // TODO: refactor multiline overlap checking inside shadow dom\n          if (stack.length && visuallyContains(stack[0], elm)) {\n            stack.push(elm);\n          }\n        } else {\n          stack.push(elm);\n        }\n        return stack;\n      }, [])\n  );\n}\n\nexport default shadowElementsFromPoint;\n"
  },
  {
    "path": "lib/commons/dom/url-props-from-attribute.js",
    "content": "/**\n * Parse resource object for a given node from a specified attribute\n * @method urlPropsFromAttribute\n * @param {HTMLElement} node given node\n * @param {String} attribute attribute of the node from which resource should be parsed\n * @returns {Object}\n */\nfunction urlPropsFromAttribute(node, attribute) {\n  if (!node.hasAttribute(attribute)) {\n    return undefined;\n  }\n\n  const nodeName = node.nodeName.toUpperCase();\n  let parser = node;\n\n  /**\n   * Note:\n   * The need to create a parser, is to keep this function generic, to be able to parse resource from element(s) like `iframe` with `src` attribute,\n   *\n   * Also, when `a` or `area` is nested inside an svg document,\n   * they do not have url properties as a HTML Node, hence the check for `ownerSVGElement`\n   */\n  if (!['A', 'AREA'].includes(nodeName) || node.ownerSVGElement) {\n    parser = document.createElement('a');\n    parser.href = node.getAttribute(attribute);\n  }\n\n  /**\n   * Curate `https` and `ftps` to `http` and `ftp` as they will resolve to same resource\n   */\n  const protocol = [`https:`, `ftps:`].includes(parser.protocol)\n    ? parser.protocol.replace(/s:$/, ':')\n    : parser.protocol;\n\n  /**\n   * certain browser (in this case IE10 & 11)\n   * does not resolve pathname with a beginning slash, thence prepending with a beginning slash\n   */\n  const parserPathname = /^\\//.test(parser.pathname)\n    ? parser.pathname\n    : `/${parser.pathname}`;\n  const { pathname, filename } = getPathnameOrFilename(parserPathname);\n\n  return {\n    protocol,\n    hostname: parser.hostname,\n    port: getPort(parser.port),\n    pathname: /\\/$/.test(pathname) ? pathname : `${pathname}/`,\n    search: getSearchPairs(parser.search),\n    hash: getHashRoute(parser.hash),\n    filename\n  };\n}\n\n/**\n * Resolve given port excluding default port(s)\n * @param {String} port port\n * @returns {String}\n */\nfunction getPort(port) {\n  const excludePorts = [\n    `443`, // default `https` port\n    `80`\n  ];\n  return !excludePorts.includes(port) ? port : ``;\n}\n\n/**\n * Resolve if a given pathname has filename & resolve the same as parts\n * @method getPathnameOrFilename\n * @param {String} pathname pathname part of a given uri\n * @returns {Array<Object>}\n */\nfunction getPathnameOrFilename(pathname) {\n  const filename = pathname.split('/').pop();\n  if (!filename || filename.indexOf('.') === -1) {\n    return {\n      pathname,\n      filename: ``\n    };\n  }\n\n  return {\n    // remove `filename` from `pathname`\n    pathname: pathname.replace(filename, ''),\n\n    // ignore filename when index.*\n    filename: /index./.test(filename) ? `` : filename\n  };\n}\n\n/**\n * Parse a given query string to key/value pairs sorted alphabetically\n * @param {String} searchStr search string\n * @returns {Object}\n */\nfunction getSearchPairs(searchStr) {\n  const query = {};\n\n  if (!searchStr || !searchStr.length) {\n    return query;\n  }\n\n  // `substring` to remove `?` at the beginning of search string\n  const pairs = searchStr.substring(1).split(`&`);\n  if (!pairs || !pairs.length) {\n    return query;\n  }\n\n  for (let index = 0; index < pairs.length; index++) {\n    const pair = pairs[index];\n    const [key, value = ''] = pair.split(`=`);\n    query[decodeURIComponent(key)] = decodeURIComponent(value);\n  }\n\n  return query;\n}\n\n/**\n * Interpret a given hash\n * if `hash`\n * -> is `hashbang` -or- `hash` is followed by `slash`\n * -> it resolves to a different resource\n * @method getHashRoute\n * @param {String} hash hash component of a parsed uri\n * @returns {String}\n */\nfunction getHashRoute(hash) {\n  if (!hash) {\n    return ``;\n  }\n\n  /**\n   * Check for any conventionally-formatted hashbang that may be present\n   * eg: `#, #/, #!, #!/`\n   */\n  const hashRegex = /#!?\\/?/g;\n  const hasMatch = hash.match(hashRegex);\n  if (!hasMatch) {\n    return ``;\n  }\n\n  // do not resolve inline link as hash\n  const [matchedStr] = hasMatch;\n  if (matchedStr === '#') {\n    return ``;\n  }\n\n  return hash;\n}\n\nexport default urlPropsFromAttribute;\n"
  },
  {
    "path": "lib/commons/dom/visibility-methods.js",
    "content": "import {\n  getScroll,\n  closest,\n  getRootNode,\n  querySelectorAll,\n  escapeSelector\n} from '../../core/utils';\nimport rectsOverlap from '../math/rects-overlap';\nimport getOverflowHiddenAncestors from './get-overflow-hidden-ancestors';\n\nconst clipRegex =\n  /rect\\s*\\(([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px\\s*\\)/;\nconst clipPathRegex = /(\\w+)\\((\\d+)/;\n\n/**\n * Determine if an element is natively hidden\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function nativelyHidden(vNode) {\n  return ['style', 'script', 'noscript', 'template'].includes(\n    vNode.props.nodeName\n  );\n}\n\n/**\n * Determine if an element is hidden using the display property\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function displayHidden(vNode) {\n  // Firefox's user-agent always sets `area` element\n  // to `display:none` so we can't rely on it to\n  // check for hidden\n  if (vNode.props.nodeName === 'area') {\n    return false;\n  }\n\n  return vNode.getComputedStylePropertyValue('display') === 'none';\n}\n\n/**\n * Determine if an element is hidden using the visibility property. Visibility is only applicable for the node itself (and not any ancestors)\n * @param {VirtualNode} vNode\n * @param {Object} [options]\n * @param {Boolean} [options.isAncestor] If this function is being called on an ancestor for the target node\n * @return {Boolean}\n */\nexport function visibilityHidden(vNode, { isAncestor } = {}) {\n  // because the parent can be hidden, and the child visible we\n  // have to ignore visibility on ancestors. we don't need to look\n  // at the parent either, because visibility inherits\n  return (\n    !isAncestor &&\n    ['hidden', 'collapse'].includes(\n      vNode.getComputedStylePropertyValue('visibility')\n    )\n  );\n}\n\n/**\n * Determine if an element is hidden using the content-visibility property\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function contentVisibiltyHidden(vNode, { isAncestor } = {}) {\n  // content-visibility only applies to descendants\n  // and does not hide the node with the property\n  return (\n    !!isAncestor &&\n    vNode.getComputedStylePropertyValue('content-visibility') === 'hidden'\n  );\n}\n\n/**\n * Determine if an element is hidden using the aria-hidden attribute\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function ariaHidden(vNode) {\n  return vNode.attr('aria-hidden') === 'true';\n}\n\n/**\n * Determine if an element is hidden by making the opacity 0\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function opacityHidden(vNode) {\n  return vNode.getComputedStylePropertyValue('opacity') === '0';\n}\n\n/**\n * Determine if an element is hidden by using scroll and dimensions\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function scrollHidden(vNode) {\n  const scroll = getScroll(vNode.actualNode);\n  const elHeight = parseInt(vNode.getComputedStylePropertyValue('height'));\n  const elWidth = parseInt(vNode.getComputedStylePropertyValue('width'));\n\n  return !!scroll && (elHeight === 0 || elWidth === 0);\n}\n\n/**\n * Determine if an element is hidden by using overflow: hidden.\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function overflowHidden(vNode, { isAncestor } = {}) {\n  // an ancestor that is hidden outside an overflow\n  // does not mean that a descendant is also hidden\n  // since the descendant can reposition itself to be\n  // in view of the overflow:hidden ancestor\n  if (isAncestor) {\n    return false;\n  }\n\n  // a node with position fixed cannot be hidden by an overflow\n  // ancestor, even when that ancestor uses a non-static position\n  const position = vNode.getComputedStylePropertyValue('position');\n  if (position === 'fixed') {\n    return false;\n  }\n\n  const nodes = getOverflowHiddenAncestors(vNode);\n  if (!nodes.length) {\n    return false;\n  }\n\n  const rect = vNode.boundingClientRect;\n  return nodes.some(node => {\n    // a node with position absolute will not be hidden by an\n    // overflow ancestor unless the ancestor uses a non-static\n    // position or a node in-between uses a position of relative\n    // or sticky\n    if (\n      position === 'absolute' &&\n      !hasPositionedAncestorBetween(vNode, node) &&\n      node.getComputedStylePropertyValue('position') === 'static'\n    ) {\n      return false;\n    }\n\n    const nodeRect = node.boundingClientRect;\n    if (nodeRect.width < 2 || nodeRect.height < 2) {\n      return true;\n    }\n\n    return !rectsOverlap(rect, nodeRect);\n  });\n}\n\n/**\n * Determines if an element is hidden with a clip or clip-path technique\n * @param {VirtualNode} vNode\n * @return {Boolean}\n */\nexport function clipHidden(vNode) {\n  const matchesClip = vNode\n    .getComputedStylePropertyValue('clip')\n    .match(clipRegex);\n  const matchesClipPath = vNode\n    .getComputedStylePropertyValue('clip-path')\n    .match(clipPathRegex);\n  if (matchesClip && matchesClip.length === 5) {\n    const position = vNode.getComputedStylePropertyValue('position');\n    // clip is only applied to absolutely positioned elements\n    if (['fixed', 'absolute'].includes(position)) {\n      return (\n        matchesClip[3] - matchesClip[1] <= 0 &&\n        matchesClip[2] - matchesClip[4] <= 0\n      );\n    }\n  }\n  if (matchesClipPath) {\n    const type = matchesClipPath[1];\n    const value = parseInt(matchesClipPath[2], 10);\n\n    switch (type) {\n      case 'inset':\n        return value >= 50;\n      case 'circle':\n        return value === 0;\n      default:\n    }\n  }\n\n  return false;\n}\n\n/**\n * Check `AREA` element is hidden\n * - validate if it is a child of `map`\n * - ensure `map` is referred by `img` using the `usemap` attribute\n * @param {VirtualNode} vNode\n * @param {Function} visibleFunction Function used to check if the image element is visible\n * @retruns {Boolean}\n */\nexport function areaHidden(vNode, visibleFunction) {\n  /**\n   * Note:\n   * - Verified that `map` element cannot refer to `area` elements across different document trees\n   * - Verified that `map` element does not get affected by altering `display` property\n   */\n  const mapEl = closest(vNode, 'map');\n  if (!mapEl) {\n    return true;\n  }\n\n  const mapElName = mapEl.attr('name');\n  if (!mapElName) {\n    return true;\n  }\n\n  /**\n   * `map` element has to be in light DOM\n   */\n  const mapElRootNode = getRootNode(vNode.actualNode);\n  if (!mapElRootNode || mapElRootNode.nodeType !== 9) {\n    return true;\n  }\n\n  const refs = querySelectorAll(\n    // TODO: es-module-_tree\n    axe._tree,\n    `img[usemap=\"#${escapeSelector(mapElName)}\"]`\n  );\n  if (!refs || !refs.length) {\n    return true;\n  }\n\n  return refs.some(ref => !visibleFunction(ref));\n}\n\n/**\n * Determine if element inside `details` is hidden\n */\nexport function detailsHidden(vNode) {\n  if (vNode.parent?.props.nodeName !== 'details') {\n    return false;\n  }\n\n  // first summary element is never hidden (subsequent ones are though)\n  if (vNode.props.nodeName === 'summary') {\n    const firstSummary = vNode.parent.children.find(\n      node => node.props.nodeName === 'summary'\n    );\n\n    if (firstSummary === vNode) {\n      return false;\n    }\n  }\n\n  return !vNode.parent.hasAttr('open');\n}\n\nfunction hasPositionedAncestorBetween(child, ancestor) {\n  let node = child.parent;\n  while (node && node !== ancestor) {\n    if (\n      ['relative', 'sticky'].includes(\n        node.getComputedStylePropertyValue('position')\n      )\n    ) {\n      return true;\n    }\n\n    node = node.parent;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "lib/commons/dom/visually-contains.js",
    "content": "import { getNodeFromTree, getScroll } from '../../core/utils';\n\n/**\n * Checks whether a parent element visually contains its child, either directly or via scrolling.\n * Assumes that |parent| is an ancestor of |node|.\n * @deprecated\n * @method visuallyContains\n * @memberof axe.commons.dom\n * @instance\n * @param {Element} node\n * @param {Element} parent\n * @return {boolean} True if node is visually contained within parent\n */\nexport default function visuallyContains(node, parent) {\n  const parentScrollAncestor = getScrollAncestor(parent);\n\n  // if the elements share a common scroll parent, we can check if the\n  // parent visually contains the node. otherwise we need to check each\n  // scroll parent in between the node and the parent since if the\n  // element is off screen due to the scroll, it won't be visually contained\n  // by the parent\n  do {\n    const nextScrollAncestor = getScrollAncestor(node);\n\n    if (\n      nextScrollAncestor === parentScrollAncestor ||\n      nextScrollAncestor === parent\n    ) {\n      return contains(node, parent);\n    }\n\n    node = nextScrollAncestor;\n  } while (node);\n\n  return false;\n}\n\n/**\n * Return the ancestor node that is a scroll region.\n * @param {VirtualNode}\n * @return {VirtualNode|null}\n */\nfunction getScrollAncestor(node) {\n  const vNode = getNodeFromTree(node);\n  let ancestor = vNode.parent;\n\n  while (ancestor) {\n    if (getScroll(ancestor.actualNode)) {\n      return ancestor.actualNode;\n    }\n\n    ancestor = ancestor.parent;\n  }\n}\n\n/**\n * Checks whether a parent element fully contains its child, either directly or via scrolling.\n * Assumes that |parent| is an ancestor of |node|.\n * @param {Element} node\n * @param {Element} parent\n * @return {boolean} True if node is visually contained within parent\n */\nfunction contains(node, parent) {\n  const style = window.getComputedStyle(parent);\n  const overflow = style.getPropertyValue('overflow');\n\n  // if parent element is inline, scrollArea will be too unpredictable\n  if (style.getPropertyValue('display') === 'inline') {\n    return true;\n  }\n\n  // use clientRects instead of boundingClientRect to account\n  // for truncation of text (one of the rects will be the size\n  // of the truncation)\n  // @see https://github.com/dequelabs/axe-core/issues/2669\n  const clientRects = Array.from(node.getClientRects());\n  // getBoundingClientRect prevents overrides of left/top\n  // (also can't destructure)\n  const boundingRect = parent.getBoundingClientRect();\n  const rect = {\n    left: boundingRect.left,\n    top: boundingRect.top,\n    width: boundingRect.width,\n    height: boundingRect.height\n  };\n\n  if (\n    ['scroll', 'auto'].includes(overflow) ||\n    parent instanceof window.HTMLHtmlElement\n  ) {\n    rect.width = parent.scrollWidth;\n    rect.height = parent.scrollHeight;\n  }\n\n  // in Chrome text truncation on the parent will cause the\n  // child to have multiple client rects (one for the bounding\n  // rect of the element and one more for the bounding rect of\n  // the truncation). however this doesn't happen for other\n  // browsers so we'll make it so that if we detect text\n  // truncation and there's only one client rect, we'll use\n  // the bounding rect of the parent as the client rect of\n  // the child\n  if (\n    clientRects.length === 1 &&\n    overflow === 'hidden' &&\n    style.getPropertyValue('white-space') === 'nowrap'\n  ) {\n    clientRects[0] = rect;\n  }\n\n  // check if any client rect is fully inside the parent rect\n  // @see https://gist.github.com/Daniel-Hug/d7984d82b58d6d2679a087d896ca3d2b\n  return clientRects.some(\n    clientRect =>\n      !(\n        Math.ceil(clientRect.left) < Math.floor(rect.left) ||\n        Math.ceil(clientRect.top) < Math.floor(rect.top) ||\n        Math.floor(clientRect.left + clientRect.width) >\n          Math.ceil(rect.left + rect.width) ||\n        Math.floor(clientRect.top + clientRect.height) >\n          Math.ceil(rect.top + rect.height)\n      )\n  );\n}\n"
  },
  {
    "path": "lib/commons/dom/visually-overlaps.js",
    "content": "/**\n * Checks whether a parent element visually overlaps a rectangle, either directly or via scrolling.\n * @method visuallyOverlaps\n * @memberof axe.commons.dom\n * @instance\n * @param {DOMRect} rect\n * @param {Element} parent\n * @return {boolean} True if rect is visually contained within parent\n */\nfunction visuallyOverlaps(rect, parent) {\n  const parentRect = parent.getBoundingClientRect();\n  const parentTop = parentRect.top;\n  const parentLeft = parentRect.left;\n  const parentScrollArea = {\n    top: parentTop - parent.scrollTop,\n    bottom: parentTop - parent.scrollTop + parent.scrollHeight,\n    left: parentLeft - parent.scrollLeft,\n    right: parentLeft - parent.scrollLeft + parent.scrollWidth\n  };\n\n  //In theory, we should just be able to look at the scroll area as a superset of the parentRect,\n  //but that's not true in Firefox\n  if (\n    (rect.left > parentScrollArea.right && rect.left > parentRect.right) ||\n    (rect.top > parentScrollArea.bottom && rect.top > parentRect.bottom) ||\n    (rect.right < parentScrollArea.left && rect.right < parentRect.left) ||\n    (rect.bottom < parentScrollArea.top && rect.bottom < parentRect.top)\n  ) {\n    return false;\n  }\n\n  const style = window.getComputedStyle(parent);\n\n  if (rect.left > parentRect.right || rect.top > parentRect.bottom) {\n    return (\n      style.overflow === 'scroll' ||\n      style.overflow === 'auto' ||\n      parent instanceof window.HTMLBodyElement ||\n      parent instanceof window.HTMLHtmlElement\n    );\n  }\n\n  return true;\n}\n\nexport default visuallyOverlaps;\n"
  },
  {
    "path": "lib/commons/dom/visually-sort.js",
    "content": "import createGrid from './create-grid';\n\n/**\n * Visually sort nodes based on their stack order\n * References:\n * https://www.w3.org/Style/css2-updates/css2/zindex.html\n * https://www.w3.org/Style/css2-updates/css2/visuren.html#layers\n * @param {VirtualNode}\n * @param {VirtualNode}\n */\nexport default function visuallySort(a, b) {\n  /*eslint no-bitwise: 0 */\n  createGrid(); // Because we need ._stackingOrder\n  const length = Math.max(a._stackingOrder.length, b._stackingOrder.length);\n\n  for (let i = 0; i < length; i++) {\n    if (typeof b._stackingOrder[i] === 'undefined') {\n      return -1;\n    } else if (typeof a._stackingOrder[i] === 'undefined') {\n      return 1;\n    }\n\n    // 7. the child stacking contexts with positive stack levels (least positive first).\n    if (b._stackingOrder[i].stackLevel > a._stackingOrder[i].stackLevel) {\n      return 1;\n    }\n\n    // 2. the child stacking contexts with negative stack levels (most negative first).\n    if (b._stackingOrder[i].stackLevel < a._stackingOrder[i].stackLevel) {\n      return -1;\n    }\n\n    // stacks share the same stack level so compare document order\n    if (b._stackingOrder[i].treeOrder !== a._stackingOrder[i].treeOrder) {\n      return b._stackingOrder[i].treeOrder - a._stackingOrder[i].treeOrder;\n    }\n  }\n\n  // nodes are the same stacking order\n  let aNode = a.actualNode;\n  let bNode = b.actualNode;\n\n  // elements don't correctly calculate document position when comparing\n  // across shadow boundaries, so we need to compare the position of a\n  // shared host instead\n\n  // elements have different hosts\n  if (aNode.getRootNode && aNode.getRootNode() !== bNode.getRootNode()) {\n    // keep track of all parent hosts and find the one both nodes share\n    const boundaries = [];\n    while (aNode) {\n      boundaries.push({\n        root: aNode.getRootNode(),\n        node: aNode\n      });\n      aNode = aNode.getRootNode().host;\n    }\n\n    while (\n      bNode &&\n      !boundaries.find(boundary => boundary.root === bNode.getRootNode())\n    ) {\n      bNode = bNode.getRootNode().host;\n    }\n\n    // bNode is a node that shares a host with some part of the a parent\n    // shadow tree, find the aNode that shares the same host as bNode\n    aNode = boundaries.find(\n      boundary => boundary.root === bNode.getRootNode()\n    ).node;\n\n    // sort child of shadow to it's host node by finding which element is\n    // the child of the host and sorting it before the host\n    if (aNode === bNode) {\n      return a.actualNode.getRootNode() !== aNode.getRootNode() ? -1 : 1;\n    }\n  }\n\n  const {\n    DOCUMENT_POSITION_FOLLOWING,\n    DOCUMENT_POSITION_CONTAINS,\n    DOCUMENT_POSITION_CONTAINED_BY\n  } = window.Node;\n\n  const docPosition = aNode.compareDocumentPosition(bNode);\n  const DOMOrder = docPosition & DOCUMENT_POSITION_FOLLOWING ? 1 : -1;\n  const isDescendant =\n    docPosition & DOCUMENT_POSITION_CONTAINS ||\n    docPosition & DOCUMENT_POSITION_CONTAINED_BY;\n  const aPosition = getPositionOrder(a);\n  const bPosition = getPositionOrder(b);\n\n  // a child of a positioned element should also be on top of the parent\n  if (aPosition === bPosition || isDescendant) {\n    return DOMOrder;\n  }\n  return bPosition - aPosition;\n}\n\n/**\n * Return the index order of how to position this element. return nodes in non-positioned, floating, positioned order\n * References:\n * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index\n * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_and_float\n * https://www.w3.org/Style/css2-updates/css2/zindex.html\n * @param {VirtualNode} vNode\n * @return {Number}\n */\nfunction getPositionOrder(vNode) {\n  // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.\n  if (vNode.getComputedStylePropertyValue('display').indexOf('inline') !== -1) {\n    return 2;\n  }\n  // 4. the non-positioned floats.\n  if (isFloated(vNode)) {\n    return 1;\n  }\n  // 3. the in-flow, non-inline-level, non-positioned descendants.\n  return 0;\n}\n\n/**\n * Check if a node or one of it's parents is floated.\n * Floating position should be inherited from the parent tree\n * @see https://github.com/dequelabs/axe-core/issues/2222\n */\nfunction isFloated(vNode) {\n  if (!vNode) {\n    return false;\n  }\n\n  if (vNode._isFloated !== undefined) {\n    return vNode._isFloated;\n  }\n\n  const floatStyle = vNode.getComputedStylePropertyValue('float');\n\n  if (floatStyle !== 'none') {\n    vNode._isFloated = true;\n    return true;\n  }\n\n  const floated = isFloated(vNode.parent);\n  vNode._isFloated = floated;\n  return floated;\n}\n"
  },
  {
    "path": "lib/commons/forms/index.js",
    "content": "/**\n * Namespace for forms-related utilities.\n * @namespace commons.forms\n * @memberof axe\n */\nexport { default as isAriaCombobox } from './is-aria-combobox';\nexport { default as isAriaListbox } from './is-aria-listbox';\nexport { default as isAriaRange } from './is-aria-range';\nexport { default as isAriaTextbox } from './is-aria-textbox';\nexport { default as isNativeSelect } from './is-native-select';\nexport { default as isNativeTextbox } from './is-native-textbox';\nexport { default as isDisabled } from './is-disabled';\n"
  },
  {
    "path": "lib/commons/forms/is-aria-combobox.js",
    "content": "import getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Determines if an element is an aria combobox element\n * @method isAriaCombobox\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if aria combobox\n * @returns {Bool}\n */\nfunction isAriaCombobox(node) {\n  const role = getExplicitRole(node);\n  return role === 'combobox';\n}\n\nexport default isAriaCombobox;\n"
  },
  {
    "path": "lib/commons/forms/is-aria-listbox.js",
    "content": "import getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Determines if an element is an aria listbox element\n * @method isAriaListbox\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if aria listbox\n * @returns {Bool}\n */\nfunction isAriaListbox(node) {\n  const role = getExplicitRole(node);\n  return role === 'listbox';\n}\n\nexport default isAriaListbox;\n"
  },
  {
    "path": "lib/commons/forms/is-aria-range.js",
    "content": "import getExplicitRole from '../aria/get-explicit-role';\n\nconst rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton'];\n\n/**\n * Determines if an element is an aria range element\n * @method isAriaRange\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if aria range\n * @returns {Bool}\n */\nfunction isAriaRange(node) {\n  const role = getExplicitRole(node);\n  return rangeRoles.includes(role);\n}\n\nexport default isAriaRange;\n"
  },
  {
    "path": "lib/commons/forms/is-aria-textbox.js",
    "content": "import getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Determines if an element is an aria textbox element\n * @method isAriaTextbox\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if aria textbox\n * @returns {Bool}\n */\nfunction isAriaTextbox(node) {\n  const role = getExplicitRole(node);\n  return role === 'textbox';\n}\n\nexport default isAriaTextbox;\n"
  },
  {
    "path": "lib/commons/forms/is-disabled.js",
    "content": "const disabledNodeNames = ['fieldset', 'button', 'select', 'input', 'textarea'];\n\n/**\n * Determines if an element disabled, or part of a disabled element\n *\n * IMPORANT: This method is fairly loose. There are significant differences in browsers of when\n * they'll announce a thing disabled. This tells us if any accessibility supported browser\n * identifies an element as disabled, but not if all of them do.\n *\n * @method isDisabled\n * @memberof axe.commons.forms\n * @param {VirtualNode} virtualNode\n * @return {boolean} whether or not the element is disabled in some way\n */\nfunction isDisabled(virtualNode) {\n  let disabledState = virtualNode._isDisabled;\n  if (typeof disabledState === 'boolean') {\n    return disabledState; // From cache\n  }\n\n  const { nodeName } = virtualNode.props;\n  const ariaDisabled = virtualNode.attr('aria-disabled');\n  if (disabledNodeNames.includes(nodeName) && virtualNode.hasAttr('disabled')) {\n    disabledState = true; // Native\n  } else if (ariaDisabled) {\n    // ARIA\n    disabledState = ariaDisabled.toLowerCase() === 'true';\n  } else if (virtualNode.parent) {\n    // Inherited\n    disabledState = isDisabled(virtualNode.parent);\n  } else {\n    // Default\n    disabledState = false;\n  }\n\n  virtualNode._isDisabled = disabledState;\n  return disabledState;\n}\n\nexport default isDisabled;\n"
  },
  {
    "path": "lib/commons/forms/is-native-select.js",
    "content": "import { getNodeFromTree } from '../../core/utils';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\n\n/**\n * Determines if an element is a native select element\n * @method isNativeSelect\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if select\n * @returns {Bool}\n */\nfunction isNativeSelect(node) {\n  node = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node);\n  const nodeName = node.props.nodeName;\n  return nodeName === 'select';\n}\n\nexport default isNativeSelect;\n"
  },
  {
    "path": "lib/commons/forms/is-native-textbox.js",
    "content": "import { getNodeFromTree } from '../../core/utils';\nimport AbstractVirtuaNode from '../../core/base/virtual-node/abstract-virtual-node';\n\nconst nonTextInputTypes = [\n  'button',\n  'checkbox',\n  'color',\n  'file',\n  'hidden',\n  'image',\n  'password',\n  'radio',\n  'reset',\n  'submit'\n];\n\n/**\n * Determines if an element is a native textbox element\n * @method isNativeTextbox\n * @memberof axe.commons.forms\n * @param {VirtualNode|Element} node Node to determine if textbox\n * @returns {Bool}\n */\nfunction isNativeTextbox(node) {\n  node = node instanceof AbstractVirtuaNode ? node : getNodeFromTree(node);\n  const nodeName = node.props.nodeName;\n  return (\n    nodeName === 'textarea' ||\n    (nodeName === 'input' &&\n      !nonTextInputTypes.includes((node.attr('type') || '').toLowerCase()))\n  );\n}\n\nexport default isNativeTextbox;\n"
  },
  {
    "path": "lib/commons/index.js",
    "content": "/* global commons */\n/*eslint no-unused-vars: 0*/\n\n/**\n * Namespace for axe common methods.\n * @namespace commons\n * @memberof axe\n */\nimport * as aria from './aria';\nimport * as color from './color';\nimport * as math from './math';\nimport * as dom from './dom';\nimport * as forms from './forms';\nimport matches from './matches';\nimport * as standards from './standards';\nimport * as table from './table';\nimport * as text from './text';\nimport * as utils from '../core/utils';\n\nconst commons = {\n  aria,\n  color,\n  dom,\n  forms,\n  math,\n  matches,\n  standards,\n  table,\n  text,\n  utils\n};\n\nexport {\n  aria,\n  color,\n  dom,\n  forms,\n  math,\n  matches,\n  standards,\n  table,\n  text,\n  utils\n};\n"
  },
  {
    "path": "lib/commons/matches/attributes.js",
    "content": "import fromFunction from './from-function';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Check if a virtual node matches some attribute(s)\n *\n * Note: matches.attributes(vNode, matcher) can be indirectly used through\n * matches(vNode, { attributes: matcher })\n *\n * Example:\n * ```js\n * matches.attributes(vNode, {\n *   'aria-live': 'assertive', // Simple string match\n *   'aria-expanded': /true|false/i, // either boolean, case insensitive\n * })\n * ```\n *\n * @deprecated HTMLElement is deprecated, use VirtualNode instead\n *\n * @param {HTMLElement|VirtualNode} vNode\n * @param {Object} Attribute matcher\n * @returns {Boolean}\n */\nfunction attributes(vNode, matcher) {\n  vNode = nodeLookup(vNode).vNode;\n  return fromFunction(attrName => vNode.attr(attrName), matcher);\n}\n\nexport default attributes;\n"
  },
  {
    "path": "lib/commons/matches/condition.js",
    "content": "/**\n * Check if a \"thing\" is truthy according to a \"condition\"\n *\n * Note: matches.condition(node, matcher) can be indirectly used through\n * matches(node, { condition: matcher })\n *\n * Example:\n * ```js\n * matches.condition(node, (arg) => arg === null)\n * ```\n *\n * @param {any} argument\n * @param {Function|Null|undefined} matcher\n * @returns {Boolean}\n */\nexport default function condition(arg, matcher) {\n  return !!matcher(arg);\n}\n"
  },
  {
    "path": "lib/commons/matches/explicit-role.js",
    "content": "import fromPrimative from './from-primative';\nimport getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Check if a virtual node matches an explicit role(s)\n *``\n * Note: matches.explicitRole(vNode, matcher) can be indirectly used through\n * matches(vNode, { explicitRole: matcher })\n *\n * Example:\n * ```js\n * matches.explicitRole(vNode, ['combobox', 'textbox']);\n * matches.explicitRole(vNode, 'combobox');\n * ```\n *\n * @param {VirtualNode} vNode\n * @param {Object} matcher\n * @returns {Boolean}\n */\nfunction explicitRole(vNode, matcher) {\n  return fromPrimative(getExplicitRole(vNode), matcher);\n}\n\nexport default explicitRole;\n"
  },
  {
    "path": "lib/commons/matches/from-definition.js",
    "content": "import hasAccessibleName from './has-accessible-name';\nimport attributes from './attributes';\nimport condition from './condition';\nimport explicitRole from './explicit-role';\nimport implicitRole from './implicit-role';\nimport nodeName from './node-name';\nimport properties from './properties';\nimport semanticRole from './semantic-role';\nimport { nodeLookup, matches } from '../../core/utils';\n\nconst matchers = {\n  hasAccessibleName,\n  attributes,\n  condition,\n  explicitRole,\n  implicitRole,\n  nodeName,\n  properties,\n  semanticRole\n};\n\n/**\n * Check if a virtual node matches some definition\n *\n * Note: matches.fromDefinition(vNode, definition) can be indirectly used through\n * matches(vNode, definition)\n *\n * Example:\n * ```js\n * matches.fromDefinition(vNode, {\n *   nodeName: ['div', 'span']\n *   attributes: {\n *     'aria-live': 'assertive'\n *   }\n * })\n * ```\n *\n * @deprecated HTMLElement is deprecated, use VirtualNode instead\n *\n * @private\n * @param {HTMLElement|VirtualNode} vNode\n * @param {Object|Array<Object>} definition\n * @returns {Boolean}\n */\nfunction fromDefinition(vNode, definition) {\n  vNode = nodeLookup(vNode).vNode;\n\n  if (Array.isArray(definition)) {\n    return definition.some(definitionItem =>\n      fromDefinition(vNode, definitionItem)\n    );\n  }\n  if (typeof definition === 'string') {\n    return matches(vNode, definition);\n  }\n\n  return Object.keys(definition).every(matcherName => {\n    if (!matchers[matcherName]) {\n      throw new Error(`Unknown matcher type \"${matcherName}\"`);\n    }\n    // Find the specific matches method to.\n    // matches.attributes, matches.nodeName, matches.properties, etc.\n    const matchMethod = matchers[matcherName];\n\n    // Find the matcher that goes into the matches method.\n    // 'div', /^div$/, (str) => str === 'div', etc.\n    const matcher = definition[matcherName];\n    return matchMethod(vNode, matcher);\n  });\n}\n\nexport default fromDefinition;\n"
  },
  {
    "path": "lib/commons/matches/from-function.js",
    "content": "import fromPrimative from './from-primative';\n\n/**\n * Check if the value from a function matches some condition\n *\n * Each key on the matcher object is passed to getValue, the returned value must match\n * with the value of that matcher\n *\n * Example:\n * ```js\n * matches.fromFunction(\n * \t (attr => node.getAttribute(attr),\n * \t {\n * \t   'aria-hidden': /^true|false$/i\n * \t }\n * )\n * ```\n *\n * @private\n * @param {Function} getValue\n * @param {Object} matcher matcher\n * @returns {Boolean}\n */\nfunction fromFunction(getValue, matcher) {\n  const matcherType = typeof matcher;\n  if (\n    matcherType !== 'object' ||\n    Array.isArray(matcher) ||\n    matcher instanceof RegExp\n  ) {\n    throw new Error('Expect matcher to be an object');\n  }\n\n  // Check that the property has all the expected values\n  return Object.keys(matcher).every(propName => {\n    return fromPrimative(getValue(propName), matcher[propName]);\n  });\n}\n\nexport default fromFunction;\n"
  },
  {
    "path": "lib/commons/matches/from-primative.js",
    "content": "/**\n * Check if some value matches\n *\n * ```js\n * match.fromPrimative('foo', 'foo') // true, string is the same\n * match.fromPrimative('foo', ['foo', 'bar']) // true, string is included\n * match.fromPrimative('foo', /foo/) // true, string matches regex\n * match.fromPrimative('foo', str => str.toUpperCase() === 'FOO') // true, function return is truthy\n * match.fromPrimative('foo', '/foo/') // true, string matches regex string\n * ```\n *\n * @private\n * @param {String|Boolean|Array|Number|Null|Undefined} someString\n * @param {String|RegExp|Function|Array<String>|Null|Undefined} matcher\n * @returns {Boolean}\n */\nfunction fromPrimative(someString, matcher) {\n  const matcherType = typeof matcher;\n  if (Array.isArray(matcher) && typeof someString !== 'undefined') {\n    return matcher.includes(someString);\n  }\n\n  if (matcherType === 'function') {\n    return !!matcher(someString);\n  }\n\n  // RegExp.test(str) typecasts the str to a String value so doing\n  // `/.*/.test(null)` returns true as null is cast to \"null\"\n  if (someString !== null && someString !== undefined) {\n    if (matcher instanceof RegExp) {\n      return matcher.test(someString);\n    }\n\n    // matcher starts and ends with \"/\"\n    if (/^\\/.*\\/$/.test(matcher)) {\n      const pattern = matcher.substring(1, matcher.length - 1);\n      return new RegExp(pattern).test(someString);\n    }\n  }\n\n  return matcher === someString;\n}\n\nexport default fromPrimative;\n"
  },
  {
    "path": "lib/commons/matches/has-accessible-name.js",
    "content": "import accessibleTextVirtual from '../text/accessible-text-virtual';\nimport fromPrimative from './from-primative';\n\n/**\n * Check if a virtual node has a non-empty accessible name\n *``\n * Note: matches.hasAccessibleName(vNode, true) can be indirectly used through\n * matches(vNode, { hasAccessibleName: boolean })\n *\n * Example:\n * ```js\n * matches.hasAccessibleName(vNode, true);\n * matches.hasAccessibleName(vNode, false);\n *\n * ```\n *\n * @param {VirtualNode} vNode\n * @param {Object} matcher\n * @returns {Boolean}\n */\nfunction hasAccessibleName(vNode, matcher) {\n  return fromPrimative(!!accessibleTextVirtual(vNode), matcher);\n}\n\nexport default hasAccessibleName;\n"
  },
  {
    "path": "lib/commons/matches/implicit-role.js",
    "content": "import fromPrimative from './from-primative';\nimport getImplicitRole from '../aria/implicit-role';\n\n/**\n * Check if a virtual node matches an implicit role(s)\n *``\n * Note: matches.implicitRole(vNode, matcher) can be indirectly used through\n * matches(vNode, { implicitRole: matcher })\n *\n * Example:\n * ```js\n * matches.implicitRole(vNode, ['combobox', 'textbox']);\n * matches.implicitRole(vNode, 'combobox');\n * ```\n *\n * @param {VirtualNode} vNode\n * @param {Object} matcher\n * @returns {Boolean}\n */\nfunction implicitRole(vNode, matcher) {\n  return fromPrimative(getImplicitRole(vNode), matcher);\n}\n\nexport default implicitRole;\n"
  },
  {
    "path": "lib/commons/matches/index.js",
    "content": "/**\n * Namespace for matching utilities.\n * @namespace commons.matches\n * @memberof axe\n */\nimport hasAccessibleName from './has-accessible-name';\nimport attributes from './attributes';\nimport condition from './condition';\nimport explicitRole from './explicit-role';\nimport fromDefinition from './from-definition';\nimport fromFunction from './from-function';\nimport fromPrimative from './from-primative';\nimport implicitRole from './implicit-role';\nimport matches from './matches';\nimport nodeName from './node-name';\nimport properties from './properties';\nimport semanticRole from './semantic-role';\n\nmatches.hasAccessibleName = hasAccessibleName;\nmatches.attributes = attributes;\nmatches.condition = condition;\nmatches.explicitRole = explicitRole;\nmatches.fromDefinition = fromDefinition;\nmatches.fromFunction = fromFunction;\nmatches.fromPrimative = fromPrimative;\nmatches.implicitRole = implicitRole;\nmatches.nodeName = nodeName;\nmatches.properties = properties;\nmatches.semanticRole = semanticRole;\n\nexport default matches;\n"
  },
  {
    "path": "lib/commons/matches/matches.js",
    "content": "import fromDefinition from './from-definition';\n\n/**\n * Check if a virtual node matches a definition\n *\n * Example:\n * ```js\n * // Match a single nodeName:\n * axe.commons.matches(vNode, 'div')\n *\n * // Match one of multiple nodeNames:\n * axe.commons.matches(vNode, ['ul', 'ol'])\n *\n * // Match a node with nodeName 'button' and with aria-hidden: true:\n * axe.commons.matches(vNode, {\n *   nodeName: 'button',\n *   attributes: { 'aria-hidden': 'true' }\n * })\n *\n * // Mixed input. Match button nodeName, input[type=button] and input[type=reset]\n * axe.commons.matches(vNode, ['button', {\n *  nodeName: 'input', // nodeName match isn't case sensitive\n *  properties: { type: ['button', 'reset'] }\n * }])\n * ```\n *\n * @deprecated HTMLElement is deprecated, use VirtualNode instead\n *\n * @namespace matches\n * @memberof axe.commons\n * @param {HTMLElement|VirtualNode} vNode virtual node to verify attributes against constraints\n * @param {Array<ElementDefinition>|String|Object|Function|Regex} definition\n * @return {Boolean} true/ false based on if vNode passes the constraints expected\n */\nfunction matches(vNode, definition) {\n  return fromDefinition(vNode, definition);\n}\n\nexport default matches;\n"
  },
  {
    "path": "lib/commons/matches/node-name.js",
    "content": "import fromPrimative from './from-primative';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Check if the nodeName of a virtual node matches some value.\n *\n * Note: matches.nodeName(vNode, matcher) can be indirectly used through\n * matches(vNode, { nodeName: matcher })\n *\n * Example:\n * ```js\n * matches.nodeName(vNode, ['div', 'span'])\n * ```\n *\n * @deprecated HTMLElement is deprecated, use VirtualNode instead\n *\n * @param {HTMLElement|VirtualNode} vNode\n * @param {Object} Attribute matcher\n * @returns {Boolean}\n */\nfunction nodeName(vNode, matcher) {\n  vNode = nodeLookup(vNode).vNode;\n  return fromPrimative(vNode.props.nodeName, matcher);\n}\n\nexport default nodeName;\n"
  },
  {
    "path": "lib/commons/matches/properties.js",
    "content": "import fromFunction from './from-function';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Check if a virtual node matches some attribute(s)\n *\n * Note: matches.properties(vNode, matcher) can be indirectly used through\n * matches(vNode, { properties: matcher })\n *\n * Example:\n * ```js\n * matches.properties(vNode, {\n *   type: 'text', // Simple string match\n *   value: value => value.trim() !== '', // None-empty value, using a function matcher\n * })\n * ```\n *\n * @deprecated HTMLElement is deprecated, use VirtualNode instead\n *\n * @param {HTMLElement|VirtualNode} vNode\n * @param {Object} matcher\n * @returns {Boolean}\n */\nfunction properties(vNode, matcher) {\n  vNode = nodeLookup(vNode).vNode;\n  return fromFunction(propName => vNode.props[propName], matcher);\n}\n\nexport default properties;\n"
  },
  {
    "path": "lib/commons/matches/semantic-role.js",
    "content": "import fromPrimative from './from-primative';\nimport getRole from '../aria/get-role';\n\n/**\n * Check if a virtual node matches an semantic role(s)\n *``\n * Note: matches.semanticRole(vNode, matcher) can be indirectly used through\n * matches(vNode, { semanticRole: matcher })\n *\n * Example:\n * ```js\n * matches.semanticRole(vNode, ['combobox', 'textbox']);\n * matches.semanticRole(vNode, 'combobox');\n * ```\n *\n * @param {VirtualNode} vNode\n * @param {Object} matcher\n * @returns {Boolean}\n */\nfunction semanticRole(vNode, matcher) {\n  return fromPrimative(getRole(vNode), matcher);\n}\n\nexport default semanticRole;\n"
  },
  {
    "path": "lib/commons/math/get-bounding-rect.js",
    "content": "/**\n * Return a new rect that wraps around both rectA and rectB\n * @method getBoundingRect\n * @memberof axe.commons.math\n * @param {DOMRect} rectA\n * @param {DOMRect} rectB\n * @returns {DOMRect}\n */\nexport function getBoundingRect(rectA, rectB) {\n  const top = Math.min(rectA.top, rectB.top);\n  const right = Math.max(rectA.right, rectB.right);\n  const bottom = Math.max(rectA.bottom, rectB.bottom);\n  const left = Math.min(rectA.left, rectB.left);\n  return new window.DOMRect(left, top, right - left, bottom - top);\n}\n"
  },
  {
    "path": "lib/commons/math/get-intersection-rect.js",
    "content": "/**\n * Get the intersection rectangle of two rectangles. Returns null if the rectangles do not intersect.\n * @see https://math.stackexchange.com/a/2477358\n * @method getIntersectionRect\n * @memberof axe.commons.math\n * @param {DOMRect} rect1\n * @param {DOMRect} rect2\n * @returns {DOMRect|Null}\n */\nexport default function getIntersectionRect(rect1, rect2) {\n  const leftX = Math.max(rect1.left, rect2.left);\n  const rightX = Math.min(rect1.right, rect2.right);\n  const topY = Math.max(rect1.top, rect2.top);\n  const bottomY = Math.min(rect1.bottom, rect2.bottom);\n\n  if (leftX >= rightX || topY >= bottomY) {\n    return null;\n  }\n\n  return new window.DOMRect(leftX, topY, rightX - leftX, bottomY - topY);\n}\n"
  },
  {
    "path": "lib/commons/math/get-offset.js",
    "content": "import { getTargetRects, getTargetSize } from '../dom';\nimport { getBoundingRect } from './get-bounding-rect';\nimport { isPointInRect } from './is-point-in-rect';\nimport { getRectCenter } from './get-rect-center';\nimport rectHasMinimumSize from './rect-has-minimum-size';\n\n/**\n * Get the offset between target A and neighbor B. This assumes that the target is undersized and needs to check the spacing exception.\n * @method getOffset\n * @memberof axe.commons.math\n * @param {VirtualNode} vTarget\n * @param {VirtualNode} vNeighbor\n * @param {Number} radius\n * @returns {number}\n */\nexport default function getOffset(vTarget, vNeighbor, minRadiusNeighbour = 12) {\n  const targetRects = getTargetRects(vTarget);\n  const neighborRects = getTargetRects(vNeighbor);\n\n  if (!targetRects.length || !neighborRects.length) {\n    return null;\n  }\n\n  // calculate distance of each target rect to the closest edge of each neighbor rect\n  return targetRects.reduce((minDistance, targetRect) => {\n    const targetCenter = getRectCenter(targetRect);\n\n    for (const rect of neighborRects) {\n      if (isPointInRect(targetCenter, rect)) {\n        return 0;\n      }\n\n      const closestPoint = getClosestPoint(targetCenter, rect);\n      const distance = pointDistance(targetCenter, closestPoint);\n      minDistance = Math.min(minDistance, distance);\n    }\n\n    const neighborTargetSize = getTargetSize(vNeighbor);\n    if (rectHasMinimumSize(minRadiusNeighbour * 2, neighborTargetSize)) {\n      return minDistance;\n    }\n\n    const neighborBoundingBox = neighborRects.reduce(getBoundingRect);\n    const neighborCenter = getRectCenter(neighborBoundingBox);\n    // subtract the radius of the circle from the distance to center to get distance to edge of the neighbor circle\n    const centerDistance =\n      pointDistance(targetCenter, neighborCenter) - minRadiusNeighbour;\n\n    return Math.max(0, Math.min(minDistance, centerDistance));\n  }, Infinity);\n}\n\nfunction getClosestPoint(point, rect) {\n  let x;\n  let y;\n\n  if (point.x < rect.left) {\n    x = rect.left;\n  } else if (point.x > rect.right) {\n    x = rect.right;\n  } else {\n    x = point.x;\n  }\n\n  if (point.y < rect.top) {\n    y = rect.top;\n  } else if (point.y > rect.bottom) {\n    y = rect.bottom;\n  } else {\n    y = point.y;\n  }\n\n  return { x, y };\n}\n\n/**\n * Distance between two points\n * @param {Point} pointA\n * @param {Point} pointB\n * @returns {number}\n */\nfunction pointDistance(pointA, pointB) {\n  return Math.hypot(pointA.x - pointB.x, pointA.y - pointB.y);\n}\n"
  },
  {
    "path": "lib/commons/math/get-rect-center.js",
    "content": "/**\n * Return the center point of a rect\n * @method getRectCenter\n * @memberof axe.commons.math\n * @param {DOMRect}\n * @returns {DOMPoint}\n */\nexport function getRectCenter({ left, top, width, height }) {\n  return new window.DOMPoint(left + width / 2, top + height / 2);\n}\n"
  },
  {
    "path": "lib/commons/math/has-visual-overlap.js",
    "content": "import { visuallySort } from '../dom';\n\n/**\n * Check if node A overlaps B\n * @method getOffset\n * @memberof axe.commons.math\n * @param {VirtualNode} vNodeA\n * @param {VirtualNode} vNodeB\n * @returns {boolean}\n */\nexport default function hasVisualOverlap(vNodeA, vNodeB) {\n  const rectA = vNodeA.boundingClientRect;\n  const rectB = vNodeB.boundingClientRect;\n  if (\n    rectA.left >= rectB.right ||\n    rectA.right <= rectB.left ||\n    rectA.top >= rectB.bottom ||\n    rectA.bottom <= rectB.top\n  ) {\n    return false;\n  }\n  return visuallySort(vNodeA, vNodeB) > 0;\n}\n"
  },
  {
    "path": "lib/commons/math/index.js",
    "content": "export { getBoundingRect } from './get-bounding-rect';\nexport { default as getIntersectionRect } from './get-intersection-rect';\nexport { default as getOffset } from './get-offset';\nexport { getRectCenter } from './get-rect-center';\nexport { default as hasVisualOverlap } from './has-visual-overlap';\nexport { isPointInRect } from './is-point-in-rect';\nexport { default as rectHasMinimumSize } from './rect-has-minimum-size';\nexport { default as rectsOverlap } from './rects-overlap';\nexport { default as splitRects } from './split-rects';\n"
  },
  {
    "path": "lib/commons/math/is-point-in-rect.js",
    "content": "/**\n * Check if the DOMPoint is within the DOMRect\n * @method isPointInRect\n * @memberof axe.commons.math\n * @param {DOMPoint}\n * @param {DOMRect}\n * @returns {boolean}\n */\nexport function isPointInRect({ x, y }, { top, right, bottom, left }) {\n  return y >= top && x <= right && y <= bottom && x >= left;\n}\n"
  },
  {
    "path": "lib/commons/math/rect-has-minimum-size.js",
    "content": "const roundingMargin = 0.05;\n\nexport default function rectHasMinimumSize(minSize, { width, height }) {\n  return (\n    width + roundingMargin >= minSize && height + roundingMargin >= minSize\n  );\n}\n"
  },
  {
    "path": "lib/commons/math/rects-overlap.js",
    "content": "/**\n * Determine if two rectangles touch.\n * @method rectsOverlap\n * @memberof axe.commons.math\n * @param {DOMRect} rect1\n * @param {DOMRect} rect2\n * @returns {Boolean}\n */\nexport default function rectsOverlap(rect1, rect2) {\n  // perform an AABB (axis-aligned bounding box) check.\n  // account for differences in how browsers handle floating\n  // point precision of bounding rects\n  // @see https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection\n\n  /* eslint-disable no-bitwise */\n  return (\n    (rect1.left | 0) < (rect2.right | 0) &&\n    (rect1.right | 0) > (rect2.left | 0) &&\n    (rect1.top | 0) < (rect2.bottom | 0) &&\n    (rect1.bottom | 0) > (rect2.top | 0)\n  );\n  /* eslint-enable no-bitwise */\n}\n"
  },
  {
    "path": "lib/commons/math/split-rects.js",
    "content": "/**\n * Given an outer rect, and a list of rects that overlap with it, find any rectangular\n * space that does not overlap.\n * @method getOffset\n * @memberof axe.commons.math\n * @param {DOMRect|DOMRect[]} outerRect\n * @param {DOMRect[]} overlapRects\n * @returns {DOMRect[]} Unique array of rects\n */\nexport default function splitRects(outerRect, overlapRects) {\n  let uniqueRects = Array.isArray(outerRect) ? outerRect : [outerRect];\n  for (const overlapRect of overlapRects) {\n    uniqueRects = uniqueRects.reduce((rects, inputRect) => {\n      return rects.concat(splitRect(inputRect, overlapRect));\n    }, []);\n\n    // exit early if we get too many rects that it starts causing\n    // a performance bottleneck\n    // @see https://github.com/dequelabs/axe-core/issues/4359\n    if (uniqueRects.length > 4000) {\n      throw new Error('splitRects: Too many rects');\n    }\n  }\n  return uniqueRects;\n}\n\n// Cut the input rect along any intersecting edge of the clip rect.\nfunction splitRect(inputRect, clipRect) {\n  const { top, left, bottom, right } = inputRect;\n  const yAligned = top < clipRect.bottom && bottom > clipRect.top;\n  const xAligned = left < clipRect.right && right > clipRect.left;\n\n  const rects = [];\n  if (between(clipRect.top, top, bottom) && xAligned) {\n    rects.push({ top, left, bottom: clipRect.top, right });\n  }\n  if (between(clipRect.right, left, right) && yAligned) {\n    rects.push({ top, left: clipRect.right, bottom, right });\n  }\n  if (between(clipRect.bottom, top, bottom) && xAligned) {\n    rects.push({ top: clipRect.bottom, right, bottom, left });\n  }\n  if (between(clipRect.left, left, right) && yAligned) {\n    rects.push({ top, left, bottom, right: clipRect.left });\n  }\n  if (rects.length === 0) {\n    // Fully overlapping\n    if (isEnclosedRect(inputRect, clipRect)) {\n      return [];\n    }\n\n    rects.push(inputRect); // No intersection\n  }\n\n  return rects.map(computeRect); // add x / y / width / height\n}\n\nconst between = (num, min, max) => num > min && num < max;\n\nfunction computeRect(baseRect) {\n  return new window.DOMRect(\n    baseRect.left,\n    baseRect.top,\n    baseRect.right - baseRect.left,\n    baseRect.bottom - baseRect.top\n  );\n}\n\nfunction isEnclosedRect(rectA, rectB) {\n  return (\n    rectA.top >= rectB.top &&\n    rectA.left >= rectB.left &&\n    rectA.bottom <= rectB.bottom &&\n    rectA.right <= rectB.right\n  );\n}\n"
  },
  {
    "path": "lib/commons/standards/get-aria-roles-by-type.js",
    "content": "import standards from '../../standards';\n\n/**\n * Return a list of aria roles whose type matches the provided value.\n * @param {String} type The desired role type\n * @return {String[]} List of all roles matching the type\n */\nfunction getAriaRolesByType(type) {\n  return Object.keys(standards.ariaRoles).filter(roleName => {\n    return standards.ariaRoles[roleName].type === type;\n  });\n}\n\nexport default getAriaRolesByType;\n"
  },
  {
    "path": "lib/commons/standards/get-aria-roles-supporting-name-from-content.js",
    "content": "import cache from '../../core/base/cache';\nimport standards from '../../standards';\n\n/**\n * Return a list of aria roles which are name from content.\n * @return {String[]} List of all roles with name from content\n */\nfunction getAriaRolesSupportingNameFromContent() {\n  return cache.get('ariaRolesNameFromContent', () =>\n    Object.keys(standards.ariaRoles).filter(roleName => {\n      return standards.ariaRoles[roleName].nameFromContent;\n    })\n  );\n}\n\nexport default getAriaRolesSupportingNameFromContent;\n"
  },
  {
    "path": "lib/commons/standards/get-element-spec.js",
    "content": "import standards from '../../standards';\nimport matchesFn from '../../commons/matches';\n\n/**\n * Return the spec for an HTML element from the standards object. Since the spec is determined by the node and what attributes it has, a node is required.\n * @param {VirtualNode} vNode The VirtualNode to get the spec for.\n * @return {Object} The standard spec object\n */\nfunction getElementSpec(vNode, { noMatchAccessibleName = false } = {}) {\n  const standard = standards.htmlElms[vNode.props.nodeName];\n\n  // invalid element name (could be an svg or custom element name)\n  if (!standard) {\n    return {};\n  }\n\n  if (!standard.variant) {\n    return standard;\n  }\n\n  // start with the information at the top level\n  const { variant, ...spec } = standard;\n\n  // loop through all variants (excluding default) finding anything\n  // that matches\n  for (const variantName in variant) {\n    if (!variant.hasOwnProperty(variantName) || variantName === 'default') {\n      continue;\n    }\n\n    const { matches, ...props } = variant[variantName];\n    const matchProperties = Array.isArray(matches) ? matches : [matches];\n    for (let i = 0; i < matchProperties.length && noMatchAccessibleName; i++) {\n      if (matchProperties[i].hasOwnProperty('hasAccessibleName')) {\n        return standard;\n      }\n    }\n\n    if (matchesFn(vNode, matches)) {\n      for (const propName in props) {\n        if (props.hasOwnProperty(propName)) {\n          spec[propName] = props[propName];\n        }\n      }\n    }\n  }\n\n  // apply defaults if properties were not found\n  for (const propName in variant.default) {\n    if (\n      variant.default.hasOwnProperty(propName) &&\n      typeof spec[propName] === 'undefined'\n    ) {\n      spec[propName] = variant.default[propName];\n    }\n  }\n\n  return spec;\n}\n\nexport default getElementSpec;\n"
  },
  {
    "path": "lib/commons/standards/get-elements-by-content-type.js",
    "content": "import standards from '../../standards';\n\n/**\n * Return a list of html elements whose content type matches the provided value. Note: this will not work for 'interactive' content types as those depend on the element.\n * @param {String} type The desired content type\n * @return {String[]} List of all elements matching the type\n */\nfunction getElementsByContentType(type) {\n  return Object.keys(standards.htmlElms).filter(nodeName => {\n    const elm = standards.htmlElms[nodeName];\n\n    if (elm.contentTypes) {\n      return elm.contentTypes.includes(type);\n    }\n\n    // some elements do not have content types\n    if (!elm.variant) {\n      return false;\n    }\n\n    if (elm.variant.default && elm.variant.default.contentTypes) {\n      return elm.variant.default.contentTypes.includes(type);\n    }\n\n    // content type depends on a virtual node\n    return false;\n  });\n}\n\nexport default getElementsByContentType;\n"
  },
  {
    "path": "lib/commons/standards/get-global-aria-attrs.js",
    "content": "import cache from '../../core/base/cache';\nimport standards from '../../standards';\n\n/**\n * Return a list of global aria attributes.\n * @return {String[]} List of all global aria attributes\n */\nfunction getGlobalAriaAttrs() {\n  return cache.get('globalAriaAttrs', () =>\n    Object.keys(standards.ariaAttrs).filter(attrName => {\n      return standards.ariaAttrs[attrName].global;\n    })\n  );\n}\n\nexport default getGlobalAriaAttrs;\n"
  },
  {
    "path": "lib/commons/standards/implicit-html-roles.js",
    "content": "// Source: https://www.w3.org/TR/html-aam-1.0/#element-mapping-table\n// Source: https://www.w3.org/TR/html-aria/\nimport getElementsByContentType from './get-elements-by-content-type';\nimport getGlobalAriaAttrs from './get-global-aria-attrs';\nimport arialabelledbyText from '../aria/arialabelledby-text';\nimport arialabelText from '../aria/arialabel-text';\nimport idrefs from '../dom/idrefs';\nimport isColumnHeader from '../table/is-column-header';\nimport isRowHeader from '../table/is-row-header';\nimport sanitize from '../text/sanitize';\nimport isFocusable from '../dom/is-focusable';\nimport { closest } from '../../core/utils';\nimport cache from '../../core/base/cache';\nimport getExplicitRole from '../aria/get-explicit-role';\n\n// Sectioning content elements: article, aside, nav, section\n// https://html.spec.whatwg.org/multipage/dom.html#sectioning-content\nconst getSectioningContentSelector = () => {\n  return cache.get('sectioningContentSelector', () => {\n    return (\n      getElementsByContentType('sectioning')\n        .map(nodeName => `${nodeName}:not([role])`)\n        .join(', ') +\n      ' , [role=article], [role=complementary], [role=navigation], [role=region]'\n    );\n  });\n};\n\nconst getSectioningContentPlusMainSelector = () => {\n  // Why is there this similar but slightly different selector?\n  // ->\n  // Asides can be scoped to body or main, but headers and footers must be\n  // scoped **only** to body (for landmark role mapping).\n  //   - Header: https://w3c.github.io/html-aam/#el-header-ancestorbody\n  //   - Footer: https://w3c.github.io/html-aam/#el-footer-ancestorbody\n  //   - Aside: https://w3c.github.io/html-aam/#el-aside-ancestorbodymain\n  return cache.get('sectioningContentPlusMainSelector', () => {\n    return getSectioningContentSelector() + ' , main:not([role]), [role=main]';\n  });\n};\n\n// sectioning elements only have an accessible name if the\n// aria-label, aria-labelledby, or title attribute has valid\n// content.\n// can't go through the normal accessible name computation\n// as it leads into an infinite loop of asking for the role\n// of the element while the implicit role needs the name.\n// Source: https://www.w3.org/TR/html-aam-1.0/#section-and-grouping-element-accessible-name-computation\n//\n// form elements also follow this same pattern although not\n// specifically called out in the spec like section elements\n// (per Scott O'Hara)\n// Source: https://web-a11y.slack.com/archives/C042TSFGN/p1590607895241100?thread_ts=1590602189.217800&cid=C042TSFGN\n//\n// `checkTitle` means - also check the title attribute and\n// return true if the node has a non-empty title\nfunction hasAccessibleName(vNode, { checkTitle = false } = {}) {\n  // testing for when browsers give a <section> a region role:\n  // chrome - always a region role\n  // firefox - if non-empty aria-labelledby, aria-label, or title\n  // safari - if non-empty aria-labelledby or aria-label\n  //\n  // we will go with safaris implementation as it is the least common\n  // denominator\n  return !!(\n    sanitize(arialabelledbyText(vNode)) ||\n    sanitize(arialabelText(vNode)) ||\n    (checkTitle && vNode?.props.nodeType === 1 && sanitize(vNode.attr('title')))\n  );\n}\n\nconst implicitHtmlRoles = {\n  a: vNode => {\n    return vNode.hasAttr('href') ? 'link' : null;\n  },\n  area: vNode => {\n    return vNode.hasAttr('href') ? 'link' : null;\n  },\n  article: 'article',\n  aside: vNode => {\n    if (\n      closest(vNode.parent, getSectioningContentSelector()) &&\n      // An aside within sectioning content can still be mapped to\n      // role=complementary if it has an accessible name\n      !hasAccessibleName(vNode, { checkTitle: true })\n    ) {\n      return null;\n    }\n\n    return 'complementary';\n  },\n  body: 'document',\n  button: 'button',\n  datalist: 'listbox',\n  dd: 'definition',\n  dfn: 'term',\n  details: 'group',\n  dialog: 'dialog',\n  dt: 'term',\n  fieldset: 'group',\n  figure: 'figure',\n  footer: vNode => {\n    const sectioningElement = closest(\n      vNode,\n      getSectioningContentPlusMainSelector()\n    );\n\n    return !sectioningElement ? 'contentinfo' : null;\n  },\n  form: vNode => {\n    return hasAccessibleName(vNode) ? 'form' : null;\n  },\n  h1: 'heading',\n  h2: 'heading',\n  h3: 'heading',\n  h4: 'heading',\n  h5: 'heading',\n  h6: 'heading',\n  header: vNode => {\n    const sectioningElement = closest(\n      vNode,\n      getSectioningContentPlusMainSelector()\n    );\n\n    return !sectioningElement ? 'banner' : null;\n  },\n  hr: 'separator',\n  img: vNode => {\n    // an images role is considered implicitly presentation if the\n    // alt attribute is empty. But that shouldn't be the case if it\n    // has global aria attributes or is focusable, so we need to\n    // override the role back to `img`\n    // e.g. <img alt=\"\" aria-label=\"foo\"></img>\n    const emptyAlt = vNode.hasAttr('alt') && !vNode.attr('alt');\n    const hasGlobalAria = getGlobalAriaAttrs().find(attr =>\n      vNode.hasAttr(attr)\n    );\n\n    return emptyAlt && !hasGlobalAria && !isFocusable(vNode)\n      ? 'presentation'\n      : 'img';\n  },\n  input: vNode => {\n    // Source: https://www.w3.org/TR/html52/sec-forms.html#suggestions-source-element\n    let suggestionsSourceElement;\n    if (vNode.hasAttr('list')) {\n      const listElement = idrefs(vNode.actualNode, 'list').filter(\n        node => !!node\n      )[0];\n      suggestionsSourceElement =\n        listElement && listElement.nodeName.toLowerCase() === 'datalist';\n    }\n\n    switch (vNode.props.type) {\n      case 'checkbox':\n        return 'checkbox';\n      case 'number':\n        return 'spinbutton';\n      case 'radio':\n        return 'radio';\n      case 'range':\n        return 'slider';\n      case 'search':\n        return !suggestionsSourceElement ? 'searchbox' : 'combobox';\n\n      case 'button':\n      case 'image':\n      case 'reset':\n      case 'submit':\n        return 'button';\n\n      case 'text':\n      case 'tel':\n      case 'url':\n      case 'email':\n      case '':\n        return !suggestionsSourceElement ? 'textbox' : 'combobox';\n\n      default:\n        return 'textbox';\n    }\n  },\n  // Note: if an li (or some other elms) do not have a required\n  // parent, Firefox ignores the implicit semantic role and treats\n  // it as a generic text.\n  li: 'listitem',\n  main: 'main',\n  math: 'math',\n  menu: 'list',\n  meter: 'meter',\n  nav: 'navigation',\n  ol: 'list',\n  optgroup: 'group',\n  option: 'option',\n  output: 'status',\n  progress: 'progressbar',\n  search: 'search',\n  section: vNode => {\n    return hasAccessibleName(vNode) ? 'region' : null;\n  },\n  select: vNode => {\n    return vNode.hasAttr('multiple') || parseInt(vNode.attr('size')) > 1\n      ? 'listbox'\n      : 'combobox';\n  },\n  summary: 'button',\n  table: 'table',\n  tbody: 'rowgroup',\n  td: vNode => {\n    const table = closest(vNode, 'table');\n    const role = getExplicitRole(table);\n\n    return ['grid', 'treegrid'].includes(role) ? 'gridcell' : 'cell';\n  },\n  textarea: 'textbox',\n  tfoot: 'rowgroup',\n  th: vNode => {\n    if (isColumnHeader(vNode)) {\n      return 'columnheader';\n    }\n    if (isRowHeader(vNode)) {\n      return 'rowheader';\n    }\n  },\n  thead: 'rowgroup',\n  tr: 'row',\n  ul: 'list'\n};\n\nexport default implicitHtmlRoles;\n"
  },
  {
    "path": "lib/commons/standards/index.js",
    "content": "/**\n * Namespace for standards-related utilities.\n * @namespace commons.standards\n * @memberof axe\n */\nexport { default as getAriaRolesByType } from './get-aria-roles-by-type';\nexport { default as getAriaRolesSupportingNameFromContent } from './get-aria-roles-supporting-name-from-content';\nexport { default as getGlobalAriaAttrs } from './get-global-aria-attrs';\nexport { default as getElementSpec } from './get-element-spec';\nexport { default as getElementsByContentType } from './get-elements-by-content-type';\nexport { default as implicitHtmlRoles } from './implicit-html-roles';\n"
  },
  {
    "path": "lib/commons/table/get-all-cells.js",
    "content": "/**\n * Returns all cells contained in given HTMLTableElement\n * @method getAllCells\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableElement} tableElm Table Element to get cells from\n * @return {Array<HTMLTableCellElement>}\n */\nfunction getAllCells(tableElm) {\n  let rowIndex, cellIndex, rowLength, cellLength;\n  const cells = [];\n  for (\n    rowIndex = 0, rowLength = tableElm.rows.length;\n    rowIndex < rowLength;\n    rowIndex++\n  ) {\n    for (\n      cellIndex = 0, cellLength = tableElm.rows[rowIndex].cells.length;\n      cellIndex < cellLength;\n      cellIndex++\n    ) {\n      cells.push(tableElm.rows[rowIndex].cells[cellIndex]);\n    }\n  }\n  return cells;\n}\n\nexport default getAllCells;\n"
  },
  {
    "path": "lib/commons/table/get-cell-position.js",
    "content": "import toGrid from './to-grid';\nimport findUp from '../dom/find-up';\nimport { memoize } from '../../core/utils';\n\n/**\n * Get the x, y coordinates of a table cell; normalized for rowspan and colspan\n * @method getCellPosition\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} cell The table cell of which to get the position\n * @return {Object} Object with `x` and `y` properties of the coordinates\n */\nfunction getCellPosition(cell, tableGrid) {\n  let rowIndex, index;\n  if (!tableGrid) {\n    tableGrid = toGrid(findUp(cell, 'table'));\n  }\n\n  for (rowIndex = 0; rowIndex < tableGrid.length; rowIndex++) {\n    if (tableGrid[rowIndex]) {\n      index = tableGrid[rowIndex].indexOf(cell);\n      if (index !== -1) {\n        return {\n          x: index,\n          y: rowIndex\n        };\n      }\n    }\n  }\n}\n\nexport default memoize(getCellPosition);\n"
  },
  {
    "path": "lib/commons/table/get-headers.js",
    "content": "import isRowHeader from './is-row-header';\nimport isColumnHeader from './is-column-header';\nimport toGrid from './to-grid';\nimport getCellPosition from './get-cell-position';\nimport idrefs from '../dom/idrefs';\nimport findUp from '../dom/find-up';\n\n/**\n * Loop through the table grid looking for headers and caching the result.\n * @param {String} headerType The type of header to look for (\"row\" or \"col\")\n * @param {Object} position The position of the cell to start looking\n * @param {Array} tablegrid A matrix of the table obtained using axe.commons.table.toGrid\n * @return {Array<HTMLTableCellElement>} Array of HTMLTableCellElements that are headers\n */\nfunction traverseForHeaders(headerType, position, tableGrid) {\n  const property = headerType === 'row' ? '_rowHeaders' : '_colHeaders';\n  const predicate = headerType === 'row' ? isRowHeader : isColumnHeader;\n  const startCell = tableGrid[position.y][position.x];\n\n  // adjust position by rowspan and colspan\n  // subtract 1 from col/rowspan to make them 0 indexed\n  const colspan = startCell.colSpan - 1;\n\n  // ie11 returns 1 as the rowspan value even if it's set to 0\n  const rowspanAttr = startCell.getAttribute('rowspan');\n  const rowspanValue =\n    parseInt(rowspanAttr) === 0 || startCell.rowspan === 0\n      ? tableGrid.length\n      : startCell.rowSpan;\n  const rowspan = rowspanValue - 1;\n\n  const rowStart = position.y + rowspan;\n  const colStart = position.x + colspan;\n\n  const rowEnd = headerType === 'row' ? position.y : 0;\n  const colEnd = headerType === 'row' ? 0 : position.x;\n\n  let headers;\n  const cells = [];\n  for (let row = rowStart; row >= rowEnd && !headers; row--) {\n    for (let col = colStart; col >= colEnd; col--) {\n      const cell = tableGrid[row] ? tableGrid[row][col] : undefined;\n\n      if (!cell) {\n        continue;\n      }\n\n      // stop traversing once we've found a cache\n      const vNode = axe.utils.getNodeFromTree(cell);\n      if (vNode[property]) {\n        headers = vNode[property];\n        break;\n      }\n\n      cells.push(cell);\n    }\n  }\n\n  // need to check that the cells we've traversed are headers\n  headers = (headers || []).concat(cells.filter(predicate));\n\n  // cache results\n  cells.forEach(tableCell => {\n    const vNode = axe.utils.getNodeFromTree(tableCell);\n    vNode[property] = headers;\n  });\n\n  return headers;\n}\n\n/**\n * Get any associated table headers for a `HTMLTableCellElement`\n * @method getHeaders\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} cell The cell of which to get headers\n * @param {Array} [tablegrid] A matrix of the table obtained using axe.commons.table.toGrid\n * @return {Array<HTMLTableCellElement>} Array of headers associated to the table cell\n */\nfunction getHeaders(cell, tableGrid) {\n  if (cell.getAttribute('headers')) {\n    const headers = idrefs(cell, 'headers');\n\n    // testing has shown that if the headers attribute is incorrect the browser\n    // will default to the table row/column headers\n    if (headers.filter(header => header).length) {\n      return headers;\n    }\n  }\n  if (!tableGrid) {\n    tableGrid = toGrid(findUp(cell, 'table'));\n  }\n  const position = getCellPosition(cell, tableGrid);\n\n  // TODO: RTL text\n  const rowHeaders = traverseForHeaders('row', position, tableGrid);\n  const colHeaders = traverseForHeaders('col', position, tableGrid);\n\n  return [].concat(rowHeaders, colHeaders).reverse();\n}\n\nexport default getHeaders;\n"
  },
  {
    "path": "lib/commons/table/get-scope.js",
    "content": "import toGrid from './to-grid';\nimport getCellPosition from './get-cell-position';\nimport findUp from '../dom/find-up';\nimport { nodeLookup } from '../../core/utils';\nimport getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Determine if a `HTMLTableCellElement` is a column header, if so get the scope of the header\n * @method getScope\n * @memberof axe.commons.table\n * @instance\n * @param {HTMLTableCellElement|AbstractVirtualNode} cell The table cell to test\n * @return {Boolean|String} Returns `false` if not a column header, or the scope of the column header element\n */\nexport default function getScope(el) {\n  const { vNode, domNode: cell } = nodeLookup(el);\n\n  const scope = vNode.attr('scope');\n  const role = getExplicitRole(vNode);\n\n  if (!['td', 'th'].includes(vNode.props.nodeName)) {\n    throw new TypeError('Expected TD or TH element');\n  }\n\n  if (role === 'columnheader') {\n    return 'col';\n  } else if (role === 'rowheader') {\n    return 'row';\n  } else if (scope === 'col' || scope === 'row') {\n    return scope;\n  } else if (vNode.props.nodeName !== 'th') {\n    return false;\n  } else if (!vNode.actualNode) {\n    return 'auto';\n  }\n  const tableGrid = toGrid(findUp(cell, 'table'));\n  const pos = getCellPosition(cell, tableGrid);\n\n  // The element is in a row with all th elements, that makes it a column header\n  const headerRow = tableGrid[pos.y].every(\n    node => node.nodeName.toUpperCase() === 'TH'\n  );\n\n  if (headerRow) {\n    return 'col';\n  }\n\n  // The element is in a column with all th elements, that makes it a row header\n  const headerCol = tableGrid\n    .map(col => col[pos.x])\n    .every(node => node && node.nodeName.toUpperCase() === 'TH');\n\n  if (headerCol) {\n    return 'row';\n  }\n  return 'auto';\n}\n"
  },
  {
    "path": "lib/commons/table/index.js",
    "content": "/**\n * Namespace for table-related utilities.\n * @namespace table\n * @memberof axe.commons\n */\nexport { default as getAllCells } from './get-all-cells';\nexport { default as getCellPosition } from './get-cell-position';\nexport { default as getHeaders } from './get-headers';\nexport { default as getScope } from './get-scope';\nexport { default as isColumnHeader } from './is-column-header';\nexport { default as isDataCell } from './is-data-cell';\nexport { default as isDataTable } from './is-data-table';\nexport { default as isHeader } from './is-header';\nexport { default as isRowHeader } from './is-row-header';\nexport { default as toGrid } from './to-grid';\nexport { default as traverse } from './traverse';\n\n// This was the old name\nexport { default as toArray } from './to-grid';\n"
  },
  {
    "path": "lib/commons/table/is-column-header.js",
    "content": "import getScope from './get-scope';\n\n/**\n * Determine if a `HTMLTableCellElement` is a column header\n * @method isColumnHeader\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} element The table cell to test\n * @return {Boolean}\n */\nfunction isColumnHeader(element) {\n  return ['col', 'auto'].indexOf(getScope(element)) !== -1;\n}\n\nexport default isColumnHeader;\n"
  },
  {
    "path": "lib/commons/table/is-data-cell.js",
    "content": "import getExplicitRole from '../aria/get-explicit-role';\n\n/**\n * Determine if a `HTMLTableCellElement` is a data cell\n * @method isDataCell\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} node The table cell to test\n * @return {Boolean}\n */\nfunction isDataCell(cell) {\n  // @see http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#empty-cell\n  if (!cell.children.length && !cell.textContent.trim()) {\n    return false;\n  }\n  const role = getExplicitRole(cell);\n  if (role) {\n    return ['cell', 'gridcell'].includes(role);\n  } else {\n    return cell.nodeName.toUpperCase() === 'TD';\n  }\n}\n\nexport default isDataCell;\n"
  },
  {
    "path": "lib/commons/table/is-data-table.js",
    "content": "import getRoleType from '../aria/get-role-type';\nimport getExplicitRole from '../aria/get-explicit-role';\nimport isFocusable from '../dom/is-focusable';\nimport findUp from '../dom/find-up';\nimport getElementCoordinates from '../dom/get-element-coordinates';\nimport getViewportSize from '../dom/get-viewport-size';\n\n/**\n * Determines whether a table is a data table\n * @method isDataTable\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableElement} node The table to test\n * @return {Boolean}\n * @see http://asurkov.blogspot.co.uk/2011/10/data-vs-layout-table.html\n */\nfunction isDataTable(node) {\n  const role = getExplicitRole(node);\n\n  // The element is not focusable and has role=presentation\n  if ((role === 'presentation' || role === 'none') && !isFocusable(node)) {\n    return false;\n  }\n\n  // Table inside editable area is data table always since the table structure is crucial for table editing\n  if (\n    node.getAttribute('contenteditable') === 'true' ||\n    findUp(node, '[contenteditable=\"true\"]')\n  ) {\n    return true;\n  }\n\n  // Table having ARIA table related role is data table\n  if (role === 'grid' || role === 'treegrid' || role === 'table') {\n    return true;\n  }\n\n  // Table having ARIA landmark role is data table\n  if (getRoleType(role) === 'landmark') {\n    return true;\n  }\n\n  // Table having datatable=\"0\" attribute is layout table\n  if (node.getAttribute('datatable') === '0') {\n    return false;\n  }\n\n  // Table having summary attribute is data table\n  if (node.getAttribute('summary')) {\n    return true;\n  }\n\n  // Table having legitimate data table structures is data table\n  if (node.tHead || node.tFoot || node.caption) {\n    return true;\n  }\n  // colgroup / col - colgroup is magically generated\n  for (\n    let childIndex = 0, childLength = node.children.length;\n    childIndex < childLength;\n    childIndex++\n  ) {\n    if (node.children[childIndex].nodeName.toUpperCase() === 'COLGROUP') {\n      return true;\n    }\n  }\n\n  let cells = 0;\n  const rowLength = node.rows.length;\n  let row, cell;\n  let hasBorder = false;\n  for (let rowIndex = 0; rowIndex < rowLength; rowIndex++) {\n    row = node.rows[rowIndex];\n    for (\n      let cellIndex = 0, cellLength = row.cells.length;\n      cellIndex < cellLength;\n      cellIndex++\n    ) {\n      cell = row.cells[cellIndex];\n      if (cell.nodeName.toUpperCase() === 'TH') {\n        return true;\n      }\n      if (\n        !hasBorder &&\n        (cell.offsetWidth !== cell.clientWidth ||\n          cell.offsetHeight !== cell.clientHeight)\n      ) {\n        hasBorder = true;\n      }\n      if (\n        cell.getAttribute('scope') ||\n        cell.getAttribute('headers') ||\n        cell.getAttribute('abbr')\n      ) {\n        return true;\n      }\n      if (['columnheader', 'rowheader'].includes(getExplicitRole(cell))) {\n        return true;\n      }\n      // abbr element as a single child element of table cell\n      if (\n        cell.children.length === 1 &&\n        cell.children[0].nodeName.toUpperCase() === 'ABBR'\n      ) {\n        return true;\n      }\n      cells++;\n    }\n  }\n\n  // Table having nested table is layout table\n  if (node.getElementsByTagName('table').length) {\n    return false;\n  }\n\n  // Table having only one row or column is layout table (row)\n  if (rowLength < 2) {\n    return false;\n  }\n\n  // Table having only one row or column is layout table (column)\n  const sampleRow = node.rows[Math.ceil(rowLength / 2)];\n  if (sampleRow.cells.length === 1 && sampleRow.cells[0].colSpan === 1) {\n    return false;\n  }\n\n  // Table having many columns (>= 5) is data table\n  if (sampleRow.cells.length >= 5) {\n    return true;\n  }\n\n  // Table having borders around cells is data table\n  if (hasBorder) {\n    return true;\n  }\n\n  // Table having differently colored rows is data table\n  let bgColor, bgImage;\n  for (let rowIndex = 0; rowIndex < rowLength; rowIndex++) {\n    row = node.rows[rowIndex];\n    if (\n      bgColor &&\n      bgColor !==\n        window.getComputedStyle(row).getPropertyValue('background-color')\n    ) {\n      return true;\n    } else {\n      bgColor = window\n        .getComputedStyle(row)\n        .getPropertyValue('background-color');\n    }\n    if (\n      bgImage &&\n      bgImage !==\n        window.getComputedStyle(row).getPropertyValue('background-image')\n    ) {\n      return true;\n    } else {\n      bgImage = window\n        .getComputedStyle(row)\n        .getPropertyValue('background-image');\n    }\n  }\n\n  // Table having many rows (>= 20) is data table\n  if (rowLength >= 20) {\n    return true;\n  }\n\n  // Wide table (more than 95% of the document width) is layout table\n  if (\n    getElementCoordinates(node).width >\n    getViewportSize(window).width * 0.95\n  ) {\n    return false;\n  }\n\n  // Table having small amount of cells (<= 10) is layout table\n  if (cells < 10) {\n    return false;\n  }\n\n  // Table containing embed, object, applet of iframe elements (typical advertisements elements) is layout table\n  if (node.querySelector('object, embed, iframe, applet')) {\n    return false;\n  }\n\n  // Otherwise it's data table\n  return true;\n}\n\nexport default isDataTable;\n"
  },
  {
    "path": "lib/commons/table/is-header.js",
    "content": "import isColumnHeader from './is-column-header';\nimport isRowHeader from './is-row-header';\nimport { escapeSelector } from '../../core/utils';\n\n/**\n * Determine if a `HTMLTableCellElement` is a header\n * @method isHeader\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} cell The table cell to test\n * @return {Boolean}\n */\nfunction isHeader(cell) {\n  if (isColumnHeader(cell) || isRowHeader(cell)) {\n    return true;\n  }\n\n  if (cell.getAttribute('id')) {\n    const id = escapeSelector(cell.getAttribute('id'));\n    return !!document.querySelector(`[headers~=\"${id}\"]`);\n  }\n\n  return false;\n}\n\nexport default isHeader;\n"
  },
  {
    "path": "lib/commons/table/is-row-header.js",
    "content": "import getScope from './get-scope';\n\n/**\n * Determine if a `HTMLTableCellElement` is a row header\n * @method isRowHeader\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableCellElement} cell The table cell to test\n * @return {Boolean}\n */\nfunction isRowHeader(cell) {\n  return ['row', 'auto'].includes(getScope(cell));\n}\n\nexport default isRowHeader;\n"
  },
  {
    "path": "lib/commons/table/to-grid.js",
    "content": "import { memoize } from '../../core/utils';\n\n/**\n * Converts a table to an Array of arrays, normalized for row and column spans\n * @method toGrid\n * @memberof axe.commons.table\n * @instance\n * @param  {HTMLTableElement} node The table to convert\n * @return {Array<HTMLTableCellElement>} Array of HTMLTableCellElements\n */\nfunction toGrid(node) {\n  const table = [];\n  const rows = node.rows;\n  for (let i = 0, rowLength = rows.length; i < rowLength; i++) {\n    const cells = rows[i].cells;\n    table[i] = table[i] || [];\n\n    let columnIndex = 0;\n\n    for (let j = 0, cellLength = cells.length; j < cellLength; j++) {\n      for (let colSpan = 0; colSpan < cells[j].colSpan; colSpan++) {\n        // if [the rowSpan] value is set to 0, it extends until the\n        // end of the table section\n        // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td#attr-rowspan\n\n        // ie11 returns 1 as the rowspan value even if it's set to 0\n        const rowspanAttr = cells[j].getAttribute('rowspan');\n        const rowspanValue =\n          parseInt(rowspanAttr) === 0 || cells[j].rowspan === 0\n            ? rows.length\n            : cells[j].rowSpan;\n\n        for (let rowSpan = 0; rowSpan < rowspanValue; rowSpan++) {\n          table[i + rowSpan] = table[i + rowSpan] || [];\n          while (table[i + rowSpan][columnIndex]) {\n            columnIndex++;\n          }\n          table[i + rowSpan][columnIndex] = cells[j];\n        }\n        columnIndex++;\n      }\n    }\n  }\n\n  return table;\n}\n\nexport default memoize(toGrid);\n"
  },
  {
    "path": "lib/commons/table/traverse.js",
    "content": "function traverseTable(dir, position, tableGrid, callback) {\n  let result;\n  const cell = tableGrid[position.y]\n    ? tableGrid[position.y][position.x]\n    : undefined;\n  if (!cell) {\n    return [];\n  }\n  if (typeof callback === 'function') {\n    result = callback(cell, position, tableGrid);\n    if (result === true) {\n      // abort\n      return [cell];\n    }\n  }\n\n  result = traverseTable(\n    dir,\n    {\n      x: position.x + dir.x,\n      y: position.y + dir.y\n    },\n    tableGrid,\n    callback\n  );\n  result.unshift(cell);\n  return result;\n}\n\n/**\n * Traverses a table in a given direction, passing the cell to the callback\n * @method traverse\n * @memberof axe.commons.table\n * @instance\n * @param  {Object|String} dir Direction that will be added recursively {x: 1, y: 0}, 'left';\n * @param  {Object}   startPos    x/y coordinate: {x: 0, y: 0};\n * @param  {Array}    [tablegrid]  A matrix of the table obtained using axe.commons.table.toArray (OPTIONAL)\n * @param  {Function} callback Function to which each cell will be passed\n * @return {NodeElement}       If the callback returns true, the traversal will end and the cell will be returned\n */\nfunction traverse(dir, startPos, tableGrid, callback) {\n  if (Array.isArray(startPos)) {\n    callback = tableGrid;\n    tableGrid = startPos;\n    startPos = { x: 0, y: 0 };\n  }\n\n  if (typeof dir === 'string') {\n    switch (dir) {\n      case 'left':\n        dir = { x: -1, y: 0 };\n        break;\n      case 'up':\n        dir = { x: 0, y: -1 };\n        break;\n      case 'right':\n        dir = { x: 1, y: 0 };\n        break;\n      case 'down':\n        dir = { x: 0, y: 1 };\n        break;\n    }\n  }\n\n  return traverseTable(\n    dir,\n    {\n      x: startPos.x + dir.x,\n      y: startPos.y + dir.y\n    },\n    tableGrid,\n    callback\n  );\n}\n\nexport default traverse;\n"
  },
  {
    "path": "lib/commons/text/accessible-text-virtual.js",
    "content": "import arialabelledbyText from '../aria/arialabelledby-text';\nimport arialabelText from '../aria/arialabel-text';\nimport nativeTextAlternative from './native-text-alternative';\nimport formControlValue from './form-control-value';\nimport subtreeText from './subtree-text';\nimport titleText from './title-text';\nimport sanitize from './sanitize';\nimport isVisibleToScreenReaders from '../dom/is-visible-to-screenreader';\nimport isIconLigature from '../text/is-icon-ligature';\n\n/**\n * Finds virtual node and calls accessibleTextVirtual()\n * IMPORTANT: This method requires the composed tree at axe._tree\n *\n * @param {HTMLElement} element The HTMLElement\n * @param {Object} context\n * @property {Bool} inControlContext\n * @property {Bool} inLabelledByContext\n * @return {string}\n */\nexport default function accessibleTextVirtual(virtualNode, context = {}) {\n  context = prepareContext(virtualNode, context);\n\n  // Step 2A, check visibility\n  if (shouldIgnoreHidden(virtualNode, context)) {\n    return '';\n  }\n\n  // Ignore ligature icons\n  if (shouldIgnoreIconLigature(virtualNode, context)) {\n    return '';\n  }\n\n  const computationSteps = [\n    arialabelledbyText, // Step 2B.1\n    arialabelText, // Step 2C\n    nativeTextAlternative, // Step 2D\n    formControlValue, // Step 2E\n    subtreeText, // Step 2F + Step 2H\n    textNodeValue, // Step 2G (order with 2H does not matter)\n    titleText // Step 2I\n  ];\n\n  // Find the first step that returns a non-empty string\n  const accessibleName = computationSteps.reduce((accName, step) => {\n    if (context.startNode === virtualNode) {\n      accName = sanitize(accName);\n    }\n\n    if (accName !== '') {\n      // yes, whitespace only a11y names halt the algorithm\n      return accName;\n    }\n    return step(virtualNode, context);\n  }, '');\n\n  if (context.debug) {\n    axe.log(accessibleName || '{empty-value}', virtualNode.actualNode, context);\n  }\n  return accessibleName;\n}\n\n/**\n * Return the nodeValue of a node\n * @param {VirtualNode} element\n * @return {String} nodeValue value\n */\nfunction textNodeValue(virtualNode) {\n  if (virtualNode.props.nodeType !== 3) {\n    return '';\n  }\n  return virtualNode.props.nodeValue;\n}\n\n/**\n * Check if the VirtualNode should be ignored because it is not visible or hidden should be included\n * @param {VirtualNode} element\n * @param {Object} context\n * @property {VirtualNode[]} processed\n * @return {Boolean}\n */\nfunction shouldIgnoreHidden(virtualNode, context) {\n  if (!virtualNode) {\n    return false;\n  }\n\n  if (\n    // If the parent isn't ignored, the text node should not be either\n    virtualNode.props.nodeType !== 1 ||\n    // If the target of aria-labelledby is hidden, ignore all descendents\n    context.includeHidden\n  ) {\n    return false;\n  }\n\n  return !isVisibleToScreenReaders(virtualNode);\n}\n\n/**\n * Check if a ligature icon should be ignored\n * @param {VirtualNode} element\n * @param {VirtualNode} element\n * @param {Object} context\n * @return {Boolean}\n */\nfunction shouldIgnoreIconLigature(virtualNode, context) {\n  const { ignoreIconLigature, pixelThreshold } = context;\n  const occurrenceThreshold =\n    context.occurrenceThreshold ?? context.occuranceThreshold;\n  if (virtualNode.props.nodeType !== 3 || !ignoreIconLigature) {\n    return false;\n  }\n  return isIconLigature(virtualNode, pixelThreshold, occurrenceThreshold);\n}\n\n/**\n * Apply defaults to the context\n * @param {VirtualNode} element\n * @param {Object} context\n * @return {Object} context object with defaults applied\n */\nfunction prepareContext(virtualNode, context) {\n  if (!context.startNode) {\n    context = { startNode: virtualNode, ...context };\n  }\n\n  /**\n   * When `aria-labelledby` directly references a `hidden` element\n   * the element needs to be included in the accessible name.\n   *\n   * When a descendent of the `aria-labelledby` reference is `hidden`\n   * the element should not be included in the accessible name.\n   *\n   * This is done by setting `includeHidden` for the `aria-labelledby` reference.\n   */\n  if (\n    virtualNode.props.nodeType === 1 &&\n    context.inLabelledByContext &&\n    context.includeHidden === undefined\n  ) {\n    context = {\n      includeHidden: !isVisibleToScreenReaders(virtualNode),\n      ...context\n    };\n  }\n  return context;\n}\n\n/**\n * Check if the node is processed with this context before\n * @param {VirtualNode} element\n * @param {Object} context\n * @property {VirtualNode[]} processed\n * @return {Boolean}\n */\naccessibleTextVirtual.alreadyProcessed = function alreadyProcessed(\n  virtualnode,\n  context\n) {\n  context.processed = context.processed || [];\n  if (context.processed.includes(virtualnode)) {\n    return true;\n  }\n\n  context.processed.push(virtualnode);\n  return false;\n};\n"
  },
  {
    "path": "lib/commons/text/accessible-text.js",
    "content": "import accessibleTextVirtual from './accessible-text-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Finds virtual node and calls accessibleTextVirtual()\n * IMPORTANT: This method requires the composed tree at axe._tree\n *\n * @param {HTMLElement} element The HTMLElement\n * @param {Object} context\n * @property {Bool} inControlContext\n * @property {Bool} inLabelledByContext\n * @return {string}\n */\nfunction accessibleText(element, context) {\n  const virtualNode = getNodeFromTree(element); // throws an exception on purpose if axe._tree not correct\n  return accessibleTextVirtual(virtualNode, context);\n}\n\nexport default accessibleText;\n"
  },
  {
    "path": "lib/commons/text/form-control-value.js",
    "content": "import getRole from '../aria/get-role';\nimport unsupported from './unsupported';\nimport visibleVirtual from './visible-virtual';\nimport accessibleTextVirtual from './accessible-text-virtual';\nimport isNativeTextbox from '../forms/is-native-textbox';\nimport isNativeSelect from '../forms/is-native-select';\nimport isAriaTextbox from '../forms/is-aria-textbox';\nimport isAriaListbox from '../forms/is-aria-listbox';\nimport isAriaCombobox from '../forms/is-aria-combobox';\nimport isAriaRange from '../forms/is-aria-range';\nimport getOwnedVirtual from '../aria/get-owned-virtual';\nimport isHiddenForEveryone from '../dom/is-hidden-for-everyone';\nimport { nodeLookup, querySelectorAll } from '../../core/utils';\nimport log from '../../core/log';\n\nexport const controlValueRoles = [\n  'textbox',\n  'progressbar',\n  'scrollbar',\n  'slider',\n  'spinbutton',\n  'combobox',\n  'listbox'\n];\n\nexport const formControlValueMethods = {\n  nativeTextboxValue,\n  nativeSelectValue,\n  ariaTextboxValue,\n  ariaListboxValue,\n  ariaComboboxValue,\n  ariaRangeValue\n};\n\n/**\n * Calculate value of a form control\n *\n * @param {VirtualNode} element The VirtualNode instance whose value we want\n * @param {Object} context\n * @property {Element} startNode First node in accessible name computation\n * @property {String[]} unsupported List of roles where value computation is unsupported\n * @property {Bool} debug Enable logging for formControlValue\n * @return {string} The calculated value\n */\nfunction formControlValue(virtualNode, context = {}) {\n  const { actualNode } = virtualNode;\n  const unsupportedRoles = unsupported.accessibleNameFromFieldValue || [];\n  const role = getRole(virtualNode);\n\n  if (\n    // For the targeted node, the accessible name is never the value:\n    context.startNode === virtualNode ||\n    // Only elements with a certain role can return their value\n    !controlValueRoles.includes(role) ||\n    // Skip unsupported roles\n    unsupportedRoles.includes(role)\n  ) {\n    return '';\n  }\n\n  // Object.values:\n  const valueMethods = Object.keys(formControlValueMethods).map(\n    name => formControlValueMethods[name]\n  );\n\n  // Return the value of the first step that returns with text\n  const valueString = valueMethods.reduce((accName, step) => {\n    return accName || step(virtualNode, context);\n  }, '');\n\n  if (context.debug) {\n    log(valueString || '{empty-value}', actualNode, context);\n  }\n  return valueString;\n}\n\n/**\n * Calculate value of a native textbox element (input and textarea)\n *\n * @param {VirtualNode|Element} element The element whose value we want\n * @return {string} The calculated value\n */\nfunction nativeTextboxValue(node) {\n  const { vNode } = nodeLookup(node);\n  if (isNativeTextbox(vNode)) {\n    return vNode.props.value || '';\n  }\n  return '';\n}\n\n/**\n * Calculate value of a select element\n *\n * @param {VirtualNode} element The VirtualNode instance whose value we want\n * @return {string} The calculated value\n */\nfunction nativeSelectValue(node) {\n  const { vNode } = nodeLookup(node);\n  if (!isNativeSelect(vNode)) {\n    return '';\n  }\n\n  const options = querySelectorAll(vNode, 'option');\n  const selectedOptions = options.filter(option => option.props.selected);\n\n  // browser automatically selects the first option\n  if (!selectedOptions.length) {\n    selectedOptions.push(options[0]);\n  }\n\n  return selectedOptions.map(option => visibleVirtual(option)).join(' ') || '';\n}\n\n/**\n * Calculate value of a an element with role=textbox\n *\n * @param {VirtualNode} element The VirtualNode instance whose value we want\n * @return {string} The calculated value\n */\nfunction ariaTextboxValue(node) {\n  const { vNode, domNode } = nodeLookup(node);\n  if (!isAriaTextbox(vNode)) {\n    return '';\n  }\n  if (!domNode || (domNode && !isHiddenForEveryone(domNode))) {\n    return visibleVirtual(vNode, true);\n  } else {\n    return domNode.textContent;\n  }\n}\n\n/**\n * Calculate value of an element with role=combobox or role=listbox\n *\n * @param {VirtualNode} element The VirtualNode instance whose value we want\n * @param {Object} context The VirtualNode instance whose value we want\n * @property {Element} startNode First node in accessible name computation\n * @property {String[]} unsupported List of roles where value computation is unsupported\n * @property {Bool} debug Enable logging for formControlValue\n * @return {string} The calculated value\n */\nfunction ariaListboxValue(node, context) {\n  const { vNode } = nodeLookup(node);\n  if (!isAriaListbox(vNode)) {\n    return '';\n  }\n\n  const selected = getOwnedVirtual(vNode).filter(\n    owned =>\n      getRole(owned) === 'option' && owned.attr('aria-selected') === 'true'\n  );\n\n  if (selected.length === 0) {\n    return '';\n  }\n  // Note: Even with aria-multiselectable, only the first value will be used\n  // in the accessible name. This isn't spec'ed out, but seems to be how all\n  // browser behave.\n  return accessibleTextVirtual(selected[0], context);\n}\n\n/**\n * Calculate value of an element with role=combobox or role=listbox\n *\n * @param {VirtualNode} element The VirtualNode instance whose value we want\n * @param {Object} context The VirtualNode instance whose value we want\n * @property {Element} startNode First node in accessible name computation\n * @property {String[]} unsupported List of roles where value computation is unsupported\n * @property {Bool} debug Enable logging for formControlValue\n * @return {string} The calculated value\n */\nfunction ariaComboboxValue(node, context) {\n  const { vNode } = nodeLookup(node);\n  // For combobox, find the first owned listbox:\n  if (!isAriaCombobox(vNode)) {\n    return '';\n  }\n  const listbox = getOwnedVirtual(vNode).filter(\n    elm => getRole(elm) === 'listbox'\n  )[0];\n\n  return listbox ? ariaListboxValue(listbox, context) : '';\n}\n\n/**\n * Calculate value of an element with range-type role\n *\n * @param {VirtualNode|Node} element The VirtualNode instance whose value we want\n * @return {string} The calculated value\n */\nfunction ariaRangeValue(node) {\n  const { vNode } = nodeLookup(node);\n  if (!isAriaRange(vNode) || !vNode.hasAttr('aria-valuenow')) {\n    return '';\n  }\n  // Validate the number, if not, return 0.\n  // Chrome 70 typecasts this, Firefox 62 does not\n  const valueNow = +vNode.attr('aria-valuenow');\n  return !isNaN(valueNow) ? String(valueNow) : '0';\n}\n\nexport default formControlValue;\n"
  },
  {
    "path": "lib/commons/text/has-unicode.js",
    "content": "import {\n  getUnicodeNonBmpRegExp,\n  getSupplementaryPrivateUseRegExp,\n  getPunctuationRegExp,\n  getCategoryFormatRegExp\n} from './unicode';\nimport { emojiRegexText } from '../../core/imports';\n\n/**\n * Determine if a given string contains unicode characters, specified in options\n *\n * @method hasUnicode\n * @memberof axe.commons.text\n * @instance\n * @param {String} str string to verify\n * @param {Object} options config containing which unicode character sets to verify\n * @property {Boolean} options.emoji verify emoji unicode\n * @property {Boolean} options.nonBmp verify nonBmp unicode\n * @property {Boolean} options.punctuations verify punctuations unicode\n * @returns {Boolean}\n */\nfunction hasUnicode(str, options) {\n  const { emoji, nonBmp, punctuations } = options;\n  let value = false;\n\n  if (emoji) {\n    value ||= emojiRegexText().test(str);\n  }\n  if (nonBmp) {\n    value ||=\n      getUnicodeNonBmpRegExp().test(str) ||\n      getSupplementaryPrivateUseRegExp().test(str) ||\n      getCategoryFormatRegExp().test(str);\n  }\n  if (punctuations) {\n    value ||= getPunctuationRegExp().test(str);\n  }\n\n  return value;\n}\n\nexport default hasUnicode;\n"
  },
  {
    "path": "lib/commons/text/index.js",
    "content": "/**\n * Namespace for text-related utilities.\n * @namespace text\n * @memberof axe.commons\n */\nexport { default as accessibleTextVirtual } from './accessible-text-virtual';\nexport { default as accessibleText } from './accessible-text';\nexport {\n  default as formControlValue,\n  formControlValueMethods\n} from './form-control-value';\nexport { default as hasUnicode } from './has-unicode';\nexport { default as isHumanInterpretable } from './is-human-interpretable';\nexport { default as isIconLigature } from './is-icon-ligature';\nexport {\n  default as isValidAutocomplete,\n  autocomplete\n} from './is-valid-autocomplete';\nexport { default as labelText } from './label-text';\nexport { default as labelVirtual } from './label-virtual';\nexport { default as label } from './label';\nexport { default as nativeElementType } from './native-element-type';\nexport { default as nativeTextAlternative } from './native-text-alternative';\nexport { default as nativeTextMethods } from './native-text-methods';\nexport { default as removeUnicode } from './remove-unicode';\nexport { default as sanitize } from './sanitize';\nexport { default as subtreeText } from './subtree-text';\nexport { default as titleText } from './title-text';\nexport { default as unsupported } from './unsupported';\nexport { default as visibleTextNodes } from './visible-text-nodes';\nexport { default as visibleVirtual } from './visible-virtual';\nexport { default as visible } from './visible';\n"
  },
  {
    "path": "lib/commons/text/is-human-interpretable.js",
    "content": "import removeUnicode from './remove-unicode';\nimport sanitize from './sanitize';\n\n/**\n * Determines if a given text is human friendly and interpretable\n *\n * @method isHumanInterpretable\n * @memberof axe.commons.text\n * @instance\n * @param  {String} str text to be validated\n * @returns {Number} Between 0 and 1, (0 -> not interpretable, 1 -> interpretable)\n */\nfunction isHumanInterpretable(str) {\n  /**\n   * Steps:\n   * 1) Early escape if string is empty\n   *\n   * 2) Check for single alpha character edge cases\n   *\n   * 3) Check for symbolic text character edge cases\n   * \t\ta) handle if character is alphanumeric & within the given icon mapping\n   * \t\t\t\t\teg: 'aA' for toggling capitalization\n   *\n   * 4) handle unicode from astral (non bilingual multi plane) unicode, emoji & punctuations\n   * \t\t\t\t\teg: Windings font\n   * \t\t\t\t\teg: '💪'\n   * \t\t\t\t\teg: I saw a shooting 💫\n   * \t\t\t\t\teg: ? (help), > (next arrow), < (back arrow), need help ?\n   */\n\n  if (\n    isEmpty(str) ||\n    isNonDigitCharacter(str) ||\n    isSymbolicText(str) ||\n    isUnicodeOrPunctuation(str)\n  ) {\n    return 0;\n  }\n\n  return 1;\n}\n\nfunction isEmpty(str) {\n  return sanitize(str).length === 0;\n}\n\nfunction isNonDigitCharacter(str) {\n  return str.length === 1 && str.match(/\\D/);\n}\n\nfunction isSymbolicText(str) {\n  const symbolicText = [\n    'aa', // toggle capitalization (aA)\n    'abc' // spelling\n  ];\n\n  return symbolicText.includes(str.toLowerCase());\n}\n\nfunction isUnicodeOrPunctuation(str) {\n  const noUnicodeStr = removeUnicode(str, {\n    emoji: true,\n    nonBmp: true,\n    punctuations: true\n  });\n\n  return !sanitize(noUnicodeStr);\n}\n\nexport default isHumanInterpretable;\n"
  },
  {
    "path": "lib/commons/text/is-icon-ligature.js",
    "content": "import sanitize from './sanitize';\nimport hasUnicode from './has-unicode';\nimport cache from '../../core/base/cache';\n\n/**\n * Determines if a given text node is an icon ligature\n *\n * @method isIconLigature\n * @memberof axe.commons.text\n * @instance\n * @param {VirtualNode} textVNode The virtual text node\n * @param {Number} occurrenceThreshold Number of times the font is encountered before auto-assigning the font as a ligature or not\n * @param {Number} differenceThreshold Percent of differences in pixel data or pixel width needed to determine if a font is a ligature font\n * @return {Boolean}\n */\nexport default function isIconLigature(\n  textVNode,\n  differenceThreshold = 0.15,\n  occurrenceThreshold = 3\n) {\n  /**\n   * Determine if the visible text is a ligature by comparing the\n   * first letters image data to the entire strings image data.\n   * If the two images are significantly different (typical set to 5%\n   * statistical significance, but we'll be using a slightly higher value\n   * of 15% to help keep the size of the canvas down) then we know the text\n   * has been replaced by a ligature.\n   *\n   * Example:\n   * If a text node was the string \"File\", looking at just the first\n   * letter \"F\" would produce the following image:\n   *\n   * ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐\n   * │ │ │█│█│█│█│█│█│█│█│█│█│█│ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│█│█│█│█│█│█│█│█│█│ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│ │ │ │ │ │ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│ │ │ │ │ │ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│█│█│█│█│█│ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│█│█│█│█│█│ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│ │ │ │ │ │ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│ │ │ │ │ │ │ │ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │ │█│█│ │ │ │ │ │ │ │ │ │ │ │\n   * └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘\n   *\n   * But if the entire string \"File\" produced an image which had at least\n   * a 15% difference in pixels, we would know that the string was replaced\n   * by a ligature:\n   *\n   * ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐\n   * │ │█│█│█│█│█│█│█│█│█│█│ │ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │ │ │ │ │ │ │ │█│█│ │ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │█│█│█│█│█│█│ │█│ │█│ │ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │ │ │ │ │ │ │ │█│█│█│█│ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │█│█│█│█│█│█│ │ │ │ │█│ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │ │ │ │ │ │ │ │ │ │ │█│ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │█│█│█│█│█│█│█│█│█│ │█│ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│ │ │ │ │ │ │ │ │ │ │ │█│ │\n   * ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤\n   * │ │█│█│█│█│█│█│█│█│█│█│█│█│█│ │\n   * └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘\n   */\n  const nodeValue = textVNode.actualNode.nodeValue.trim();\n\n  // text with unicode or non-bmp letters cannot be ligature icons\n  if (\n    !sanitize(nodeValue) ||\n    hasUnicode(nodeValue, { emoji: true, nonBmp: true })\n  ) {\n    return false;\n  }\n\n  const canvasContext = cache.get('canvasContext', () =>\n    document\n      .createElement('canvas')\n      .getContext('2d', { willReadFrequently: true })\n  );\n  const canvas = canvasContext.canvas;\n\n  // keep track of each font encountered and the number of times it shows up\n  // as a ligature.\n  const fonts = cache.get('fonts', () => ({}));\n  const style = window.getComputedStyle(textVNode.parent.actualNode);\n  const fontFamily = style.getPropertyValue('font-family');\n\n  if (!fonts[fontFamily]) {\n    fonts[fontFamily] = {\n      occurrences: 0,\n      numLigatures: 0\n    };\n  }\n  const font = fonts[fontFamily];\n\n  // improve the performance by only comparing the image data of a font a certain number of times\n  // NOTE: This MIGHT cause an issue if someone uses an icon font to render actual text.\n  //   We're leaving this as-is, unless someone reports a false positive over it.\n  if (font.occurrences >= occurrenceThreshold) {\n    // if the font has always been a ligature assume it's a ligature font\n    if (font.numLigatures / font.occurrences === 1) {\n      return true;\n    }\n    // inversely, if it's never been a ligature assume it's not a ligature font\n    else if (font.numLigatures === 0) {\n      return false;\n    }\n\n    // we could theoretically get into an odd middle ground scenario in which\n    // the font family is being used as normal text sometimes and as icons\n    // other times. in these cases we would need to always check the text\n    // to know if it's an icon or not\n  }\n  font.occurrences++;\n\n  // 30px was chosen to account for common ligatures in normal fonts\n  // such as fi, ff, ffi. If a ligature would add a single column of\n  // pixels to a 30x30 grid, it would not meet the statistical significance\n  // threshold of 15% (30x30 = 900; 30/900 = 3.333%). this also allows for\n  // more than 1 column differences (60/900 = 6.666%) and things like\n  // extending the top of the f in the fi ligature.\n  let fontSize = 30;\n  let fontStyle = `${fontSize}px ${fontFamily}`;\n\n  // set the size of the canvas to the width of the first letter\n  canvasContext.font = fontStyle;\n  const firstChar = nodeValue.charAt(0);\n  let width = canvasContext.measureText(firstChar).width;\n\n  // we already checked for typical zero-width unicode formatting characters further up,\n  // so we assume that any remaining zero-width characters are part of an icon ligature\n  // @see https://github.com/dequelabs/axe-core/issues/3918\n  if (width === 0) {\n    font.numLigatures++;\n    return true;\n  }\n\n  // ensure font meets the 30px width requirement (30px font-size doesn't\n  // necessarily mean its 30px wide when drawn)\n  if (width < 30) {\n    const diff = 30 / width;\n    width *= diff;\n    fontSize *= diff;\n    fontStyle = `${fontSize}px ${fontFamily}`;\n  }\n  canvas.width = width;\n  canvas.height = fontSize;\n\n  // changing the dimensions of a canvas resets all properties (include font)\n  // and clears it\n  canvasContext.font = fontStyle;\n  canvasContext.textAlign = 'left';\n  canvasContext.textBaseline = 'top';\n  canvasContext.fillText(firstChar, 0, 0);\n  const compareData = new Uint32Array(\n    canvasContext.getImageData(0, 0, width, fontSize).data.buffer\n  );\n\n  // if the font doesn't even have character data for a single char then\n  // it has to be an icon ligature (e.g. Material Icon)\n  if (!compareData.some(pixel => pixel)) {\n    font.numLigatures++;\n    return true;\n  }\n\n  canvasContext.clearRect(0, 0, width, fontSize);\n  canvasContext.fillText(nodeValue, 0, 0);\n  const compareWith = new Uint32Array(\n    canvasContext.getImageData(0, 0, width, fontSize).data.buffer\n  );\n\n  // calculate the number of differences between the first letter and the\n  // entire string, ignoring color differences\n  const differences = compareData.reduce((diff, pixel, i) => {\n    if (pixel === 0 && compareWith[i] === 0) {\n      return diff;\n    }\n    if (pixel !== 0 && compareWith[i] !== 0) {\n      return diff;\n    }\n    return ++diff;\n  }, 0);\n\n  // calculate the difference between the width of each character and the\n  // combined with of all characters\n  const expectedWidth = nodeValue.split('').reduce((totalWidth, char) => {\n    return totalWidth + canvasContext.measureText(char).width;\n  }, 0);\n  const actualWidth = canvasContext.measureText(nodeValue).width;\n\n  const pixelDifference = differences / compareData.length;\n  const sizeDifference = 1 - actualWidth / expectedWidth;\n\n  if (\n    pixelDifference >= differenceThreshold &&\n    sizeDifference >= differenceThreshold\n  ) {\n    font.numLigatures++;\n    return true;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "lib/commons/text/is-valid-autocomplete.js",
    "content": "export const autocomplete = {\n  stateTerms: ['on', 'off'],\n  standaloneTerms: [\n    'name',\n    'honorific-prefix',\n    'given-name',\n    'additional-name',\n    'family-name',\n    'honorific-suffix',\n    'nickname',\n    'username',\n    'new-password',\n    'current-password',\n    'organization-title',\n    'organization',\n    'street-address',\n    'address-line1',\n    'address-line2',\n    'address-line3',\n    'address-level4',\n    'address-level3',\n    'address-level2',\n    'address-level1',\n    'country',\n    'country-name',\n    'postal-code',\n    'cc-name',\n    'cc-given-name',\n    'cc-additional-name',\n    'cc-family-name',\n    'cc-number',\n    'cc-exp',\n    'cc-exp-month',\n    'cc-exp-year',\n    'cc-csc',\n    'cc-type',\n    'transaction-currency',\n    'transaction-amount',\n    'language',\n    'bday',\n    'bday-day',\n    'bday-month',\n    'bday-year',\n    'sex',\n    'url',\n    'photo',\n    'one-time-code'\n  ],\n  qualifiers: ['home', 'work', 'mobile', 'fax', 'pager'],\n  qualifiedTerms: [\n    'tel',\n    'tel-country-code',\n    'tel-national',\n    'tel-area-code',\n    'tel-local',\n    'tel-local-prefix',\n    'tel-local-suffix',\n    'tel-extension',\n    'email',\n    'impp'\n  ],\n  locations: ['billing', 'shipping']\n};\n\nfunction isValidAutocomplete(\n  autocompleteValue,\n  {\n    looseTyped = false,\n    stateTerms = [],\n    locations = [],\n    qualifiers = [],\n    standaloneTerms = [],\n    qualifiedTerms = [],\n    ignoredValues = []\n  } = {}\n) {\n  autocompleteValue = autocompleteValue.toLowerCase().trim();\n  stateTerms = stateTerms.concat(autocomplete.stateTerms);\n  if (stateTerms.includes(autocompleteValue) || autocompleteValue === '') {\n    return true;\n  }\n\n  qualifiers = qualifiers.concat(autocomplete.qualifiers);\n  locations = locations.concat(autocomplete.locations);\n  standaloneTerms = standaloneTerms.concat(autocomplete.standaloneTerms);\n  qualifiedTerms = qualifiedTerms.concat(autocomplete.qualifiedTerms);\n\n  const autocompleteTerms = autocompleteValue.split(/\\s+/g);\n\n  if (autocompleteTerms[autocompleteTerms.length - 1] === 'webauthn') {\n    autocompleteTerms.pop();\n    if (autocompleteTerms.length === 0) {\n      return false;\n    }\n  }\n\n  if (!looseTyped) {\n    if (\n      autocompleteTerms[0].length > 8 &&\n      autocompleteTerms[0].substr(0, 8) === 'section-'\n    ) {\n      autocompleteTerms.shift();\n    }\n\n    if (locations.includes(autocompleteTerms[0])) {\n      autocompleteTerms.shift();\n    }\n\n    if (qualifiers.includes(autocompleteTerms[0])) {\n      autocompleteTerms.shift();\n      // only quantifiers allowed at this point\n      standaloneTerms = [];\n    }\n\n    if (autocompleteTerms.length !== 1) {\n      return false;\n    }\n  }\n\n  const purposeTerm = autocompleteTerms[autocompleteTerms.length - 1];\n\n  if (ignoredValues.includes(purposeTerm)) {\n    return undefined;\n  }\n\n  return (\n    standaloneTerms.includes(purposeTerm) ||\n    qualifiedTerms.includes(purposeTerm)\n  );\n}\n\nexport default isValidAutocomplete;\n"
  },
  {
    "path": "lib/commons/text/label-text.js",
    "content": "import accessibleTextVirtual from './accessible-text-virtual';\nimport accessibleText from './accessible-text';\nimport findElmsInContext from '../dom/find-elms-in-context';\nimport { closest, nodeSorter } from '../../core/utils';\n\n/**\n * Return accessible text for an implicit and/or explicit HTML label element\n * @param {VirtualNode} element\n * @param {Object} context\n * @property {Bool} inControlContext\n * @property {Bool} inLabelledByContext\n * @return {String} Label text\n */\nfunction labelText(virtualNode, context = {}) {\n  const { alreadyProcessed } = accessibleTextVirtual;\n  if (\n    context.inControlContext ||\n    context.inLabelledByContext ||\n    alreadyProcessed(virtualNode, context)\n  ) {\n    return '';\n  }\n  if (!context.startNode) {\n    context.startNode = virtualNode;\n  }\n\n  const labelContext = { inControlContext: true, ...context };\n  const explicitLabels = getExplicitLabels(virtualNode);\n  const implicitLabel = closest(virtualNode, 'label');\n\n  let labels;\n  if (implicitLabel) {\n    labels = [...explicitLabels, implicitLabel.actualNode];\n    labels.sort(nodeSorter);\n  } else {\n    labels = explicitLabels;\n  }\n\n  return labels\n    .map(label => accessibleText(label, labelContext))\n    .filter(text => text !== '')\n    .join(' ');\n}\n\n/**\n * Find a non-ARIA label for an element\n * @private\n * @param {VirtualNode} element The VirtualNode instance whose label we are seeking\n * @return {HTMLElement} The label element, or null if none is found\n */\nfunction getExplicitLabels(virtualNode) {\n  if (!virtualNode.attr('id')) {\n    return [];\n  }\n\n  if (!virtualNode.actualNode) {\n    throw new TypeError(\n      'Cannot resolve explicit label reference for non-DOM nodes'\n    );\n  }\n\n  return findElmsInContext({\n    elm: 'label',\n    attr: 'for',\n    value: virtualNode.attr('id'),\n    context: virtualNode.actualNode\n  });\n}\n\nexport default labelText;\n"
  },
  {
    "path": "lib/commons/text/label-virtual.js",
    "content": "import ariaLabelVirtual from '../aria/label-virtual';\nimport visible from './visible';\nimport visibleVirtual from './visible-virtual';\nimport getRootNode from '../dom/get-root-node';\nimport { closest, escapeSelector } from '../../core/utils';\n\n/**\n * Gets the visible text of a label for a given input\n * @see http://www.w3.org/WAI/PF/aria/roles#namecalculation\n * @method labelVirtual\n * @memberof axe.commons.text\n * @instance\n * @param  {VirtualNode} node The virtual node mapping to the input to test\n * @return {Mixed} String of visible text, or `null` if no label is found\n */\nfunction labelVirtual(virtualNode) {\n  let ref, candidate, doc;\n\n  candidate = ariaLabelVirtual(virtualNode);\n  if (candidate) {\n    return candidate;\n  }\n\n  // explicit label\n  if (virtualNode.attr('id')) {\n    if (!virtualNode.actualNode) {\n      throw new TypeError(\n        'Cannot resolve explicit label reference for non-DOM nodes'\n      );\n    }\n\n    const id = escapeSelector(virtualNode.attr('id'));\n    doc = getRootNode(virtualNode.actualNode);\n    ref = doc.querySelector('label[for=\"' + id + '\"]');\n    candidate = ref && visible(ref, true);\n    if (candidate) {\n      return candidate;\n    }\n  }\n\n  ref = closest(virtualNode, 'label');\n  candidate = ref && visibleVirtual(ref, true);\n  if (candidate) {\n    return candidate;\n  }\n\n  return null;\n}\n\nexport default labelVirtual;\n"
  },
  {
    "path": "lib/commons/text/label.js",
    "content": "import labelVirtual from './label-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Finds virtual node and calls labelVirtual()\n * IMPORTANT: This method requires the composed tree at axe._tree\n * @see axe.commons.text.virtualLabel\n * @method label\n * @memberof axe.commons.text\n * @instance\n * @param  {Element} node The virtual node mapping to the input to test\n * @return {Mixed} String of visible text, or `null` if no label is found\n */\nfunction label(node) {\n  node = getNodeFromTree(node);\n  return labelVirtual(node);\n}\n\nexport default label;\n"
  },
  {
    "path": "lib/commons/text/native-element-type.js",
    "content": "const nativeElementType = [\n  {\n    // 5.1 input type=\"text\", input type=\"password\", input type=\"search\", input type=\"tel\", input type=\"url\" and textarea Element\n    matches: [\n      {\n        nodeName: 'textarea'\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          type: ['text', 'password', 'search', 'tel', 'email', 'url']\n        }\n      }\n    ],\n    namingMethods: 'labelText'\n  },\n  {\n    // 5.2 input type=\"button\", input type=\"submit\" and input type=\"reset\"\n    matches: {\n      nodeName: 'input',\n      properties: {\n        type: ['button', 'submit', 'reset']\n      }\n    },\n    namingMethods: ['valueText', 'titleText', 'buttonDefaultText']\n  },\n  {\n    // 5.3 input type=\"image\"\n    matches: {\n      nodeName: 'input',\n      properties: {\n        type: 'image'\n      }\n    },\n    namingMethods: [\n      'altText',\n      'valueText',\n      'labelText',\n      'titleText',\n      'buttonDefaultText'\n    ]\n  },\n  {\n    // 5.4 button Element\n    matches: 'button',\n    namingMethods: 'subtreeText'\n  },\n  {\n    // 5.5 fieldset and legend Elements\n    matches: 'fieldset',\n    namingMethods: 'fieldsetLegendText'\n  },\n  {\n    // 5.6 output Element\n    matches: 'OUTPUT',\n    namingMethods: 'subtreeText'\n  },\n  {\n    // 5.7 Other Form Elements\n    matches: [\n      {\n        nodeName: 'select'\n      },\n      {\n        nodeName: 'input',\n        properties: {\n          // Regex: Everything other than these\n          type: /^(?!text|password|search|tel|email|url|button|submit|reset)/\n        }\n      }\n    ],\n    namingMethods: 'labelText'\n  },\n  {\n    // 5.8 summary Element\n    matches: 'summary',\n    namingMethods: 'subtreeText'\n  },\n  {\n    // 5.9 figure and figcaption Elements\n    matches: 'figure',\n    namingMethods: ['figureText', 'titleText']\n  },\n  {\n    // 5.10 img Element\n    matches: 'img',\n    namingMethods: 'altText'\n  },\n  {\n    // 5.11 table Element\n    matches: 'table',\n    namingMethods: ['tableCaptionText', 'tableSummaryText']\n  },\n  {\n    matches: ['hr', 'br'],\n    namingMethods: ['titleText', 'singleSpace']\n  }\n  // All else defaults to just title\n];\n\nexport default nativeElementType;\n"
  },
  {
    "path": "lib/commons/text/native-text-alternative.js",
    "content": "import getRole from '../aria/get-role';\nimport getElementSpec from '../standards/get-element-spec';\nimport nativeTextMethods from './native-text-methods';\n\n/**\n * Get the accessible text using native HTML methods only\n * @param {VirtualNode} element\n * @param {Object} context\n * @property {Bool} debug Enable logging for formControlValue\n * @return {String} Accessible text\n */\nexport default function nativeTextAlternative(virtualNode, context = {}) {\n  const { actualNode } = virtualNode;\n  if (\n    virtualNode.props.nodeType !== 1 ||\n    ['presentation', 'none'].includes(getRole(virtualNode))\n  ) {\n    return '';\n  }\n\n  const textMethods = findTextMethods(virtualNode);\n  // Find the first step that returns a non-empty string\n  const accessibleName = textMethods.reduce((accName, step) => {\n    return accName || step(virtualNode, context);\n  }, '');\n\n  if (context.debug) {\n    axe.log(accessibleName || '{empty-value}', actualNode, context);\n  }\n  return accessibleName;\n}\n\n/**\n * Get accessible text functions for a specific native HTML element\n * @private\n * @param {VirtualNode} element\n * @return {Function[]} Array of native accessible name computation methods\n */\nfunction findTextMethods(virtualNode) {\n  const elmSpec = getElementSpec(virtualNode, { noMatchAccessibleName: true });\n  const methods = elmSpec.namingMethods || [];\n\n  return methods.map(methodName => nativeTextMethods[methodName]);\n}\n"
  },
  {
    "path": "lib/commons/text/native-text-methods.js",
    "content": "import titleText from './title-text';\nimport subtreeText from './subtree-text';\nimport labelText from './label-text';\nimport accessibleText from './accessible-text';\n\nconst defaultButtonValues = {\n  submit: 'Submit',\n  image: 'Submit',\n  reset: 'Reset',\n  button: '' // No default for \"button\"\n};\n\nconst nativeTextMethods = {\n  /**\n   * Return the value of a DOM node\n   * @param {VirtualNode} element\n   * @return {String} value text\n   */\n  valueText: vNode => vNode.props.value || '',\n\n  /**\n   * Return default value of a button\n   * @param {VirtualNode} element\n   * @return {String} default button text\n   */\n  buttonDefaultText: vNode => defaultButtonValues[vNode.props.type] || '',\n\n  /**\n   * Return caption text of an HTML table element\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} caption text\n   */\n  tableCaptionText: descendantText.bind(null, 'caption'),\n\n  /**\n   * Return figcaption text of an HTML figure element\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} figcaption text\n   */\n  figureText: descendantText.bind(null, 'figcaption'),\n\n  /**\n   * Return figcaption text of an HTML figure element\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} figcaption text\n   */\n  svgTitleText: descendantText.bind(null, 'title'),\n\n  /**\n   * Return legend text of an HTML fieldset element\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} legend text\n   */\n  fieldsetLegendText: descendantText.bind(null, 'legend'),\n\n  /**\n   * Return the alt text\n   * @param {VirtualNode} element\n   * @return {String} alt text\n   */\n  altText: attrText.bind(null, 'alt'),\n\n  /**\n   * Return summary text for an HTML table element\n   * @param {VirtualNode} element\n   * @return {String} summary text\n   */\n  tableSummaryText: attrText.bind(null, 'summary'),\n\n  /**\n   * Return the title text\n   * @param {VirtualNode} element\n   * @return {String} title text\n   */\n  titleText,\n\n  /**\n   * Return accessible text of the subtree\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} Subtree text\n   */\n  subtreeText,\n\n  /**\n   * Return accessible text for an implicit and/or explicit HTML label element\n   * @param {VirtualNode} element\n   * @param {Object} context\n   * @return {String} Label text\n   */\n  labelText,\n\n  /**\n   * Return a single space\n   * @return {String} Returns ` `\n   */\n  singleSpace: function singleSpace() {\n    return ' ';\n  },\n\n  /**\n   * Return the placeholder text\n   * @param {VirtualNode} element\n   * @return {String} placeholder text\n   */\n  placeholderText: attrText.bind(null, 'placeholder')\n};\n\nfunction attrText(attr, vNode) {\n  return vNode.attr(attr) || '';\n}\n\n/**\n * Get the accessible text of first matching node\n * IMPORTANT: This method does not look at the composed tree\n * @private\n */\nfunction descendantText(nodeName, { actualNode }, context) {\n  nodeName = nodeName.toLowerCase();\n  // Prevent accidently getting the nested element, like:\n  // fieldset > fielset > legend (1st fieldset has no legend)\n  const nodeNames = [nodeName, actualNode.nodeName.toLowerCase()].join(',');\n  const candidate = actualNode.querySelector(nodeNames);\n\n  if (!candidate || candidate.nodeName.toLowerCase() !== nodeName) {\n    return '';\n  }\n  return accessibleText(candidate, context);\n}\n\nexport default nativeTextMethods;\n"
  },
  {
    "path": "lib/commons/text/remove-unicode.js",
    "content": "import {\n  getUnicodeNonBmpRegExp,\n  getSupplementaryPrivateUseRegExp,\n  getPunctuationRegExp,\n  getCategoryFormatRegExp\n} from './unicode.js';\nimport { emojiRegexText } from '../../core/imports';\n\n/**\n * Remove specified type(s) unicode characters\n *\n * @method removeUnicode\n * @memberof axe.commons.text\n * @instance\n * @param {String} str string to operate on\n * @param {Object} options config containing which unicode character sets to remove\n * @property {Boolean} options.emoji remove emoji unicode\n * @property {Boolean} options.nonBmp remove nonBmp unicode\n * @property {Boolean} options.punctuations remove punctuations unicode\n * @returns {String}\n */\nfunction removeUnicode(str, options) {\n  const { emoji, nonBmp, punctuations } = options;\n\n  if (emoji) {\n    str = str.replace(emojiRegexText(), '');\n  }\n  if (nonBmp) {\n    str = str\n      .replace(getUnicodeNonBmpRegExp(), '')\n      .replace(getSupplementaryPrivateUseRegExp(), '')\n      .replace(getCategoryFormatRegExp(), '');\n  }\n  if (punctuations) {\n    str = str.replace(getPunctuationRegExp(), '');\n  }\n\n  return str;\n}\n\nexport default removeUnicode;\n"
  },
  {
    "path": "lib/commons/text/sanitize.js",
    "content": "/**\n * Removes carriage returns, newline characters, tabs, non-breaking spaces, and trailing spaces from string\n * @method sanitize\n * @memberof axe.commons.text\n * @instance\n * @param  {String} str String to be cleaned\n * @return {String} Sanitized string\n */\nfunction sanitize(str) {\n  if (!str) {\n    return '';\n  }\n\n  return str\n    .replace(/\\r\\n/g, '\\n')\n    .replace(/\\u00A0/g, ' ')\n    .replace(/[\\s]{2,}/g, ' ')\n    .trim();\n}\n\nexport default sanitize;\n"
  },
  {
    "path": "lib/commons/text/subtree-text.js",
    "content": "import accessibleTextVirtual from './accessible-text-virtual';\nimport namedFromContents from '../aria/named-from-contents';\nimport getOwnedVirtual from '../aria/get-owned-virtual';\nimport getRole from '../aria/get-role';\nimport getElementsByContentType from '../standards/get-elements-by-content-type';\nimport getElementSpec from '../standards/get-element-spec';\nimport { controlValueRoles } from './form-control-value';\n\n/**\n * Get the accessible text for an element that can get its name from content\n *\n * @param {VirtualNode} element\n * @param {Object} context\n * @property {Bool} strict Should the name computation strictly follow AccName 1.1\n * @return {String} Accessible text\n */\nfunction subtreeText(virtualNode, context = {}) {\n  const { alreadyProcessed } = accessibleTextVirtual;\n  context.startNode = context.startNode || virtualNode;\n  const { strict, inControlContext, inLabelledByContext } = context;\n  const role = getRole(virtualNode);\n  const { contentTypes } = getElementSpec(virtualNode, {\n    noMatchAccessibleName: true\n  });\n  if (\n    alreadyProcessed(virtualNode, context) ||\n    virtualNode.props.nodeType !== 1 ||\n    contentTypes?.includes('embedded') || // canvas, video, etc\n    controlValueRoles.includes(role)\n  ) {\n    return '';\n  }\n\n  if (\n    !context.subtreeDescendant &&\n    !context.inLabelledByContext &&\n    !namedFromContents(virtualNode, { strict })\n  ) {\n    return '';\n  }\n\n  /**\n   * Note: Strictly speaking if a child isn't named from content and it has no accessible name\n   * accName says to ignore it. Browsers do this fairly consistently, but screen readers have\n   * chosen to ignore this, but only for direct content, not for labels / aria-labelledby.\n   * That way in `a[href] > article > #text` the text is used for the accessible name,\n   * See: https://github.com/dequelabs/axe-core/issues/1461\n   * See: https://github.com/w3c/accname/issues/120\n   */\n  if (!strict) {\n    const subtreeDescendant = !inControlContext && !inLabelledByContext;\n    context = { subtreeDescendant, ...context };\n  }\n\n  return getOwnedVirtual(virtualNode).reduce((contentText, child) => {\n    return appendAccessibleText(contentText, child, context);\n  }, '');\n}\n\nconst phrasingElements = getElementsByContentType('phrasing').concat(['#text']);\n\nfunction appendAccessibleText(contentText, virtualNode, context) {\n  const nodeName = virtualNode.props.nodeName;\n  let contentTextAdd = accessibleTextVirtual(virtualNode, context);\n  if (!contentTextAdd) {\n    return contentText;\n  }\n\n  if (!phrasingElements.includes(nodeName)) {\n    // Append space, if necessary\n    if (contentTextAdd[0] !== ' ') {\n      contentTextAdd += ' ';\n    }\n    // Prepend space, if necessary\n    if (contentText && contentText[contentText.length - 1] !== ' ') {\n      contentTextAdd = ' ' + contentTextAdd;\n    }\n  }\n  return contentText + contentTextAdd;\n}\n\nexport default subtreeText;\n"
  },
  {
    "path": "lib/commons/text/title-text.js",
    "content": "import matches from '../matches/matches';\nimport getRole from '../aria/get-role';\nimport { nodeLookup } from '../../core/utils';\n\nconst alwaysTitleElements = ['iframe'];\n\n/**\n * Get title text\n * @param {HTMLElement|VirtualNode}node the node to verify\n * @return {String}\n */\nfunction titleText(node) {\n  const { vNode } = nodeLookup(node);\n\n  if (vNode.props.nodeType !== 1 || !node.hasAttr('title')) {\n    return '';\n  }\n\n  // Some elements return the title even with role=presentation\n  // This does appear in any spec, but its remarkably consistent\n  if (\n    !matches(vNode, alwaysTitleElements) &&\n    ['none', 'presentation'].includes(getRole(vNode))\n  ) {\n    return '';\n  }\n\n  return vNode.attr('title');\n}\n\nexport default titleText;\n"
  },
  {
    "path": "lib/commons/text/unicode.js",
    "content": "/**\n * Regex for matching unicode values out of Basic Multilingual Plane (BMP)\n * Reference:\n * - https://github.com/mathiasbynens/regenerate\n * - https://unicode-table.com/\n * - https://mathiasbynens.be/notes/javascript-unicode\n *\n * @returns {RegExp}\n */\nexport function getUnicodeNonBmpRegExp() {\n  /**\n   * Regex for matching astral plane unicode\n   * - http://kourge.net/projects/regexp-unicode-block\n   */\n\n  /**\n   * Notes on various unicode planes being used in the regex below:\n   * '\\u1D00-\\u1D7F'  Phonetic Extensions\n   * '\\u1D80-\\u1DBF'  Phonetic Extensions Supplement\n   * '\\u1DC0-\\u1DFF'  Combining Diacritical Marks Supplement\n   * '\\u20A0-\\u20CF'  Currency symbols\n   * '\\u20D0-\\u20FF'  Combining Diacritical Marks for Symbols\n   * '\\u2100-\\u214F'  Letter like symbols\n   * '\\u2150-\\u218F'  Number forms (eg: Roman numbers)\n   * '\\u2190-\\u21FF'  Arrows\n   * '\\u2200-\\u22FF'  Mathematical operators\n   * '\\u2300-\\u23FF'  Misc Technical\n   * '\\u2400-\\u243F'  Control pictures\n   * '\\u2440-\\u245F'  OCR\n   * '\\u2460-\\u24FF'  Enclosed alpha numerics\n   * '\\u2500-\\u257F'  Box Drawing\n   * '\\u2580-\\u259F'  Block Elements\n   * '\\u25A0-\\u25FF'  Geometric Shapes\n   * '\\u2600-\\u26FF'  Misc Symbols\n   * '\\u2700-\\u27BF'  Dingbats\n   * '\\uE000-\\uF8FF'  Private Use\n   *\n   * Note: plane '\\u2000-\\u206F' used for General punctuation is excluded as it is handled in -> getPunctuationRegExp\n   */\n\n  return /[\\u1D00-\\u1D7F\\u1D80-\\u1DBF\\u1DC0-\\u1DFF\\u20A0-\\u20CF\\u20D0-\\u20FF\\u2100-\\u214F\\u2150-\\u218F\\u2190-\\u21FF\\u2200-\\u22FF\\u2300-\\u23FF\\u2400-\\u243F\\u2440-\\u245F\\u2460-\\u24FF\\u2500-\\u257F\\u2580-\\u259F\\u25A0-\\u25FF\\u2600-\\u26FF\\u2700-\\u27BF\\uE000-\\uF8FF]/g;\n}\n\n/**\n * Get regular expression for matching punctuations\n *\n * @returns {RegExp}\n */\nexport function getPunctuationRegExp() {\n  /**\n   * Reference: http://kunststube.net/encoding/\n   * US-ASCII\n   * -> !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n   *\n   * General Punctuation block\n   * -> \\u2000-\\u206F\n   *\n   * Supplemental Punctuation block\n   * Reference: https://en.wikipedia.org/wiki/Supplemental_Punctuation\n   * -> \\u2E00-\\u2E7F Reference\n   */\n  return /[\\u2000-\\u206F\\u2E00-\\u2E7F\\\\'!\"#$%&£¢¥§€()*+,\\-.\\/:;<=>?@\\[\\]^_`{|}~±]/g;\n}\n\n/**\n * Get regular expression for supplementary private use\n *\n * @returns {RegExp}\n */\nexport function getSupplementaryPrivateUseRegExp() {\n  // Supplementary private use area A (https://www.unicode.org/charts/PDF/UF0000.pdf) contains\n  // characters between F0000 and FFFFF. Because ES5 doesn't have a syntax for regular expressions\n  // of such characters, search instead for the corresponding surrogate pairs.\n  //\n  // Code points FFFFD and FFFFF are \"noncharacters\", but the regex still matches them, because its\n  // intent is to match things we don't want to check color contrast for. This is why the low\n  // surrogate range in the regex ends at DFFF, not DFFD.\n  //\n  // 1. High surrogate area (https://www.unicode.org/charts/PDF/UD800.pdf)\n  // 2. Low surrogate area (https://www.unicode.org/charts/PDF/UDC00.pdf)\n  //\n  //             1              2\n  //      ┏━━━━━━┻━━━━━━┓┏━━━━━━┻━━━━━━┓\n  return /[\\uDB80-\\uDBBF][\\uDC00-\\uDFFF]/g;\n}\n\n/**\n * Get regular expression for unicode format category.\n * When we drop IE11 we can instead use unicode character escape `/p{Cf}/gu`\n * Reference:\n * - https://www.compart.com/en/unicode/category/Cf\n *\n * @returns {RegExp}\n */\nexport function getCategoryFormatRegExp() {\n  return /[\\xAD\\u0600-\\u0605\\u061C\\u06DD\\u070F\\u08E2\\u180E\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF\\uFFF9-\\uFFFB]|\\uD804[\\uDCBD\\uDCCD]|\\uD80D[\\uDC30-\\uDC38]|\\uD82F[\\uDCA0-\\uDCA3]|\\uD834[\\uDD73-\\uDD7A]|\\uDB40[\\uDC01\\uDC20-\\uDC7F]/g;\n}\n"
  },
  {
    "path": "lib/commons/text/unsupported.js",
    "content": "export default {\n  // Element's who's value is not consistently picked up in the accessible name\n  // Supported in Chrome 114, Firefox 115, but not Safari 16.5:\n  // <input aria-labelledby=\"lbl\">\n  // <div id=\"lbl\" role=\"progressbar\" aria-valuenow=\"23\"></div>\n  accessibleNameFromFieldValue: ['progressbar']\n};\n"
  },
  {
    "path": "lib/commons/text/visible-text-nodes.js",
    "content": "import isVisibleOnScreen from '../dom/is-visible-on-screen';\n\n/**\n * Returns an array of visible text virtual nodes\n *\n * @method visibleTextNodes\n * @memberof axe.commons.text\n * @instance\n * @param {VirtualNode} vNode\n * @return {VitrualNode[]}\n * @deprecated\n */\nfunction visibleTextNodes(vNode) {\n  const parentVisible = isVisibleOnScreen(vNode);\n  let nodes = [];\n  vNode.children.forEach(child => {\n    if (child.actualNode.nodeType === 3) {\n      if (parentVisible) {\n        nodes.push(child);\n      }\n    } else {\n      nodes = nodes.concat(visibleTextNodes(child));\n    }\n  });\n  return nodes;\n}\n\nexport default visibleTextNodes;\n"
  },
  {
    "path": "lib/commons/text/visible-virtual.js",
    "content": "import sanitize from './sanitize';\nimport isVisibleOnScreen from '../dom/is-visible-on-screen';\nimport isVisibleToScreenReaders from '../dom/is-visible-to-screenreader';\nimport { nodeLookup } from '../../core/utils';\n\n/**\n * Returns the visible text of the virtual node\n * NOTE: when calculating the text or accessible text of a node that includes shadow\n * roots attached to it or its children, the flattened tree must be considered\n * rather than the \"light DOM\"\n * @method visibleVirtual\n * @memberof axe.commons.text\n * @instance\n * @param  {VirtualNode} element\n * @param  {Boolean} screenReader When provided, will evaluate visibility from the perspective of a screen reader\n * @param  {Boolean} noRecursing When False, the result will contain text from the element and it's children.\n * When True, the result will only contain text from the element\n * @return {String}\n */\nfunction visibleVirtual(element, screenReader, noRecursing) {\n  const { vNode } = nodeLookup(element);\n  const visibleMethod = screenReader\n    ? isVisibleToScreenReaders\n    : isVisibleOnScreen;\n\n  // if the element does not have an actual node treat it as if\n  // it is visible\n  const visible =\n    !element.actualNode || (element.actualNode && visibleMethod(element));\n\n  const result = vNode.children\n    .map(child => {\n      const { nodeType, nodeValue } = child.props;\n      if (nodeType === 3) {\n        // filter on text nodes\n        if (nodeValue && visible) {\n          return nodeValue;\n        }\n      } else if (!noRecursing) {\n        return visibleVirtual(child, screenReader);\n      }\n    })\n    .join('');\n  return sanitize(result);\n}\n\nexport default visibleVirtual;\n"
  },
  {
    "path": "lib/commons/text/visible.js",
    "content": "import visibleVirtual from './visible-virtual';\nimport { getNodeFromTree } from '../../core/utils';\n\n/**\n * Finds virtual node and calls visibleVirtual()\n * IMPORTANT: This method requires the composed tree at axe._tree\n * @param  {Element} element\n * @param  {Boolean} screenReader When provided, will evaluate visibility from the perspective of a screen reader\n * @param  {Boolean} noRecursing When False, the result will contain text from the element and it's children.\n * When True, the result will only contain text from the element\n * @return {String}\n */\nfunction visible(element, screenReader, noRecursing) {\n  element = getNodeFromTree(element);\n  return visibleVirtual(element, screenReader, noRecursing);\n}\n\nexport default visible;\n"
  },
  {
    "path": "lib/core/_exposed-for-testing.js",
    "content": "/*\n  This file only exists to expose inner axe-core functionality for testing prior to being able to support ES6 imports in our tests.\n  TODO: remove `_thisWillBeDeletedDoNotUse` once we can support imports in our tests\n*/\nimport Audit from './base/audit';\nimport CheckResult from './base/check-result';\nimport Check from './base/check';\nimport Context from './base/context';\nimport metadataFunctionMap from './base/metadata-function-map';\nimport RuleResult from './base/rule-result';\nimport Rule from './base/rule';\n\nimport { reporters } from './public/reporter';\n\nimport failureSummary from './reporters/helpers/failure-summary';\nimport incompleteFallbackMessage from './reporters/helpers/incomplete-fallback-msg';\nimport processAggregate from './reporters/helpers/process-aggregate';\n\nimport { setDefaultFrameMessenger } from './utils/frame-messenger';\nimport {\n  cacheNodeSelectors,\n  getNodesMatchingExpression\n} from './utils/selector-cache';\nimport { convertSelector } from './utils/matches';\n\nimport {\n  nativelyHidden,\n  displayHidden,\n  visibilityHidden,\n  contentVisibiltyHidden,\n  ariaHidden,\n  opacityHidden,\n  scrollHidden,\n  overflowHidden,\n  clipHidden,\n  areaHidden,\n  detailsHidden\n} from '../commons/dom/visibility-methods';\n\nconst _thisWillBeDeletedDoNotUse = {\n  base: {\n    Audit,\n    CheckResult,\n    Check,\n    Context,\n    RuleResult,\n    Rule,\n    metadataFunctionMap\n  },\n  public: {\n    reporters\n  },\n  helpers: {\n    failureSummary,\n    incompleteFallbackMessage,\n    processAggregate\n  },\n  utils: {\n    setDefaultFrameMessenger,\n    cacheNodeSelectors,\n    getNodesMatchingExpression,\n    convertSelector\n  },\n  commons: {\n    dom: {\n      nativelyHidden,\n      displayHidden,\n      visibilityHidden,\n      contentVisibiltyHidden,\n      ariaHidden,\n      opacityHidden,\n      scrollHidden,\n      overflowHidden,\n      clipHidden,\n      areaHidden,\n      detailsHidden\n    }\n  }\n};\n\nexport default _thisWillBeDeletedDoNotUse;\n"
  },
  {
    "path": "lib/core/base/audit.js",
    "content": "import Rule from './rule';\nimport Check from './check';\nimport standards from '../../standards';\nimport RuleResult from './rule-result';\nimport {\n  clone,\n  DqElement,\n  queue,\n  preload,\n  findBy,\n  ruleShouldRun,\n  performanceTimer,\n  serializeError,\n  normalizeRunOptions\n} from '../utils';\nimport { doT } from '../imports';\nimport constants from '../constants';\n\nconst dotRegex = /\\{\\{.+?\\}\\}/g;\n\n/**\n * Constructor which holds configured rules and information about the document under test\n */\nexport default class Audit {\n  constructor(audit) {\n    // defaults\n    this.lang = 'en';\n    this.defaultConfig = audit;\n    this.standards = standards;\n    this._init();\n    // A copy of the \"default\" locale. This will be set if the user\n    // provides a new locale to `axe.configure()` and used to undo\n    // changes in `axe.reset()`.\n    this._defaultLocale = null;\n  }\n  /**\n   * Build and set the previous locale. Will noop if a previous\n   * locale was already set, as we want the ability to \"reset\"\n   * to the default (\"first\") configuration.\n   */\n  _setDefaultLocale() {\n    if (this._defaultLocale) {\n      return;\n    }\n    const locale = {\n      checks: {},\n      rules: {},\n      failureSummaries: {},\n      incompleteFallbackMessage: '',\n      lang: this.lang\n    };\n    // XXX: unable to use `for-of` here, as doing so would\n    // require us to polyfill `Symbol`.\n    const checkIDs = Object.keys(this.data.checks);\n    for (let i = 0; i < checkIDs.length; i++) {\n      const id = checkIDs[i];\n      const check = this.data.checks[id];\n      const { pass, fail, incomplete } = check.messages;\n      locale.checks[id] = {\n        pass,\n        fail,\n        incomplete\n      };\n    }\n    const ruleIDs = Object.keys(this.data.rules);\n    for (let i = 0; i < ruleIDs.length; i++) {\n      const id = ruleIDs[i];\n      const rule = this.data.rules[id];\n      const { description, help } = rule;\n      locale.rules[id] = { description, help };\n    }\n    const failureSummaries = Object.keys(this.data.failureSummaries);\n    for (let i = 0; i < failureSummaries.length; i++) {\n      const type = failureSummaries[i];\n      const failureSummary = this.data.failureSummaries[type];\n      const { failureMessage } = failureSummary;\n      locale.failureSummaries[type] = { failureMessage };\n    }\n    locale.incompleteFallbackMessage = this.data.incompleteFallbackMessage;\n    this._defaultLocale = locale;\n  }\n  /**\n   * Reset the locale to the \"default\".\n   */\n  _resetLocale() {\n    // If the default locale has not already been set, we can exit early.\n    const defaultLocale = this._defaultLocale;\n    if (!defaultLocale) {\n      return;\n    }\n    // Apply the default locale\n    this.applyLocale(defaultLocale);\n  }\n  /**\n   * Apply locale for the given `checks`.\n   */\n  _applyCheckLocale(checks) {\n    const keys = Object.keys(checks);\n    for (let i = 0; i < keys.length; i++) {\n      const id = keys[i];\n      if (!this.data.checks[id]) {\n        throw new Error(`Locale provided for unknown check: \"${id}\"`);\n      }\n      this.data.checks[id] = mergeCheckLocale(this.data.checks[id], checks[id]);\n    }\n  }\n  /**\n   * Apply locale for the given `rules`.\n   */\n  _applyRuleLocale(rules) {\n    const keys = Object.keys(rules);\n    for (let i = 0; i < keys.length; i++) {\n      const id = keys[i];\n      if (!this.data.rules[id]) {\n        throw new Error(`Locale provided for unknown rule: \"${id}\"`);\n      }\n      this.data.rules[id] = mergeRuleLocale(this.data.rules[id], rules[id]);\n    }\n  }\n  /**\n   * Apply locale for the given failureMessage\n   */\n  _applyFailureSummaries(messages) {\n    const keys = Object.keys(messages);\n    for (let i = 0; i < keys.length; i++) {\n      const key = keys[i];\n      if (!this.data.failureSummaries[key]) {\n        throw new Error(`Locale provided for unknown failureMessage: \"${key}\"`);\n      }\n      this.data.failureSummaries[key] = mergeFailureMessage(\n        this.data.failureSummaries[key],\n        messages[key]\n      );\n    }\n  }\n  /**\n   * Apply the given `locale`.\n   *\n   * @param {axe.Locale}\n   */\n  applyLocale(locale) {\n    this._setDefaultLocale();\n    if (locale.checks) {\n      this._applyCheckLocale(locale.checks);\n    }\n    if (locale.rules) {\n      this._applyRuleLocale(locale.rules);\n    }\n    if (locale.failureSummaries) {\n      this._applyFailureSummaries(locale.failureSummaries, 'failureSummaries');\n    }\n    if (locale.incompleteFallbackMessage) {\n      this.data.incompleteFallbackMessage = mergeFallbackMessage(\n        this.data.incompleteFallbackMessage,\n        locale.incompleteFallbackMessage\n      );\n    }\n    if (locale.lang) {\n      this.lang = locale.lang;\n    }\n  }\n  /**\n   * Set the normalized allowed origins.\n   *\n   * @param {String[]}\n   */\n  setAllowedOrigins(allowedOrigins) {\n    const defaultOrigin = getDefaultOrigin();\n\n    this.allowedOrigins = [];\n    for (const origin of allowedOrigins) {\n      if (origin === constants.allOrigins) {\n        // No other origins needed. Set '*' and exit\n        this.allowedOrigins = ['*'];\n        return;\n      } else if (origin !== constants.sameOrigin) {\n        this.allowedOrigins.push(origin);\n      } else if (defaultOrigin) {\n        // sameOrigin, only if the default is known\n        this.allowedOrigins.push(defaultOrigin);\n      }\n    }\n  }\n  /**\n   * Initializes the rules and checks\n   */\n  _init() {\n    const audit = getDefaultConfiguration(this.defaultConfig);\n    this.lang = audit.lang || 'en';\n    this.reporter = audit.reporter;\n    this.commands = {};\n    this.rules = [];\n    this.checks = {};\n    this.brand = 'axe';\n    this.application = 'axeAPI';\n    this.tagExclude = ['experimental', 'deprecated'];\n    this.noHtml = audit.noHtml;\n    this.allowedOrigins = audit.allowedOrigins;\n    unpackToObject(audit.rules, this, 'addRule');\n    unpackToObject(audit.checks, this, 'addCheck');\n    this.data = {};\n    this.data.checks = (audit.data && audit.data.checks) || {};\n    this.data.rules = (audit.data && audit.data.rules) || {};\n    this.data.failureSummaries =\n      (audit.data && audit.data.failureSummaries) || {};\n    this.data.incompleteFallbackMessage =\n      (audit.data && audit.data.incompleteFallbackMessage) || '';\n    this._constructHelpUrls(); // create default helpUrls\n  }\n  /**\n   * Adds a new command to the audit\n   */\n  registerCommand(command) {\n    this.commands[command.id] = command.callback;\n  }\n  /**\n   * Adds a new rule to the Audit.  If a rule with specified ID already exists, it will be overridden\n   * @param {Object} spec Rule specification object\n   */\n  addRule(spec) {\n    if (spec.metadata) {\n      this.data.rules[spec.id] = spec.metadata;\n    }\n    const rule = this.getRule(spec.id);\n    if (rule) {\n      rule.configure(spec);\n    } else {\n      this.rules.push(new Rule(spec, this));\n    }\n  }\n  /**\n   * Adds a new check to the Audit.  If a Check with specified ID already exists, it will be\n   * reconfigured\n   *\n   * @param {Object} spec Check specification object\n   */\n  addCheck(spec) {\n    /*eslint no-eval: 0 */\n\n    const metadata = spec.metadata;\n    if (typeof metadata === 'object') {\n      this.data.checks[spec.id] = metadata;\n      // Transform messages into functions:\n      if (typeof metadata.messages === 'object') {\n        Object.keys(metadata.messages)\n          .filter(\n            prop =>\n              metadata.messages.hasOwnProperty(prop) &&\n              typeof metadata.messages[prop] === 'string'\n          )\n          .forEach(prop => {\n            if (metadata.messages[prop].indexOf('function') === 0) {\n              metadata.messages[prop] = new Function(\n                'return ' + metadata.messages[prop] + ';'\n              )();\n            }\n          });\n      }\n    }\n    if (this.checks[spec.id]) {\n      this.checks[spec.id].configure(spec);\n    } else {\n      this.checks[spec.id] = new Check(spec);\n    }\n  }\n  /**\n   * Runs the Audit; which in turn should call `run` on each rule.\n   * @async\n   * @param  {Context}   context The scope definition/context for analysis (include/exclude)\n   * @param  {Object}    options Options object to pass into rules and/or disable rules or checks\n   * @param  {Function}  resolve Callback function to fire when audit is complete\n   * @param  {Function}  reject  Callback function to fire when audit experiences an error\n   */\n  run(context, options, resolve, reject) {\n    normalizeRunOptions(options);\n    DqElement.setRunOptions(options);\n\n    // TODO: es-modules_selectCache\n    axe._selectCache = [];\n    // get a list of rules to run NOW vs. LATER (later are preload assets dependent rules)\n    const allRulesToRun = getRulesToRun(this.rules, context, options);\n    const runNowRules = allRulesToRun.now;\n    const runLaterRules = allRulesToRun.later;\n    // init a NOW queue for rules to run immediately\n    const nowRulesQueue = queue();\n    // construct can run NOW rules into NOW queue\n    runNowRules.forEach(rule => {\n      nowRulesQueue.defer(getDefferedRule(rule, context, options));\n    });\n    // init a PRELOADER queue to start preloading assets\n    const preloaderQueue = queue();\n    // defer preload if preload dependent rules exist\n    if (runLaterRules.length) {\n      preloaderQueue.defer(res => {\n        // handle both success and fail of preload\n        // and resolve, to allow to run all checks\n        preload(options)\n          .then(assets => res(assets))\n          .catch(err => {\n            /**\n             * Note:\n             * we do not reject, to allow other (non-preload) rules to `run`\n             * -> instead we resolve as `undefined`\n             */\n            console.warn(`Couldn't load preload assets: `, err);\n            res(undefined);\n          });\n      });\n    }\n    // defer now and preload queue to run immediately\n    const queueForNowRulesAndPreloader = queue();\n    queueForNowRulesAndPreloader.defer(nowRulesQueue);\n    queueForNowRulesAndPreloader.defer(preloaderQueue);\n    // invoke the now queue\n    queueForNowRulesAndPreloader\n      .then(nowRulesAndPreloaderResults => {\n        // interpolate results into separate variables\n        const assetsFromQueue = nowRulesAndPreloaderResults.pop();\n        if (assetsFromQueue && assetsFromQueue.length) {\n          // result is a queue (again), hence the index resolution\n          // assets is either an object of key value pairs of asset type and values\n          // eg: cssom: [stylesheets]\n          // or undefined if preload failed\n          const assets = assetsFromQueue[0];\n          // extend context with preloaded assets\n          if (assets) {\n            context = {\n              ...context,\n              ...assets\n            };\n          }\n        }\n        // the reminder of the results are RuleResults\n        const nowRulesResults = nowRulesAndPreloaderResults[0];\n        // if there are no rules to run LATER - resolve with rule results\n        if (!runLaterRules.length) {\n          // remove the cache\n          axe._selectCache = undefined;\n          // resolve\n          resolve(nowRulesResults.filter(result => !!result));\n          return;\n        }\n        // init a LATER queue for rules that are dependant on preloaded assets\n        const laterRulesQueue = queue();\n        runLaterRules.forEach(rule => {\n          const deferredRule = getDefferedRule(rule, context, options);\n          laterRulesQueue.defer(deferredRule);\n        });\n        // invoke the later queue\n        laterRulesQueue\n          .then(laterRuleResults => {\n            // remove the cache\n            axe._selectCache = undefined;\n            // resolve\n            resolve(\n              nowRulesResults\n                .concat(laterRuleResults)\n                .filter(result => !!result)\n            );\n          })\n          .catch(reject);\n      })\n      .catch(reject);\n  }\n  /**\n   * Runs Rule `after` post processing functions\n   * @param  {Array} results  Array of RuleResults to postprocess\n   * @param  {Mixed} options  Options object to pass into rules and/or disable rules or checks\n   */\n  after(results, options) {\n    const rules = this.rules;\n    return results.map(ruleResult => {\n      if (ruleResult.error) {\n        return ruleResult;\n      }\n      const rule = findBy(rules, 'id', ruleResult.id);\n      if (!rule) {\n        // If you see this, you're probably running the Mocha tests with the axe extension installed\n        throw new Error(\n          'Result for unknown rule. You may be running mismatch axe-core versions'\n        );\n      }\n      try {\n        return rule.after(ruleResult, options);\n      } catch (err) {\n        if (options.debug) {\n          throw err;\n        }\n        return createIncompleteErrorResult(rule, err);\n      }\n    });\n  }\n  /**\n   * Get the rule with a given ID\n   * @param  {string}\n   * @return {Rule}\n   */\n  getRule(ruleId) {\n    return this.rules.find(rule => rule.id === ruleId);\n  }\n\n  /*\n   * Updates the default options and then applies them\n   * @param  {Mixed} options  Options object\n   */\n  setBranding(branding) {\n    const previous = {\n      brand: this.brand,\n      application: this.application\n    };\n    if (typeof branding === 'string') {\n      this.application = branding;\n    }\n    if (\n      branding &&\n      branding.hasOwnProperty('brand') &&\n      branding.brand &&\n      typeof branding.brand === 'string'\n    ) {\n      this.brand = branding.brand;\n    }\n    if (\n      branding &&\n      branding.hasOwnProperty('application') &&\n      branding.application &&\n      typeof branding.application === 'string'\n    ) {\n      this.application = branding.application;\n    }\n    this._constructHelpUrls(previous);\n  }\n  _constructHelpUrls(previous = null) {\n    // TODO: es-modules-version\n    const version = (axe.version.match(/^[1-9][0-9]*\\.[0-9]+/) || ['x.y'])[0];\n    this.rules.forEach(rule => {\n      if (!this.data.rules[rule.id]) {\n        this.data.rules[rule.id] = {};\n      }\n      const metaData = this.data.rules[rule.id];\n      if (\n        typeof metaData.helpUrl !== 'string' ||\n        (previous &&\n          metaData.helpUrl === getHelpUrl(previous, rule.id, version))\n      ) {\n        metaData.helpUrl = getHelpUrl(this, rule.id, version);\n      }\n    });\n  }\n  /**\n   * Reset the default rules, checks and meta data\n   */\n  resetRulesAndChecks() {\n    this._init();\n    this._resetLocale();\n  }\n}\n\nfunction getDefaultOrigin() {\n  // @see https://html.spec.whatwg.org/multipage/webappapis.html#dom-origin-dev\n  // window.origin does not exist in ie11\n  // prevent origin default \"null\" string on CDP `Page.setDocumentContent` https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-setDocumentContent\n  if (window.origin && window.origin !== 'null') {\n    return window.origin;\n  }\n  // window.location does not exist in node when we run the build\n  if (\n    window.location &&\n    window.location.origin &&\n    window.location.origin !== 'null'\n  ) {\n    return window.location.origin;\n  }\n}\n\nfunction getDefaultConfiguration(audit) {\n  let config;\n  if (audit) {\n    config = clone(audit);\n    // Commons are configured into axe like everything else,\n    // however because things go funky if we have multiple commons objects\n    // we're not using the copy of that.\n    config.commons = audit.commons;\n  } else {\n    config = {};\n  }\n\n  config.reporter = config.reporter || null;\n  config.noHtml = config.noHtml || false;\n\n  if (!config.allowedOrigins) {\n    const defaultOrigin = getDefaultOrigin();\n    config.allowedOrigins = defaultOrigin ? [defaultOrigin] : [];\n  }\n\n  config.rules = config.rules || [];\n  config.checks = config.checks || [];\n  config.data = { checks: {}, rules: {}, ...config.data };\n  return config;\n}\n\nfunction unpackToObject(collection, audit, method) {\n  let i, l;\n  for (i = 0, l = collection.length; i < l; i++) {\n    audit[method](collection[i]);\n  }\n}\n\n/**\n * Merge two check locales (a, b), favoring `b`.\n *\n * Both locale `a` and the returned shape resemble:\n *\n *    {\n *      impact: string,\n *      messages: {\n *        pass: string | function,\n *        fail: string | function,\n *        incomplete: string | {\n *          [key: string]: string | function\n *        }\n *      }\n *    }\n *\n * Locale `b` follows the `axe.CheckLocale` shape and resembles:\n *\n *    {\n *      pass: string,\n *      fail: string,\n *      incomplete: string | { [key: string]: string }\n *    }\n */\n\nconst mergeCheckLocale = (a, b) => {\n  let { pass, fail } = b;\n  // If the message(s) are Strings, they have not yet been run\n  // thru doT (which will return a Function).\n  if (typeof pass === 'string' && dotRegex.test(pass)) {\n    pass = doT.compile(pass);\n  }\n  if (typeof fail === 'string' && dotRegex.test(fail)) {\n    fail = doT.compile(fail);\n  }\n  return {\n    ...a,\n    messages: {\n      pass: pass || a.messages.pass,\n      fail: fail || a.messages.fail,\n      incomplete:\n        typeof a.messages.incomplete === 'object'\n          ? // TODO: for compleness-sake, we should be running\n            // incomplete messages thru doT as well. This was\n            // out-of-scope for runtime localization, but should\n            // eventually be addressed.\n            { ...a.messages.incomplete, ...b.incomplete }\n          : b.incomplete\n    }\n  };\n};\n\n/**\n * Merge two rule locales (a, b), favoring `b`.\n */\n\nconst mergeRuleLocale = (a, b) => {\n  let { help, description } = b;\n  // If the message(s) are Strings, they have not yet been run\n  // thru doT (which will return a Function).\n  if (typeof help === 'string' && dotRegex.test(help)) {\n    help = doT.compile(help);\n  }\n  if (typeof description === 'string' && dotRegex.test(description)) {\n    description = doT.compile(description);\n  }\n  return {\n    ...a,\n    help: help || a.help,\n    description: description || a.description\n  };\n};\n\n/**\n * Merge two failure messages (a, b), favoring `b`.\n */\n\nconst mergeFailureMessage = (a, b) => {\n  let { failureMessage } = b;\n  // If the message(s) are Strings, they have not yet been run\n  // thru doT (which will return a Function).\n  if (typeof failureMessage === 'string' && dotRegex.test(failureMessage)) {\n    failureMessage = doT.compile(failureMessage);\n  }\n  return {\n    ...a,\n    failureMessage: failureMessage || a.failureMessage\n  };\n};\n\n/**\n * Merge two incomplete fallback messages (a, b), favoring `b`.\n */\n\nconst mergeFallbackMessage = (a, b) => {\n  if (typeof b === 'string' && dotRegex.test(b)) {\n    b = doT.compile(b);\n  }\n  return b || a;\n};\n\n/**\n * Splits a given array of rules to two, with rules that can be run immediately and one's that are dependent on preloadedAssets\n * @method getRulesToRun\n * @param {Array<Object>} rules complete list of rules\n * @param {Object} context\n * @param {Object} options\n * @return {Object} out, an object containing two arrays, one being list of rules to run now and list of rules to run later\n * @private\n */\nfunction getRulesToRun(rules, context, options) {\n  // entry object for reduce function below\n  const base = {\n    now: [],\n    later: []\n  };\n\n  // iterate through rules and separate out rules that need to be run now vs later\n  const splitRules = rules.reduce((out, rule) => {\n    // ensure rule can run\n    if (!ruleShouldRun(rule, context, options)) {\n      return out;\n    }\n\n    // does rule require preload assets - push to later array\n    if (rule.preload) {\n      out.later.push(rule);\n      return out;\n    }\n\n    // default to now array\n    out.now.push(rule);\n\n    // return\n    return out;\n  }, base);\n\n  // return\n  return splitRules;\n}\n\n/**\n * Convenience method, that consturcts a rule `run` function that can be deferred\n * @param {Object} rule rule to be deferred\n * @param {Object} context context object essential to be passed into rule `run`\n * @param {Object} options normalised options to be passed into rule `run`\n * @param {Object} assets (optional) preloaded assets to be passed into rule and checks (if the rule is preload dependent)\n * @return {Function} a deferrable function for rule\n */\nfunction getDefferedRule(rule, context, options) {\n  // init performance timer of requested via options\n  if (options.performanceTimer) {\n    performanceTimer.mark('mark_rule_start_' + rule.id);\n  }\n\n  return (resolve, reject) => {\n    // invoke `rule.run`\n    rule.run(\n      context,\n      options,\n      ruleResult => resolve(ruleResult),\n      err => {\n        if (options.debug) {\n          reject(err);\n        } else {\n          resolve(createIncompleteErrorResult(rule, err));\n        }\n      }\n    );\n  };\n}\n\nfunction createIncompleteErrorResult(rule, error) {\n  const { errorNode } = error;\n  const serialError = serializeError(error);\n  const none = [\n    {\n      id: 'error-occurred',\n      result: undefined,\n      data: serialError,\n      relatedNodes: []\n    }\n  ];\n  const node = errorNode || new DqElement(document.documentElement);\n  return Object.assign(new RuleResult(rule), {\n    error: serialError,\n    result: constants.CANTTELL,\n    nodes: [{ any: [], all: [], none, node }]\n  });\n}\n\n/**\n * For all the rules, create the helpUrl and add it to the data for that rule\n */\nfunction getHelpUrl({ brand, application, lang }, ruleId, version) {\n  return (\n    constants.helpUrlBase +\n    brand +\n    '/' +\n    (version || axe.version.substring(0, axe.version.lastIndexOf('.'))) +\n    '/' +\n    ruleId +\n    '?application=' +\n    encodeURIComponent(application) +\n    (lang && lang !== 'en' ? '&lang=' + encodeURIComponent(lang) : '')\n  );\n}\n"
  },
  {
    "path": "lib/core/base/cache.js",
    "content": "import assert from '../utils/assert';\n\nlet _cache = {};\n\nconst cache = {\n  /**\n   * Set an item in the cache.\n   * @param {String} key - Name of the key.\n   * @param {*} value - Value to store.\n   */\n  set(key, value) {\n    validateKey(key);\n\n    _cache[key] = value;\n  },\n\n  /**\n   * Retrieve an item from the cache.\n   * @param {String} key - Name of the key the value was stored as.\n   * @param {Function} [creator] - Default value to set if there is a cache miss. Functions are evaluated before caching. To override a value already saved, use `set()`.\n   * @returns {*} The item stored\n   */\n  get(key, creator) {\n    validateCreator(creator);\n\n    if (key in _cache) {\n      return _cache[key];\n    }\n\n    if (typeof creator === 'function') {\n      const value = creator();\n      assert(\n        value !== undefined,\n        'Cache creator function should not return undefined'\n      );\n      this.set(key, value);\n      return _cache[key];\n    }\n  },\n\n  /**\n   * Clear the cache.\n   */\n  clear() {\n    _cache = {};\n  }\n};\n\n/**\n * Validates a cache key or throws an error.\n * @param {String} key - The key to validate.\n * @throws {Error} If the key is not a string.\n */\nfunction validateKey(key) {\n  assert(\n    typeof key === 'string',\n    'key must be a string, ' + typeof key + ' given'\n  );\n  assert(key !== '', 'key must not be empty');\n}\n\n/**\n * Validates a cache creator by throwing if it is not a function or undefined.\n * @param {Function|undefined} creator - The creator function to validate.\n * @throws {Error} If the creator is not a function or undefined.\n */\nfunction validateCreator(creator) {\n  assert(\n    typeof creator === 'function' || typeof creator === 'undefined',\n    'creator must be a function or undefined, ' + typeof creator + ' given'\n  );\n}\n\nexport default cache;\n"
  },
  {
    "path": "lib/core/base/check-result.js",
    "content": "/**\n * Constructor for the result of checks\n * @param {Check} check\n */\nfunction CheckResult(check) {\n  /**\n   * ID of the check.  Unique in the context of a rule.\n   * @type {String}\n   */\n  this.id = check.id;\n\n  /**\n   * Any data passed by Check (by calling `this.data()`)\n   * @type {Mixed}\n   */\n  this.data = null;\n\n  /**\n   * Any node that is related to the Check, specified by calling `this.relatedNodes([HTMLElement...])` inside the Check\n   * @type {Array}\n   */\n  this.relatedNodes = [];\n\n  /**\n   * The return value of the Check's evaluate function\n   * @type {Mixed}\n   */\n  this.result = null;\n}\n\nexport default CheckResult;\n"
  },
  {
    "path": "lib/core/base/check.js",
    "content": "import metadataFunctionMap from './metadata-function-map';\nimport CheckResult from './check-result';\nimport { nodeSerializer, checkHelper, deepMerge } from '../utils';\n\nexport function createExecutionContext(spec) {\n  /*eslint no-eval:0 */\n  if (typeof spec === 'string') {\n    if (metadataFunctionMap[spec]) {\n      return metadataFunctionMap[spec];\n    }\n\n    // execution contexts can only be functions\n    if (/^\\s*function[\\s\\w]*\\(/.test(spec)) {\n      return new Function('return ' + spec + ';')();\n    }\n\n    throw new ReferenceError(\n      `Function ID does not exist in the metadata-function-map: ${spec}`\n    );\n  }\n  return spec;\n}\n\n/**\n * Normalize check options to always be an object.\n * @param {Object} options\n * @return Object\n */\nfunction normalizeOptions(options = {}) {\n  if (Array.isArray(options) || typeof options !== 'object') {\n    options = { value: options };\n  }\n\n  return options;\n}\n\nfunction Check(spec) {\n  if (spec) {\n    this.id = spec.id;\n    this.configure(spec);\n  }\n}\n\n/**\n * Unique ID for the check.  Checks may be re-used, so there may be additional instances of checks\n * with the same ID.\n * @type {String}\n */\n// Check.prototype.id;\n\n/**\n * Free-form options that are passed as the second parameter to the `evaluate`\n * @type {Mixed}\n */\n// Check.prototype.options;\n\n/**\n * The actual code, accepts 2 parameters: node (the node under test), options (see this.options).\n * This function is run in the context of a checkHelper, which has the following methods\n * - `async()` - if called, the check is considered to be asynchronous; returns a callback function\n * - `data()` - free-form data object, associated to the `CheckResult` which is specific to each node\n * @type {Function}\n */\n// Check.prototype.evaluate;\n\n/**\n * Optional. Filter and/or modify checks for all nodes\n * @type {Function}\n */\n// Check.prototype.after;\n\n/**\n * enabled by default, if false, this check will not be included in the rule's evaluation\n * @type {Boolean}\n */\nCheck.prototype.enabled = true;\n\n/**\n * Run the check's evaluate function (call `this.evaluate(node, options)`)\n * @param  {HTMLElement} node  The node to test\n * @param  {Object} options    The options that override the defaults and provide additional\n *                             information for the check\n * @param  {Function} callback Function to fire when check is complete\n */\nCheck.prototype.run = function run(node, options, context, resolve, reject) {\n  options = options || {};\n  const enabled = options.hasOwnProperty('enabled')\n    ? options.enabled\n    : this.enabled;\n  const checkOptions = this.getOptions(options.options);\n\n  if (enabled) {\n    const checkResult = new CheckResult(this);\n    const helper = checkHelper(checkResult, options, resolve, reject);\n    let result;\n\n    try {\n      result = this.evaluate.call(\n        helper,\n        node.actualNode,\n        checkOptions,\n        node,\n        context\n      );\n    } catch (e) {\n      // In the \"Audit#run: should run all the rules\" test, there is no `node` here. I do\n      // not know if this is intentional or not, so to be safe, we guard against the\n      // possible reference error.\n      if (node && node.actualNode) {\n        // Save a reference to the node we errored on for futher debugging.\n        e.errorNode = nodeSerializer.toSpec(node);\n      }\n      reject(e);\n      return;\n    }\n\n    if (!helper.isAsync) {\n      checkResult.result = result;\n      resolve(checkResult);\n    }\n  } else {\n    resolve(null);\n  }\n};\n\n/**\n * Run the check's evaluate function (call `this.evaluate(node, options)`) synchronously\n * @param  {HTMLElement} node  The node to test\n * @param  {Object} options    The options that override the defaults and provide additional\n *                             information for the check\n */\nCheck.prototype.runSync = function runSync(node, options, context) {\n  options = options || {};\n  const { enabled = this.enabled } = options;\n\n  if (!enabled) {\n    return null;\n  }\n\n  const checkOptions = this.getOptions(options.options);\n  const checkResult = new CheckResult(this);\n  const helper = checkHelper(checkResult, options);\n\n  // throw error if a check is run that requires async behavior\n  helper.async = function async() {\n    throw new Error('Cannot run async check while in a synchronous run');\n  };\n\n  let result;\n\n  try {\n    result = this.evaluate.call(\n      helper,\n      node.actualNode,\n      checkOptions,\n      node,\n      context\n    );\n  } catch (e) {\n    // In the \"Audit#run: should run all the rules\" test, there is no `node` here. I do\n    // not know if this is intentional or not, so to be safe, we guard against the\n    // possible reference error.\n    if (node && node.actualNode) {\n      // Save a reference to the node we errored on for futher debugging.\n      e.errorNode = nodeSerializer.toSpec(node);\n    }\n    throw e;\n  }\n\n  checkResult.result = result;\n  return checkResult;\n};\n\n/**\n * Override a check's settings after construction to allow for changing options\n * without having to implement the entire check\n *\n * @param {Object} spec - the specification of the attributes to be changed\n */\n\nCheck.prototype.configure = function configure(spec) {\n  // allow test specs (without evaluate functions) to work as\n  // internal checks\n  if (!spec.evaluate || metadataFunctionMap[spec.evaluate]) {\n    this._internalCheck = true;\n  }\n\n  if (spec.hasOwnProperty('enabled')) {\n    this.enabled = spec.enabled;\n  }\n\n  if (spec.hasOwnProperty('options')) {\n    // only normalize options for internal checks\n    if (this._internalCheck) {\n      this.options = normalizeOptions(spec.options);\n    } else {\n      this.options = spec.options;\n    }\n  }\n\n  ['evaluate', 'after']\n    .filter(prop => spec.hasOwnProperty(prop))\n    .forEach(prop => (this[prop] = createExecutionContext(spec[prop])));\n};\n\nCheck.prototype.getOptions = function getOptions(options) {\n  // only merge and normalize options for internal checks\n  if (this._internalCheck) {\n    return deepMerge(this.options, normalizeOptions(options || {}));\n  } else {\n    return options || this.options;\n  }\n};\n\nexport default Check;\n"
  },
  {
    "path": "lib/core/base/context/create-frame-context.js",
    "content": "import { parseTabindex } from '../../utils';\n\nexport function createFrameContext(frame, { focusable, page }) {\n  return {\n    node: frame,\n    include: [],\n    exclude: [],\n    initiator: false,\n    focusable: focusable && frameFocusable(frame),\n    size: getBoundingSize(frame),\n    page\n  };\n}\n\nfunction frameFocusable(frame) {\n  const tabIndex = parseTabindex(frame.getAttribute('tabindex'));\n  return tabIndex === null || tabIndex >= 0;\n}\n\nfunction getBoundingSize(domNode) {\n  let width = parseInt(domNode.getAttribute('width'), 10);\n  let height = parseInt(domNode.getAttribute('height'), 10);\n\n  if (isNaN(width) || isNaN(height)) {\n    const rect = domNode.getBoundingClientRect();\n    width = isNaN(width) ? rect.width : width;\n    height = isNaN(height) ? rect.height : height;\n  }\n  return { width, height };\n}\n"
  },
  {
    "path": "lib/core/base/context/normalize-context.js",
    "content": "import {\n  assert as utilsAssert,\n  objectHasOwn,\n  isArrayLike,\n  isContextObject,\n  isContextProp,\n  isLabelledFramesSelector,\n  isLabelledShadowDomSelector\n} from '../../utils';\n\n/**\n * Normalize the input of \"context\" so that many different methods of input are accepted\n * @private\n * @param  {Mixed} contextSpec The configuration object passed to `Context`\n * @return {Object}            Normalized context spec to include both `include` and `exclude` arrays\n */\nexport function normalizeContext(contextSpec) {\n  if (isContextObject(contextSpec)) {\n    // Assert include / exclude isn't mixed with fromFrames / fromShadowDom\n    const msg =\n      ' must be used inside include or exclude. It should not be on the same object.';\n    assert(!objectHasOwn(contextSpec, 'fromFrames'), 'fromFrames' + msg);\n    assert(!objectHasOwn(contextSpec, 'fromShadowDom'), 'fromShadowDom' + msg);\n  } else if (isContextProp(contextSpec)) {\n    // Wrap in include\n    contextSpec = { include: contextSpec, exclude: [] };\n  } else {\n    // Spec is unknown\n    return { include: [document], exclude: [] };\n  }\n\n  const include = normalizeContextList(contextSpec.include);\n  if (include.length === 0) {\n    include.push(document); // Include defaults to [document] if empty\n  }\n  const exclude = normalizeContextList(contextSpec.exclude);\n  return { include, exclude };\n}\n\nfunction normalizeContextList(selectorList = []) {\n  const normalizedList = [];\n  if (!isArrayLike(selectorList)) {\n    selectorList = [selectorList];\n  }\n  // Use .length to handle jQuery-like objects\n  for (let i = 0; i < selectorList.length; i++) {\n    const normalizedSelector = normalizeContextSelector(selectorList[i]);\n    if (normalizedSelector) {\n      normalizedList.push(normalizedSelector);\n    }\n  }\n  return normalizedList;\n}\n\nfunction normalizeContextSelector(selector) {\n  if (selector instanceof window.Node) {\n    return selector; // Nodes must not be wrapped in an array\n  }\n  if (typeof selector === 'string') {\n    return [selector]; // Convert to frame selector\n  }\n\n  if (isLabelledFramesSelector(selector)) {\n    assertLabelledFrameSelector(selector);\n    selector = selector.fromFrames;\n  } else if (isLabelledShadowDomSelector(selector)) {\n    selector = [selector];\n  }\n  return normalizeFrameSelectors(selector);\n}\n\nfunction normalizeFrameSelectors(frameSelectors) {\n  if (!Array.isArray(frameSelectors)) {\n    return; // Invalid. Skip this selector\n  }\n  const normalizedSelectors = [];\n  for (let selector of frameSelectors) {\n    if (isLabelledShadowDomSelector(selector)) {\n      assertLabelledShadowDomSelector(selector);\n      selector = selector.fromShadowDom;\n    }\n    if (typeof selector !== 'string' && !isShadowSelector(selector)) {\n      return; // Invalid. Skip this selector\n    }\n    normalizedSelectors.push(selector);\n  }\n  return normalizedSelectors;\n}\n\nfunction assertLabelledFrameSelector(selector) {\n  assert(\n    Array.isArray(selector.fromFrames),\n    'fromFrames property must be an array'\n  );\n  assert(\n    selector.fromFrames.every(\n      fromFrameSelector => !objectHasOwn(fromFrameSelector, 'fromFrames')\n    ),\n    'Invalid context; fromFrames selector must be appended, rather than nested'\n  );\n  assert(\n    !objectHasOwn(selector, 'fromShadowDom'),\n    'fromFrames and fromShadowDom cannot be used on the same object'\n  );\n}\n\nfunction assertLabelledShadowDomSelector(selector) {\n  assert(\n    Array.isArray(selector.fromShadowDom),\n    'fromShadowDom property must be an array'\n  );\n  assert(\n    selector.fromShadowDom.every(\n      fromShadowDomSelector =>\n        !objectHasOwn(fromShadowDomSelector, 'fromFrames')\n    ),\n    'shadow selector must be inside fromFrame instead'\n  );\n  assert(\n    selector.fromShadowDom.every(\n      fromShadowDomSelector =>\n        !objectHasOwn(fromShadowDomSelector, 'fromShadowDom')\n    ),\n    'fromShadowDom selector must be appended, rather than nested'\n  );\n}\n\nfunction isShadowSelector(selector) {\n  return (\n    Array.isArray(selector) && selector.every(str => typeof str === 'string')\n  );\n}\n\n// Wrapper to ensure the correct message\nfunction assert(bool, str) {\n  utilsAssert(\n    bool,\n    `Invalid context; ${str}\\nSee: https://github.com/dequelabs/axe-core/blob/master/doc/context.md`\n  );\n}\n"
  },
  {
    "path": "lib/core/base/context/parse-selector-array.js",
    "content": "import { createFrameContext } from './create-frame-context';\nimport { getNodeFromTree, shadowSelectAll } from '../../utils';\n\n/**\n * Finds frames in context, converts selectors to Element references and pushes unique frames\n * @private\n * @param  {Context} context The instance of Context to operate on\n * @param  {String} type     The \"type\" of thing to parse, \"include\" or \"exclude\"\n * @return {Array}           Parsed array of matching elements\n */\nexport function parseSelectorArray(context, type) {\n  const result = [];\n  for (let i = 0, l = context[type].length; i < l; i++) {\n    const item = context[type][i];\n    // Handle nodes\n    if (item instanceof window.Node) {\n      if (item.documentElement instanceof window.Node) {\n        result.push(context.flatTree[0]);\n      } else if (item.host instanceof window.Node) {\n        // Item is a shadow root. We only cache instances of `Element`,\n        // not `DocumentFragment`, so instead of the shadow root itself,\n        // we'll push all of its children to context.\n        const children = Array.from(item.children).map(child =>\n          getNodeFromTree(child)\n        );\n        result.push(...children);\n      } else {\n        result.push(getNodeFromTree(item));\n      }\n\n      // Handle Iframe selection\n    } else if (item && item.length) {\n      if (item.length > 1) {\n        pushUniqueFrameSelector(context, type, item);\n      } else {\n        const nodeList = shadowSelectAll(item[0]);\n        result.push(...nodeList.map(node => getNodeFromTree(node)));\n      }\n    }\n  }\n\n  // filter nulls\n  return result.filter(r => r);\n}\n\n/**\n * Unshift selectors of matching iframes\n * @private\n * @param  {Context} context \t  The context object to operate on and assign to\n * @param  {String} type          The \"type\" of context, 'include' or 'exclude'\n * @param  {Array} selectorArray  Array of CSS selectors, each element represents a frame;\n * where the last element is the actual node\n */\nfunction pushUniqueFrameSelector(context, type, selectorArray) {\n  context.frames = context.frames || [];\n\n  const frameSelector = selectorArray.shift();\n  const frames = shadowSelectAll(frameSelector);\n  frames.forEach(frame => {\n    let frameContext = context.frames.find(result => result.node === frame);\n    if (!frameContext) {\n      frameContext = createFrameContext(frame, context);\n      context.frames.push(frameContext);\n    }\n    frameContext[type].push(selectorArray);\n  });\n}\n"
  },
  {
    "path": "lib/core/base/context.js",
    "content": "import { createFrameContext } from './context/create-frame-context';\nimport { normalizeContext } from './context/normalize-context';\nimport { parseSelectorArray } from './context/parse-selector-array';\nimport {\n  findBy,\n  getFlattenedTree,\n  select,\n  isNodeInContext,\n  nodeSorter,\n  respondable,\n  clone\n} from '../utils';\nimport { isVisibleToScreenReaders } from '../../commons/dom';\n\n/**\n * Holds context of includes, excludes and frames for analysis.\n *\n * @todo  clarify and sync changes to design doc\n * Context : {IncludeStrings} || {\n *   // defaults to document/all\n *   include: {IncludeStrings},\n *   exclude : {ExcludeStrings}\n * }\n *\n * IncludeStrings : [{CSSSelectorArray}] || Node\n * ExcludeStrings : [{CSSSelectorArray}]\n * `CSSSelectorArray` an Array of selector strings that addresses a Node in a multi-frame document. All addresses\n * are in this form regardless of whether the document contains any frames.To evaluate the selectors to\n * find the node referenced by the array, evaluate the selectors in-order, starting in window.top. If N\n * is the length of the array, then the first N-1 selectors should result in an iframe and the last\n * selector should result in the specific node.\n *\n * @param {Object} spec Configuration or \"specification\" object\n */\nexport default function Context(spec, flatTree) {\n  spec = clone(spec);\n  this.frames = [];\n  this.page = typeof spec?.page === 'boolean' ? spec.page : undefined;\n  this.initiator = typeof spec?.initiator === 'boolean' ? spec.initiator : true;\n  this.focusable = typeof spec?.focusable === 'boolean' ? spec.focusable : true;\n  this.size = typeof spec?.size === 'object' ? spec.size : {};\n\n  spec = normalizeContext(spec);\n\n  // cache the flattened tree\n  this.flatTree = flatTree ?? getFlattenedTree(getRootNode(spec));\n  this.exclude = spec.exclude;\n  this.include = spec.include;\n\n  this.include = parseSelectorArray(this, 'include');\n  this.exclude = parseSelectorArray(this, 'exclude');\n\n  select('frame, iframe', this).forEach(frame => {\n    if (isNodeInContext(frame, this)) {\n      pushUniqueFrame(this, frame.actualNode);\n    }\n  });\n\n  if (typeof this.page === 'undefined') {\n    // Figure out if the entire page is in scope\n    this.page = isPageContext(this);\n    this.frames.forEach(frame => {\n      frame.page = this.page;\n    });\n  }\n\n  // Validate outside of a frame\n  validateContext(this);\n\n  if (!Array.isArray(this.include)) {\n    this.include = Array.from(this.include);\n  }\n  this.include.sort(nodeSorter); // ensure that the order of the include nodes is document order\n}\n\n/**\n * Pushes a unique frame onto `frames` array, filtering any hidden iframes\n * @private\n * @param  {Object} Context      Parent context for the frame\n * @param  {HTMLElement} frame   The frame to push onto Context\n */\nfunction pushUniqueFrame(context, frame) {\n  if (\n    !isVisibleToScreenReaders(frame) ||\n    findBy(context.frames, 'node', frame)\n  ) {\n    return;\n  }\n  context.frames.push(createFrameContext(frame, context));\n}\n\n/**\n * Check if a normalized context tests the full page\n * @private\n */\nfunction isPageContext({ include }) {\n  return (\n    include.length === 1 && include[0].actualNode === document.documentElement\n  );\n}\n\n/**\n * Check that the context, as well as each frame includes at least 1 element\n * @private\n * @param  {context} context\n * @return {Error}\n */\nfunction validateContext(context) {\n  if (context.include.length === 0 && context.frames.length === 0) {\n    const env = respondable.isInFrame() ? 'frame' : 'page';\n    throw new Error('No elements found for include in ' + env + ' Context');\n  }\n}\n\n/**\n * For a context-like object, find its shared root node\n */\nfunction getRootNode({ include, exclude }) {\n  const selectors = Array.from(include).concat(Array.from(exclude));\n  // Find the first Element.ownerDocument or Document\n  for (let i = 0; i < selectors.length; i++) {\n    const item = selectors[i];\n    if (item instanceof window.Element) {\n      return item.ownerDocument.documentElement;\n    }\n\n    if (item instanceof window.Document) {\n      return item.documentElement;\n    }\n  }\n  return document.documentElement;\n}\n"
  },
  {
    "path": "lib/core/base/rule-result.js",
    "content": "import constants from '../constants';\n\n/**\n * Constructor for the result of Rules\n * @param {Rule} rule\n */\nfunction RuleResult(rule) {\n  /**\n   * The ID of the Rule whom this result belongs to\n   * @type {String}\n   */\n  this.id = rule.id;\n\n  /**\n   * The calculated result of the Rule, either PASS, FAIL or NA\n   * @type {String}\n   */\n  this.result = constants.NA;\n\n  /**\n   * Whether the Rule is a \"pageLevel\" rule\n   * @type {Boolean}\n   */\n  this.pageLevel = rule.pageLevel;\n\n  /**\n   * Impact of the violation\n   * @type {String}  Plain-english impact or null if rule passes\n   */\n  this.impact = null;\n\n  /**\n   * Holds information regarding nodes and individual CheckResults\n   * @type {Array}\n   */\n  this.nodes = [];\n}\n\nexport default RuleResult;\n"
  },
  {
    "path": "lib/core/base/rule.js",
    "content": "import { createExecutionContext } from './check';\nimport RuleResult from './rule-result';\nimport {\n  performanceTimer,\n  getAllChecks,\n  getCheckOption,\n  queue,\n  DqElement,\n  select,\n  assert,\n  RuleError\n} from '../utils';\nimport { isVisibleToScreenReaders } from '../../commons/dom';\nimport constants from '../constants';\nimport log from '../log';\n\nexport default function Rule(spec, parentAudit) {\n  this._audit = parentAudit;\n\n  /**\n   * The code, or string ID of the rule\n   * @type {String}\n   */\n  this.id = spec.id;\n\n  /**\n   * Selector that this rule applies to\n   * @type {String}\n   */\n  this.selector = spec.selector || '*';\n\n  /**\n   * Impact of the rule (optional)\n   * @type {\"minor\" | \"moderate\" | \"serious\" | \"critical\"}\n   */\n  if (spec.impact) {\n    assert(\n      constants.impact.includes(spec.impact),\n      `Impact ${spec.impact} is not a valid impact`\n    );\n    this.impact = spec.impact;\n  }\n\n  /**\n   * Whether to exclude hiddden elements form analysis.  Defaults to true.\n   * @type {Boolean}\n   */\n  this.excludeHidden =\n    typeof spec.excludeHidden === 'boolean' ? spec.excludeHidden : true;\n\n  /**\n   * Flag to enable or disable rule\n   * @type {Boolean}\n   */\n  this.enabled = typeof spec.enabled === 'boolean' ? spec.enabled : true;\n\n  /**\n   * Denotes if the rule should be run if Context is not an entire page AND whether\n   * the Rule should be satisified regardless of Node\n   * @type {Boolean}\n   */\n  this.pageLevel = typeof spec.pageLevel === 'boolean' ? spec.pageLevel : false;\n\n  /**\n   * Flag to force the rule to return as needs review rather than a violation if any of the checks fail.\n   * @type {Boolean}\n   */\n  this.reviewOnFail =\n    typeof spec.reviewOnFail === 'boolean' ? spec.reviewOnFail : false;\n\n  /**\n   * Checks that any may return true to satisfy rule\n   * @type {Array}\n   */\n  this.any = spec.any || [];\n\n  /**\n   * Checks that must all return true to satisfy rule\n   * @type {Array}\n   */\n  this.all = spec.all || [];\n\n  /**\n   * Checks that none may return true to satisfy rule\n   * @type {Array}\n   */\n  this.none = spec.none || [];\n\n  /**\n   * Tags associated to this rule\n   * @type {Array}\n   */\n  this.tags = spec.tags || [];\n\n  /**\n   * Preload necessary for this rule\n   */\n  this.preload = spec.preload ? true : false;\n\n  /**\n   * IDs of ACT rules the axe-core rule maps to\n   * @type {Array|undefined}\n   */\n  this.actIds = spec.actIds;\n\n  if (spec.matches) {\n    /**\n     * Optional function to test if rule should be run against a node, overrides Rule#matches\n     * @type {Function}\n     */\n    this.matches = createExecutionContext(spec.matches);\n  }\n}\n\n/**\n * Optionally test each node against a `matches` function to determine if the rule should run against\n * a given node.  Defaults to `true`.\n * @return {Boolean}    Whether the rule should run\n */\nRule.prototype.matches = function matches() {\n  return true;\n};\n\n/**\n * Selects `HTMLElement`s based on configured selector\n * @param  {Context} context The resolved Context object\n * @param  {Mixed}   options Options specific to this rule\n * @return {Array}           All matching `HTMLElement`s\n */\nRule.prototype.gather = function gather(context, options = {}) {\n  const markStart = 'mark_gather_start_' + this.id;\n  const markEnd = 'mark_gather_end_' + this.id;\n  const markHiddenStart = 'mark_isVisibleToScreenReaders_start_' + this.id;\n  const markHiddenEnd = 'mark_isVisibleToScreenReaders_end_' + this.id;\n\n  if (options.performanceTimer) {\n    performanceTimer.mark(markStart);\n  }\n\n  let elements = select(this.selector, context);\n  if (this.excludeHidden) {\n    if (options.performanceTimer) {\n      performanceTimer.mark(markHiddenStart);\n    }\n\n    elements = elements.filter(element => {\n      return isVisibleToScreenReaders(element);\n    });\n\n    if (options.performanceTimer) {\n      performanceTimer.mark(markHiddenEnd);\n      performanceTimer.measure(\n        'rule_' + this.id + '#gather_axe.utils.isVisibleToScreenReaders',\n        markHiddenStart,\n        markHiddenEnd\n      );\n    }\n  }\n\n  if (options.performanceTimer) {\n    performanceTimer.mark(markEnd);\n    performanceTimer.measure('rule_' + this.id + '#gather', markStart, markEnd);\n  }\n\n  return elements;\n};\n\nRule.prototype.runChecks = function runChecks(\n  type,\n  node,\n  options,\n  context,\n  resolve,\n  reject\n) {\n  const self = this;\n\n  const checkQueue = queue();\n\n  this[type].forEach(c => {\n    const check = self._audit.checks[c.id || c];\n    const option = getCheckOption(check, self.id, options);\n    checkQueue.defer((res, rej) => {\n      check.run(node, option, context, res, error => {\n        rej(\n          new RuleError({\n            ruleId: self.id,\n            method: `${check.id}#evaluate`,\n            errorNode: new DqElement(node),\n            error\n          })\n        );\n      });\n    });\n  });\n\n  checkQueue\n    .then(results => {\n      results = results.filter(check => check);\n      resolve({ type: type, results: results });\n    })\n    .catch(reject);\n};\n\n/**\n * Run a check for a rule synchronously.\n */\nRule.prototype.runChecksSync = function runChecksSync(\n  type,\n  node,\n  options,\n  context\n) {\n  const self = this;\n  let results = [];\n\n  this[type].forEach(c => {\n    const check = self._audit.checks[c.id || c];\n    const option = getCheckOption(check, self.id, options);\n    results.push(check.runSync(node, option, context));\n  });\n\n  results = results.filter(check => check);\n\n  return { type: type, results: results };\n};\n\n/**\n * Runs the Rule's `evaluate` function\n * @param  {Context}   context  The resolved Context object\n * @param  {Mixed}   options  Options specific to this rule\n * @param  {Function} callback Function to call when evaluate is complete; receives a RuleResult instance\n */\nRule.prototype.run = function run(context, options = {}, resolve, reject) {\n  if (options.performanceTimer) {\n    this._trackPerformance();\n  }\n\n  const q = queue();\n  const ruleResult = new RuleResult(this);\n  let nodes;\n\n  try {\n    // Matches throws an error when it lacks support for document methods\n    nodes = this.gatherAndMatchNodes(context, options);\n  } catch (error) {\n    reject(error);\n    return;\n  }\n\n  if (options.performanceTimer) {\n    this._logGatherPerformance(nodes);\n  }\n\n  nodes.forEach(node => {\n    q.defer((resolveNode, rejectNode) => {\n      const checkQueue = queue();\n\n      ['any', 'all', 'none'].forEach(type => {\n        checkQueue.defer((res, rej) => {\n          this.runChecks(type, node, options, context, res, rej);\n        });\n      });\n\n      checkQueue\n        .then(results => {\n          const result = getResult(results);\n          if (result) {\n            result.node = new DqElement(node);\n            ruleResult.nodes.push(result);\n\n            // mark rule as incomplete rather than failure for rules with reviewOnFail\n            if (this.reviewOnFail) {\n              ['any', 'all'].forEach(type => {\n                result[type].forEach(checkResult => {\n                  if (checkResult.result === false) {\n                    checkResult.result = undefined;\n                  }\n                });\n              });\n\n              result.none.forEach(checkResult => {\n                if (checkResult.result === true) {\n                  checkResult.result = undefined;\n                }\n              });\n            }\n          }\n          resolveNode();\n        })\n        .catch(err => rejectNode(err));\n    });\n  });\n\n  q.then(() => {\n    if (options.performanceTimer) {\n      this._logRulePerformance();\n    }\n    // Defer the rule's execution to prevent \"unresponsive script\" warnings.\n    // See https://github.com/dequelabs/axe-core/pull/1172 for discussion and details.\n    setTimeout(() => {\n      resolve(ruleResult);\n    }, 0);\n  }).catch(error => {\n    if (options.performanceTimer) {\n      this._logRulePerformance();\n    }\n    reject(error);\n  });\n};\n\n/**\n * Runs the Rule's `evaluate` function synchronously\n * @param  {Context}   context  The resolved Context object\n * @param  {Mixed}   options  Options specific to this rule\n */\nRule.prototype.runSync = function runSync(context, options = {}) {\n  if (options.performanceTimer) {\n    this._trackPerformance();\n  }\n\n  const ruleResult = new RuleResult(this);\n  const nodes = this.gatherAndMatchNodes(context, options);\n  if (options.performanceTimer) {\n    this._logGatherPerformance(nodes);\n  }\n\n  nodes.forEach(node => {\n    const results = [];\n    ['any', 'all', 'none'].forEach(type => {\n      results.push(this.runChecksSync(type, node, options, context));\n    });\n\n    const result = getResult(results);\n    if (result) {\n      result.node = node.actualNode ? new DqElement(node) : null;\n      ruleResult.nodes.push(result);\n\n      // mark rule as incomplete rather than failure for rules with reviewOnFail\n      if (this.reviewOnFail) {\n        ['any', 'all'].forEach(type => {\n          result[type].forEach(checkResult => {\n            if (checkResult.result === false) {\n              checkResult.result = undefined;\n            }\n          });\n        });\n\n        result.none.forEach(checkResult => {\n          if (checkResult.result === true) {\n            checkResult.result = undefined;\n          }\n        });\n      }\n    }\n  });\n\n  if (options.performanceTimer) {\n    this._logRulePerformance();\n  }\n\n  return ruleResult;\n};\n\n/**\n * Add performance tracking properties to the rule\n * @private\n */\nRule.prototype._trackPerformance = function _trackPerformance() {\n  this._markStart = 'mark_rule_start_' + this.id;\n  this._markEnd = 'mark_rule_end_' + this.id;\n  this._markChecksStart = 'mark_runchecks_start_' + this.id;\n  this._markChecksEnd = 'mark_runchecks_end_' + this.id;\n};\n\n/**\n * Log performance of rule.gather\n * @private\n * @param {Rule} rule The rule to log\n * @param {Array} nodes Result of rule.gather\n */\nRule.prototype._logGatherPerformance = function _logGatherPerformance(nodes) {\n  log(\n    `gather for ${this.id} (${nodes.length} nodes): ${performanceTimer.timeElapsed()}ms`\n  );\n  performanceTimer.mark(this._markChecksStart);\n};\n\n/**\n * Log performance of the rule\n * @private\n * @param {Rule} rule The rule to log\n */\nRule.prototype._logRulePerformance = function _logRulePerformance() {\n  performanceTimer.mark(this._markChecksEnd);\n  performanceTimer.mark(this._markEnd);\n  performanceTimer.measure(\n    'runchecks_' + this.id,\n    this._markChecksStart,\n    this._markChecksEnd\n  );\n\n  performanceTimer.measure('rule_' + this.id, this._markStart, this._markEnd);\n};\n\n/**\n * Process the results of each check and return the result if a check\n * has a result\n * @private\n * @param {Array} results  Array of each check result\n * @returns {Object|null}\n */\nfunction getResult(results) {\n  if (results.length) {\n    let hasResults = false;\n    const result = {};\n    results.forEach(r => {\n      const res = r.results.filter(_result => _result);\n      result[r.type] = res;\n      if (res.length) {\n        hasResults = true;\n      }\n    });\n\n    if (hasResults) {\n      return result;\n    }\n\n    return null;\n  }\n}\n\n/**\n * Selects `HTMLElement`s based on configured selector and filters them based on\n * the rules matches function\n * @param  {Rule} rule The rule to check for after checks\n * @param  {Context} context The resolved Context object\n * @param  {Mixed}   options Options specific to this rule\n * @return {Array}           All matching `HTMLElement`s\n */\nRule.prototype.gatherAndMatchNodes = function gatherAndMatchNodes(\n  context,\n  options\n) {\n  const markMatchesStart = 'mark_matches_start_' + this.id;\n  const markMatchesEnd = 'mark_matches_end_' + this.id;\n\n  let nodes = this.gather(context, options);\n\n  if (options.performanceTimer) {\n    performanceTimer.mark(markMatchesStart);\n  }\n\n  nodes = nodes.filter(node => {\n    try {\n      return this.matches(node.actualNode, node, context);\n    } catch (error) {\n      throw new RuleError({\n        ruleId: this.id,\n        method: `#matches`,\n        errorNode: new DqElement(node),\n        error\n      });\n    }\n  });\n\n  if (options.performanceTimer) {\n    performanceTimer.mark(markMatchesEnd);\n    performanceTimer.measure(\n      'rule_' + this.id + '#matches',\n      markMatchesStart,\n      markMatchesEnd\n    );\n  }\n\n  return nodes;\n};\n\n/**\n * Iterates the rule's Checks looking for ones that have an after function\n * @private\n * @param  {Rule} rule The rule to check for after checks\n * @return {Array}      Checks that have an after function\n */\nfunction findAfterChecks(rule) {\n  return getAllChecks(rule)\n    .map(c => {\n      const check = rule._audit.checks[c.id || c];\n      return check && typeof check.after === 'function' ? check : null;\n    })\n    .filter(Boolean);\n}\n\n/**\n * Finds and collates all results for a given Check on a specific Rule\n * @private\n * @param  {Array} nodes RuleResult#nodes; array of 'detail' objects\n * @param  {String} checkID The ID of the Check to find\n * @return {Array}         Matching CheckResults\n */\nfunction findCheckResults(nodes, checkID) {\n  const checkResults = [];\n  nodes.forEach(nodeResult => {\n    const checks = getAllChecks(nodeResult);\n    checks.forEach(checkResult => {\n      if (checkResult.id === checkID) {\n        checkResult.node = nodeResult.node;\n        checkResults.push(checkResult);\n      }\n    });\n  });\n  return checkResults;\n}\n\nfunction filterChecks(checks) {\n  return checks.filter(check => {\n    return check.filtered !== true;\n  });\n}\n\nfunction sanitizeNodes(result) {\n  const checkTypes = ['any', 'all', 'none'];\n\n  let nodes = result.nodes.filter(detail => {\n    let length = 0;\n    checkTypes.forEach(type => {\n      detail[type] = filterChecks(detail[type]);\n      length += detail[type].length;\n    });\n    return length > 0;\n  });\n\n  if (result.pageLevel && nodes.length) {\n    nodes = [\n      nodes.reduce((a, b) => {\n        if (a) {\n          checkTypes.forEach(type => {\n            a[type].push.apply(a[type], b[type]);\n          });\n          return a;\n        }\n      })\n    ];\n  }\n  return nodes;\n}\n\n/**\n * Runs all of the Rule's Check#after methods\n * @param  {RuleResult} result  The \"pre-after\" RuleResult\n * @param  {Mixed} options Options specific to the rule\n * @return {RuleResult}         The RuleResult as filtered by after functions\n */\nRule.prototype.after = function after(result, options) {\n  const afterChecks = findAfterChecks(this);\n  afterChecks.forEach(check => {\n    const beforeResults = findCheckResults(result.nodes, check.id);\n    const checkOption = getCheckOption(check, this.id, options);\n    let afterResults;\n    try {\n      afterResults = check.after(beforeResults, checkOption.options);\n    } catch (error) {\n      throw new RuleError({\n        ruleId: this.id,\n        method: `${check.id}#after`,\n        errorNode: result.nodes?.[0]?.node,\n        error\n      });\n    }\n\n    if (this.reviewOnFail) {\n      afterResults.forEach(checkResult => {\n        const changeAnyAllResults =\n          (this.any.includes(checkResult.id) ||\n            this.all.includes(checkResult.id)) &&\n          checkResult.result === false;\n        const changeNoneResult =\n          this.none.includes(checkResult.id) && checkResult.result === true;\n\n        if (changeAnyAllResults || changeNoneResult) {\n          checkResult.result = undefined;\n        }\n      });\n    }\n\n    beforeResults.forEach(item => {\n      // only add the node property for the check.after so we can\n      // look at which iframe a check result came from, but we don't\n      // want it for the final results object\n      delete item.node;\n      if (afterResults.indexOf(item) === -1) {\n        item.filtered = true;\n      }\n    });\n  });\n\n  result.nodes = sanitizeNodes(result);\n  return result;\n};\n\n/**\n * Reconfigure a rule after it has been added\n * @param {Object} spec - the attributes to be reconfigured\n */\nRule.prototype.configure = function configure(spec) {\n  /*eslint no-eval:0 */\n\n  if (spec.hasOwnProperty('selector')) {\n    this.selector = spec.selector;\n  }\n\n  if (spec.hasOwnProperty('excludeHidden')) {\n    this.excludeHidden =\n      typeof spec.excludeHidden === 'boolean' ? spec.excludeHidden : true;\n  }\n\n  if (spec.hasOwnProperty('enabled')) {\n    this.enabled = typeof spec.enabled === 'boolean' ? spec.enabled : true;\n  }\n\n  if (spec.hasOwnProperty('pageLevel')) {\n    this.pageLevel =\n      typeof spec.pageLevel === 'boolean' ? spec.pageLevel : false;\n  }\n\n  if (spec.hasOwnProperty('reviewOnFail')) {\n    this.reviewOnFail =\n      typeof spec.reviewOnFail === 'boolean' ? spec.reviewOnFail : false;\n  }\n\n  if (spec.hasOwnProperty('any')) {\n    this.any = spec.any;\n  }\n\n  if (spec.hasOwnProperty('all')) {\n    this.all = spec.all;\n  }\n\n  if (spec.hasOwnProperty('none')) {\n    this.none = spec.none;\n  }\n\n  if (spec.hasOwnProperty('tags')) {\n    this.tags = spec.tags;\n  }\n\n  if (spec.hasOwnProperty('actIds')) {\n    this.actIds = spec.actIds;\n  }\n\n  if (spec.hasOwnProperty('matches')) {\n    this.matches = createExecutionContext(spec.matches);\n  }\n\n  if (spec.impact) {\n    assert(\n      constants.impact.includes(spec.impact),\n      `Impact ${spec.impact} is not a valid impact`\n    );\n    this.impact = spec.impact;\n  }\n};\n"
  },
  {
    "path": "lib/core/base/virtual-node/abstract-virtual-node.js",
    "content": "const whitespaceRegex = /[\\t\\r\\n\\f]/g;\n\nclass AbstractVirtualNode {\n  constructor() {\n    this.parent = undefined;\n  }\n\n  get props() {\n    throw new Error(\n      'VirtualNode class must have a \"props\" object consisting ' +\n        'of \"nodeType\" and \"nodeName\" properties'\n    );\n  }\n\n  get attrNames() {\n    throw new Error('VirtualNode class must have an \"attrNames\" property');\n  }\n\n  attr() {\n    throw new Error('VirtualNode class must have an \"attr\" function');\n  }\n\n  hasAttr() {\n    throw new Error('VirtualNode class must have a \"hasAttr\" function');\n  }\n\n  hasClass(className) {\n    // get the value of the class attribute as svgs return a SVGAnimatedString\n    // if you access the className property\n    const classAttr = this.attr('class');\n    if (!classAttr) {\n      return false;\n    }\n\n    const selector = ' ' + className + ' ';\n    return (\n      (' ' + classAttr + ' ').replace(whitespaceRegex, ' ').indexOf(selector) >=\n      0\n    );\n  }\n}\n\nexport default AbstractVirtualNode;\n"
  },
  {
    "path": "lib/core/base/virtual-node/serial-virtual-node.js",
    "content": "import AbstractVirtualNode from './abstract-virtual-node';\nimport { assert, validInputTypes } from '../../utils';\n\nclass SerialVirtualNode extends AbstractVirtualNode {\n  /**\n   * Convert a serialised node into a VirtualNode object\n   * @param {Object} node Serialised node\n   */\n  constructor(serialNode) {\n    super();\n    this._props = normaliseProps(serialNode);\n    this._attrs = normaliseAttrs(serialNode);\n  }\n\n  // Accessof for DOM node properties\n  get props() {\n    return this._props;\n  }\n\n  /**\n   * Get the value of the given attribute name.\n   * @param {String} attrName The name of the attribute.\n   * @return {String|null} The value of the attribute or null if the attribute does not exist\n   */\n  attr(attrName) {\n    return this._attrs[attrName] ?? null;\n  }\n\n  /**\n   * Determine if the element has the given attribute.\n   * @param {String} attrName The name of the attribute\n   * @return {Boolean} True if the element has the attribute, false otherwise.\n   */\n  hasAttr(attrName) {\n    return this._attrs[attrName] !== undefined;\n  }\n\n  /**\n   * Return a list of attribute names for the element.\n   * @return {String[]}\n   */\n  get attrNames() {\n    return Object.keys(this._attrs);\n  }\n}\n\nconst nodeNamesToTypes = {\n  '#cdata-section': 2,\n  '#text': 3,\n  '#comment': 8,\n  '#document': 9,\n  '#document-fragment': 11\n};\nconst nodeTypeToName = {};\nconst nodeNames = Object.keys(nodeNamesToTypes);\nnodeNames.forEach(nodeName => {\n  nodeTypeToName[nodeNamesToTypes[nodeName]] = nodeName;\n});\n\n/**\n * Convert between serialised props and DOM-like properties\n * @param {Object} serialNode\n * @return {Object} normalProperties\n */\nfunction normaliseProps(serialNode) {\n  let nodeName = serialNode.nodeName ?? nodeTypeToName[serialNode.nodeType];\n  const nodeType =\n    serialNode.nodeType ?? nodeNamesToTypes[serialNode.nodeName] ?? 1;\n\n  assert(\n    typeof nodeType === 'number',\n    `nodeType has to be a number, got '${nodeType}'`\n  );\n  assert(\n    typeof nodeName === 'string',\n    `nodeName has to be a string, got '${nodeName}'`\n  );\n\n  nodeName = nodeName.toLowerCase();\n  let type = null;\n  if (nodeName === 'input') {\n    type = (\n      serialNode.type ||\n      (serialNode.attributes && serialNode.attributes.type) ||\n      ''\n    ).toLowerCase();\n\n    if (!validInputTypes().includes(type)) {\n      type = 'text';\n    }\n  }\n\n  const props = {\n    ...serialNode,\n    nodeType,\n    nodeName\n  };\n  if (type) {\n    props.type = type;\n  }\n\n  delete props.attributes;\n  return Object.freeze(props);\n}\n\n/**\n * Convert between serialised attributes and DOM-like attributes\n * @param {Object} serialNode\n * @return {Object} normalAttributes\n */\nfunction normaliseAttrs({ attributes = {} }) {\n  const attrMap = {\n    htmlFor: 'for',\n    className: 'class'\n  };\n\n  return Object.keys(attributes).reduce((attrs, attrName) => {\n    const value = attributes[attrName];\n    assert(\n      typeof value !== 'object' || value === null,\n      `expects attributes not to be an object, '${attrName}' was`\n    );\n\n    if (value !== undefined) {\n      const mappedName = attrMap[attrName] || attrName;\n      attrs[mappedName] = value !== null ? String(value) : null;\n    }\n    return attrs;\n  }, {});\n}\n\nexport default SerialVirtualNode;\n"
  },
  {
    "path": "lib/core/base/virtual-node/virtual-node.js",
    "content": "import AbstractVirtualNode from './abstract-virtual-node';\nimport { isXHTML, validInputTypes } from '../../utils';\nimport { isFocusable, getTabbableElements } from '../../../commons/dom';\nimport cache from '../cache';\n\nlet nodeIndex = 0;\n\nclass VirtualNode extends AbstractVirtualNode {\n  /**\n   * Wrap the real node and provide list of the flattened children\n   * @param {Node} node the node in question\n   * @param {VirtualNode} parent The parent VirtualNode\n   * @param {String} shadowId the ID of the shadow DOM to which this node belongs\n   */\n  constructor(node, parent, shadowId) {\n    super();\n    this.shadowId = shadowId;\n    this.children = [];\n    this.actualNode = node;\n    this.parent = parent;\n\n    if (!parent) {\n      nodeIndex = 0;\n    }\n    this.nodeIndex = nodeIndex++;\n\n    this._isHidden = null; // will be populated by axe.utils.isHidden\n    this._cache = {};\n    this._isXHTML = isXHTML(node.ownerDocument);\n\n    // we will normalize the type prop for inputs by looking strictly\n    // at the attribute and not what the browser resolves the type\n    // to be\n    if (node.nodeName.toLowerCase() === 'input') {\n      let type = node.getAttribute('type');\n      type = this._isXHTML ? type : (type || '').toLowerCase();\n\n      if (!validInputTypes().includes(type)) {\n        type = 'text';\n      }\n\n      this._type = type;\n    }\n\n    if (cache.get('nodeMap')) {\n      cache.get('nodeMap').set(node, this);\n    }\n  }\n\n  // abstract Node properties so we can run axe in DOM-less environments.\n  // add to the prototype so memory is shared across all virtual nodes\n  get props() {\n    if (!this._cache.hasOwnProperty('props')) {\n      const { nodeType, nodeName, id, nodeValue } = this.actualNode;\n\n      this._cache.props = {\n        nodeType,\n        nodeName: this._isXHTML ? nodeName : nodeName.toLowerCase(),\n        id,\n        type: this._type,\n        nodeValue\n      };\n\n      // We avoid reading these on node types where they won't be relevant\n      // to work around issues like #4316.\n      if (nodeType === 1) {\n        this._cache.props.multiple = this.actualNode.multiple;\n        this._cache.props.value = this.actualNode.value;\n        this._cache.props.selected = this.actualNode.selected;\n        this._cache.props.checked = this.actualNode.checked;\n        this._cache.props.indeterminate = this.actualNode.indeterminate;\n      }\n    }\n\n    return this._cache.props;\n  }\n\n  /**\n   * Get the value of the given attribute name.\n   * @param {String} attrName The name of the attribute.\n   * @return {String|null} The value of the attribute or null if the attribute does not exist\n   */\n  attr(attrName) {\n    if (typeof this.actualNode.getAttribute !== 'function') {\n      return null;\n    }\n\n    return this.actualNode.getAttribute(attrName);\n  }\n\n  /**\n   * Determine if the element has the given attribute.\n   * @param {String} attrName The name of the attribute\n   * @return {Boolean} True if the element has the attribute, false otherwise.\n   */\n  hasAttr(attrName) {\n    if (typeof this.actualNode.hasAttribute !== 'function') {\n      return false;\n    }\n\n    return this.actualNode.hasAttribute(attrName);\n  }\n\n  /**\n   * Return a list of attribute names for the element.\n   * @return {String[]}\n   */\n  get attrNames() {\n    if (!this._cache.hasOwnProperty('attrNames')) {\n      let attrs;\n\n      // eslint-disable-next-line no-restricted-syntax\n      if (this.actualNode.attributes instanceof window.NamedNodeMap) {\n        // eslint-disable-next-line no-restricted-syntax\n        attrs = this.actualNode.attributes;\n      }\n      // if the attributes property is not of type NamedNodeMap\n      // then the DOM has been clobbered. E.g. <form><input name=\"attributes\"></form>.\n      // We can clone the node to isolate it and then return\n      // the attributes\n      else {\n        attrs = this.actualNode.cloneNode(false).attributes;\n      }\n\n      this._cache.attrNames = Array.from(attrs).map(attr => attr.name);\n    }\n    return this._cache.attrNames;\n  }\n\n  /**\n   * Return a property of the computed style for this element and cache the result. This is much faster than called `getPropteryValue` every time.\n   * @see https://jsperf.com/get-property-value\n   * @return {String}\n   */\n  getComputedStylePropertyValue(property) {\n    const key = 'computedStyle_' + property;\n    if (!this._cache.hasOwnProperty(key)) {\n      if (!this._cache.hasOwnProperty('computedStyle')) {\n        this._cache.computedStyle = window.getComputedStyle(this.actualNode);\n      }\n\n      this._cache[key] = this._cache.computedStyle.getPropertyValue(property);\n    }\n    return this._cache[key];\n  }\n\n  /**\n   * Determine if the element is focusable and cache the result.\n   * @return {Boolean} True if the element is focusable, false otherwise.\n   */\n  get isFocusable() {\n    if (!this._cache.hasOwnProperty('isFocusable')) {\n      this._cache.isFocusable = isFocusable(this.actualNode);\n    }\n    return this._cache.isFocusable;\n  }\n\n  /**\n   * Return the list of tabbable elements for this element and cache the result.\n   * @return {VirtualNode[]}\n   */\n  get tabbableElements() {\n    if (!this._cache.hasOwnProperty('tabbableElements')) {\n      this._cache.tabbableElements = getTabbableElements(this);\n    }\n    return this._cache.tabbableElements;\n  }\n\n  /**\n   * Return the client rects for this element and cache the result.\n   * @return {DOMRect[]}\n   */\n  get clientRects() {\n    if (!this._cache.hasOwnProperty('clientRects')) {\n      this._cache.clientRects = Array.from(\n        this.actualNode.getClientRects()\n      ).filter(rect => rect.width > 0);\n    }\n    return this._cache.clientRects;\n  }\n\n  /**\n   * Return the bounding rect for this element and cache the result.\n   * @return {DOMRect}\n   */\n  get boundingClientRect() {\n    if (!this._cache.hasOwnProperty('boundingClientRect')) {\n      this._cache.boundingClientRect = this.actualNode.getBoundingClientRect();\n    }\n    return this._cache.boundingClientRect;\n  }\n}\n\nexport default VirtualNode;\n"
  },
  {
    "path": "lib/core/constants.js",
    "content": "const definitions = [\n  {\n    name: 'NA',\n    value: 'inapplicable',\n    priority: 0,\n    group: 'inapplicable'\n  },\n  {\n    name: 'PASS',\n    value: 'passed',\n    priority: 1,\n    group: 'passes'\n  },\n  {\n    name: 'CANTTELL',\n    value: 'cantTell',\n    priority: 2,\n    group: 'incomplete'\n  },\n  {\n    name: 'FAIL',\n    value: 'failed',\n    priority: 3,\n    group: 'violations'\n  }\n];\n\nconst constants = {\n  helpUrlBase: 'https://dequeuniversity.com/rules/',\n  // Size of a grid square in pixels\n  gridSize: 200,\n  // At a certain point, looping over an array of elements and using .match on them\n  // is slower than just running querySelectorAll again.\n  selectorSimilarFilterLimit: 700,\n  results: [],\n  resultGroups: [],\n  resultGroupMap: {},\n  impact: Object.freeze(['minor', 'moderate', 'serious', 'critical']),\n  preload: Object.freeze({\n    /**\n     * array of supported & preload(able) asset types.\n     */\n    assets: ['cssom', 'media'],\n    /**\n     * timeout value when resolving preload(able) assets\n     */\n    timeout: 10000\n  }),\n  allOrigins: '<unsafe_all_origins>',\n  sameOrigin: '<same_origin>',\n  serializableErrorProps: Object.freeze([\n    'message',\n    'stack',\n    'name',\n    'code',\n    'ruleId',\n    'method'\n  ])\n};\n\ndefinitions.forEach(definition => {\n  const name = definition.name;\n  const value = definition.value;\n  const priority = definition.priority;\n  const group = definition.group;\n\n  constants[name] = value;\n  constants[name + '_PRIO'] = priority;\n  constants[name + '_GROUP'] = group;\n\n  constants.results[priority] = value;\n  constants.resultGroups[priority] = group;\n\n  constants.resultGroupMap[value] = group;\n});\n\n// Freeze everything\nObject.freeze(constants.results);\nObject.freeze(constants.resultGroups);\nObject.freeze(constants.resultGroupMap);\nObject.freeze(constants);\n\nexport default constants;\n"
  },
  {
    "path": "lib/core/core.js",
    "content": "import constants from './constants';\nimport log from './log';\n\nimport AbstractVirtualNode from './base/virtual-node/abstract-virtual-node';\nimport SerialVirtualNode from './base/virtual-node/serial-virtual-node';\nimport VirtualNode from './base/virtual-node/virtual-node';\nimport cache from './base/cache';\n\nimport * as imports from './imports';\n\nimport cleanup from './public/cleanup';\nimport configure from './public/configure';\nimport frameMessenger from './public/frame-messenger';\nimport getRules from './public/get-rules';\nimport load from './public/load';\nimport registerPlugin from './public/plugins';\nimport { hasReporter, getReporter, addReporter } from './public/reporter';\nimport reset from './public/reset';\nimport runRules from './public/run-rules';\nimport runVirtualRule from './public/run-virtual-rule';\nimport run from './public/run';\nimport runPartial from './public/run-partial';\nimport finishRun from './public/finish-run';\nimport setup from './public/setup';\nimport teardown from './public/teardown';\n\nimport naReporter from './reporters/na';\nimport noPassesReporter from './reporters/no-passes';\nimport rawEnvReporter from './reporters/raw-env';\nimport rawReporter from './reporters/raw';\nimport v1Reporter from './reporters/v1';\nimport v2Reporter from './reporters/v2';\n\nimport * as commons from '../commons';\nimport * as utils from './utils';\n\nimport _thisWillBeDeletedDoNotUse from './_exposed-for-testing.js';\naxe._thisWillBeDeletedDoNotUse = _thisWillBeDeletedDoNotUse;\n\naxe.constants = constants;\naxe.log = log;\n\naxe.AbstractVirtualNode = AbstractVirtualNode;\naxe.SerialVirtualNode = SerialVirtualNode;\naxe.VirtualNode = VirtualNode;\naxe._cache = cache;\n\naxe.imports = imports;\n\naxe.cleanup = cleanup;\naxe.configure = configure;\naxe.frameMessenger = frameMessenger;\naxe.getRules = getRules;\naxe._load = load;\naxe.plugins = {};\naxe.registerPlugin = registerPlugin;\naxe.hasReporter = hasReporter;\naxe.getReporter = getReporter;\naxe.addReporter = addReporter;\naxe.reset = reset;\naxe._runRules = runRules;\naxe.runVirtualRule = runVirtualRule;\naxe.run = run;\naxe.setup = setup;\naxe.teardown = teardown;\naxe.runPartial = runPartial;\naxe.finishRun = finishRun;\n\naxe.commons = commons;\naxe.utils = utils;\n\naxe.addReporter('na', naReporter);\naxe.addReporter('no-passes', noPassesReporter);\naxe.addReporter('rawEnv', rawEnvReporter);\naxe.addReporter('raw', rawReporter);\naxe.addReporter('v1', v1Reporter);\naxe.addReporter('v2', v2Reporter, true);\n"
  },
  {
    "path": "lib/core/imports/index.js",
    "content": "import './polyfills';\n\n// some of these imports require polyfills to be loaded first\nimport { CssSelectorParser } from 'css-selector-parser';\nimport doT from '@deque/dot';\nimport emojiRegexText from 'emoji-regex';\nimport memoize from 'memoizee';\nimport Color from 'colorjs.io';\nimport ArrayFrom from 'core-js-pure/actual/array/from';\n\n// prevent striping newline characters from strings (e.g. failure\n// summaries). value must be synced with build/configure.js\ndoT.templateSettings.strip = false;\n\n/**\n * Namespace `axe.imports` which holds required external dependencies\n *\n * @namespace imports\n * @memberof axe\n */\nexport {\n  CssSelectorParser,\n  doT,\n  emojiRegexText,\n  memoize,\n  Color as Colorjs,\n  ArrayFrom\n};\n"
  },
  {
    "path": "lib/core/imports/polyfills.js",
    "content": "import es6promise from 'es6-promise';\nimport { Uint32Array } from 'typedarray';\nimport 'weakmap-polyfill';\nimport hasOwn from 'core-js-pure/actual/object/has-own';\nimport values from 'core-js-pure/actual/object/values';\nimport ArrayFrom from 'core-js-pure/actual/array/from';\n\nif (!('hasOwn' in Object)) {\n  Object.hasOwn = hasOwn;\n}\n\nif (!('values' in Object)) {\n  Object.values = values;\n}\n\nif (!('Promise' in window)) {\n  es6promise.polyfill();\n}\n\nif (!('Uint32Array' in window)) {\n  window.Uint32Array = Uint32Array;\n}\nif (window.Uint32Array) {\n  // @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/some\n  if (!('some' in window.Uint32Array.prototype)) {\n    Object.defineProperty(window.Uint32Array.prototype, 'some', {\n      value: Array.prototype.some\n    });\n  }\n\n  if (!('reduce' in window.Uint32Array.prototype)) {\n    // @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/reduce\n    Object.defineProperty(window.Uint32Array.prototype, 'reduce', {\n      value: Array.prototype.reduce\n    });\n  }\n}\n\n/*\n These polyfills came directly from the ES Specification itself\n Contained within:\n  - Object.assign\n  - Array.prototype.find\n*/\nif (typeof Object.assign !== 'function') {\n  (function () {\n    Object.assign = function (target) {\n      if (target === undefined || target === null) {\n        throw new TypeError('Cannot convert undefined or null to object');\n      }\n\n      let output = Object(target);\n      for (let index = 1; index < arguments.length; index++) {\n        let source = arguments[index];\n        if (source !== undefined && source !== null) {\n          for (let nextKey in source) {\n            if (source.hasOwnProperty(nextKey)) {\n              output[nextKey] = source[nextKey];\n            }\n          }\n        }\n      }\n      return output;\n    };\n  })();\n}\n\nif (!Array.prototype.find) {\n  Object.defineProperty(Array.prototype, 'find', {\n    value: function (predicate) {\n      if (this === null) {\n        throw new TypeError('Array.prototype.find called on null or undefined');\n      }\n      if (typeof predicate !== 'function') {\n        throw new TypeError('predicate must be a function');\n      }\n      let list = Object(this);\n      let length = list.length >>> 0;\n      let thisArg = arguments[1];\n      let value;\n\n      for (let i = 0; i < length; i++) {\n        value = list[i];\n        if (predicate.call(thisArg, value, i, list)) {\n          return value;\n        }\n      }\n      return undefined;\n    }\n  });\n}\n\nif (!Array.prototype.findIndex) {\n  Object.defineProperty(Array.prototype, 'findIndex', {\n    value: function (predicate, thisArg) {\n      if (this === null) {\n        throw new TypeError('Array.prototype.find called on null or undefined');\n      }\n      if (typeof predicate !== 'function') {\n        throw new TypeError('predicate must be a function');\n      }\n      let list = Object(this);\n      let length = list.length >>> 0;\n      let value;\n\n      for (let i = 0; i < length; i++) {\n        value = list[i];\n        if (predicate.call(thisArg, value, i, list)) {\n          return i;\n        }\n      }\n      return -1;\n    }\n  });\n}\n\nif (!Array.prototype.includes) {\n  Object.defineProperty(Array.prototype, 'includes', {\n    value: function (searchElement) {\n      let O = Object(this);\n      let len = parseInt(O.length, 10) || 0;\n      if (len === 0) {\n        return false;\n      }\n      let n = parseInt(arguments[1], 10) || 0;\n      let k;\n      if (n >= 0) {\n        k = n;\n      } else {\n        k = len + n;\n        if (k < 0) {\n          k = 0;\n        }\n      }\n      let currentElement;\n      while (k < len) {\n        currentElement = O[k];\n        if (\n          searchElement === currentElement ||\n          (searchElement !== searchElement && currentElement !== currentElement)\n        ) {\n          // NaN !== NaN\n          return true;\n        }\n        k++;\n      }\n      return false;\n    }\n  });\n}\n\n// Production steps of ECMA-262, Edition 5, 15.4.4.17\n// Reference: http://es5.github.io/#x15.4.4.17\nif (!Array.prototype.some) {\n  Object.defineProperty(Array.prototype, 'some', {\n    value: function (fun) {\n      if (this == null) {\n        throw new TypeError('Array.prototype.some called on null or undefined');\n      }\n\n      if (typeof fun !== 'function') {\n        throw new TypeError();\n      }\n\n      let t = Object(this);\n      let len = t.length >>> 0;\n\n      let thisArg = arguments.length >= 2 ? arguments[1] : void 0;\n      for (let i = 0; i < len; i++) {\n        if (i in t && fun.call(thisArg, t[i], i, t)) {\n          return true;\n        }\n      }\n\n      return false;\n    }\n  });\n}\n\nif (!Array.from) {\n  Array.from = ArrayFrom;\n}\n\nif (!String.prototype.includes) {\n  String.prototype.includes = function (search, start) {\n    if (typeof start !== 'number') {\n      start = 0;\n    }\n    if (start + search.length > this.length) {\n      return false;\n    } else {\n      return this.indexOf(search, start) !== -1;\n    }\n  };\n}\n\n// @see https://github.com/jonathantneal/array-flat-polyfill/blob/master/src/polyfill-flat.js\nif (!Array.prototype.flat) {\n  Object.defineProperty(Array.prototype, 'flat', {\n    configurable: true,\n    value: function flat() {\n      let depth = isNaN(arguments[0]) ? 1 : Number(arguments[0]);\n\n      return depth\n        ? Array.prototype.reduce.call(\n            this,\n            function (acc, cur) {\n              if (Array.isArray(cur)) {\n                acc.push.apply(acc, flat.call(cur, depth - 1));\n              } else {\n                acc.push(cur);\n              }\n\n              return acc;\n            },\n            []\n          )\n        : Array.prototype.slice.call(this);\n    },\n    writable: true\n  });\n}\n\n// linked from MDN docs on isConnected\n// @see https://gist.github.com/eligrey/f109a6d0bf4efe3461201c3d7b745e8f\nif (window.Node && !('isConnected' in window.Node.prototype)) {\n  Object.defineProperty(window.Node.prototype, 'isConnected', {\n    get() {\n      return (\n        !this.ownerDocument ||\n        !(\n          this.ownerDocument.compareDocumentPosition(this) &\n          this.DOCUMENT_POSITION_DISCONNECTED\n        )\n      );\n    }\n  });\n}\n"
  },
  {
    "path": "lib/core/index.js",
    "content": "/*exported axe, commons */\n/*global axeFunction, module, define */\n// exported namespace for axe\n/*eslint no-use-before-define: 0, no-unused-vars: 0*/\nconst axe = axe || {};\naxe.version = '<%= pkg.version %>';\n\nif (typeof define === 'function' && define.amd) {\n  // Explicitly naming the module to avoid mismatched anonymous define() modules when injected in a page\n  define('axe-core', [], () => axe);\n}\nif (\n  typeof module === 'object' &&\n  module.exports &&\n  typeof axeFunction.toString === 'function'\n) {\n  axe.source =\n    '(' +\n    axeFunction.toString() +\n    ')(typeof window === \"object\" ? window : this);';\n  module.exports = axe;\n}\nif (typeof window.getComputedStyle === 'function') {\n  window.axe = axe;\n}\n// local namespace for common functions\nlet commons;\n"
  },
  {
    "path": "lib/core/log.js",
    "content": "/*eslint no-console: 0 */\n\n/**\n * Logs a message to the developer console (if it exists and is active).\n */\nfunction log() {\n  if (typeof console === 'object' && console.log) {\n    // IE does not support console.log.apply\n    Function.prototype.apply.call(console.log, console, arguments);\n  }\n}\n\nexport default log;\n"
  },
  {
    "path": "lib/core/public/cleanup.js",
    "content": "function cleanup(resolve, reject) {\n  resolve = resolve || function res() {};\n  reject = reject || axe.log;\n\n  if (!axe._audit) {\n    throw new Error('No audit configured');\n  }\n\n  const q = axe.utils.queue();\n  // If a plugin fails it's cleanup, we still want the others to run\n  const cleanupErrors = [];\n\n  Object.keys(axe.plugins).forEach(key => {\n    q.defer(res => {\n      const rej = function rej(err) {\n        cleanupErrors.push(err);\n        res();\n      };\n      try {\n        axe.plugins[key].cleanup(res, rej);\n      } catch (err) {\n        rej(err);\n      }\n    });\n  });\n\n  const flattenedTree = axe.utils.getFlattenedTree(document.body);\n\n  axe.utils.querySelectorAll(flattenedTree, 'iframe, frame').forEach(node => {\n    q.defer((res, rej) => {\n      return axe.utils.sendCommandToFrame(\n        node.actualNode,\n        {\n          command: 'cleanup-plugin'\n        },\n        res,\n        rej\n      );\n    });\n  });\n\n  q.then(results => {\n    if (cleanupErrors.length === 0) {\n      resolve(results);\n    } else {\n      reject(cleanupErrors);\n    }\n  }).catch(reject);\n}\n\nexport default cleanup;\n"
  },
  {
    "path": "lib/core/public/configure.js",
    "content": "import { hasReporter } from './reporter';\nimport { configureStandards } from '../../standards';\nimport constants from '../constants';\n\nfunction configure(spec) {\n  const audit = axe._audit;\n\n  if (!audit) {\n    throw new Error('No audit configured');\n  }\n\n  if (spec.axeVersion || spec.ver) {\n    const specVersion = spec.axeVersion || spec.ver;\n    if (!/^\\d+\\.\\d+\\.\\d+(-canary)?/.test(specVersion)) {\n      throw new Error(`Invalid configured version ${specVersion}`);\n    }\n\n    const [version, canary] = specVersion.split('-');\n    const [major, minor, patch] = version.split('.').map(Number);\n\n    const [axeVersion, axeCanary] = axe.version.split('-');\n    const [axeMajor, axeMinor, axePatch] = axeVersion.split('.').map(Number);\n\n    if (\n      major !== axeMajor ||\n      axeMinor < minor ||\n      (axeMinor === minor && axePatch < patch) ||\n      (major === axeMajor &&\n        minor === axeMinor &&\n        patch === axePatch &&\n        canary &&\n        canary !== axeCanary)\n    ) {\n      throw new Error(\n        `Configured version ${specVersion} is not compatible with current axe version ${axe.version}`\n      );\n    }\n  }\n\n  if (\n    spec.reporter &&\n    (typeof spec.reporter === 'function' || hasReporter(spec.reporter))\n  ) {\n    audit.reporter = spec.reporter;\n  }\n\n  if (spec.checks) {\n    if (!Array.isArray(spec.checks)) {\n      throw new TypeError('Checks property must be an array');\n    }\n\n    spec.checks.forEach(check => {\n      if (!check.id) {\n        throw new TypeError(\n          // eslint-disable-next-line max-len\n          `Configured check ${JSON.stringify(\n            check\n          )} is invalid. Checks must be an object with at least an id property`\n        );\n      }\n\n      audit.addCheck(check);\n    });\n  }\n\n  const modifiedRules = [];\n  if (spec.rules) {\n    if (!Array.isArray(spec.rules)) {\n      throw new TypeError('Rules property must be an array');\n    }\n\n    spec.rules.forEach(rule => {\n      if (!rule.id) {\n        throw new TypeError(\n          // eslint-disable-next-line max-len\n          `Configured rule ${JSON.stringify(\n            rule\n          )} is invalid. Rules must be an object with at least an id property`\n        );\n      }\n\n      modifiedRules.push(rule.id);\n      audit.addRule(rule);\n    });\n  }\n\n  if (spec.disableOtherRules) {\n    audit.rules.forEach(rule => {\n      if (modifiedRules.includes(rule.id) === false) {\n        rule.enabled = false;\n      }\n    });\n  }\n\n  if (typeof spec.branding !== 'undefined') {\n    audit.setBranding(spec.branding);\n  } else {\n    audit._constructHelpUrls();\n  }\n\n  if (spec.tagExclude) {\n    audit.tagExclude = spec.tagExclude;\n  }\n\n  // Support runtime localization\n  if (spec.locale) {\n    audit.applyLocale(spec.locale);\n  }\n\n  if (spec.standards) {\n    configureStandards(spec.standards);\n  }\n\n  if (spec.noHtml) {\n    audit.noHtml = true;\n  }\n\n  if (spec.allowedOrigins) {\n    if (!Array.isArray(spec.allowedOrigins)) {\n      throw new TypeError('Allowed origins property must be an array');\n    }\n\n    if (spec.allowedOrigins.includes('*')) {\n      throw new Error(\n        `\"*\" is not allowed. Use \"${constants.allOrigins}\" instead`\n      );\n    }\n\n    audit.setAllowedOrigins(spec.allowedOrigins);\n  }\n}\n\nexport default configure;\n"
  },
  {
    "path": "lib/core/public/finish-run.js",
    "content": "import { getReporter } from './reporter';\nimport {\n  mergeResults,\n  publishMetaData,\n  finalizeRuleResult,\n  nodeSerializer,\n  clone,\n  normalizeRunOptions\n} from '../utils';\n\nexport default function finishRun(partialResults, options = {}) {\n  options = clone(options);\n  const { environmentData } = partialResults.find(r => r.environmentData) || {};\n\n  // normalize the runOnly option for the output of reporters toolOptions\n  normalizeRunOptions(options);\n  options.reporter = options.reporter ?? axe._audit?.reporter ?? 'v1';\n\n  setFrameSpec(partialResults);\n  let results = mergeResults(partialResults);\n  results = axe._audit.after(results, options);\n  results.forEach(publishMetaData);\n  results = results.map(finalizeRuleResult);\n\n  return createReport(results, { environmentData, ...options });\n}\n\nfunction setFrameSpec(partialResults) {\n  const frameStack = [];\n  for (const partialResult of partialResults) {\n    const frameSpec = frameStack.shift();\n    if (!partialResult) {\n      continue;\n    }\n\n    partialResult.frameSpec = frameSpec ?? null;\n    const frameSpecs = getMergedFrameSpecs(partialResult);\n    frameStack.unshift(...frameSpecs);\n  }\n}\n\nfunction getMergedFrameSpecs({\n  frames: childFrameSpecs,\n  frameSpec: parentFrameSpec\n}) {\n  if (!parentFrameSpec) {\n    return childFrameSpecs;\n  }\n  // Include the selector/ancestry/... from the parent frames\n  return childFrameSpecs.map(childFrameSpec => {\n    return nodeSerializer.mergeSpecs(childFrameSpec, parentFrameSpec);\n  });\n}\n\nfunction createReport(results, options) {\n  return new Promise((resolve, reject) => {\n    const reporter = getReporter(options.reporter);\n    reporter(results, options, resolve, reject);\n  });\n}\n"
  },
  {
    "path": "lib/core/public/frame-messenger.js",
    "content": "import { respondable } from '../utils';\n\nexport default function frameMessenger(frameHandler) {\n  respondable.updateMessenger(frameHandler);\n}\n"
  },
  {
    "path": "lib/core/public/get-rules.js",
    "content": "/**\n * Searches and returns rules that contain a tag in the list of tags.\n * @param  {Array}   tags  Optional array of tags\n * @return {Array}  Array of rules\n */\nfunction getRules(tags) {\n  tags = tags || [];\n  const matchingRules = !tags.length\n    ? axe._audit.rules\n    : axe._audit.rules.filter(item => {\n        return !!tags.filter(tag => {\n          return item.tags.indexOf(tag) !== -1;\n        }).length;\n      });\n\n  const ruleData = axe._audit.data.rules || {};\n  return matchingRules.map(matchingRule => {\n    const rd = ruleData[matchingRule.id] || {};\n    return {\n      ruleId: matchingRule.id,\n      description: rd.description,\n      help: rd.help,\n      helpUrl: rd.helpUrl,\n      tags: matchingRule.tags,\n      actIds: matchingRule.actIds\n    };\n  });\n}\n\nexport default getRules;\n"
  },
  {
    "path": "lib/core/public/load.js",
    "content": "import Audit from '../base/audit';\nimport cleanup from './cleanup';\nimport runRules from './run-rules';\nimport respondable from '../utils/respondable';\nimport nodeSerializer from '../utils/node-serializer';\n\n/**\n * Sets up Rules, Messages and default options for Checks, must be invoked before attempting analysis\n * @param  {Object} audit The \"audit specification\" object\n * @private\n */\nexport default function load(audit) {\n  axe._audit = new Audit(audit);\n}\n\nfunction runCommand(data, keepalive, callback) {\n  const resolve = callback;\n  const reject = function reject(err) {\n    if (err instanceof Error === false) {\n      err = new Error(err);\n    }\n    callback(err);\n  };\n\n  const context = (data && data.context) || {};\n  if (context.hasOwnProperty('include') && !context.include.length) {\n    context.include = [document];\n  }\n  const options = (data && data.options) || {};\n\n  switch (data.command) {\n    case 'rules':\n      return runRules(\n        context,\n        options,\n        (results, cleanupFn) => {\n          // Serialize all DqElements\n          results = nodeSerializer.mapRawResults(results);\n          resolve(results);\n          // Cleanup AFTER resolve so that selectors can be generated\n          cleanupFn();\n        },\n        reject\n      );\n    case 'cleanup-plugin':\n      return cleanup(resolve, reject);\n    default:\n      // go through the registered commands\n      if (\n        axe._audit &&\n        axe._audit.commands &&\n        axe._audit.commands[data.command]\n      ) {\n        return axe._audit.commands[data.command](data, callback);\n      }\n  }\n}\n\nif (window.top !== window) {\n  respondable.subscribe('axe.start', runCommand);\n  respondable.subscribe('axe.ping', (data, keepalive, respond) => {\n    respond({\n      axe: true\n    });\n  });\n}\n"
  },
  {
    "path": "lib/core/public/plugins.js",
    "content": "/*eslint no-use-before-define:0 */\n\nfunction Plugin(spec) {\n  this._run = spec.run;\n  this._collect = spec.collect;\n  this._registry = {};\n  spec.commands.forEach(command => {\n    axe._audit.registerCommand(command);\n  });\n}\n\nPlugin.prototype.run = function run() {\n  return this._run.apply(this, arguments);\n};\n\nPlugin.prototype.collect = function collect() {\n  return this._collect.apply(this, arguments);\n};\n\nPlugin.prototype.cleanup = function cleanup(done) {\n  const q = axe.utils.queue();\n  const that = this;\n  Object.keys(this._registry).forEach(key => {\n    q.defer(_done => {\n      that._registry[key].cleanup(_done);\n    });\n  });\n  q.then(done);\n};\n\nPlugin.prototype.add = function add(impl) {\n  this._registry[impl.id] = impl;\n};\n\nfunction registerPlugin(plugin) {\n  axe.plugins[plugin.id] = new Plugin(plugin);\n}\n\nexport default registerPlugin;\n"
  },
  {
    "path": "lib/core/public/reporter.js",
    "content": "export const reporters = {};\nlet defaultReporter;\n\nexport function hasReporter(reporterName) {\n  return reporters.hasOwnProperty(reporterName);\n}\n\nexport function getReporter(reporter) {\n  if (typeof reporter === 'string' && reporters[reporter]) {\n    return reporters[reporter];\n  }\n\n  if (typeof reporter === 'function') {\n    return reporter;\n  }\n\n  return defaultReporter;\n}\n\nexport function addReporter(name, cb, isDefault) {\n  reporters[name] = cb;\n  if (isDefault) {\n    defaultReporter = cb;\n  }\n}\n"
  },
  {
    "path": "lib/core/public/reset.js",
    "content": "import { resetStandards } from '../../standards';\n\nfunction reset() {\n  const audit = axe._audit;\n\n  if (!audit) {\n    throw new Error('No audit configured');\n  }\n  audit.resetRulesAndChecks();\n  resetStandards();\n}\n\nexport default reset;\n"
  },
  {
    "path": "lib/core/public/run/globals-setup.js",
    "content": "import cache from '../../base/cache';\n\nexport function setupGlobals(context) {\n  // if window or document are not defined and context was passed in\n  // we can use it to configure them\n  // NOTE: because our polyfills run first, the global window object\n  // always exists but may not have things we expect\n  const hasWindow = window && 'Node' in window && 'NodeList' in window;\n  const hasDoc = !!document;\n  if (hasWindow && hasDoc) {\n    return;\n  }\n  if (!context || !context.ownerDocument) {\n    throw new Error(\n      'Required \"window\" or \"document\" globals not defined and cannot be deduced from the context. ' +\n        'Either set the globals before running or pass in a valid Element.'\n    );\n  }\n\n  if (!hasDoc) {\n    cache.set('globalDocumentSet', true);\n    document = context.ownerDocument;\n  }\n\n  if (!hasWindow) {\n    cache.set('globalWindowSet', true);\n    window = document.defaultView;\n  }\n}\n\nexport function resetGlobals() {\n  if (cache.get('globalDocumentSet')) {\n    cache.set('globalDocumentSet', false);\n    document = null;\n  }\n  if (cache.get('globalWindowSet')) {\n    cache.set('globalWindowSet', false);\n    window = null;\n  }\n}\n"
  },
  {
    "path": "lib/core/public/run/normalize-run-params.js",
    "content": "import { clone, isContextSpec } from '../../utils';\n\n/**\n * Normalize the optional params of axe.run()\n * @param  {object}   context\n * @param  {object}   options\n * @param  {Function} callback\n * @return {object}            With 3 keys: context, options, callback\n */\nexport default function normalizeRunParams([context, options, callback]) {\n  const typeErr = new TypeError('axe.run arguments are invalid');\n\n  // Determine the context\n  if (!isContextSpec(context)) {\n    if (callback !== undefined) {\n      // Either context is invalid or there are too many params\n      throw typeErr;\n    }\n    // Set default and shift one over\n    callback = options;\n    options = context;\n    context = document;\n  }\n\n  // Determine the options\n  if (typeof options !== 'object') {\n    if (callback !== undefined) {\n      // Either options is invalid or there are too many params\n      throw typeErr;\n    }\n    // Set default and shift one over\n    callback = options;\n    options = {};\n  }\n\n  // Set the callback or noop;\n  if (typeof callback !== 'function' && callback !== undefined) {\n    throw typeErr;\n  }\n\n  options = clone(options);\n  options.reporter = options.reporter ?? axe._audit?.reporter ?? 'v1';\n  return { context, options, callback };\n}\n"
  },
  {
    "path": "lib/core/public/run-partial.js",
    "content": "import Context from '../base/context';\nimport teardown from './teardown';\nimport {\n  nodeSerializer,\n  getSelectorData,\n  assert,\n  getEnvironmentData\n} from '../utils';\nimport normalizeRunParams from './run/normalize-run-params';\n\nexport default function runPartial(...args) {\n  const { options, context } = normalizeRunParams(args);\n  assert(axe._audit, 'Axe is not configured. Audit is missing.');\n  assert(\n    !axe._running,\n    'Axe is already running. Use `await axe.run()` to wait ' +\n      'for the previous run to finish before starting a new run.'\n  );\n\n  const contextObj = new Context(context, axe._tree);\n  axe._tree = contextObj.flatTree;\n  axe._selectorData = getSelectorData(contextObj.flatTree);\n  axe._running = true;\n\n  // Even in the top frame, we don't support this with runPartial\n  options.elementRef = false;\n\n  return (\n    new Promise((res, rej) => {\n      axe._audit.run(contextObj, options, res, rej);\n    })\n      .then(results => {\n        results = nodeSerializer.mapRawResults(results);\n        const frames = contextObj.frames.map(({ node }) => {\n          return nodeSerializer.toSpec(node);\n        });\n        let environmentData;\n        if (contextObj.initiator) {\n          environmentData = getEnvironmentData();\n        }\n        axe._running = false;\n        teardown();\n        return { results, frames, environmentData };\n      })\n      // Avoid .finally() to deal with Mocha 9 + IE issues\n      .catch(err => {\n        axe._running = false;\n        teardown();\n        return Promise.reject(err);\n      })\n  );\n}\n"
  },
  {
    "path": "lib/core/public/run-rules.js",
    "content": "import Context from '../base/context';\nimport teardown from './teardown';\nimport {\n  getSelectorData,\n  queue,\n  performanceTimer,\n  collectResultsFromFrames,\n  mergeResults,\n  publishMetaData,\n  finalizeRuleResult\n} from '../utils';\nimport log from '../log';\n\n/**\n * Starts analysis on the current document and its subframes\n * @private\n * @param  {Object}   context  The `Context` specification object @see Context\n * @param  {Array}    options  Optional RuleOptions\n * @param  {Function} resolve  Called when done running rules, receives ([results : Object], teardown : Function)\n * @param  {Function} reject   Called when execution failed, receives (err : Error)\n */\nexport default function runRules(context, options, resolve, reject) {\n  try {\n    context = new Context(context);\n    axe._tree = context.flatTree;\n    axe._selectorData = getSelectorData(context.flatTree);\n  } catch (e) {\n    teardown();\n    return reject(e);\n  }\n\n  const q = queue();\n  const audit = axe._audit;\n\n  if (options.performanceTimer) {\n    performanceTimer.auditStart();\n  }\n\n  if (context.frames.length && options.iframes !== false) {\n    q.defer((res, rej) => {\n      collectResultsFromFrames(context, options, 'rules', null, res, rej);\n    });\n  }\n  q.defer((res, rej) => {\n    audit.run(context, options, res, rej);\n  });\n  q.then(data => {\n    try {\n      if (options.performanceTimer) {\n        performanceTimer.auditEnd();\n      }\n\n      // Add wrapper object so that we may use the same \"merge\" function for results from inside and outside frames\n      let results = mergeResults(\n        data.map(res => {\n          return { results: res };\n        })\n      );\n\n      // after should only run once, so ensure we are in the top level window\n      if (context.initiator) {\n        if (options.performanceTimer) {\n          performanceTimer.mark('auditAfterStart');\n        }\n        results = audit.after(results, options);\n        if (options.performanceTimer) {\n          performanceTimer.mark('auditAfterEnd');\n          performanceTimer.measure(\n            'audit.after',\n            'auditAfterStart',\n            'auditAfterEnd'\n          );\n          performanceTimer.logMeasures('audit.after');\n        }\n        results.forEach(publishMetaData);\n        results = results.map(finalizeRuleResult);\n      }\n      try {\n        resolve(results, teardown);\n      } catch (e) {\n        teardown();\n        log(e);\n      }\n    } catch (e) {\n      teardown();\n      reject(e);\n    }\n  }).catch(e => {\n    teardown();\n    reject(e);\n  });\n}\n"
  },
  {
    "path": "lib/core/public/run-virtual-rule.js",
    "content": "import SerialVirtualNode from '../base/virtual-node/serial-virtual-node';\nimport AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node';\nimport * as helpers from '../reporters/helpers';\nimport {\n  publishMetaData,\n  finalizeRuleResult,\n  aggregateResult,\n  getEnvironmentData,\n  getRule\n} from '../utils';\n\n/**\n * Run a rule in a non-browser environment\n * @param {String} ruleId  Id of the rule\n * @param {VirtualNode} vNode  The virtual node to run the rule against\n * @param {Object} options  (optional) Set of options passed into rules or checks\n * @return {Object} axe results for the rule run\n */\nexport default function runVirtualRule(ruleId, vNode, options = {}) {\n  // TODO: es-modules axe._audit\n  // TODO: es-modules axe._selectorData\n  options.reporter = options.reporter || axe._audit.reporter || 'v1';\n  axe._selectorData = {};\n\n  if (!(vNode instanceof AbstractVirtualNode)) {\n    vNode = new SerialVirtualNode(vNode);\n  }\n\n  let rule = getRule(ruleId);\n\n  if (!rule) {\n    throw new Error('unknown rule `' + ruleId + '`');\n  }\n\n  // rule.prototype.gather calls axe.utils.isHidden which in turn calls\n  // window.getComputedStyle if the rule excludes hidden elements. we\n  // can avoid this call by forcing the rule to not exclude hidden\n  // elements\n  rule = Object.create(rule, { excludeHidden: { value: false } });\n  const context = {\n    initiator: true,\n    include: [vNode],\n    exclude: [],\n    frames: [],\n    page: false,\n    focusable: true,\n    size: {},\n    flatTree: []\n  };\n\n  const rawResults = rule.runSync(context, options);\n  publishMetaData(rawResults);\n  finalizeRuleResult(rawResults);\n  const results = aggregateResult([rawResults]);\n\n  results.violations.forEach(result =>\n    result.nodes.forEach(nodeResult => {\n      nodeResult.failureSummary = helpers.failureSummary(nodeResult);\n    })\n  );\n\n  return {\n    ...getEnvironmentData(),\n    ...results,\n    toolOptions: options\n  };\n}\n"
  },
  {
    "path": "lib/core/public/run.js",
    "content": "import { getReporter } from './reporter';\nimport normalizeRunParams from './run/normalize-run-params';\nimport { setupGlobals } from './run/globals-setup';\nimport { assert } from '../utils';\nimport performanceTimer from '../utils/performance-timer';\n\nconst noop = () => {};\n\n/**\n * Runs a number of rules against the provided HTML page and returns the\n * resulting issue list\n *\n * @param  {Object}   context  (optional) Defines the scope of the analysis\n * @param  {Object}   options  (optional) Set of options passed into rules or checks\n * @param  {Function} callback (optional) The callback when axe is done, given 2 params:\n *                             - Error    If any errors occured, otherwise null\n *                             - Results  The results object / array, or undefined on error\n * @return {Promise}           Resolves with the axe results. Only available when natively supported\n */\nexport default function run(...args) {\n  setupGlobals(args[0]);\n  const { context, options, callback = noop } = normalizeRunParams(args);\n  const { thenable, resolve, reject } = getPromiseHandlers(callback);\n  try {\n    assert(axe._audit, 'No audit configured');\n    assert(\n      !axe._running,\n      'Axe is already running. Use `await axe.run()` to wait ' +\n        'for the previous run to finish before starting a new run.'\n    );\n  } catch (e) {\n    return handleError(e, callback);\n  }\n\n  axe._running = true;\n  if (options.performanceTimer) {\n    performanceTimer.start();\n  }\n\n  function handleRunRules(rawResults, teardown) {\n    const respond = results => {\n      if (options.performanceTimer) {\n        performanceTimer.mark('reporterEnd');\n        performanceTimer.measure('reporter', 'reporterStart', 'reporterEnd');\n        performanceTimer.logMeasures('reporter');\n        performanceTimer.end();\n      }\n      axe._running = false;\n      teardown();\n      try {\n        resolve(results);\n      } catch (e) {\n        axe.log(e);\n      }\n    };\n    const wrappedReject = err => {\n      axe._running = false;\n      teardown();\n      try {\n        reject(err);\n      } catch (e) {\n        axe.log(e);\n      }\n    };\n\n    try {\n      if (options.performanceTimer) {\n        performanceTimer.mark('reporterStart');\n      }\n      createReport(rawResults, options, respond, wrappedReject);\n    } catch (err) {\n      wrappedReject(err);\n    }\n  }\n\n  function errorRunRules(err) {\n    if (options.performanceTimer) {\n      performanceTimer.end();\n    }\n    axe._running = false;\n    callback(err);\n    reject(err);\n  }\n\n  axe._runRules(context, options, handleRunRules, errorRunRules);\n  return thenable;\n}\n\nfunction getPromiseHandlers(callback) {\n  let thenable, reject, resolve;\n  if (typeof Promise === 'function' && callback === noop) {\n    thenable = new Promise((_resolve, _reject) => {\n      reject = _reject;\n      resolve = _resolve;\n    });\n  } else {\n    resolve = result => callback(null, result);\n    reject = err => callback(err);\n  }\n  return { thenable, reject, resolve };\n}\n\nfunction createReport(rawResults, options, respond, reject) {\n  const reporter = getReporter(options.reporter);\n  const results = reporter(rawResults, options, respond, reject);\n  if (results !== undefined) {\n    respond(results);\n  }\n}\n\nfunction handleError(err, callback) {\n  if (typeof callback === 'function' && callback !== noop) {\n    callback(err.message);\n    return;\n  }\n  throw err;\n}\n"
  },
  {
    "path": "lib/core/public/setup.js",
    "content": "import { getFlattenedTree, getSelectorData } from '../utils';\nimport { setupGlobals } from './run/globals-setup';\n\n/**\n * Setup axe-core so axe.common functions can work properly.\n * @param {Node} [node=document.documentElement] optional node. NOTE: passing in anything other than body or the documentElement may result in incomplete results.\n */\nfunction setup(node) {\n  if (axe._tree) {\n    throw new Error(\n      'Axe is already setup. Call `axe.teardown()` before calling `axe.setup` again.'\n    );\n  }\n  // Normalize document\n  if (\n    node &&\n    typeof node.documentElement === 'object' &&\n    typeof node.defaultView === 'object'\n  ) {\n    node = node.documentElement;\n  }\n\n  setupGlobals(node);\n  axe._tree = getFlattenedTree(node);\n  axe._selectorData = getSelectorData(axe._tree);\n\n  return axe._tree[0];\n}\n\nexport default setup;\n"
  },
  {
    "path": "lib/core/public/teardown.js",
    "content": "import cache from '../base/cache';\nimport { resetGlobals } from './run/globals-setup';\n\n/**\n * Clean up axe-core tree and caches. `axe.run` will call this function at the end of the run so there's no need to call it yourself afterwards.\n */\nfunction teardown() {\n  // Reset MUST to happen before the cache is cleared\n  resetGlobals();\n  axe._memoizedFns.forEach(fn => fn.clear());\n  cache.clear();\n  axe._tree = undefined;\n  axe._selectorData = undefined;\n  axe._selectCache = undefined;\n}\n\nexport default teardown;\n"
  },
  {
    "path": "lib/core/reporters/helpers/failure-summary.js",
    "content": "/**\n * Finds failing Checks and combines each help message into an array\n * @param  {Object} nodeData Individual \"detail\" object to generate help messages for\n * @return {String}          failure messages\n */\nfunction failureSummary(nodeData) {\n  const failingChecks = {};\n  // combine \"all\" and \"none\" as messaging is the same\n  failingChecks.none = nodeData.none.concat(nodeData.all);\n  failingChecks.any = nodeData.any;\n\n  return Object.keys(failingChecks)\n    .map(key => {\n      if (!failingChecks[key].length) {\n        return;\n      }\n\n      const sum = axe._audit.data.failureSummaries[key];\n      if (sum && typeof sum.failureMessage === 'function') {\n        return sum.failureMessage(\n          failingChecks[key].map(check => {\n            return check.message || '';\n          })\n        );\n      }\n    })\n    .filter(i => {\n      return i !== undefined;\n    })\n    .join('\\n\\n');\n}\n\nexport default failureSummary;\n"
  },
  {
    "path": "lib/core/reporters/helpers/incomplete-fallback-msg.js",
    "content": "/**\n * Provides a fallback message in case incomplete checks don't provide one\n * This mechanism allows the string to be localized.\n * @return {String}\n */\nexport default function incompleteFallbackMessage() {\n  let { incompleteFallbackMessage: message } = axe._audit.data;\n  if (typeof message === 'function') {\n    message = message();\n  }\n  if (typeof message !== 'string') {\n    return '';\n  }\n  return message;\n}\n"
  },
  {
    "path": "lib/core/reporters/helpers/index.js",
    "content": "import failureSummary from './failure-summary';\nimport incompleteFallbackMessage from './incomplete-fallback-msg';\nimport processAggregate from './process-aggregate';\n\nexport { failureSummary, incompleteFallbackMessage, processAggregate };\n"
  },
  {
    "path": "lib/core/reporters/helpers/process-aggregate.js",
    "content": "import constants from '../../constants';\nimport { nodeSerializer } from '../../utils';\n\nconst resultKeys = constants.resultGroups;\n\n/**\n * Configures the processing of axe results.\n *\n * @typedef ProcessOptions\n * @property {Array} resultsTypes limit the types of results to process ('passes', 'violations', etc.)\n * @property {Boolean} elementRef display node's element references\n * @property {Boolean} selectors display node's selectors\n * @property {Boolean} xpath display node's xpaths\n */\n\n/**\n * Aggregate and process the axe results,\n * adding desired data to nodes and relatedNodes in each rule result.\n *\n * Prepares result data for reporters.\n *\n * @method processAggregate\n * @memberof helpers\n * @param\t{Array} results\n * @param\t{ProcessOptions} options\n * @return {Object}\n *\n */\nexport default function processAggregate(results, options) {\n  const resultObject = axe.utils.aggregateResult(results);\n\n  resultKeys.forEach(key => {\n    if (options.resultTypes && !options.resultTypes.includes(key)) {\n      // If the user asks us to, truncate certain finding types to maximum one finding\n      (resultObject[key] || []).forEach(ruleResult => {\n        if (Array.isArray(ruleResult.nodes) && ruleResult.nodes.length > 0) {\n          ruleResult.nodes = [ruleResult.nodes[0]];\n        }\n      });\n    }\n    resultObject[key] = (resultObject[key] || []).map(ruleResult => {\n      ruleResult = Object.assign({}, ruleResult);\n\n      if (Array.isArray(ruleResult.nodes) && ruleResult.nodes.length > 0) {\n        ruleResult.nodes = ruleResult.nodes.map(subResult => {\n          if (typeof subResult.node === 'object') {\n            const serialElm = trimElementSpec(subResult.node, options);\n            Object.assign(subResult, serialElm);\n          }\n          delete subResult.result;\n          delete subResult.node;\n\n          normalizeRelatedNodes(subResult, options);\n\n          return subResult;\n        });\n      }\n\n      resultKeys.forEach(resultKey => delete ruleResult[resultKey]);\n      delete ruleResult.pageLevel;\n      delete ruleResult.result;\n\n      return ruleResult;\n    });\n  });\n\n  return resultObject;\n}\n\nfunction normalizeRelatedNodes(node, options) {\n  ['any', 'all', 'none'].forEach(type => {\n    if (!Array.isArray(node[type])) {\n      return;\n    }\n    node[type]\n      .filter(checkRes => Array.isArray(checkRes.relatedNodes))\n      .forEach(checkRes => {\n        checkRes.relatedNodes = checkRes.relatedNodes.map(relatedNode => {\n          return trimElementSpec(relatedNode, options);\n        });\n      });\n  });\n}\n\nfunction trimElementSpec(elmSpec = {}, runOptions) {\n  // Pass options to limit which properties are calculated\n  elmSpec = nodeSerializer.dqElmToSpec(elmSpec, runOptions);\n  const serialElm = {};\n  if (axe._audit.noHtml) {\n    serialElm.html = null;\n  } else {\n    serialElm.html = elmSpec.source ?? 'Undefined';\n  }\n  if (runOptions.elementRef && !elmSpec.fromFrame) {\n    serialElm.element = elmSpec.element ?? null;\n  }\n  if (runOptions.selectors !== false || elmSpec.fromFrame) {\n    serialElm.target = elmSpec.selector ?? [':root'];\n  }\n  if (runOptions.ancestry) {\n    serialElm.ancestry = elmSpec.ancestry ?? [':root'];\n  }\n  if (runOptions.xpath) {\n    serialElm.xpath = elmSpec.xpath ?? ['/'];\n  }\n  return serialElm;\n}\n"
  },
  {
    "path": "lib/core/reporters/na.js",
    "content": "import { processAggregate } from './helpers';\nimport { getEnvironmentData } from '../utils';\n\n// @deprecated\nconst naReporter = (results, options, callback) => {\n  console.warn(\n    '\"na\" reporter will be deprecated in axe v4.0. Use the \"v2\" reporter instead.'\n  );\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n\n  const { environmentData, ...toolOptions } = options;\n  callback({\n    ...getEnvironmentData(environmentData),\n    toolOptions,\n    ...processAggregate(results, options)\n  });\n};\n\nexport default naReporter;\n"
  },
  {
    "path": "lib/core/reporters/no-passes.js",
    "content": "import { processAggregate } from './helpers';\nimport { getEnvironmentData } from '../utils';\n\nconst noPassesReporter = (results, options, callback) => {\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n  const { environmentData, ...toolOptions } = options;\n  // limit result processing to types we want to include in the output\n  options.resultTypes = ['violations'];\n\n  const { violations } = processAggregate(results, options);\n\n  callback({\n    ...getEnvironmentData(environmentData),\n    toolOptions,\n    violations\n  });\n};\n\nexport default noPassesReporter;\n"
  },
  {
    "path": "lib/core/reporters/raw-env.js",
    "content": "import { getEnvironmentData } from '../utils';\nimport rawReporter from './raw';\n\nconst rawEnvReporter = (results, options, callback) => {\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n  const { environmentData, ...toolOptions } = options;\n  rawReporter(results, toolOptions, raw => {\n    const env = getEnvironmentData(environmentData);\n    callback({ raw, env });\n  });\n};\n\nexport default rawEnvReporter;\n"
  },
  {
    "path": "lib/core/reporters/raw.js",
    "content": "import { nodeSerializer } from '../utils';\n\nconst rawReporter = (results, options, callback) => {\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n\n  // Guard against tests which don't pass an array as the first param here.\n  if (!results || !Array.isArray(results)) {\n    return callback(results);\n  }\n\n  const transformedResults = results.map(result => {\n    const transformedResult = { ...result };\n    const types = ['passes', 'violations', 'incomplete', 'inapplicable'];\n    for (const type of types) {\n      transformedResult[type] = nodeSerializer.mapRawNodeResults(\n        transformedResult[type]\n      );\n    }\n\n    return transformedResult;\n  });\n\n  callback(transformedResults);\n};\n\nexport default rawReporter;\n"
  },
  {
    "path": "lib/core/reporters/v1.js",
    "content": "import { processAggregate, failureSummary } from './helpers';\nimport { getEnvironmentData } from '../utils';\n\nconst v1Reporter = (results, options, callback) => {\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n  const { environmentData, ...toolOptions } = options;\n  const out = processAggregate(results, options);\n\n  const addFailureSummaries = result => {\n    result.nodes.forEach(nodeResult => {\n      nodeResult.failureSummary = failureSummary(nodeResult);\n    });\n  };\n\n  out.incomplete.forEach(addFailureSummaries);\n  out.violations.forEach(addFailureSummaries);\n\n  callback({\n    ...getEnvironmentData(environmentData),\n    toolOptions,\n    ...out\n  });\n};\n\nexport default v1Reporter;\n"
  },
  {
    "path": "lib/core/reporters/v2.js",
    "content": "import { processAggregate } from './helpers';\nimport { getEnvironmentData } from '../utils';\n\nconst v2Reporter = (results, options, callback) => {\n  if (typeof options === 'function') {\n    callback = options;\n    options = {};\n  }\n  const { environmentData, ...toolOptions } = options;\n  const out = processAggregate(results, options);\n  callback({\n    ...getEnvironmentData(environmentData),\n    toolOptions,\n    ...out\n  });\n};\n\nexport default v2Reporter;\n"
  },
  {
    "path": "lib/core/utils/aggregate-checks.js",
    "content": "import constants from '../constants';\nimport aggregate from './aggregate';\n\nconst { CANTTELL_PRIO, FAIL_PRIO } = constants;\nconst checkMap = [];\ncheckMap[constants.PASS_PRIO] = true;\ncheckMap[constants.CANTTELL_PRIO] = null;\ncheckMap[constants.FAIL_PRIO] = false;\n\n/**\n * Map over the any / all / none properties\n */\nconst checkTypes = ['any', 'all', 'none'];\nfunction anyAllNone(obj, functor) {\n  return checkTypes.reduce((out, type) => {\n    out[type] = (obj[type] || []).map(val => functor(val, type));\n    return out;\n  }, {});\n}\n\nfunction aggregateChecks(nodeResOriginal) {\n  // Create a copy\n  const nodeResult = Object.assign({}, nodeResOriginal);\n\n  // map each result value to a priority\n  anyAllNone(nodeResult, (check, type) => {\n    const i =\n      typeof check.result === 'undefined' ? -1 : checkMap.indexOf(check.result);\n    // default to cantTell\n    check.priority = i !== -1 ? i : constants.CANTTELL_PRIO;\n\n    if (type === 'none') {\n      // For none, swap pass and fail outcomes.\n      // none-type checks should pass when result is false rather than true.\n      if (check.priority === constants.PASS_PRIO) {\n        check.priority = constants.FAIL_PRIO;\n      } else if (check.priority === constants.FAIL_PRIO) {\n        check.priority = constants.PASS_PRIO;\n      }\n    }\n  });\n\n  // Find the result with the highest priority\n  const priorities = {\n    all: nodeResult.all.reduce((a, b) => Math.max(a, b.priority), 0),\n    none: nodeResult.none.reduce((a, b) => Math.max(a, b.priority), 0),\n    // get the lowest passing of 'any' defaulting\n    // to 0 by wrapping around 4 to 0 (inapplicable)\n    any: nodeResult.any.reduce((a, b) => Math.min(a, b.priority), 4) % 4\n  };\n\n  nodeResult.priority = Math.max(\n    priorities.all,\n    priorities.none,\n    priorities.any\n  );\n\n  // Of each type, filter out all results not matching the final priority\n  const impacts = [];\n  checkTypes.forEach(type => {\n    nodeResult[type] = nodeResult[type].filter(check => {\n      return (\n        check.priority === nodeResult.priority &&\n        check.priority === priorities[type]\n      );\n    });\n    nodeResult[type].forEach(check => impacts.push(check.impact));\n  });\n\n  // for failed nodes, define the impact\n  if ([CANTTELL_PRIO, FAIL_PRIO].includes(nodeResult.priority)) {\n    nodeResult.impact = aggregate(constants.impact, impacts);\n  } else {\n    nodeResult.impact = null;\n  }\n\n  // Delete the old result and priority properties\n  anyAllNone(nodeResult, c => {\n    delete c.result;\n    delete c.priority;\n  });\n\n  // Convert the index to a result string value\n  nodeResult.result = constants.results[nodeResult.priority];\n  delete nodeResult.priority;\n\n  return nodeResult;\n}\n\nexport default aggregateChecks;\n"
  },
  {
    "path": "lib/core/utils/aggregate-node-results.js",
    "content": "import aggregateChecks from './aggregate-checks';\nimport aggregate from './aggregate';\nimport finalizeRuleResult from './finalize-result';\nimport constants from '../constants';\n\n/**\n * Calculates the result of a Rule based on its types and the result of its child Checks\n * @param\t{Array} nodeResults The array of nodes tested by the Rule\n */\nfunction aggregateNodeResults(nodeResults) {\n  const ruleResult = {};\n\n  // For each node, retrieve the result and impact\n  nodeResults = nodeResults.map(nodeResult => {\n    // Known result\n    if (nodeResult.any && nodeResult.all && nodeResult.none) {\n      return aggregateChecks(nodeResult);\n    } else if (Array.isArray(nodeResult.node)) {\n      return finalizeRuleResult(nodeResult);\n    } else {\n      throw new TypeError('Invalid Result type');\n    }\n  });\n\n  // Aggregate the result\n  // If there were no nodes passed in, mark the test as inapplicable\n  if (nodeResults && nodeResults.length) {\n    const resultList = nodeResults.map(node => node.result);\n    ruleResult.result = aggregate(\n      constants.results,\n      resultList,\n      ruleResult.result\n    );\n  } else {\n    ruleResult.result = 'inapplicable';\n  }\n\n  // Create an array for each type\n  constants.resultGroups.forEach(group => (ruleResult[group] = []));\n\n  // Fill the array with nodes\n  nodeResults.forEach(nodeResult => {\n    const groupName = constants.resultGroupMap[nodeResult.result];\n    ruleResult[groupName].push(nodeResult);\n  });\n\n  // Take the highest impact of failed or canttell rules\n  let impactGroup = constants.FAIL_GROUP;\n  if (ruleResult[impactGroup].length === 0) {\n    impactGroup = constants.CANTTELL_GROUP;\n  }\n\n  if (ruleResult[impactGroup].length > 0) {\n    // Get the impact of all issues\n    const impactList = ruleResult[impactGroup].map(failure => failure.impact);\n\n    ruleResult.impact = aggregate(constants.impact, impactList) || null;\n  } else {\n    ruleResult.impact = null;\n  }\n\n  return ruleResult;\n}\n\nexport default aggregateNodeResults;\n"
  },
  {
    "path": "lib/core/utils/aggregate-result.js",
    "content": "import constants from '../constants';\n\nfunction copyToGroup(resultObject, subResult, group) {\n  const resultCopy = Object.assign({}, subResult);\n  resultCopy.nodes = (resultCopy[group] || []).concat();\n  constants.resultGroups.forEach(resultGroup => {\n    delete resultCopy[resultGroup];\n  });\n  resultObject[group].push(resultCopy);\n}\n\n/**\n * Calculates the result of a Rule based on its types and the result of its child Checks\n * @param  {RuleResult} ruleResult The RuleResult to calculate the result of\n */\nfunction aggregateResult(results) {\n  const resultObject = {};\n\n  // Create an array for each type\n  constants.resultGroups.forEach(groupName => (resultObject[groupName] = []));\n\n  // Fill the array with nodes\n  results.forEach(subResult => {\n    if (subResult.error) {\n      copyToGroup(resultObject, subResult, constants.CANTTELL_GROUP);\n    } else if (subResult.result === constants.NA) {\n      copyToGroup(resultObject, subResult, constants.NA_GROUP);\n    } else {\n      constants.resultGroups.forEach(group => {\n        if (Array.isArray(subResult[group]) && subResult[group].length > 0) {\n          copyToGroup(resultObject, subResult, group);\n        }\n      });\n    }\n  });\n  return resultObject;\n}\n\nexport default aggregateResult;\n"
  },
  {
    "path": "lib/core/utils/aggregate.js",
    "content": "/**\n * From a list of values, find the one with the greatest weight according to\n * the supplied map\n * @param  {object} params Contains 3 properties:\n * - map: a map indicating the order of values to run in\n *        example: ['small', 'medium', 'large']\n * - values: Array of values to take the highest from\n * - initial: optional starting value\n */\nfunction aggregate(map, values, initial) {\n  values = values.slice();\n  if (initial) {\n    values.push(initial);\n  }\n\n  const sorting = values.map(val => map.indexOf(val)).sort(); // Stupid NodeJS array.sort functor doesn't work!!\n\n  return map[sorting.pop()];\n}\n\nexport default aggregate;\n"
  },
  {
    "path": "lib/core/utils/are-styles-set.js",
    "content": "function areStylesSet(el, styles, stopAt) {\n  const styl = window.getComputedStyle(el, null);\n  if (!styl) {\n    return false;\n  }\n  for (let i = 0; i < styles.length; ++i) {\n    const att = styles[i];\n    if (styl.getPropertyValue(att.property) === att.value) {\n      return true;\n    }\n  }\n  if (!el.parentNode || el.nodeName.toUpperCase() === stopAt.toUpperCase()) {\n    return false;\n  }\n  return areStylesSet(el.parentNode, styles, stopAt);\n}\n\nexport default areStylesSet;\n"
  },
  {
    "path": "lib/core/utils/assert.js",
    "content": "/**\n * If the first argument is falsy, throw an error using the second argument as a message.\n * @param {boolean} bool\n * @param {string} message\n */\nfunction assert(bool, message) {\n  if (!bool) {\n    throw new Error(message);\n  }\n}\n\nexport default assert;\n"
  },
  {
    "path": "lib/core/utils/check-helper.js",
    "content": "import toArray from './to-array';\nimport DqElement from './dq-element';\nimport AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node';\n\n/**\n * Helper to denote which checks are asyncronous and provide callbacks and pass data back to the CheckResult\n * @param  {CheckResult}   checkResult The target object\n * @param  {Function} callback    The callback to expose when `this.async()` is called\n * @return {Object}               Bound to `this` for a check's fn\n */\nfunction checkHelper(checkResult, options, resolve, reject) {\n  return {\n    isAsync: false,\n    async() {\n      this.isAsync = true;\n      return result => {\n        if (result instanceof Error === false) {\n          checkResult.result = result;\n          resolve(checkResult);\n        } else {\n          reject(result);\n        }\n      };\n    },\n    data(data) {\n      checkResult.data = data;\n    },\n    relatedNodes(nodes) {\n      if (!window.Node) {\n        return;\n      }\n      if (\n        nodes instanceof window.Node ||\n        nodes instanceof AbstractVirtualNode\n      ) {\n        nodes = [nodes];\n      } else {\n        nodes = toArray(nodes);\n      }\n      checkResult.relatedNodes = [];\n      nodes.forEach(node => {\n        if (node instanceof AbstractVirtualNode) {\n          node = node.actualNode;\n        }\n        if (node instanceof window.Node) {\n          const dqElm = new DqElement(node);\n          checkResult.relatedNodes.push(dqElm);\n        }\n      });\n    }\n  };\n}\n\nexport default checkHelper;\n"
  },
  {
    "path": "lib/core/utils/clone.js",
    "content": "/**\n * Deeply clones an object or array. DOM nodes or collections of DOM nodes are not deeply cloned and are instead returned as is.\n * @param  {Mixed} obj The object/array to clone\n * @return {Mixed} A clone of the initial object or array\n */\nexport default function clone(obj) {\n  return cloneRecused(obj, new Map());\n}\n\n// internal function to hide non-user facing parameters\nfunction cloneRecused(obj, seen) {\n  if (obj === null || typeof obj !== 'object') {\n    return obj;\n  }\n\n  // don't clone DOM nodes. since we can pass nodes from different window contexts\n  // we'll also use duck typing to determine what is a DOM node\n  if (\n    (window?.Node && obj instanceof window.Node) ||\n    (window?.HTMLCollection && obj instanceof window.HTMLCollection) ||\n    ('nodeName' in obj && 'nodeType' in obj && 'ownerDocument' in obj)\n  ) {\n    return obj;\n  }\n\n  // handle circular references by caching the cloned object and returning it\n  if (seen.has(obj)) {\n    return seen.get(obj);\n  }\n\n  if (Array.isArray(obj)) {\n    const out = [];\n    seen.set(obj, out);\n    obj.forEach(value => {\n      out.push(cloneRecused(value, seen));\n    });\n    return out;\n  }\n\n  const out = {};\n  seen.set(obj, out);\n  // eslint-disable-next-line guard-for-in\n  for (const key in obj) {\n    out[key] = cloneRecused(obj[key], seen);\n  }\n  return out;\n}\n"
  },
  {
    "path": "lib/core/utils/closest.js",
    "content": "import matches from './matches';\n\n/**\n * closest implementation that operates on a VirtualNode\n *\n * @method closest\n * @memberof axe.utils\n * @param {VirtualNode} vNode VirtualNode to match\n * @param {String} selector CSS selector string\n * @return {VirtualNode | null}\n */\nfunction closest(vNode, selector) {\n  while (vNode) {\n    if (matches(vNode, selector)) {\n      return vNode;\n    }\n\n    // the top node of the tree will have parent === null, so a\n    // undefined parent means we are in a disconnected tree\n    if (typeof vNode.parent === 'undefined') {\n      throw new TypeError('Cannot resolve parent for non-DOM nodes');\n    }\n\n    vNode = vNode.parent;\n  }\n\n  return null;\n}\n\nexport default closest;\n"
  },
  {
    "path": "lib/core/utils/collect-results-from-frames.js",
    "content": "import queue from './queue';\nimport sendCommandToFrame from './send-command-to-frame';\nimport mergeResults from './merge-results';\n\n/**\n * Sends a message to axe running in frames to start analysis and collate results (via `mergeResults`)\n * @private\n * @param  {Context}  parentContent   The resolved Context object\n * @param  {Object}   options   Options object (as passed to `runRules`)\n * @param  {string}   command   Command sent to all frames\n * @param  {Array}    parameter Array of values to be passed along side the command\n * @param  {Function} callback  Function to call when results from all frames have returned\n */\nexport default function collectResultsFromFrames(\n  parentContent,\n  options,\n  command,\n  parameter,\n  resolve,\n  reject\n) {\n  // elementRefs can't be passed across frame boundaries\n  options = { ...options, elementRef: false };\n\n  const q = queue();\n  const frames = parentContent.frames;\n\n  // Tell each axe running in each frame to collect results\n  frames.forEach(({ node: frameElement, ...context }) => {\n    q.defer((res, rej) => {\n      const params = { options, command, parameter, context };\n      function callback(results) {\n        if (!results) {\n          return res(null);\n        }\n        return res({ results, frameElement });\n      }\n\n      sendCommandToFrame(frameElement, params, callback, rej);\n    });\n  });\n\n  // Combine results from all frames and give it back\n  q.then(data => {\n    resolve(mergeResults(data, options));\n  }).catch(reject);\n}\n"
  },
  {
    "path": "lib/core/utils/contains.js",
    "content": "/**\n * Wrapper for Node#contains\n * @method contains\n * @memberof axe.utils\n * @param  {VirtualNode} vNode     The candidate container VirtualNode\n * @param  {VirtualNode} otherVNode The vNode to test is contained by `vNode`\n * @return {Boolean}           Whether `vNode` contains `otherVNode`\n */\nexport default function contains(vNode, otherVNode) {\n  // Native light DOM method\n  if (\n    !vNode.shadowId &&\n    !otherVNode.shadowId &&\n    vNode.actualNode &&\n    // eslint-disable-next-line no-restricted-syntax\n    typeof vNode.actualNode.contains === 'function'\n  ) {\n    // eslint-disable-next-line no-restricted-syntax\n    return vNode.actualNode.contains(otherVNode.actualNode);\n  }\n\n  // Alternative method for shadow DOM / virtual tree tests\n  do {\n    if (vNode === otherVNode) {\n      return true;\n    } else if (otherVNode.nodeIndex < vNode.nodeIndex) {\n      return false;\n    }\n    otherVNode = otherVNode.parent;\n  } while (otherVNode);\n\n  return false;\n}\n"
  },
  {
    "path": "lib/core/utils/css-parser.js",
    "content": "import { CssSelectorParser } from '../imports';\n\nconst parser = new CssSelectorParser();\nparser.registerSelectorPseudos('not');\nparser.registerSelectorPseudos('is');\nparser.registerNestingOperators('>');\nparser.registerAttrEqualityMods('^', '$', '*', '~');\n\nexport default parser;\n"
  },
  {
    "path": "lib/core/utils/deep-merge.js",
    "content": "/**\n * Deeply merge two objects into a new object without changing any of the source objects.\n * @see https://medium.com/javascript-in-plain-english/how-to-merge-objects-in-javascript-98f2209710e3\n * @param {...Object} sources\n * @return {Object}\n */\nfunction deepMerge(...sources) {\n  const target = {};\n\n  sources.forEach(source => {\n    if (!source || typeof source !== 'object' || Array.isArray(source)) {\n      return;\n    }\n\n    for (const key of Object.keys(source)) {\n      if (\n        !target.hasOwnProperty(key) ||\n        typeof source[key] !== 'object' ||\n        Array.isArray(target[key])\n      ) {\n        target[key] = source[key];\n      } else {\n        target[key] = deepMerge(target[key], source[key]);\n      }\n    }\n  });\n\n  return target;\n}\n\nexport default deepMerge;\n"
  },
  {
    "path": "lib/core/utils/dq-element.js",
    "content": "import getSelector from './get-selector';\nimport getAncestry from './get-ancestry';\nimport getXpath from './get-xpath';\nimport getNodeFromTree from './get-node-from-tree';\nimport AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node';\nimport cache from '../base/cache';\nimport memoize from './memoize';\nimport getSource from './get-element-source';\n\nconst CACHE_KEY = 'DqElm.RunOptions';\n\n/**\n * \"Serialized\" `HTMLElement`. It will calculate the CSS selector,\n * grab the source (outerHTML) and offer an array for storing frame paths\n * @param {HTMLElement} element The element to serialize\n * @param {Object} options Propagated from axe.run/etc\n * @param {Object} spec Properties to use in place of the element when instantiated on Elements from other frames\n */\nconst DqElement = memoize(function DqElement(elm, options, spec) {\n  options ??= null;\n  spec ??= {};\n\n  if (!options) {\n    options = cache.get(CACHE_KEY) ?? {};\n  }\n\n  this.spec = spec;\n  if (elm instanceof AbstractVirtualNode) {\n    this._virtualNode = elm;\n    this._element = elm.actualNode;\n  } else {\n    this._element = elm;\n    this._virtualNode = getNodeFromTree(elm);\n  }\n\n  /**\n   * Whether DqElement was created from an iframe\n   * @type {boolean}\n   */\n  this.fromFrame = this.spec.selector?.length > 1;\n\n  this._includeElementInJson = options.elementRef;\n\n  if (options.absolutePaths) {\n    this._options = { toRoot: true };\n  }\n\n  /**\n   * Number by which nodes in the flat tree can be sorted\n   * @type {Number}\n   */\n  this.nodeIndexes = [];\n  if (Array.isArray(this.spec.nodeIndexes)) {\n    this.nodeIndexes = this.spec.nodeIndexes;\n  } else if (typeof this._virtualNode?.nodeIndex === 'number') {\n    this.nodeIndexes = [this._virtualNode.nodeIndex];\n  }\n\n  /**\n   * The generated HTML source code of the element\n   * @type {String|null}\n   */\n  this.source = null;\n  // TODO: es-modules_audit\n  if (!axe._audit.noHtml) {\n    this.source = this.spec.source ?? getSource(this._element);\n  }\n\n  return this;\n});\n\nDqElement.prototype = {\n  /**\n   * A unique CSS selector for the element, designed for readability\n   * @return {String}\n   */\n  get selector() {\n    return this.spec.selector || [getSelector(this.element, this._options)];\n  },\n\n  /**\n   * A unique CSS selector for the element, including its ancestors down to the root node\n   * @return {String}\n   */\n  get ancestry() {\n    return this.spec.ancestry || [getAncestry(this.element)];\n  },\n\n  /**\n   * Xpath to the element\n   * @return {String}\n   */\n  get xpath() {\n    return this.spec.xpath || [getXpath(this.element)];\n  },\n\n  /**\n   * Direct reference to the `HTMLElement` wrapped by this `DQElement`.\n   */\n  get element() {\n    return this._element;\n  },\n\n  /**\n   * Converts to a \"spec\", a form suitable for use with JSON.stringify\n   * (*not* to pre-stringified JSON)\n   * @returns {Object}\n   */\n  toJSON() {\n    const spec = {\n      selector: this.selector,\n      source: this.source,\n      xpath: this.xpath,\n      ancestry: this.ancestry,\n      nodeIndexes: this.nodeIndexes,\n      fromFrame: this.fromFrame\n    };\n    if (this._includeElementInJson) {\n      spec.element = this._element;\n    }\n    return spec;\n  }\n};\n\n/** @deprecated */\nDqElement.fromFrame = function fromFrame(node, options, frame) {\n  const spec = DqElement.mergeSpecs(node, frame);\n  return new DqElement(frame.element, options, spec);\n};\n\nDqElement.mergeSpecs = function mergeSpecs(child, parentFrame) {\n  // Parameter order reversed for backcompat\n  return {\n    ...child,\n    selector: [...parentFrame.selector, ...child.selector],\n    ancestry: [...parentFrame.ancestry, ...child.ancestry],\n    xpath: [...parentFrame.xpath, ...child.xpath],\n    nodeIndexes: [...parentFrame.nodeIndexes, ...child.nodeIndexes],\n    fromFrame: true\n  };\n};\n\n/**\n * Set the default options to be used\n * @param {Object} RunOptions Options passed to axe.run()\n * @property {boolean} elementRef Add element when toJSON is called\n * @property {boolean} absolutePaths Use absolute path fro selectors\n */\nDqElement.setRunOptions = function setRunOptions({\n  elementRef,\n  absolutePaths\n}) {\n  cache.set(CACHE_KEY, { elementRef, absolutePaths });\n};\n\nexport default DqElement;\n"
  },
  {
    "path": "lib/core/utils/element-matches.js",
    "content": "/**\n * Polyfill for Element#matches\n * @param {HTMLElement} node The element to test\n * @param {String} selector The selector to test element against\n * @return {Boolean}\n */\nconst matchesSelector = (() => {\n  let method;\n\n  function getMethod(node) {\n    const candidates = [\n      'matches',\n      'matchesSelector',\n      'mozMatchesSelector',\n      'webkitMatchesSelector',\n      'msMatchesSelector'\n    ];\n    const length = candidates.length;\n    let index, candidate;\n\n    for (index = 0; index < length; index++) {\n      candidate = candidates[index];\n      if (node[candidate]) {\n        return candidate;\n      }\n    }\n  }\n\n  return (node, selector) => {\n    if (!method || !node[method]) {\n      method = getMethod(node);\n    }\n\n    if (node[method]) {\n      return node[method](selector);\n    }\n\n    return false;\n  };\n})();\n\nexport default matchesSelector;\n"
  },
  {
    "path": "lib/core/utils/escape-selector.js",
    "content": "/**\n * Escapes a property value of a CSS selector\n * @see https://github.com/mathiasbynens/CSS.escape/\n * @see http://dev.w3.org/csswg/cssom/#serialize-an-identifier\n * @param  {String} value The piece of the selector to escape\n * @return {String}        The escaped selector\n */\nfunction escapeSelector(value) {\n  /*eslint no-bitwise: 0, eqeqeq: 0, one-var: 0 */\n  const string = String(value);\n  const length = string.length;\n  let index = -1;\n  let codeUnit;\n  let result = '';\n  const firstCodeUnit = string.charCodeAt(0);\n  while (++index < length) {\n    codeUnit = string.charCodeAt(index);\n    // Note: there’s no need to special-case astral symbols, surrogate\n    // pairs, or lone surrogates.\n\n    // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER\n    // (U+FFFD).\n    if (codeUnit == 0x0000) {\n      result += '\\uFFFD';\n      continue;\n    }\n\n    if (\n      // If the character is in the range [\\1-\\1F] (U+0001 to U+001F) or is\n      // U+007F, […]\n      (codeUnit >= 0x0001 && codeUnit <= 0x001f) ||\n      codeUnit == 0x007f ||\n      // If the character is the first character and is in the range [0-9]\n      // (U+0030 to U+0039), […]\n      (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||\n      // If the character is the second character and is in the range [0-9]\n      // (U+0030 to U+0039) and the first character is a `-` (U+002D), […]\n      (index == 1 &&\n        codeUnit >= 0x0030 &&\n        codeUnit <= 0x0039 &&\n        firstCodeUnit == 0x002d)\n    ) {\n      // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point\n      result += '\\\\' + codeUnit.toString(16) + ' ';\n      continue;\n    }\n\n    // If the character is the first character and is a `-` (U+002D), and\n    // there is no second character, […]\n    if (index == 0 && length == 1 && codeUnit == 0x002d) {\n      result += '\\\\' + string.charAt(index);\n      continue;\n    }\n\n    // If the character is not handled by one of the above rules and is\n    // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or\n    // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to\n    // U+005A), or [a-z] (U+0061 to U+007A), […]\n    if (\n      codeUnit >= 0x0080 ||\n      codeUnit == 0x002d ||\n      codeUnit == 0x005f ||\n      (codeUnit >= 0x0030 && codeUnit <= 0x0039) ||\n      (codeUnit >= 0x0041 && codeUnit <= 0x005a) ||\n      (codeUnit >= 0x0061 && codeUnit <= 0x007a)\n    ) {\n      // the character itself\n      result += string.charAt(index);\n      continue;\n    }\n\n    // Otherwise, the escaped character.\n    // https://drafts.csswg.org/cssom/#escape-a-character\n    result += '\\\\' + string.charAt(index);\n  }\n  return result;\n}\n\nexport default escapeSelector;\n"
  },
  {
    "path": "lib/core/utils/extend-meta-data.js",
    "content": "/**\n * Extends metadata onto result object and executes any functions\n * @param  {Object} to   The target of the extend\n * @param  {Object} from Metadata to extend\n */\nfunction extendMetaData(to, from) {\n  Object.assign(to, from);\n  Object.keys(from)\n    .filter(prop => typeof from[prop] === 'function')\n    .forEach(prop => {\n      to[prop] = null;\n      try {\n        to[prop] = from[prop](to);\n      } catch {\n        // Ignore\n      }\n    });\n}\n\nexport default extendMetaData;\n"
  },
  {
    "path": "lib/core/utils/filter-html-attrs.js",
    "content": "import cache from '../base/cache';\nimport getNodeAttributes from './get-node-attributes';\nimport matchesSelector from './element-matches';\n\n/**\n * Filter attributes from an html element and all of its children.\n *\n * Example:\n * ```js\n * // Remove attribute if present\n * axe.utils.filterHtmlAttrs('<div data-attr=\"foo\">my div</div>', { 'data-attr': true });\n *\n * // Remove attribute if CSS selector matches\n * axe.utils.filterHtmlAttrs('<div data-attr=\"foo\">my div</div>', { 'data-attr': 'div' });\n * ```\n *\n * @method filterHtmlAttrs\n * @memberof axe.utils\n * @param {HTMLElement} element - HTML element to remove attributes from\n * @param {Object} filterAttrs - Attributes to remove and the qualifier of when to remove them\n * @returns {HTMLElement} Element with filtered attributes removed\n */\nexport default function filterHtmlAttrs(element, filterAttrs) {\n  if (!filterAttrs) {\n    return element;\n  }\n\n  let node = element.cloneNode(false);\n  const attributes = getNodeAttributes(node);\n\n  if (node.nodeType === 1) {\n    const outerHTML = node.outerHTML;\n    node = cache.get(outerHTML, () =>\n      setNodeAttributes(node, attributes, element, filterAttrs)\n    );\n  } else {\n    node = setNodeAttributes(node, attributes, element, filterAttrs);\n  }\n\n  // be sure to append text nodes as well\n  Array.from(element.childNodes).forEach(child => {\n    node.appendChild(filterHtmlAttrs(child, filterAttrs));\n  });\n\n  return node;\n}\n\nfunction setNodeAttributes(node, attributes, element, filterAttrs) {\n  if (!attributes) {\n    return node;\n  }\n  node = document.createElement(node.nodeName);\n  Array.from(attributes).forEach(attr => {\n    if (!attributeMatches(element, attr.name, filterAttrs)) {\n      // can't remove the value attribute from an input in IE11 so\n      // we'll have to reconstruct the node and set attribute\n      // manually\n      node.setAttribute(attr.name, attr.value);\n    }\n  });\n  return node;\n}\n\n/**\n * Test if node and attribute match one of the filtered attributes.\n * @param {HTMLElement} node - node to match\n * @param {String} attrname - Name of the attribute to test\n * @param {Object} filterAttrs - Attributes to remove and the qualifier of when to remove them.\n */\nfunction attributeMatches(node, attrName, filterAttrs) {\n  if (typeof filterAttrs[attrName] === 'undefined') {\n    return false;\n  }\n\n  // filterAttrs can only be a boolean or a CSS selector\n  if (filterAttrs[attrName] === true) {\n    return true;\n  }\n\n  return matchesSelector(node, filterAttrs[attrName]);\n}\n"
  },
  {
    "path": "lib/core/utils/finalize-result.js",
    "content": "import aggregateNodeResults from './aggregate-node-results';\n\n/**\n * Process rule results, grouping them by outcome\n * @param ruleResult {object}\n * @return {object}\n */\nexport default function finalizeRuleResult(ruleResult) {\n  // we don't use getRule so that this code does not throw but returns\n  // the results\n  const rule = axe._audit.rules.find(({ id }) => id === ruleResult.id);\n  if (rule && rule.impact) {\n    ruleResult.nodes.forEach(node => {\n      ['any', 'all', 'none'].forEach(checkType => {\n        (node[checkType] || []).forEach(checkResult => {\n          checkResult.impact = rule.impact;\n        });\n      });\n    });\n  }\n\n  Object.assign(ruleResult, aggregateNodeResults(ruleResult.nodes));\n  delete ruleResult.nodes;\n\n  return ruleResult;\n}\n"
  },
  {
    "path": "lib/core/utils/find-by.js",
    "content": "/**\n * Iterates an array of objects looking for a property with a specific value\n * @method findBy\n * @memberof axe.utils\n * @param  {Array} array  The array of objects to iterate\n * @param  {String} key   The property name to test against\n * @param  {Mixed} value  The value to find\n * @return {Object}       The first matching object or `undefined` if no match\n */\nfunction findBy(array, key, value) {\n  if (Array.isArray(array)) {\n    return array.find(\n      obj =>\n        obj !== null &&\n        typeof obj === 'object' &&\n        Object.hasOwn(obj, key) &&\n        obj[key] === value\n    );\n  }\n}\n\nexport default findBy;\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/assert-window.js",
    "content": "import assert from '../assert';\n\nexport function assertIsParentWindow(win) {\n  assetNotGlobalWindow(win);\n  assert(\n    window.parent === win,\n    'Source of the response must be the parent window.'\n  );\n}\n\nexport function assertIsFrameWindow(win) {\n  assetNotGlobalWindow(win);\n  assert(\n    win.parent === window,\n    'Respondable target must be a frame in the current window'\n  );\n}\n\nexport function assetNotGlobalWindow(win) {\n  assert(window !== win, 'Messages can not be sent to the same window.');\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/channel-store.js",
    "content": "import assert from '../assert';\n\nconst channels = {};\n\nexport function storeReplyHandler(\n  channelId,\n  replyHandler,\n  sendToParent = true\n) {\n  assert(\n    !channels[channelId],\n    `A replyHandler already exists for this message channel.`\n  );\n  channels[channelId] = { replyHandler, sendToParent };\n}\n\nexport function getReplyHandler(channelId) {\n  return channels[channelId];\n}\n\nexport function deleteReplyHandler(channelId) {\n  delete channels[channelId];\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/create-responder.js",
    "content": "import { postMessage } from './post-message';\n\n/**\n * Helper closure to create a function that may be used to respond to a message\n * @private\n * @param  {Window} win    The window from which the message originated\n * @param  {String} channelId   The \"unique\" ID of the original message\n * @return {Function}      A function that may be invoked to respond to the message\n */\nexport function createResponder(win, channelId, sendToParent = true) {\n  return function respond(message, keepalive, replyHandler) {\n    const data = { channelId, message, keepalive };\n    postMessage(win, data, sendToParent, replyHandler);\n  };\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/message-handler.js",
    "content": "import { processError } from './process-error';\nimport { createResponder } from './create-responder';\nimport { parseMessage } from './message-parser';\nimport { assertIsFrameWindow, assertIsParentWindow } from './assert-window';\nimport { getReplyHandler, deleteReplyHandler } from './channel-store';\nimport { isNewMessage } from './message-id';\n\nfunction originIsAllowed(origin) {\n  // TODO: es_modules_audit\n  const { allowedOrigins } = axe._audit;\n  return (\n    (allowedOrigins && allowedOrigins.includes('*')) ||\n    allowedOrigins.includes(origin)\n  );\n}\n\n/**\n * Handle incoming window messages\n * @param  {MessageEvent}\n * @param  {Function} topicHandler\n */\nexport function messageHandler(\n  { origin, data: dataString, source: win },\n  topicHandler\n) {\n  try {\n    const data = parseMessage(dataString) || {};\n    const { channelId, message, messageId } = data;\n\n    if (!originIsAllowed(origin) || !isNewMessage(messageId)) {\n      return;\n    }\n\n    // An error should never come from a parent. Log it and exit.\n    if (message instanceof Error && win.parent !== window) {\n      axe.log(message);\n      return false;\n    }\n\n    try {\n      if (data.topic) {\n        const responder = createResponder(win, channelId);\n        assertIsParentWindow(win);\n        topicHandler(data, responder);\n      } else {\n        callReplyHandler(win, data);\n      }\n    } catch (error) {\n      processError(win, error, channelId);\n    }\n  } catch (error) {\n    axe.log(error);\n    return false;\n  }\n}\n\nfunction callReplyHandler(win, data) {\n  const { channelId, message, keepalive } = data;\n  const { replyHandler, sendToParent } = getReplyHandler(channelId) || {};\n\n  if (!replyHandler) {\n    return;\n  }\n  sendToParent ? assertIsParentWindow(win) : assertIsFrameWindow(win);\n  const responder = createResponder(win, channelId, sendToParent);\n\n  if (!keepalive && channelId) {\n    deleteReplyHandler(channelId);\n  }\n\n  try {\n    replyHandler(message, keepalive, responder);\n  } catch (error) {\n    axe.log(error);\n    responder(error, keepalive);\n  }\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/message-id.js",
    "content": "import { v4 as createUuid } from '../uuid';\n\n// No cache, so that this can persist across axe.run calls\nconst messageIds = [];\n\nexport function createMessageId() {\n  const uuid = `${createUuid()}:${createUuid()}`;\n  // Prevent repeats\n  if (messageIds.includes(uuid)) {\n    return createMessageId();\n  }\n\n  messageIds.push(uuid);\n  return uuid;\n}\n\nexport function isNewMessage(uuid) {\n  if (messageIds.includes(uuid)) {\n    return false;\n  }\n  messageIds.push(uuid);\n  return true;\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/message-parser.js",
    "content": "const errorTypes = Object.freeze([\n  'EvalError',\n  'RangeError',\n  'ReferenceError',\n  'SyntaxError',\n  'TypeError',\n  'URIError'\n]);\n\nexport function stringifyMessage({\n  topic,\n  channelId,\n  message,\n  messageId,\n  keepalive\n}) {\n  const data = {\n    channelId,\n    topic,\n    messageId,\n    keepalive: !!keepalive,\n    source: getSource()\n  };\n\n  if (message instanceof Error) {\n    data.error = {\n      name: message.name,\n      message: message.message,\n      stack: message.stack\n    };\n  } else {\n    data.payload = message;\n  }\n  return JSON.stringify(data);\n}\n\n/**\n * Parse the received message for processing\n * @param  {string} dataString Message received\n * @return {object}            Object to be used for pub/sub\n */\nexport function parseMessage(dataString) {\n  let data;\n  try {\n    data = JSON.parse(dataString);\n  } catch {\n    return; // Wasn't meant for us.\n  }\n  if (!isRespondableMessage(data)) {\n    return;\n  }\n\n  const { topic, channelId, messageId, keepalive } = data;\n  const message =\n    typeof data.error === 'object'\n      ? buildErrorObject(data.error)\n      : data.payload;\n\n  return { topic, message, messageId, channelId, keepalive: !!keepalive };\n}\n\n/**\n * Verify the received message is from the \"respondable\" module\n * @private\n * @param  {Object} postedMessage The message received via postMessage\n * @return {Boolean}              `true` if the message is verified from respondable\n */\nfunction isRespondableMessage(postedMessage) {\n  return (\n    postedMessage !== null &&\n    typeof postedMessage === 'object' &&\n    typeof postedMessage.channelId === 'string' &&\n    postedMessage.source === getSource()\n  );\n}\n\n/**\n * Convert a javascript Error into something that can be stringified\n * @param  {Error} error  Any type of error\n * @return {Object}       Processable object\n */\nfunction buildErrorObject(error) {\n  let msg = error.message || 'Unknown error occurred';\n  const errorName = errorTypes.includes(error.name) ? error.name : 'Error';\n  const ErrConstructor = window[errorName] || Error;\n\n  if (error.stack) {\n    msg += '\\n' + error.stack.replace(error.message, '');\n  }\n  return new ErrConstructor(msg);\n}\n\n/**\n * get the unique string to be used to identify our instance of axe\n * @private\n */\nfunction getSource() {\n  let application = 'axeAPI';\n  let version = '';\n\n  // TODO: es-modules_audit\n  if (typeof axe !== 'undefined' && axe._audit && axe._audit.application) {\n    application = axe._audit.application;\n  }\n  if (typeof axe !== 'undefined') {\n    // TODO: es-modules-version\n    version = axe.version;\n  }\n  return application + '.' + version;\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/post-message.js",
    "content": "import { stringifyMessage } from './message-parser';\nimport { assertIsParentWindow, assertIsFrameWindow } from './assert-window';\nimport { storeReplyHandler } from './channel-store';\nimport { createMessageId } from './message-id';\n\n/**\n * Posts the message to correct frame.\n * This abstraction necessary because IE9 & 10 do not support posting Objects; only strings\n * @private\n * @param  {Window}   win         The `window` to post the message to\n * @param  {Object}   data        Payload with topic, message, channelId & keepalive\n * @param  {Boolean}  sendToParent Whether the message goes to the parent or the child frame\n * @param  {Function} replyNandler Function to call with the response\n *\n * @return {Boolean} true if the message was sent\n */\nexport function postMessage(win, data, sendToParent, replyHandler) {\n  // Prevent messaging to an inappropriate window\n  sendToParent ? assertIsParentWindow(win) : assertIsFrameWindow(win);\n  if (data.message instanceof Error && !sendToParent) {\n    axe.log(data.message);\n    return false;\n  }\n\n  const dataString = stringifyMessage({\n    messageId: createMessageId(),\n    ...data\n  });\n\n  // TODO: es_modules_audit\n  const { allowedOrigins } = axe._audit;\n  if (!allowedOrigins || !allowedOrigins.length) {\n    return false;\n  }\n\n  if (typeof replyHandler === 'function') {\n    storeReplyHandler(data.channelId, replyHandler, sendToParent);\n  }\n  // There is no way to know the origin of `win`, so we'll try them all.\n  allowedOrigins.forEach(origin => {\n    try {\n      win.postMessage(dataString, origin);\n    } catch (err) {\n      if (err instanceof win.DOMException) {\n        throw new Error(\n          `allowedOrigins value \"${origin}\" is not a valid origin`\n        );\n      }\n      throw err;\n    }\n  });\n  return true;\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger/process-error.js",
    "content": "import { postMessage } from './post-message';\nimport { createMessageId } from './message-id';\n\n/**\n * Log, or post an error to the parent window\n * @param {window} win\n * @param {object} messageData\n */\nexport function processError(win, error, channelId) {\n  if (!win.parent !== window) {\n    return axe.log(error);\n  }\n\n  try {\n    postMessage(\n      win,\n      {\n        topic: null,\n        channelId,\n        message: error,\n        messageId: createMessageId(),\n        keepalive: true\n      },\n      true\n    );\n  } catch (err) {\n    // Last resort, logging\n    return axe.log(err);\n  }\n}\n"
  },
  {
    "path": "lib/core/utils/frame-messenger.js",
    "content": "import { postMessage } from './frame-messenger/post-message';\nimport { messageHandler } from './frame-messenger/message-handler';\n\n/**\n * Setup default axe frame messenger (make a function so we can\n * call it during tests to reset respondable to default state).\n * @param {Object} respondable\n */\nexport const frameMessenger = {\n  open(topicHandler) {\n    if (typeof window.addEventListener !== 'function') {\n      return;\n    }\n\n    const handler = function (messageEvent) {\n      messageHandler(messageEvent, topicHandler);\n    };\n    window.addEventListener('message', handler, false);\n\n    return () => {\n      window.removeEventListener('message', handler, false);\n    };\n  },\n\n  post(win, data, replyHandler) {\n    if (typeof window.addEventListener !== 'function') {\n      return false;\n    }\n    return postMessage(win, data, false, replyHandler);\n  }\n};\n\n/**\n * Setup default axe frame messenger (make a function so we can\n * call it during tests to reset respondable to default state).\n * @param {Object} respondable\n */\nexport function setDefaultFrameMessenger(respondable) {\n  respondable.updateMessenger(frameMessenger);\n}\n"
  },
  {
    "path": "lib/core/utils/get-all-checks.js",
    "content": "/**\n * Gets all Checks (or CheckResults) for a given Rule or RuleResult\n * @param {RuleResult|Rule} rule\n */\nfunction getAllChecks(object) {\n  const result = [];\n  return result\n    .concat(object.any || [])\n    .concat(object.all || [])\n    .concat(object.none || []);\n}\n\nexport default getAllChecks;\n"
  },
  {
    "path": "lib/core/utils/get-ancestry.js",
    "content": "import getShadowSelector from './get-shadow-selector';\n\nfunction generateAncestry(node) {\n  const nodeName = node.nodeName.toLowerCase();\n  const parentElement = node.parentElement;\n  const parentNode = node.parentNode;\n\n  let nthChild = '';\n  if (\n    nodeName !== 'head' &&\n    nodeName !== 'body' &&\n    parentNode?.children.length > 1\n  ) {\n    const index = Array.prototype.indexOf.call(parentNode.children, node) + 1;\n    nthChild = `:nth-child(${index})`;\n  }\n\n  if (!parentElement) {\n    return nodeName + nthChild;\n  }\n\n  return generateAncestry(parentElement) + ' > ' + nodeName + nthChild;\n}\n\n/**\n * Gets a unique CSS selector\n * @param {HTMLElement} node The element to get the selector for\n * @param {Object} optional options\n * @returns {String|Array<String>} Unique CSS selector for the node\n */\nexport default function getAncestry(elm, options) {\n  return getShadowSelector(generateAncestry, elm, options);\n}\n"
  },
  {
    "path": "lib/core/utils/get-base-lang.js",
    "content": "/**\n * Convenience function to extract primary language subtag from a given value\n * @method getBaseLang\n * @memberof axe.utils\n * @param {String} value value specified as lang or xml:lang attribute\n * @return {String}\n */\nfunction getBaseLang(lang) {\n  if (!lang) {\n    return '';\n  }\n  return lang.trim().split('-')[0].toLowerCase();\n}\n\nexport default getBaseLang;\n"
  },
  {
    "path": "lib/core/utils/get-check-message.js",
    "content": "import processMessage from './process-message';\n\n/**\n * Get the pass, fail, or incomplete message for a check.\n * @param {String} checkId The check id\n * @param {String} type The message type ('pass', 'fail', or 'incomplete')\n * @param {Object} [data] The check data\n * @return {String}\n */\nfunction getCheckMessage(checkId, type, data) {\n  // TODO: es-modules_audit\n  const check = axe._audit.data.checks[checkId];\n\n  if (!check) {\n    throw new Error(`Cannot get message for unknown check: ${checkId}.`);\n  }\n  if (!check.messages[type]) {\n    throw new Error(`Check \"${checkId}\"\" does not have a \"${type}\" message.`);\n  }\n\n  return processMessage(check.messages[type], data);\n}\n\nexport default getCheckMessage;\n"
  },
  {
    "path": "lib/core/utils/get-check-option.js",
    "content": "/**\n * Determines which CheckOption to use, either defined on the rule options, global check options or the check itself\n * @param  {Check} check    The Check object\n * @param  {String} ruleID  The ID of the rule\n * @param  {Object} options Options object as passed to main API\n * @return {Object}         The resolved object with `options` and `enabled` keys\n */\nfunction getCheckOption(check, ruleID, options) {\n  const ruleCheckOption = (((options.rules && options.rules[ruleID]) || {})\n    .checks || {})[check.id];\n  const checkOption = (options.checks || {})[check.id];\n\n  let enabled = check.enabled;\n  let opts = check.options;\n\n  if (checkOption) {\n    if (checkOption.hasOwnProperty('enabled')) {\n      enabled = checkOption.enabled;\n    }\n    if (checkOption.hasOwnProperty('options')) {\n      opts = checkOption.options;\n    }\n  }\n\n  if (ruleCheckOption) {\n    if (ruleCheckOption.hasOwnProperty('enabled')) {\n      enabled = ruleCheckOption.enabled;\n    }\n    if (ruleCheckOption.hasOwnProperty('options')) {\n      opts = ruleCheckOption.options;\n    }\n  }\n\n  return {\n    enabled: enabled,\n    options: opts,\n    absolutePaths: options.absolutePaths\n  };\n}\n\nexport default getCheckOption;\n"
  },
  {
    "path": "lib/core/utils/get-element-source.js",
    "content": "import getNodeAttributes from './get-node-attributes';\nimport isXHTML from './is-xhtml';\n\n/**\n * Gets the truncated HTML source of an element, or nodeValue for non-element nodes\n * @param  {Node}   node the DOM node (element, text, comment, etc.)\n * @param  {Object} options truncation options\n * @param  {Number} [options.maxLength=300] maximum length of the output\n * @param  {Number} [options.attrLimit=20] maximum length for attribute names and values\n * @returns {String} The outerHTML, truncated representation, or nodeValue for non-elements\n */\nexport default function getElementSource(\n  node,\n  { maxLength = 300, attrLimit = 20 } = {}\n) {\n  if (!node) {\n    return '';\n  }\n  // non-element nodes\n  if (node.nodeType !== 1) {\n    const value = node.nodeValue ?? '';\n    return truncate(value, maxLength);\n  }\n\n  const deepStr = getOuterHtml(node);\n  if (deepStr.length > maxLength) {\n    return getTruncatedElementSource(node, { maxLength, attrLimit });\n  }\n\n  return deepStr;\n}\n\n/**\n * Gets the outerHTML of an element, using XMLSerializer as fallback for SVG/MathML\n * @param  {Element} element the DOM element\n * @returns {String} The serialized HTML or empty string\n */\nfunction getOuterHtml(element) {\n  let source = element.outerHTML;\n  if (!source && typeof window.XMLSerializer === 'function') {\n    source = new window.XMLSerializer().serializeToString(element);\n  }\n  return source || '';\n}\n\n/**\n * Builds a truncated HTML representation of an element when outerHTML exceeds maxLength.\n * Note: attribute order may differ from the original source as node.attributes order is not guaranteed.\n * @param  {Element} elm the DOM element\n * @param  {Object} options truncation options\n * @param  {Number} options.maxLength maximum length of the output\n * @param  {Number} options.attrLimit maximum length for attribute names and values\n * @returns {String} Truncated opening tag (e.g. '<div id=\"foo\" ...>')\n */\nfunction getTruncatedElementSource(elm, { maxLength, attrLimit }) {\n  const nodeName = isXHTML(elm.ownerDocument || document)\n    ? elm.nodeName\n    : elm.nodeName.toLowerCase();\n\n  // Get a mutable attribute map, and work out their rendered length\n  const nodeAttrs = Array.from(getNodeAttributes(elm)).map(\n    ({ name, value }) => ({ name, value })\n  );\n  const attrsLength = nodeAttrs.reduce((acc, { name, value }) => {\n    // 4 = space before name + equals sign + opening quote + closing quote\n    return acc + name.length + value.length + 4;\n  }, 0);\n\n  // 2 = opening \"<\" + space before first attribute\n  if (2 + nodeName.length + attrsLength > maxLength) {\n    nodeAttrs.forEach(attr => {\n      attr.name = truncate(attr.name, attrLimit);\n      attr.value = truncate(attr.value, attrLimit);\n    });\n  }\n\n  let source = `<${nodeName}`;\n  let tagEnd = '>';\n  const truncateEnd = ' ...>';\n  // Must check every attribute: an attr that doesn't fit may be followed by one that does\n  for (const attr of nodeAttrs) {\n    const attrStr = ` ${attr.name}=\"${attr.value}\"`;\n    if (source.length + attrStr.length > maxLength - truncateEnd.length) {\n      tagEnd = truncateEnd;\n      continue;\n    }\n    source += attrStr;\n  }\n\n  return source + tagEnd;\n}\n\n/**\n * Truncates a string to max length, appending '...' when truncated\n * @param  {String} str the string to truncate\n * @param  {Number} attrLimit maximum length before truncation\n * @returns {String} The original string or truncated version with '...' suffix\n */\nfunction truncate(str, attrLimit) {\n  return str.length <= attrLimit ? str : str.substring(0, attrLimit) + '...';\n}\n"
  },
  {
    "path": "lib/core/utils/get-environment-data.js",
    "content": "/**\n * Add information about the environment axe was run in.\n * @return {EnvironmentData}\n */\nexport default function getEnvironmentData(metadata = null, win = window) {\n  if (metadata && typeof metadata === 'object') {\n    return metadata;\n  } else if (typeof win !== 'object') {\n    return {};\n  }\n\n  return {\n    testEngine: {\n      name: 'axe-core',\n      version: axe.version\n    },\n    testRunner: {\n      name: axe._audit.brand\n    },\n    testEnvironment: getTestEnvironment(win),\n    timestamp: new Date().toISOString(),\n    url: win.location?.href\n  };\n}\n\nfunction getTestEnvironment(win) {\n  if (!win.navigator || typeof win.navigator !== 'object') {\n    return {};\n  }\n  const { navigator, innerHeight, innerWidth } = win;\n  const { angle, type } = getOrientation(win) || {};\n  return {\n    userAgent: navigator.userAgent,\n    windowWidth: innerWidth,\n    windowHeight: innerHeight,\n    orientationAngle: angle,\n    orientationType: type\n  };\n}\n\nfunction getOrientation({ screen }) {\n  return screen.orientation || screen.msOrientation || screen.mozOrientation;\n}\n"
  },
  {
    "path": "lib/core/utils/get-flattened-tree.js",
    "content": "import isShadowRoot from './is-shadow-root';\nimport VirtualNode from '../base/virtual-node/virtual-node';\nimport cache from '../base/cache';\nimport { cacheNodeSelectors } from './selector-cache';\n\n/**\n * This implements the flatten-tree algorithm specified:\n * Originally here https://drafts.csswg.org/css-scoping/#flat-tree\n * Hopefully soon published here: https://www.w3.org/TR/css-scoping-1/#flat-tree\n *\n * Some notable information:\n ******* NOTE: as of Chrome 59, this is broken in Chrome so that tests fail completely\n ******* removed functionality for now\n * 1. <slot> elements do not have boxes by default (i.e. they do not get rendered and\n *    their CSS properties are ignored)\n * 2. <slot> elements can be made to have a box by overriding the display property\n *    which is 'contents' by default\n * 3. Even boxed <slot> elements do not show up in the accessibility tree until\n *    they have a tabindex applied to them OR they have a role applied to them AND\n *    they have a box (this is observed behavior in Safari on OS X, I cannot find\n *    the spec for this)\n */\n\nlet hasShadowRoot;\n\n/**\n * Recursvely returns an array of the virtual DOM nodes at this level\n * excluding comment nodes and the shadow DOM nodes <content> and <slot>\n *\n * @param {Node} [node=document.documentElement] optional node. NOTE: passing in anything other than body or the documentElement may result in incomplete results.\n * @param {String} [shadowId] optional ID of the shadow DOM that is the closest shadow\n *                           ancestor of the node\n */\nexport default function getFlattenedTree(\n  node = document.documentElement,\n  shadowId\n) {\n  hasShadowRoot = false;\n  const selectorMap = {};\n  cache.set('nodeMap', new WeakMap());\n  cache.set('selectorMap', selectorMap);\n\n  // specifically pass `null` to the parent to designate the top\n  // node of the tree. if parent === undefined then we know\n  // we are in a disconnected tree\n  const tree = flattenTree(node, shadowId, null);\n  tree[0]._selectorMap = selectorMap;\n\n  // allow rules and checks to know if there is a shadow root attached\n  // to the current tree\n  tree[0]._hasShadowRoot = hasShadowRoot;\n\n  return tree;\n}\n\n/**\n * find all the fallback content for a <slot> and return these as an array\n * this array will also include any #text nodes\n *\n * @param node {Node} - the slot Node\n * @return Array{Nodes}\n */\nfunction getSlotChildren(node) {\n  const childNodes = [];\n\n  node = node.firstChild;\n  while (node) {\n    childNodes.push(node);\n    node = node.nextSibling;\n  }\n  return childNodes;\n}\n\n/**\n * Create a virtual node\n * @param {Node} node the current node\n * @param {VirtualNode} parent the parent VirtualNode\n * @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow ancestor of the node\n * @return {VirtualNode}\n */\nfunction createNode(node, parent, shadowId) {\n  const vNode = new VirtualNode(node, parent, shadowId);\n  cacheNodeSelectors(vNode, cache.get('selectorMap'));\n\n  return vNode;\n}\n\n/**\n * Add children to the parent virtual node\n * @param {Node[]} childNodes the children of the parent\n * @param {VirtualNode} parent the parent VirtualNode\n * @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow ancestor of the node\n */\nfunction createChildren(childNodes, parent, shadowId) {\n  const children = [];\n\n  childNodes.forEach(childNode => {\n    const child = flattenTree(childNode, shadowId, parent);\n    if (child) {\n      children.push(...child);\n    }\n  });\n\n  return children;\n}\n\n/**\n * Recursively returns an array of the virtual DOM nodes at this level\n * excluding comment nodes and the shadow DOM nodes <content> and <slot>\n *\n * @param {Node} node the current node\n * @param {String} shadowId, optional ID of the shadow DOM that is the closest shadow ancestor of the node\n * @param {VirtualNode} parent the parent VirtualNode\n */\nfunction flattenTree(node, shadowId, parent) {\n  let vNode, childNodes;\n\n  if (node.documentElement) {\n    // document\n    node = node.documentElement;\n  }\n  const nodeName = node.nodeName.toLowerCase();\n\n  if (isShadowRoot(node)) {\n    hasShadowRoot = true;\n\n    // generate an ID for this shadow root and overwrite the current\n    // closure shadowId with this value so that it cascades down the tree\n    vNode = createNode(node, parent, shadowId);\n    shadowId = 'a' + Math.random().toString().substring(2);\n    childNodes = Array.from(node.shadowRoot.childNodes);\n    vNode.children = createChildren(childNodes, vNode, shadowId);\n\n    return [vNode];\n  }\n\n  if (\n    nodeName === 'content' &&\n    typeof node.getDistributedNodes === 'function'\n  ) {\n    childNodes = Array.from(node.getDistributedNodes());\n    return createChildren(childNodes, parent, shadowId);\n  }\n\n  if (nodeName === 'slot' && typeof node.assignedNodes === 'function') {\n    childNodes = Array.from(node.assignedNodes());\n    if (!childNodes.length) {\n      // fallback content\n      childNodes = getSlotChildren(node);\n    }\n\n    const styl = window.getComputedStyle(node);\n\n    // check the display property. intentionally does not run, see notable information at top of file\n    if (false && styl.display !== 'contents') {\n      // has a box\n      vNode = createNode(node, parent, shadowId);\n      vNode.children = createChildren(childNodes, vNode, shadowId);\n\n      return [vNode];\n    }\n\n    return createChildren(childNodes, parent, shadowId);\n  }\n\n  if (node.nodeType === document.ELEMENT_NODE) {\n    vNode = createNode(node, parent, shadowId);\n    childNodes = Array.from(node.childNodes);\n    vNode.children = createChildren(childNodes, vNode, shadowId);\n\n    return [vNode];\n  }\n\n  if (node.nodeType === document.TEXT_NODE) {\n    return [createNode(node, parent)];\n  }\n\n  return undefined;\n}\n"
  },
  {
    "path": "lib/core/utils/get-frame-contexts.js",
    "content": "import Context from '../base/context';\nimport getAncestry from './get-ancestry';\n\nexport default function getFrameContexts(context, options = {}) {\n  if (options.iframes === false) {\n    return [];\n  }\n\n  const { frames } = new Context(context);\n  return frames.map(({ node, ...frameContext }) => {\n    frameContext.initiator = false;\n    const frameSelector = getAncestry(node);\n    return { frameSelector, frameContext };\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/get-friendly-uri-end.js",
    "content": "/* eslint no-script-url:0 */\n/**\n * Check if a string contains mostly numbers\n */\nfunction isMostlyNumbers(str = '') {\n  return (\n    str.length !== 0 && (str.match(/[0-9]/g) || '').length >= str.length / 2\n  );\n}\n\n/**\n * Spit a string into an array with two pieces, at a given index\n * @param String\tstring to split\n * @param Number\tindex at which to split\n * @return Array\n */\nfunction splitString(str, splitIndex) {\n  return [str.substring(0, splitIndex), str.substring(splitIndex)];\n}\n\nfunction trimRight(str) {\n  return str.replace(/\\s+$/, '');\n}\n\n/**\n * Take a relative or absolute URL and pull it into it's indivisual pieces\n *\n * @param url (string)\n * @return urlPieces\n *\t .protocol\tThe protocol used, e.g. 'https://'\n *\t .domain\t\tDomain name including sub domains and TLD, e.g. 'docs.deque.com'\n *\t .port\t\t\tThe port number, e.g. ':8080'\n *\t .path\t\t\tPath after the domain, e.g. '/home.html'\n *\t .query\t\t Query string, e.g. '?user=admin&password=pass'\n *\t .hash\t\t\tHash / internal reference, e.g. '#footer'\n */\nfunction uriParser(url) {\n  const original = url;\n  let protocol = '',\n    domain = '',\n    port = '',\n    path = '',\n    query = '',\n    hash = '';\n  if (url.includes('#')) {\n    [url, hash] = splitString(url, url.indexOf('#'));\n  }\n\n  if (url.includes('?')) {\n    [url, query] = splitString(url, url.indexOf('?'));\n  }\n\n  if (url.includes('://')) {\n    [protocol, url] = url.split('://');\n    [domain, url] = splitString(url, url.indexOf('/'));\n  } else if (url.substr(0, 2) === '//') {\n    url = url.substr(2);\n    [domain, url] = splitString(url, url.indexOf('/'));\n  }\n\n  if (domain.substr(0, 4) === 'www.') {\n    domain = domain.substr(4);\n  }\n\n  if (domain && domain.includes(':')) {\n    [domain, port] = splitString(domain, domain.indexOf(':'));\n  }\n\n  path = url; // Whatever is left, must be the path\n  return { original, protocol, domain, port, path, query, hash };\n}\n\n/**\n * Try to, at the end of the URI, find a string that a user can identify the URI by\n *\n * @param uri\t\t\t The URI to use\n * @param options\n *\t .currentDomain\tThe current domain name (optional)\n *\t .maxLength\t\t\tMax length of the returned string (default: 25)\n * @return string\t A portion at the end of the uri, no longer than maxLength\n */\nfunction getFriendlyUriEnd(uri = '', options = {}) {\n  if (\n    // Skip certain URIs:\n    uri.length <= 1 || // very short\n    uri.substr(0, 5) === 'data:' || // data URIs are unreadable\n    uri.substr(0, 11) === 'javascript:' || // JS isn't a URL\n    uri.includes('?') // query strings aren't very readable either\n  ) {\n    return;\n  }\n\n  const { currentDomain, maxLength = 25 } = options;\n  const { path, domain, hash } = uriParser(uri);\n  // Split the path at the last / that has text after it\n  const pathEnd = path.substr(\n    path.substr(0, path.length - 2).lastIndexOf('/') + 1\n  );\n\n  if (hash) {\n    if (pathEnd && (pathEnd + hash).length <= maxLength) {\n      return trimRight(pathEnd + hash);\n    } else if (\n      pathEnd.length < 2 &&\n      hash.length > 2 &&\n      hash.length <= maxLength\n    ) {\n      return trimRight(hash);\n    } else {\n      return;\n    }\n  } else if (domain && domain.length < maxLength && path.length <= 1) {\n    // '' or '/'\n    return trimRight(domain + path);\n  }\n\n  // See if the domain should be returned\n  if (\n    path === '/' + pathEnd &&\n    domain &&\n    currentDomain &&\n    domain !== currentDomain &&\n    (domain + path).length <= maxLength\n  ) {\n    return trimRight(domain + path);\n  }\n\n  const lastDotIndex = pathEnd.lastIndexOf('.');\n  if (\n    // Exclude very short or very long string\n    (lastDotIndex === -1 || lastDotIndex > 1) &&\n    (lastDotIndex !== -1 || pathEnd.length > 2) &&\n    pathEnd.length <= maxLength &&\n    // Exclude index files\n    !pathEnd.match(/index(\\.[a-zA-Z]{2-4})?/) &&\n    // Exclude files that are likely to be database IDs\n    !isMostlyNumbers(pathEnd)\n  ) {\n    return trimRight(pathEnd);\n  }\n}\n\nexport default getFriendlyUriEnd;\n"
  },
  {
    "path": "lib/core/utils/get-node-attributes.js",
    "content": "/**\n * Return the list of attributes of a node.\n * @method getNodeAttributes\n * @memberof axe.utils\n * @param {Element} node\n * @deprecated\n * @returns {NamedNodeMap}\n */\nfunction getNodeAttributes(node) {\n  // eslint-disable-next-line no-restricted-syntax\n  if (node.attributes instanceof window.NamedNodeMap) {\n    // eslint-disable-next-line no-restricted-syntax\n    return node.attributes;\n  }\n\n  // if the attributes property is not of type NamedNodeMap then the DOM\n  // has been clobbered. E.g. <form><input name=\"attributes\"></form>.\n  // We can clone the node to isolate it and then return the attributes\n  return node.cloneNode(false).attributes;\n}\n\nexport default getNodeAttributes;\n"
  },
  {
    "path": "lib/core/utils/get-node-from-tree.js",
    "content": "import cache from '../base/cache';\n\n/**\n * Return a single node from the virtual dom tree\n *\n * @param {Object} vNode The flattened, virtual DOM tree\n * @param {Node}   node  The HTML DOM node\n */\nfunction getNodeFromTree(vNode, node) {\n  const el = node || vNode;\n  return cache.get('nodeMap') ? cache.get('nodeMap').get(el) : null;\n}\n\nexport default getNodeFromTree;\n"
  },
  {
    "path": "lib/core/utils/get-root-node.js",
    "content": "/**\n * Return the document or document fragment (shadow DOM)\n * @method getRootNode\n * @memberof axe.utils\n * @param {Element} node\n * @returns {DocumentFragment|Document}\n */\nfunction getRootNode(node) {\n  let doc = (node.getRootNode && node.getRootNode()) || document; // this is for backwards compatibility\n  if (doc === node) {\n    // disconnected node\n    doc = document;\n  }\n  return doc;\n}\n\nexport default getRootNode;\n"
  },
  {
    "path": "lib/core/utils/get-rule.js",
    "content": "/**\n * Get an axe rule by id.\n * @param {String} ruelId the rule id\n * @return {Rule}\n */\nexport default function getRule(ruleId) {\n  // TODO: es-modules_audit\n  const rule = axe._audit.rules.find(({ id }) => id === ruleId);\n\n  if (!rule) {\n    throw new Error(`Cannot find rule by id: ${ruleId}`);\n  }\n\n  return rule;\n}\n"
  },
  {
    "path": "lib/core/utils/get-scroll-state.js",
    "content": "import getScroll from './get-scroll';\n\n/**\n * Create an array scroll positions from descending elements\n */\nfunction getElmScrollRecursive(root) {\n  // Need to also get .childNodes since SVGs in IE don't have .children.\n  return Array.from(root.children || root.childNodes || []).reduce(\n    (scrolls, elm) => {\n      const scroll = getScroll(elm);\n      if (scroll) {\n        scrolls.push(scroll);\n      }\n      return scrolls.concat(getElmScrollRecursive(elm));\n    },\n    []\n  );\n}\n\n/**\n * Get the scroll position of all scrollable elements in a page\n * @deprecated\n */\nfunction getScrollState(win = window) {\n  const root = win.document.documentElement;\n  const windowScroll = [\n    win.pageXOffset !== undefined\n      ? {\n          elm: win,\n          top: win.pageYOffset,\n          left: win.pageXOffset\n        }\n      : {\n          elm: root,\n          top: root.scrollTop,\n          left: root.scrollLeft\n        }\n  ];\n\n  return windowScroll.concat(getElmScrollRecursive(document.body));\n}\n\nexport default getScrollState;\n"
  },
  {
    "path": "lib/core/utils/get-scroll.js",
    "content": "import memoize from './memoize';\n\n/**\n * Get the scroll position of given element\n * @method getScroll\n * @memberof axe.utils\n * @param {Element} elm\n * @param {buffer} (Optional) allowed negligence in overflow\n * @returns {Object | undefined}\n */\nfunction getScroll(elm, buffer = 0) {\n  const overflowX = elm.scrollWidth > elm.clientWidth + buffer;\n  const overflowY = elm.scrollHeight > elm.clientHeight + buffer;\n\n  /**\n   * if there is neither `overflow-x` or `overflow-y`\n   * -> return\n   */\n  if (!(overflowX || overflowY)) {\n    return;\n  }\n\n  const style = window.getComputedStyle(elm);\n  const scrollableX = isScrollable(style, 'overflow-x');\n  const scrollableY = isScrollable(style, 'overflow-y');\n\n  /**\n   * check direction of `overflow` and `scrollable`\n   */\n  if ((overflowX && scrollableX) || (overflowY && scrollableY)) {\n    return {\n      elm,\n      top: elm.scrollTop,\n      left: elm.scrollLeft\n    };\n  }\n}\n\nfunction isScrollable(style, prop) {\n  const overflowProp = style.getPropertyValue(prop);\n  return ['scroll', 'auto'].includes(overflowProp);\n}\n\nexport default memoize(getScroll);\n"
  },
  {
    "path": "lib/core/utils/get-selector.js",
    "content": "import escapeSelector from './escape-selector';\nimport getFriendlyUriEnd from './get-friendly-uri-end';\nimport getNodeAttributes from './get-node-attributes';\nimport matchesSelector from './element-matches';\nimport isXHTML from './is-xhtml';\nimport getShadowSelector from './get-shadow-selector';\nimport memoize from './memoize';\nimport constants from '../../core/constants';\n\nconst ignoredAttributes = [\n  'class',\n  'style',\n  'id',\n  'selected',\n  'checked',\n  'disabled',\n  'tabindex',\n  'aria-checked',\n  'aria-selected',\n  'aria-invalid',\n  'aria-activedescendant',\n  'aria-busy',\n  'aria-disabled',\n  'aria-expanded',\n  'aria-grabbed',\n  'aria-pressed',\n  'aria-valuenow',\n  'xmlns'\n];\n\nconst MAXATTRIBUTELENGTH = 31;\nconst attrCharsRegex = /([\\\\\"])/g;\nconst newlineChars = /(\\r\\n|\\r|\\n)/g;\n\n/**\n * Escape an attribute selector string.\n * @param {String} str\n * @return {String}\n */\nfunction escapeAttribute(str) {\n  return (\n    str\n      // @see https://www.py4u.net/discuss/286669\n      .replace(attrCharsRegex, '\\\\$1')\n      // @see https://stackoverflow.com/a/20354013/2124254\n      .replace(newlineChars, '\\\\a ')\n  );\n}\n\n/**\n * get the attribute name and value as a string\n * @param {Element} node\t\tThe element that has the attribute\n * @param {Attribute} at\t\tThe attribute\n * @return {String}\n */\nfunction getAttributeNameValue(node, at) {\n  const name = at.name;\n  let atnv;\n\n  if (name.indexOf('href') !== -1 || name.indexOf('src') !== -1) {\n    const friendly = getFriendlyUriEnd(node.getAttribute(name));\n    if (friendly) {\n      atnv = escapeSelector(at.name) + '$=\"' + escapeAttribute(friendly) + '\"';\n    } else {\n      atnv =\n        escapeSelector(at.name) +\n        '=\"' +\n        escapeAttribute(node.getAttribute(name)) +\n        '\"';\n    }\n  } else {\n    atnv = escapeSelector(name) + '=\"' + escapeAttribute(at.value) + '\"';\n  }\n  return atnv;\n}\n\nfunction countSort(a, b) {\n  return a.count < b.count ? -1 : a.count === b.count ? 0 : 1;\n}\n\n/**\n * Filter the attributes\n * @param {Attribute}\t\tThe potential attribute\n * @return {Boolean}\t\t Whether to include or exclude\n */\nfunction filterAttributes(at) {\n  return (\n    !ignoredAttributes.includes(at.name) &&\n    at.name.indexOf(':') === -1 &&\n    (!at.value || at.value.length < MAXATTRIBUTELENGTH)\n  );\n}\n\n/**\n * Calculate the statistics for the classes, attributes and tags on the page, using\n * the virtual DOM tree\n * @param {Object} domTree\t\tThe root node of the virtual DOM tree\n * @returns {Object}\t\t\t\t\tThe statistics consisting of three maps, one for classes,\n *\t\t\t\t\t\t\t\t\t\t\t\t\t\tone for tags and one for attributes. The map values are\n *\t\t\t\t\t\t\t\t\t\t\t\t\t\tthe counts for how many elements with that feature exist\n */\nexport function getSelectorData(domTree) {\n  // Initialize the return structure with the three maps\n  const data = {\n    classes: {},\n    tags: {},\n    attributes: {}\n  };\n\n  domTree = Array.isArray(domTree) ? domTree : [domTree];\n  let currentLevel = domTree.slice();\n  const stack = [];\n  while (currentLevel.length) {\n    const current = currentLevel.pop();\n    const node = current.actualNode;\n\n    if (!!node.querySelectorAll) {\n      // ignore #text nodes\n\n      // count the tag\n      const tag = node.nodeName;\n      if (data.tags[tag]) {\n        data.tags[tag]++;\n      } else {\n        data.tags[tag] = 1;\n      }\n\n      // count all the classes\n      if (node.classList) {\n        Array.from(node.classList).forEach(cl => {\n          const ind = escapeSelector(cl);\n          if (data.classes[ind]) {\n            data.classes[ind]++;\n          } else {\n            data.classes[ind] = 1;\n          }\n        });\n      }\n\n      // count all the filtered attributes\n      if (node.hasAttributes()) {\n        Array.from(getNodeAttributes(node))\n          .filter(filterAttributes)\n          .forEach(at => {\n            const atnv = getAttributeNameValue(node, at);\n            if (atnv) {\n              if (data.attributes[atnv]) {\n                data.attributes[atnv]++;\n              } else {\n                data.attributes[atnv] = 1;\n              }\n            }\n          });\n      }\n    }\n    if (current.children.length) {\n      // \"recurse\"\n      stack.push(currentLevel);\n      currentLevel = current.children.slice();\n    }\n    while (!currentLevel.length && stack.length) {\n      currentLevel = stack.pop();\n    }\n  }\n  return data;\n}\n\n/**\n * Given a node and the statistics on class frequency on the page,\n * return all its uncommon class data sorted in order of decreasing uniqueness\n * @param {Element} node\t\t\tThe node\n * @param {Object} classData\tThe map of classes to counts\n * @return {Array}\t\t\t\t\t\tThe sorted array of uncommon class data\n */\nfunction uncommonClasses(node, selectorData) {\n  // eslint no-loop-func:false\n  const retVal = [];\n  const classData = selectorData.classes;\n  const tagData = selectorData.tags;\n\n  if (node.classList) {\n    Array.from(node.classList).forEach(cl => {\n      const ind = escapeSelector(cl);\n      if (classData[ind] < tagData[node.nodeName]) {\n        retVal.push({\n          name: ind,\n          count: classData[ind],\n          species: 'class'\n        });\n      }\n    });\n  }\n  return retVal.sort(countSort);\n}\n\n/**\n * Given an element and a selector that finds that element (but possibly other sibling elements)\n * return the :nth-child(n) pseudo selector that uniquely finds the node within its siblings\n * @param {Element} elm\t\t\t The Element\n * @param {String} selector\t The selector\n * @return {String}\t\t\t\t\t The nth-child selector\n */\nfunction getNthChildString(elm, selector) {\n  const siblings =\n    (elm.parentNode && Array.from(elm.parentNode.children || '')) || [];\n  const hasMatchingSiblings = siblings.find(\n    sibling => sibling !== elm && matchesSelector(sibling, selector)\n  );\n  if (hasMatchingSiblings) {\n    const nthChild = 1 + siblings.indexOf(elm);\n    return ':nth-child(' + nthChild + ')';\n  } else {\n    return '';\n  }\n}\n\n/**\n * Get ID selector\n */\nfunction getElmId(elm) {\n  if (!elm.getAttribute('id')) {\n    return;\n  }\n  const doc = (elm.getRootNode && elm.getRootNode()) || document;\n  const id = '#' + escapeSelector(elm.getAttribute('id') || '');\n  if (\n    // Don't include youtube's uid values, they change\ton reload\n    !id.match(/player_uid_/) &&\n    // Don't include IDs that occur more then once on the page\n    doc.querySelectorAll(id).length === 1\n  ) {\n    return id;\n  }\n}\n\n/**\n * Return the base CSS selector for a given element\n * @param\t{HTMLElement} elm\t\t\t\t The element to get the selector for\n * @return {String|Array<String>}\tBase CSS selector for the node\n */\nfunction getBaseSelector(elm) {\n  const xhtml = isXHTML(document);\n  return escapeSelector(xhtml ? elm.localName : elm.nodeName.toLowerCase());\n}\n\n/**\n * Given a node and the statistics on attribute frequency on the page,\n * return all its uncommon attribute data sorted in order of decreasing uniqueness\n * @param {Element} node\t\t\tThe node\n * @param {Object} attData\t\tThe map of attributes to counts\n * @return {Array}\t\t\t\t\t\tThe sorted array of uncommon attribute data\n */\nfunction uncommonAttributes(node, selectorData) {\n  const retVal = [];\n  const attData = selectorData.attributes;\n  const tagData = selectorData.tags;\n\n  if (node.hasAttributes()) {\n    Array.from(getNodeAttributes(node))\n      .filter(filterAttributes)\n      .forEach(at => {\n        const atnv = getAttributeNameValue(node, at);\n\n        if (atnv && attData[atnv] < tagData[node.nodeName]) {\n          retVal.push({\n            name: atnv,\n            count: attData[atnv],\n            species: 'attribute'\n          });\n        }\n      });\n  }\n  return retVal.sort(countSort);\n}\n\n/**\n * generates a selector fragment for an element based on the statistics of the page in\n * which the element exists. This function takes into account the fact that selectors that\n * use classes and tags are much faster than universal selectors. It also tries to use a\n * unique class selector before a unique attribute selector (with the tag), followed by\n * a selector made up of the three least common features statistically. A feature will\n * also only be used if it is less common than the tag of the element itself.\n *\n * @param {Element} elm\t\t\tThe element for which to generate a selector\n * @param {Object} options\t Options for how to generate the selector\n * @param {RootNode} doc\t\t The root node of the document or document fragment\n * @returns {String}\t\t\t\t The selector\n */\n\nfunction getThreeLeastCommonFeatures(elm, selectorData) {\n  let selector = '';\n  let features;\n  const clss = uncommonClasses(elm, selectorData);\n  const atts = uncommonAttributes(elm, selectorData);\n\n  if (clss.length && clss[0].count === 1) {\n    // only use the unique class\n    features = [clss[0]];\n  } else if (atts.length && atts[0].count === 1) {\n    // only use the unique attribute value\n    features = [atts[0]];\n    // if no class, add the tag\n    selector = getBaseSelector(elm);\n  } else {\n    features = clss.concat(atts);\n    // sort by least common\n    features.sort(countSort);\n\n    // select three least common features\n    features = features.slice(0, 3);\n\n    // if no class, add the tag\n    if (\n      !features.some(feat => {\n        return feat.species === 'class';\n      })\n    ) {\n      // has no class\n      selector = getBaseSelector(elm);\n    } else {\n      // put the classes at the front of the selector\n      features.sort((a, b) => {\n        return a.species !== b.species && a.species === 'class'\n          ? -1\n          : a.species === b.species\n            ? 0\n            : 1;\n      });\n    }\n  }\n\n  // construct the return value\n  return (selector += features.reduce((val, feat) => {\n    /*eslint indent: 0*/\n    switch (feat.species) {\n      case 'class':\n        return val + '.' + feat.name;\n      case 'attribute':\n        return val + '[' + feat.name + ']';\n    }\n    return val; // should never happen\n  }, ''));\n}\n\n/**\n * generates a single selector for an element\n * @param {Element} elm\t\t\tThe element for which to generate a selector\n * @param {Object} options\t Options for how to generate the selector\n * @param {RootNode} doc\t\t The root node of the document or document fragment\n * @returns {String}\t\t\t\t The selector\n */\nfunction generateSelector(elm, options, doc) {\n  /*eslint no-loop-func:0*/\n  // TODO: es-modules_selectorData\n  if (!axe._selectorData) {\n    throw new Error('Expect axe._selectorData to be set up');\n  }\n  const { toRoot = false } = options;\n  let selector;\n  let similar;\n\n  /**\n   * Try to find a unique selector by filtering out all the clashing\n   * nodes by adding ancestor selectors iteratively.\n   * This loop is much faster than recursing and using querySelectorAll\n   */\n  do {\n    let features = getElmId(elm);\n    if (!features) {\n      features = getThreeLeastCommonFeatures(elm, axe._selectorData);\n      features += getNthChildString(elm, features);\n    }\n    if (selector) {\n      selector = features + ' > ' + selector;\n    } else {\n      selector = features;\n    }\n    // If there are too many similar element running QSA again is faster\n    if (!similar || similar.length > constants.selectorSimilarFilterLimit) {\n      similar = findSimilar(doc, selector);\n    } else {\n      similar = similar.filter(item => {\n        return matchesSelector(item, selector);\n      });\n    }\n    elm = elm.parentElement;\n  } while ((similar.length > 1 || toRoot) && elm && elm.nodeType !== 11);\n\n  if (similar.length === 1) {\n    return selector;\n  } else if (selector.indexOf(' > ') !== -1) {\n    // For the odd case that document doesn't have a unique selector\n    return ':root' + selector.substring(selector.indexOf(' > '));\n  }\n  return ':root';\n}\n\n/**\n * Gets a unique CSS selector\n * @param {HTMLElement} node The element to get the selector for\n * @param {Object} optional options\n * @returns {String|Array<String>} Unique CSS selector for the node\n */\nfunction getSelector(elm, options) {\n  return getShadowSelector(generateSelector, elm, options);\n}\n\n// Axe can call getSelector more than once for the same element because\n// the same element can end up on multiple DqElements.\nexport default memoize(getSelector);\n\n// Similar elements create similar selectors. If there are lots of similar elements on the page,\n// axe ends up needing to run that same selector many times. We can memoize for a huge perf boost.\nconst findSimilar = memoize((doc, selector) =>\n  Array.from(doc.querySelectorAll(selector))\n);\n"
  },
  {
    "path": "lib/core/utils/get-shadow-selector.js",
    "content": "/**\n * Gets a unique CSS selector\n * @param {HTMLElement} node The element to get the selector for\n * @param {Object} optional options\n * @returns {String|Array<String>} Unique CSS selector for the node\n */\nexport default function getShadowSelector(generateSelector, elm, options = {}) {\n  if (!elm) {\n    return '';\n  }\n  let doc = (elm.getRootNode && elm.getRootNode()) || document;\n  // Not a DOCUMENT_FRAGMENT - shadow DOM\n  if (doc.nodeType !== 11) {\n    return generateSelector(elm, options, doc);\n  }\n\n  const stack = [];\n  while (doc.nodeType === 11) {\n    if (!doc.host) {\n      return '';\n    }\n    stack.unshift({ elm, doc });\n    elm = doc.host;\n    doc = elm.getRootNode();\n  }\n\n  stack.unshift({ elm, doc });\n  return stack.map(item => generateSelector(item.elm, options, item.doc));\n}\n"
  },
  {
    "path": "lib/core/utils/get-standards.js",
    "content": "import standards from '../../standards';\nimport clone from './clone';\n\nexport default function getStandards() {\n  return clone(standards);\n}\n"
  },
  {
    "path": "lib/core/utils/get-stylesheet-factory.js",
    "content": "/**\n * Function which converts given text to `CSSStyleSheet`\n * - used in `CSSOM` computation.\n * - factory (closure) function, initialized with `document.implementation.createHTMLDocument()`, which uses DOM API for creating `style` elements.\n *\n * @method axe.utils.getStyleSheetFactory\n * @memberof axe.utils\n * @param {Object} dynamicDoc `document.implementation.createHTMLDocument()\n * @param {Object} options an object with properties to construct stylesheet\n * @property {String} options.data text content of the stylesheet\n * @property {Boolean} options.isCrossOrigin flag to notify if the resource was fetched from the network\n * @property {String} options.shadowId (Optional) shadowId if shadowDOM\n * @property {Object} options.root implementation document to create style elements\n * @property {String} options.priority a number indicating the loaded priority of CSS, to denote specificity of styles contained in the sheet.\n * @returns {Function}\n */\nfunction getStyleSheetFactory(dynamicDoc) {\n  if (!dynamicDoc) {\n    throw new Error(\n      'axe.utils.getStyleSheetFactory should be invoked with an argument'\n    );\n  }\n\n  return options => {\n    const {\n      data,\n      isCrossOrigin = false,\n      shadowId,\n      root,\n      priority,\n      isLink = false\n    } = options;\n    const style = dynamicDoc.createElement('style');\n    if (isLink) {\n      // as creating a stylesheet as link will need to be awaited\n      // till `onload`, it is wise to convert link href to @import statement\n      const text = dynamicDoc.createTextNode(`@import \"${data.href}\"`);\n      style.appendChild(text);\n    } else {\n      style.appendChild(dynamicDoc.createTextNode(data));\n    }\n    dynamicDoc.head.appendChild(style);\n    return {\n      sheet: style.sheet,\n      isCrossOrigin,\n      shadowId,\n      root,\n      priority\n    };\n  };\n}\n\nexport default getStyleSheetFactory;\n"
  },
  {
    "path": "lib/core/utils/get-xpath.js",
    "content": "import escapeSelector from './escape-selector';\n\nfunction getXPathArray(node, path) {\n  let sibling, count;\n  // Gets an XPath for an element which describes its hierarchical location.\n  if (!node) {\n    return [];\n  }\n  if (!path && node.nodeType === 9) {\n    // special case for when we are called and give the document itself as the starting node\n    path = [\n      {\n        str: 'html'\n      }\n    ];\n    return path;\n  }\n  path = path || [];\n  if (node.parentNode && node.parentNode !== node) {\n    path = getXPathArray(node.parentNode, path);\n  }\n\n  if (node.previousSibling) {\n    count = 1;\n    sibling = node.previousSibling;\n    do {\n      if (sibling.nodeType === 1 && sibling.nodeName === node.nodeName) {\n        count++;\n      }\n      sibling = sibling.previousSibling;\n    } while (sibling);\n    if (count === 1) {\n      count = null;\n    }\n  } else if (node.nextSibling) {\n    sibling = node.nextSibling;\n    do {\n      if (sibling.nodeType === 1 && sibling.nodeName === node.nodeName) {\n        count = 1;\n        sibling = null;\n      } else {\n        count = null;\n        sibling = sibling.previousSibling;\n      }\n    } while (sibling);\n  }\n\n  if (node.nodeType === 1) {\n    const element = {};\n    element.str = node.nodeName.toLowerCase();\n    // add the id and the count so we can construct robust versions of the xpath\n    const id = node.getAttribute && escapeSelector(node.getAttribute('id'));\n    if (id && node.ownerDocument.querySelectorAll('#' + id).length === 1) {\n      element.id = node.getAttribute('id');\n    }\n    if (count > 1) {\n      element.count = count;\n    }\n    path.push(element);\n  }\n  return path;\n}\n\n// Robust is intended to allow xpaths to be robust to changes in the HTML structure of the page\n// This means always use the id when present\n// Non robust means always use the count (i.e. the exact position of the element)\n// Ironically this is a bit of a misnomer because in very, very dynamic pages (e.g. where ids are generated on the fly)\n// the non-ribust Xpaths will work whereas the robust ones will not work\nfunction xpathToString(xpathArray) {\n  return xpathArray.reduce((str, elm) => {\n    if (elm.id) {\n      return `//${elm.str}[@id='${elm.id}']`;\n    } else {\n      return str + `/${elm.str}` + (elm.count > 0 ? `[${elm.count}]` : '');\n    }\n  }, '');\n}\n\nfunction getXpath(node) {\n  const xpathArray = getXPathArray(node);\n  return xpathToString(xpathArray);\n}\n\nexport default getXpath;\n"
  },
  {
    "path": "lib/core/utils/index.js",
    "content": "/**\n * Namespace for utility helper methods.\n * @namespace utils\n * @memberof axe\n */\nexport { default as aggregate } from './aggregate';\nexport { default as aggregateChecks } from './aggregate-checks';\nexport { default as aggregateNodeResults } from './aggregate-node-results';\nexport { default as aggregateResult } from './aggregate-result';\nexport { default as areStylesSet } from './are-styles-set';\nexport { default as assert } from './assert';\nexport { default as checkHelper } from './check-helper';\nexport { default as clone } from './clone';\nexport { default as closest } from './closest';\nexport { default as collectResultsFromFrames } from './collect-results-from-frames';\nexport { default as contains } from './contains';\nexport { default as cssParser } from './css-parser';\nexport { default as deepMerge } from './deep-merge';\nexport { default as DqElement } from './dq-element';\nexport { default as matchesSelector } from './element-matches';\nexport { default as escapeSelector } from './escape-selector';\nexport { default as extendMetaData } from './extend-meta-data';\nexport { default as finalizeRuleResult } from './finalize-result';\nexport { default as findBy } from './find-by';\nexport { default as getFlattenedTree } from './get-flattened-tree';\nexport { default as getAllChecks } from './get-all-checks';\nexport { default as getBaseLang } from './get-base-lang';\nexport { default as getCheckMessage } from './get-check-message';\nexport { default as getCheckOption } from './get-check-option';\nexport { default as getEnvironmentData } from './get-environment-data';\nexport { default as getFrameContexts } from './get-frame-contexts';\nexport { default as getFriendlyUriEnd } from './get-friendly-uri-end';\nexport { default as getNodeAttributes } from './get-node-attributes';\nexport { default as getNodeFromTree } from './get-node-from-tree';\nexport { default as getRootNode } from './get-root-node';\nexport { default as getRule } from './get-rule';\nexport { default as getScrollState } from './get-scroll-state';\nexport { default as getScroll } from './get-scroll';\nexport { default as getShadowSelector } from './get-shadow-selector';\nexport { default as getSelector, getSelectorData } from './get-selector';\nexport { default as getStandards } from './get-standards';\nexport { default as getStyleSheetFactory } from './get-stylesheet-factory';\nexport { default as getXpath } from './get-xpath';\nexport { default as getAncestry } from './get-ancestry';\nexport { default as getElementSource } from './get-element-source';\nexport { default as injectStyle } from './inject-style';\nexport { default as isArrayLike } from './is-array-like';\nexport {\n  isContextSpec,\n  isContextObject,\n  isContextProp,\n  isLabelledShadowDomSelector,\n  isLabelledFramesSelector\n} from './is-context';\nexport { default as isHidden } from './is-hidden';\nexport { default as isHtmlElement } from './is-html-element';\nexport { default as isNodeInContext } from './is-node-in-context';\nexport { default as isShadowRoot } from './is-shadow-root';\nexport { default as isXHTML } from './is-xhtml';\nexport {\n  default as matches,\n  matchesExpression,\n  convertSelector\n} from './matches';\nexport { default as matchAncestry } from './match-ancestry';\nexport { default as memoize } from './memoize';\nexport { default as mergeResults } from './merge-results';\nexport { default as nodeSerializer } from './node-serializer';\nexport { default as nodeSorter } from './node-sorter';\nexport { default as nodeLookup } from './node-lookup';\nexport { default as objectHasOwn } from './object-has-own';\nexport { default as parseCrossOriginStylesheet } from './parse-crossorigin-stylesheet';\nexport { default as parseSameOriginStylesheet } from './parse-sameorigin-stylesheet';\nexport { default as parseStylesheet } from './parse-stylesheet';\nexport { default as parseTabindex } from './parse-tabindex';\nexport { default as performanceTimer } from './performance-timer';\nexport { pollyfillElementsFromPoint } from './pollyfill-elements-from-point';\nexport { default as preloadCssom } from './preload-cssom';\nexport { default as preloadMedia } from './preload-media';\nexport { default as preload, shouldPreload, getPreloadConfig } from './preload';\nexport { default as processMessage } from './process-message';\nexport { default as publishMetaData } from './publish-metadata';\nexport { default as querySelectorAllFilter } from './query-selector-all-filter';\nexport { default as querySelectorAll } from './query-selector-all';\nexport { default as queue } from './queue';\nexport { default as respondable } from './respondable';\nexport { default as ruleShouldRun } from './rule-should-run';\nexport { default as filterHtmlAttrs } from './filter-html-attrs';\nexport { default as select } from './select';\nexport { default as sendCommandToFrame } from './send-command-to-frame';\nexport { default as serializeError } from './serialize-error';\nexport { default as RuleError } from './rule-error';\nexport { default as setScrollState } from './set-scroll-state';\nexport { default as shadowSelect } from './shadow-select';\nexport { default as shadowSelectAll } from './shadow-select-all';\nexport { default as toArray } from './to-array';\nexport { default as tokenList } from './token-list';\nexport { default as uniqueArray } from './unique-array';\nexport { default as uuid } from './uuid';\nexport { default as validInputTypes } from './valid-input-type';\nexport { default as isValidLang, validLangs } from './valid-langs';\nexport { default as normalizeRunOptions } from './normalize-run-options';\n"
  },
  {
    "path": "lib/core/utils/inject-style.js",
    "content": "let styleSheet;\nfunction injectStyle(style) {\n  if (styleSheet && styleSheet.parentNode) {\n    // append the style to the existing sheet\n    if (styleSheet.styleSheet === undefined) {\n      // Not old IE\n      styleSheet.appendChild(document.createTextNode(style));\n    } else {\n      styleSheet.styleSheet.cssText += style;\n    }\n    return styleSheet;\n  }\n  if (!style) {\n    return;\n  }\n\n  const head = document.head || document.getElementsByTagName('head')[0];\n  styleSheet = document.createElement('style');\n  styleSheet.type = 'text/css';\n\n  if (styleSheet.styleSheet === undefined) {\n    // Not old IE\n    styleSheet.appendChild(document.createTextNode(style));\n  } else {\n    styleSheet.styleSheet.cssText = style;\n  }\n\n  head.appendChild(styleSheet);\n\n  return styleSheet;\n}\n\nexport default injectStyle;\n"
  },
  {
    "path": "lib/core/utils/is-array-like.js",
    "content": "/**\n * Checks if a value is array-like.\n *\n * @param {any} arr - The value to check.\n * @returns {boolean} - Returns true if the value is array-like, false otherwise.\n */\nexport default function isArrayLike(arr) {\n  return (\n    !!arr &&\n    typeof arr === 'object' &&\n    typeof arr.length === 'number' &&\n    // Avoid DOM weirdness\n    arr instanceof window.Node === false\n  );\n}\n"
  },
  {
    "path": "lib/core/utils/is-context.js",
    "content": "import objectHasOwn from './object-has-own';\nimport isArrayLike from './is-array-like';\n\n/**\n * Determine if some value can be parsed as a context\n * @private\n * @param  {Mixed} contextSpec The configuration object passed to `Context`\n * @return {boolea}\n */\nexport function isContextSpec(contextSpec) {\n  return isContextObject(contextSpec) || isContextProp(contextSpec);\n}\n\n/**\n * Checks if the given context specification is a valid context object.\n *\n * @param {Object} contextSpec - The context specification object to check.\n * @returns {boolean} - Returns true if the context specification is a valid context object, otherwise returns false.\n */\nexport function isContextObject(contextSpec) {\n  return ['include', 'exclude'].some(\n    prop => objectHasOwn(contextSpec, prop) && isContextProp(contextSpec[prop])\n  );\n}\n\n/**\n * Checks if the given contextList is a valid context property.\n * @param {string|Node|Array} contextList - The contextList to check.\n * @returns {boolean} - Returns true if the contextList is a valid context property, otherwise false.\n */\nexport function isContextProp(contextList) {\n  return (\n    typeof contextList === 'string' ||\n    contextList instanceof window.Node ||\n    isLabelledFramesSelector(contextList) ||\n    isLabelledShadowDomSelector(contextList) ||\n    isArrayLike(contextList)\n  );\n}\n\nexport function isLabelledFramesSelector(selector) {\n  // This doesn't guarantee the selector is valid.\n  // Just that this isn't a runOptions object\n  // Normalization will ignore invalid selectors\n  return objectHasOwn(selector, 'fromFrames');\n}\n\nexport function isLabelledShadowDomSelector(selector) {\n  // This doesn't guarantee the selector is valid.\n  // Just that this isn't a runOptions object\n  // Normalization will ignore invalid selectors\n  return objectHasOwn(selector, 'fromShadowDom');\n}\n"
  },
  {
    "path": "lib/core/utils/is-hidden.js",
    "content": "import getNodeFromTree from './get-node-from-tree';\n\n/**\n * Determine whether an element is visible\n * @method isHidden\n * @memberof axe.utils\n * @param {HTMLElement} el The HTMLElement\n * @param {Boolean} recursed\n * @return {Boolean} The element's visibilty status\n * @deprecated use isVisibleToScreenreader\n */\nfunction isHidden(el, recursed) {\n  const node = getNodeFromTree(el);\n\n  // 9 === Node.DOCUMENT\n  if (el.nodeType === 9) {\n    return false;\n  }\n\n  // 11 === Node.DOCUMENT_FRAGMENT_NODE\n  if (el.nodeType === 11) {\n    el = el.host; // grab the host Node\n  }\n\n  if (node && node._isHidden !== null) {\n    return node._isHidden;\n  }\n\n  const style = window.getComputedStyle(el, null);\n\n  if (\n    !style ||\n    !el.parentNode ||\n    style.getPropertyValue('display') === 'none' ||\n    (!recursed &&\n      // visibility is only accurate on the first element\n      style.getPropertyValue('visibility') === 'hidden') ||\n    el.getAttribute('aria-hidden') === 'true'\n  ) {\n    return true;\n  }\n\n  const parent = el.assignedSlot ? el.assignedSlot : el.parentNode;\n  const hidden = isHidden(parent, true);\n\n  // cache the results of the isHidden check on the parent tree\n  // so we don't have to look at the parent tree again for all its\n  // descendants\n  if (node) {\n    node._isHidden = hidden;\n  }\n\n  return hidden;\n}\n\nexport default isHidden;\n"
  },
  {
    "path": "lib/core/utils/is-html-element.js",
    "content": "import standards from '../../standards';\n\n/**\n * Verifies that if a given html tag is valid\n * @method isHtmlElement\n * @memberof axe.utils\n * @param {HTMLElement|VirtualNode} node node to check if valid\n * @return {Boolean} true/ false\n */\nfunction isHtmlElement(node) {\n  const nodeName = node.props?.nodeName ?? node.nodeName.toLowerCase();\n\n  if (node.namespaceURI === 'http://www.w3.org/2000/svg') {\n    return false;\n  }\n\n  return !!standards.htmlElms[nodeName];\n}\n\nexport default isHtmlElement;\n"
  },
  {
    "path": "lib/core/utils/is-node-in-context.js",
    "content": "import contains from './contains';\n\n/**\n * Determines if a node is included or excluded in a given context\n * @private\n * @param  {Node}  node     The node to test\n * @param  {Object}  context \"Resolved\" context object, @see resolveContext\n * @return {Boolean}         [description]\n */\nexport default function isNodeInContext(node, { include = [], exclude = [] }) {\n  const filterInclude = include.filter(candidate => contains(candidate, node));\n  if (filterInclude.length === 0) {\n    return false;\n  }\n  const filterExcluded = exclude.filter(candidate => contains(candidate, node));\n  if (filterExcluded.length === 0) {\n    return true;\n  }\n  const deepestInclude = getDeepest(filterInclude);\n  const deepestExclude = getDeepest(filterExcluded);\n  return contains(deepestExclude, deepestInclude);\n}\n\n/**\n * Get the deepest node in a given collection\n * @private\n * @param  {Array} collection Array of nodes to test\n * @return {Node}             The deepest node\n */\nfunction getDeepest(collection) {\n  let deepest;\n  for (const node of collection) {\n    if (!deepest || !contains(node, deepest)) {\n      deepest = node;\n    }\n  }\n  return deepest;\n}\n"
  },
  {
    "path": "lib/core/utils/is-shadow-root.js",
    "content": "const possibleShadowRoots = [\n  'article',\n  'aside',\n  'blockquote',\n  'body',\n  'div',\n  'footer',\n  'h1',\n  'h2',\n  'h3',\n  'h4',\n  'h5',\n  'h6',\n  'header',\n  'main',\n  'nav',\n  'p',\n  'section',\n  'span'\n];\n/**\n * Test a node to see if it has a spec-conforming shadow root\n *\n * @param {Node}   node  The HTML DOM node\n * @return {Boolean}\n */\nfunction isShadowRoot(node) {\n  if (node.shadowRoot) {\n    const nodeName = node.nodeName.toLowerCase();\n    if (\n      possibleShadowRoots.includes(nodeName) ||\n      /^[a-z][a-z0-9_.-]*-[a-z0-9_.-]*$/.test(nodeName)\n    ) {\n      return true;\n    }\n  }\n  return false;\n}\n\nexport default isShadowRoot;\n"
  },
  {
    "path": "lib/core/utils/is-xhtml.js",
    "content": "import memoize from './memoize';\n\n/**\n * Determines if a document node is XHTML\n * @method isXHTML\n * @memberof axe.utils\n * @param {Node} doc a document node\n * @return {Boolean}\n */\nconst isXHTML = memoize(doc => {\n  if (!doc?.createElement) {\n    return false;\n  }\n  return doc.createElement('A').localName === 'A';\n});\n\nexport default isXHTML;\n"
  },
  {
    "path": "lib/core/utils/match-ancestry.js",
    "content": "/**\n * Check if two ancestries are identical\n */\nexport default function matchAncestry(ancestryA, ancestryB) {\n  if (ancestryA.length !== ancestryB.length) {\n    return false;\n  }\n  return ancestryA.every((selectorA, ancestorIndex) => {\n    const selectorB = ancestryB[ancestorIndex];\n    if (!Array.isArray(selectorA)) {\n      return selectorA === selectorB;\n    }\n    if (selectorA.length !== selectorB.length) {\n      return false;\n    }\n    return selectorA.every(\n      (str, selectorIndex) => selectorB[selectorIndex] === str\n    );\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/matches.js",
    "content": "import cssParser from './css-parser';\n\n/**\n * matches implementation that operates on a VirtualNode\n *\n * @method matches\n * @memberof axe.utils\n * @param {VirtualNode} vNode VirtualNode to match\n * @param {String} selector CSS selector string\n * @return {Boolean}\n */\nexport default function matches(vNode, selector) {\n  const expressions = convertSelector(selector);\n  return expressions.some(expression => matchesExpression(vNode, expression));\n}\n\nfunction matchesTag(vNode, exp) {\n  return (\n    vNode.props.nodeType === 1 &&\n    (exp.tag === '*' || vNode.props.nodeName === exp.tag)\n  );\n}\n\nfunction matchesClasses(vNode, exp) {\n  return !exp.classes || exp.classes.every(cl => vNode.hasClass(cl.value));\n}\n\nfunction matchesAttributes(vNode, exp) {\n  return (\n    !exp.attributes ||\n    exp.attributes.every(att => {\n      const nodeAtt = vNode.attr(att.key);\n      return nodeAtt !== null && att.test(nodeAtt);\n    })\n  );\n}\n\nfunction matchesId(vNode, exp) {\n  return !exp.id || vNode.props.id === exp.id;\n}\n\nfunction matchesPseudos(target, exp) {\n  if (\n    !exp.pseudos ||\n    exp.pseudos.every(pseudo => {\n      if (pseudo.name === 'not') {\n        return !pseudo.expressions.some(expression => {\n          return matchesExpression(target, expression);\n        });\n      } else if (pseudo.name === 'is') {\n        return pseudo.expressions.some(expression => {\n          return matchesExpression(target, expression);\n        });\n      }\n      throw new Error(\n        'the pseudo selector ' + pseudo.name + ' has not yet been implemented'\n      );\n    })\n  ) {\n    return true;\n  }\n  return false;\n}\n\nfunction matchExpression(vNode, expression) {\n  return (\n    matchesTag(vNode, expression) &&\n    matchesClasses(vNode, expression) &&\n    matchesAttributes(vNode, expression) &&\n    matchesId(vNode, expression) &&\n    matchesPseudos(vNode, expression)\n  );\n}\n\nconst escapeRegExp = (() => {\n  /*! Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License */\n  const from = /(?=[\\-\\[\\]{}()*+?.\\\\\\^$|,#\\s])/g;\n  const to = '\\\\';\n  return string => {\n    return string.replace(from, to);\n  };\n})();\n\nconst reUnescape = /\\\\/g;\nfunction convertAttributes(atts) {\n  /*! Credit Mootools Copyright Mootools, MIT License */\n  if (!atts) {\n    return;\n  }\n  return atts.map(att => {\n    const attributeKey = att.name.replace(reUnescape, '');\n    const attributeValue = (att.value || '').replace(reUnescape, '');\n    let test, regexp;\n\n    switch (att.operator) {\n      case '^=':\n        regexp = new RegExp('^' + escapeRegExp(attributeValue));\n        break;\n      case '$=':\n        regexp = new RegExp(escapeRegExp(attributeValue) + '$');\n        break;\n      case '~=':\n        regexp = new RegExp(\n          '(^|\\\\s)' + escapeRegExp(attributeValue) + '(\\\\s|$)'\n        );\n        break;\n      case '|=':\n        regexp = new RegExp('^' + escapeRegExp(attributeValue) + '(-|$)');\n        break;\n      case '=':\n        test = value => {\n          return attributeValue === value;\n        };\n        break;\n      case '*=':\n        test = value => {\n          return value && value.includes(attributeValue);\n        };\n        break;\n      case '!=':\n        test = value => {\n          return attributeValue !== value;\n        };\n        break;\n      // attribute existence\n      default:\n        test = value => {\n          return value !== null;\n        };\n    }\n\n    if (attributeValue === '' && /^[*$^]=$/.test(att.operator)) {\n      test = () => {\n        return false;\n      };\n    }\n\n    if (!test) {\n      test = value => {\n        return value && regexp.test(value);\n      };\n    }\n    return {\n      key: attributeKey,\n      value: attributeValue,\n      type: typeof att.value === 'undefined' ? 'attrExist' : 'attrValue',\n      test: test\n    };\n  });\n}\n\nfunction convertClasses(classes) {\n  if (!classes) {\n    return;\n  }\n  return classes.map(className => {\n    className = className.replace(reUnescape, '');\n\n    return {\n      value: className,\n      regexp: new RegExp('(^|\\\\s)' + escapeRegExp(className) + '(\\\\s|$)')\n    };\n  });\n}\n\nfunction convertPseudos(pseudos) {\n  if (!pseudos) {\n    return;\n  }\n  return pseudos.map(p => {\n    let expressions;\n\n    if (['is', 'not'].includes(p.name)) {\n      expressions = p.value;\n      expressions = expressions.selectors\n        ? expressions.selectors\n        : [expressions];\n      expressions = convertExpressions(expressions);\n    }\n    return {\n      name: p.name,\n      expressions: expressions,\n      value: p.value\n    };\n  });\n}\n\n/**\n * convert the css-selector-parser format into the Slick format\n * @private\n * @param Array {Object} expressions\n * @return Array {Object}\n *\n */\nfunction convertExpressions(expressions) {\n  return expressions.map(exp => {\n    const newExp = [];\n    let rule = exp.rule;\n    while (rule) {\n      /* eslint no-restricted-syntax: 0 */\n      // `.tagName` is a property coming from the `CSSSelectorParser` library\n      newExp.push({\n        tag: rule.tagName ? rule.tagName.toLowerCase() : '*',\n        combinator: rule.nestingOperator ? rule.nestingOperator : ' ',\n        id: rule.id,\n        attributes: convertAttributes(rule.attrs),\n        classes: convertClasses(rule.classNames),\n        pseudos: convertPseudos(rule.pseudos)\n      });\n      rule = rule.rule;\n    }\n    return newExp;\n  });\n}\n\n/**\n * Convert a CSS selector to the Slick format expression\n *\n * @private\n * @param {String} selector CSS selector to convert\n * @returns {Object[]} Array of Slick format expressions\n */\nexport function convertSelector(selector) {\n  let expressions = cssParser.parse(selector);\n  expressions = expressions.selectors ? expressions.selectors : [expressions];\n  return convertExpressions(expressions);\n}\n\n/**\n * Determine if a virtual node matches a Slick format CSS expression.\n *\n * Note: this function is in the hot path, avoid memory allocation.\n *\n * @private\n * @method optimizedMatchesExpression\n * @memberof axe.utils\n * @param {VirtualNode} vNode VirtualNode to match\n * @param {Object|Object[]} expressions CSS selector expression or array of expressions\n * @param {Number|NaN} index index of expression to match on if expressions is array\n * @returns {Boolean}\n */\nfunction optimizedMatchesExpression(vNode, expressions, index, matchAnyParent) {\n  if (!vNode) {\n    return false;\n  }\n\n  const isArray = Array.isArray(expressions);\n  const expression = isArray ? expressions[index] : expressions;\n  let machedExpression = matchExpression(vNode, expression);\n\n  while (!machedExpression && matchAnyParent && vNode.parent) {\n    vNode = vNode.parent;\n    machedExpression = matchExpression(vNode, expression);\n  }\n\n  if (index > 0) {\n    if ([' ', '>'].includes(expression.combinator) === false) {\n      throw new Error(\n        'axe.utils.matchesExpression does not support the combinator: ' +\n          expression.combinator\n      );\n    }\n\n    machedExpression =\n      machedExpression &&\n      optimizedMatchesExpression(\n        vNode.parent,\n        expressions,\n        index - 1,\n        expression.combinator === ' '\n      );\n  }\n\n  return machedExpression;\n}\n\n/**\n * Determine if a virtual node matches a Slick format CSS expression\n *\n * @private\n * @method matchesExpression\n * @memberof axe.utils\n * @param {VirtualNode} vNode VirtualNode to match\n * @param {Object|Object[]} expressions CSS selector expression or array of expressions\n * @returns {Boolean}\n */\nexport function matchesExpression(vNode, expressions, matchAnyParent) {\n  return optimizedMatchesExpression(\n    vNode,\n    expressions,\n    expressions.length - 1,\n    matchAnyParent\n  );\n}\n"
  },
  {
    "path": "lib/core/utils/memoize.js",
    "content": "import { memoize } from '../imports';\n\n// FYI: memoize does not always play nice with esbuild\n// and sometimes is built out of order.\n// See: https://github.com/evanw/esbuild/issues/1433\n//\n// To get around this, you may need to import this\n// file directly in the file you want to memoize.\n//\n// For example:\n// import memoize from '../../core/utils/memoize';\n// vs\n// import memoize from '../../core/utils';\n\n/**\n * Memoize a function.\n * @method memoize\n * @memberof axe.utils\n * @param {Function} fn Function to memoize\n * @return {Function}\n */\n// TODO: es-modules._memoziedFns\naxe._memoizedFns = [];\nfunction memoizeImplementation(fn) {\n  // keep track of each function that is memoized so it can be cleared at\n  // the end of a run. each memoized function has its own cache, so there is\n  // no method to clear all memoized caches. instead, we have to clear each\n  // individual memoized function ourselves.\n  const memoized = memoize(fn);\n  axe._memoizedFns.push(memoized);\n  return memoized;\n}\n\nexport default memoizeImplementation;\n"
  },
  {
    "path": "lib/core/utils/merge-results.js",
    "content": "import nodeSerializer from './node-serializer';\nimport getAllChecks from './get-all-checks';\nimport findBy from './find-by';\n\n/**\n * Adds the owning frame's CSS selector onto each instance of DqElement\n * @private\n * @param\t{Array} resultSet `nodes` array on a `RuleResult`\n * @param {Object} options Propagated from axe.run/etc\n * @param\t{Object} frameSpec The spec describing the owning frame (see nodeSerializer.toSpec)\n */\nfunction pushFrame(resultSet, options, frameSpec) {\n  resultSet.forEach(res => {\n    res.node = nodeSerializer.mergeSpecs(res.node, frameSpec);\n    const checks = getAllChecks(res);\n\n    checks.forEach(check => {\n      check.relatedNodes = check.relatedNodes.map(node =>\n        nodeSerializer.mergeSpecs(node, frameSpec)\n      );\n    });\n  });\n}\n\n/**\n * Adds `to` to `from` and then re-sorts by DOM order\n * @private\n * @param\t{Array} target\t`nodes` array on a `RuleResult`\n * @param\t{Array} to\t `nodes` array on a `RuleResult`\n * @return {Array}\t\t\tThe merged and sorted result\n */\nfunction spliceNodes(target, to) {\n  const firstFromFrame = to[0].node;\n  let node;\n  for (let i = 0; i < target.length; i++) {\n    node = target[i].node;\n    const resultSort = nodeIndexSort(\n      node.nodeIndexes,\n      firstFromFrame.nodeIndexes\n    );\n    if (\n      resultSort > 0 ||\n      (resultSort === 0 &&\n        firstFromFrame.selector.length < node.selector.length)\n    ) {\n      target.splice(i, 0, ...to);\n      return;\n    }\n  }\n  target.push(...to);\n}\n\nfunction normalizeResult(result) {\n  if (!result || !result.results) {\n    return null;\n  }\n\n  if (!Array.isArray(result.results)) {\n    return [result.results];\n  }\n\n  if (!result.results.length) {\n    return null;\n  }\n\n  return result.results;\n}\n\n/**\n * Merges one or more RuleResults (possibly from different frames) into one RuleResult\n * @private\n * @param\t{Array} frameResults Array of objects including the RuleResults as `results` and\n * owning frame as either an Element `frameElement` or a spec `frameSpec` (see nodeSerializer.toSpec)\n * @param {Object} options Propagated from axe.run/etc\n * @return {Array} The merged RuleResults; should only have one result per rule\n */\nfunction mergeResults(frameResults, options) {\n  const mergedResult = [];\n  frameResults.forEach(frameResult => {\n    const results = normalizeResult(frameResult);\n    if (!results || !results.length) {\n      return;\n    }\n\n    const frameSpec = getFrameSpec(frameResult);\n    results.forEach(ruleResult => {\n      if (ruleResult.nodes && frameSpec) {\n        pushFrame(ruleResult.nodes, options, frameSpec);\n      }\n\n      const res = findBy(mergedResult, 'id', ruleResult.id);\n      if (!res) {\n        mergedResult.push(ruleResult);\n      } else {\n        if (ruleResult.nodes.length) {\n          spliceNodes(res.nodes, ruleResult.nodes);\n        }\n        if (ruleResult.error) {\n          res.error ??= ruleResult.error;\n        }\n      }\n    });\n  });\n\n  // Sort results in DOM order\n  mergedResult.forEach(result => {\n    if (result.nodes) {\n      result.nodes.sort((nodeA, nodeB) => {\n        return nodeIndexSort(nodeA.node.nodeIndexes, nodeB.node.nodeIndexes);\n      });\n    }\n  });\n  return mergedResult;\n}\n\nfunction nodeIndexSort(nodeIndexesA = [], nodeIndexesB = []) {\n  const length = Math.max(nodeIndexesA?.length, nodeIndexesB?.length);\n  for (let i = 0; i < length; i++) {\n    const indexA = nodeIndexesA?.[i];\n    const indexB = nodeIndexesB?.[i];\n    if (typeof indexA !== 'number' || isNaN(indexA)) {\n      // Empty arrays go at the end, otherwise shortest array first\n      return i === 0 ? 1 : -1;\n    }\n    if (typeof indexB !== 'number' || isNaN(indexB)) {\n      return i === 0 ? -1 : 1;\n    }\n    if (indexA !== indexB) {\n      return indexA - indexB;\n    }\n  }\n  return 0;\n}\n\nexport default mergeResults;\n\nfunction getFrameSpec(frameResult) {\n  if (frameResult.frameElement) {\n    return nodeSerializer.toSpec(frameResult.frameElement);\n  } else if (frameResult.frameSpec) {\n    return frameResult.frameSpec;\n  }\n  return null;\n}\n"
  },
  {
    "path": "lib/core/utils/node-lookup.js",
    "content": "import AbstractVirtualNode from '../base/virtual-node/abstract-virtual-node';\nimport getNodeFromTree from './get-node-from-tree';\n\n/**\n * Get the virtual node and actual node from a parameter that could be either.\n *\n * @param {VirtualNode|Element} node\n * @return {Object} The Virtual Node and actual node\n */\nexport default function nodeLookup(node) {\n  if (node instanceof AbstractVirtualNode) {\n    return {\n      vNode: node,\n      domNode: node.actualNode\n    };\n  }\n\n  return {\n    vNode: getNodeFromTree(node),\n    domNode: node\n  };\n}\n"
  },
  {
    "path": "lib/core/utils/node-serializer.js",
    "content": "import assert from './assert';\nimport DqElement from './dq-element';\n\nlet customSerializer = null;\n\nconst nodeSerializer = {\n  /**\n   * @param {Object} newSerializer\n   * @property {Function} toSpec (Optional) Converts a DqElement to a \"spec\", a form\n   * suitable for JSON.stringify to consume. Output must include all properties\n   * that DqElement.toJSON() would have. Will always be invoked from the\n   * input element's original page context.\n   * @property {Function} mergeSpecs (Optional) Merges two specs (produced by toSpec) which\n   * represent element's parent frame and an element, respectively. Will\n   * *not* necessarily be invoked from *either* node's original page context.\n   * This operation must be associative, that is, these two expressions must\n   * produce the same result:\n   * - mergeSpecs(a, mergeSpecs(b, c))\n   * - mergeSpecs(mergeSpecs(a, b), c)\n   */\n  update(serializer) {\n    assert(typeof serializer === 'object', 'serializer must be an object');\n    customSerializer = serializer;\n  },\n\n  /**\n   * Converts an Element or VirtualNode to something that can be serialized.\n   * @param {Element|VirtualNode} node\n   * @return {Object} A \"spec\", a form suitable for JSON.stringify to consume.\n   */\n  toSpec(node) {\n    return nodeSerializer.dqElmToSpec(new DqElement(node));\n  },\n\n  /**\n   * Converts an DqElement to a serializable object. Optionally provide runOptions\n   * to limit which properties are included.\n   * @param {DqElement|SpecObject} dqElm\n   * @param {Object} runOptions (Optional) Set of options passed into rules or checks\n   * @param {Boolean} runOptions.selectors (Optional) Include selector in output\n   * @param {Boolean} runOptions.ancestry (Optional) Include ancestry in output\n   * @param {Boolean} runOptions.xpath (Optional) Include xpath in output\n   * @return {SpecObject} A \"spec\", a form suitable for JSON.stringify to consume.\n   */\n  dqElmToSpec(dqElm, runOptions) {\n    if (dqElm instanceof DqElement === false) {\n      return dqElm;\n    }\n    // Optionally remove selector, ancestry, xpath\n    // to prevent unnecessary calculations\n    if (runOptions) {\n      dqElm = cloneLimitedDqElement(dqElm, runOptions);\n    }\n\n    if (typeof customSerializer?.toSpec === 'function') {\n      return customSerializer.toSpec(dqElm);\n    }\n    return dqElm.toJSON();\n  },\n\n  /**\n   * Merges two specs (produced by toSpec) which represent\n   * element's parent frame and an element,\n   * @param {Object} nodeSpec\n   * @param {Object} parentFrameSpec\n   * @returns {Object} The merged spec\n   */\n  mergeSpecs(nodeSpec, parentFrameSpec) {\n    if (typeof customSerializer?.mergeSpecs === 'function') {\n      return customSerializer.mergeSpecs(nodeSpec, parentFrameSpec);\n    }\n    return DqElement.mergeSpecs(nodeSpec, parentFrameSpec);\n  },\n\n  /**\n   * Convert DqElements in RawResults to serialized nodes\n   * @param {undefined|RawNodeResult[]} rawResults\n   * @returns {undefined|RawNodeResult[]}\n   */\n  mapRawResults(rawResults) {\n    return rawResults.map(rawResult => ({\n      ...rawResult,\n      nodes: nodeSerializer.mapRawNodeResults(rawResult.nodes)\n    }));\n  },\n\n  /**\n   * Convert DqElements in RawNodeResults to serialized nodes\n   * @param {undefined|RawNodeResult[]} rawResults\n   * @returns {undefined|RawNodeResult[]}\n   */\n  mapRawNodeResults(nodeResults) {\n    return nodeResults?.map(({ node, ...nodeResult }) => {\n      nodeResult.node = nodeSerializer.dqElmToSpec(node);\n\n      for (const type of ['any', 'all', 'none']) {\n        nodeResult[type] = nodeResult[type].map(\n          ({ relatedNodes, ...checkResult }) => {\n            checkResult.relatedNodes = relatedNodes.map(\n              nodeSerializer.dqElmToSpec\n            );\n            return checkResult;\n          }\n        );\n      }\n      return nodeResult;\n    });\n  }\n};\n\nexport default nodeSerializer;\n\n/**\n * Create a new DqElement with only the properties we actually want serialized\n * This prevents nodeSerializer from generating selectors / xpath / ancestry\n * when it's not needed. The rest is dummy data to prevent possible errors.\n */\nfunction cloneLimitedDqElement(dqElm, runOptions) {\n  const fromFrame = dqElm.fromFrame;\n  const { ancestry: hasAncestry, xpath: hasXpath } = runOptions;\n  const hasSelectors = runOptions.selectors !== false || fromFrame;\n\n  dqElm = new DqElement(dqElm.element, runOptions, {\n    source: dqElm.source,\n    nodeIndexes: dqElm.nodeIndexes,\n    selector: hasSelectors ? dqElm.selector : [':root'],\n    ancestry: hasAncestry ? dqElm.ancestry : [':root'],\n    xpath: hasXpath ? dqElm.xpath : '/'\n  });\n\n  dqElm.fromFrame = fromFrame;\n  return dqElm;\n}\n"
  },
  {
    "path": "lib/core/utils/node-sorter.js",
    "content": "/**\n * Array#sort callback to sort nodes by DOM order\n * @private\n * @param  {Node} nodeA\n * @param  {Node} nodeB\n * @return {Integer}   @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Sort\n */\nfunction nodeSorter(nodeA, nodeB) {\n  /*eslint no-bitwise: 0 */\n  nodeA = nodeA.actualNode || nodeA;\n  nodeB = nodeB.actualNode || nodeB;\n  if (nodeA === nodeB) {\n    return 0;\n  }\n\n  if (nodeA.compareDocumentPosition(nodeB) & 4) {\n    return -1; // a before b\n  } else {\n    return 1; // b before a\n  }\n}\n\nexport default nodeSorter;\n"
  },
  {
    "path": "lib/core/utils/normalize-run-options.js",
    "content": "/**\n * Ensure all rules that are expected to run exist\n * @throws {Error} If any tag or rule specified in options is unknown\n * @param  {Object} options  Options object\n * @return {Object}          Validated options object\n */\nexport default function normalizeRunOptions(options = {}) {\n  const tags = [];\n  const ruleIds = [];\n  axe._audit.rules.forEach(rule => {\n    ruleIds.push(rule.id);\n    rule.tags.forEach(tag => {\n      if (!tags.includes(tag)) {\n        tags.push(tag);\n      }\n    });\n  });\n  // Validate runOnly\n  if (['object', 'string'].includes(typeof options.runOnly)) {\n    if (typeof options.runOnly === 'string') {\n      options.runOnly = [options.runOnly];\n    }\n    if (Array.isArray(options.runOnly)) {\n      const hasTag = options.runOnly.find(value => tags.includes(value));\n      const hasRule = options.runOnly.find(value => ruleIds.includes(value));\n      if (hasTag && hasRule) {\n        throw new Error('runOnly cannot be both rules and tags');\n      }\n      if (hasRule) {\n        options.runOnly = {\n          type: 'rule',\n          values: options.runOnly\n        };\n      } else {\n        options.runOnly = {\n          type: 'tag',\n          values: options.runOnly\n        };\n      }\n    }\n    const only = options.runOnly;\n    if (only.value && !only.values) {\n      only.values = only.value;\n      delete only.value;\n    }\n    if (!Array.isArray(only.values) || only.values.length === 0) {\n      throw new Error('runOnly.values must be a non-empty array');\n    }\n    // Check if every value in options.runOnly is a known rule ID\n    if (['rule', 'rules'].includes(only.type)) {\n      only.type = 'rule';\n      only.values.forEach(ruleId => {\n        if (!ruleIds.includes(ruleId)) {\n          throw new Error('unknown rule `' + ruleId + '` in options.runOnly');\n        }\n      });\n      // Validate 'tags' (e.g. anything not 'rule')\n    } else if (['tag', 'tags', undefined].includes(only.type)) {\n      only.type = 'tag';\n\n      const unmatchedTags = only.values.filter(\n        tag => !tags.includes(tag) && !/wcag2[1-3]a{1,3}/.test(tag)\n      );\n      if (unmatchedTags.length !== 0) {\n        axe.log('Could not find tags `' + unmatchedTags.join('`, `') + '`');\n      }\n    } else {\n      throw new Error(`Unknown runOnly type '${only.type}'`);\n    }\n  }\n  if (typeof options.rules === 'object') {\n    Object.keys(options.rules).forEach(ruleId => {\n      if (!ruleIds.includes(ruleId)) {\n        throw new Error('unknown rule `' + ruleId + '` in options.rules');\n      }\n    });\n  }\n  return options;\n}\n"
  },
  {
    "path": "lib/core/utils/object-has-own.js",
    "content": "// Wrapper to prevent throwing for non-objects & null\nexport default function objectHasOwn(obj, prop) {\n  if (!obj || typeof obj !== 'object') {\n    return false;\n  }\n  return Object.prototype.hasOwnProperty.call(obj, prop);\n}\n"
  },
  {
    "path": "lib/core/utils/parse-crossorigin-stylesheet.js",
    "content": "import parseStylesheet from './parse-stylesheet';\nimport constants from '../constants';\n\n/**\n * Parse cross-origin stylesheets\n *\n * @method parseCrossOriginStylesheet\n * @memberof axe.utils\n * @param {String} url url from which to fetch stylesheet\n * @param {Object} options options object from `axe.utils.parseStylesheet`\n * @param {Array<Number>} priority sheet priority\n * @param {Array<String>} importedUrls urls of already imported stylesheets\n * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`\n * @returns {Promise}\n */\nfunction parseCrossOriginStylesheet(\n  url,\n  options,\n  priority,\n  importedUrls,\n  isCrossOrigin\n) {\n  /**\n   * Add `url` to `importedUrls`\n   */\n  importedUrls.push(url);\n\n  return new Promise((resolve, reject) => {\n    const request = new window.XMLHttpRequest();\n    request.open('GET', url);\n\n    request.timeout = constants.preload.timeout;\n    request.addEventListener('error', reject);\n    request.addEventListener('timeout', reject);\n    request.addEventListener('loadend', event => {\n      if (event.loaded && request.responseText) {\n        return resolve(request.responseText);\n      }\n\n      reject(request.responseText);\n    });\n\n    request.send();\n  }).then(data => {\n    const result = options.convertDataToStylesheet({\n      data,\n      isCrossOrigin,\n      priority,\n      root: options.rootNode,\n      shadowId: options.shadowId\n    });\n\n    /**\n     * Parse resolved stylesheet further for any `@import` styles\n     */\n    return parseStylesheet(\n      result.sheet,\n      options,\n      priority,\n      importedUrls,\n      result.isCrossOrigin\n    );\n  });\n}\n\nexport default parseCrossOriginStylesheet;\n"
  },
  {
    "path": "lib/core/utils/parse-sameorigin-stylesheet.js",
    "content": "import parseCrossOriginStylesheet from './parse-crossorigin-stylesheet';\n\n/**\n * Parse non cross-origin stylesheets\n *\n * @method parseSameOriginStylesheet\n * @memberof axe.utils\n * @param {Object} sheet CSSStylesheet object\n * @param {Object} options options object from `axe.utils.parseStylesheet`\n * @param {Array<Number>} priority sheet priority\n * @param {Array<String>} importedUrls urls of already imported stylesheets\n * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`\n * @returns {Promise}\n */\nfunction parseSameOriginStylesheet(\n  sheet,\n  options,\n  priority,\n  importedUrls,\n  isCrossOrigin = false\n) {\n  const rules = Array.from(sheet.cssRules);\n\n  if (!rules) {\n    return Promise.resolve();\n  }\n\n  /**\n   * reference -> https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants\n   */\n  const cssImportRules = rules.filter(r => r.type === 3); // type === 3 -> CSSRule.IMPORT_RULE\n\n  /**\n   * when no `@import` rules in given sheet -> resolve the current `sheet` & exit\n   */\n  if (!cssImportRules.length) {\n    // exit\n    return Promise.resolve({\n      isCrossOrigin,\n      priority,\n      root: options.rootNode,\n      shadowId: options.shadowId,\n      sheet\n    });\n  }\n\n  /**\n   * filter rules that are not already fetched\n   */\n  const cssImportUrlsNotAlreadyImported = cssImportRules\n    // ensure rule has a href\n    .filter(rule => rule.href)\n    // extract href from object\n    .map(rule => rule.href)\n    // only href that are not already imported\n    .filter(url => !importedUrls.includes(url));\n\n  /**\n   * iterate `@import` rules and fetch styles\n   */\n  const promises = cssImportUrlsNotAlreadyImported.map(\n    (importUrl, cssRuleIndex) => {\n      const newPriority = [...priority, cssRuleIndex];\n      const isCrossOriginRequest = /^https?:\\/\\/|^\\/\\//i.test(importUrl);\n\n      return parseCrossOriginStylesheet(\n        importUrl,\n        options,\n        newPriority,\n        importedUrls,\n        isCrossOriginRequest\n      );\n    }\n  );\n\n  const nonImportCSSRules = rules.filter(r => r.type !== 3);\n\n  // no further rules to process in this sheet\n  if (!nonImportCSSRules.length) {\n    return Promise.all(promises);\n  }\n\n  // convert all `nonImportCSSRules` style rules into `text` and chain\n\n  promises.push(\n    Promise.resolve(\n      options.convertDataToStylesheet({\n        data: nonImportCSSRules.map(rule => rule.cssText).join(),\n        isCrossOrigin,\n        priority,\n        root: options.rootNode,\n        shadowId: options.shadowId\n      })\n    )\n  );\n\n  return Promise.all(promises);\n}\n\nexport default parseSameOriginStylesheet;\n"
  },
  {
    "path": "lib/core/utils/parse-stylesheet.js",
    "content": "import parseSameOriginStylesheet from './parse-sameorigin-stylesheet';\nimport parseCrossOriginStylesheet from './parse-crossorigin-stylesheet';\n\n/**\n * Parse a given stylesheet\n *\n * @method parseStylesheet\n * @memberof axe.utils\n * @param {Object} sheet stylesheet to parse\n * @param {Object} options configuration options object from `parseStylesheets`\n * @param {Array<Number>} priority priority of stylesheet\n * @param {Array<String>} importedUrls list of resolved `@import` urls\n * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`, passed for re-parsing `cross-origin` sheets\n * @returns {Promise}\n */\nfunction parseStylesheet(\n  sheet,\n  options,\n  priority,\n  importedUrls,\n  isCrossOrigin = false\n) {\n  const isSameOrigin = isSameOriginStylesheet(sheet);\n  if (isSameOrigin) {\n    /**\n     * resolve `same-origin` stylesheet\n     */\n    return parseSameOriginStylesheet(\n      sheet,\n      options,\n      priority,\n      importedUrls,\n      isCrossOrigin\n    );\n  }\n\n  /**\n   * resolve `cross-origin` stylesheet\n   */\n  return parseCrossOriginStylesheet(\n    sheet.href,\n    options,\n    priority,\n    importedUrls,\n    true // -> isCrossOrigin\n  );\n}\n\n/**\n * Check if a given stylesheet is from the `same-origin`\n * Note:\n * `sheet.cssRules` throws an error on `cross-origin` stylesheets\n *\n * @param {Object} sheet CSS stylesheet\n * @returns {Boolean}\n */\nfunction isSameOriginStylesheet(sheet) {\n  try {\n    /*eslint no-unused-vars: 0*/\n    const rules = sheet.cssRules;\n\n    /**\n     * Safari, does not throw an error when accessing cssRules property,\n     */\n    if (!rules && sheet.href) {\n      return false;\n    }\n\n    return true;\n  } catch (e) {\n    return false;\n  }\n}\n\nexport default parseStylesheet;\n"
  },
  {
    "path": "lib/core/utils/parse-tabindex.js",
    "content": "/**\n * Parses a tabindex value to return an integer if valid, or null if invalid.\n * @method parseTabindex\n * @memberof axe.utils\n * @param  {string|null} str\n * @return {number|null}\n */\nfunction parseTabindex(value) {\n  if (typeof value !== 'string') {\n    return null;\n  }\n\n  // spec: https://html.spec.whatwg.org/#rules-for-parsing-integers\n  const match = value.trim().match(/^([-+]?\\d+)/);\n  if (match) {\n    return Number(match[1]);\n  }\n\n  return null;\n}\n\nexport default parseTabindex;\n"
  },
  {
    "path": "lib/core/utils/performance-timer.js",
    "content": "import log from '../log';\n\n/**\n * Performance measuring utility shimming the User Timing API\n *\n * https://www.html5rocks.com/en/tutorials/webperformance/usertiming/\n * http://caniuse.com/#search=User%20Timing\n *\n */\nconst performanceTimer = (() => {\n  /**\n   * Get a time/date object using performance.now() if supported\n   * @return {DOMTimeStamp}\n   */\n  function now() {\n    if (window.performance && window.performance) {\n      return window.performance.now();\n    }\n  }\n  let axeStartTime = now();\n  let axeStarted = false;\n\n  /**\n   * @typedef {utils.performanceTimer} Public API Methods\n   */\n  return {\n    /**\n     * @member {Function} start Kicks off performance timing for overall axe audit\n     */\n    start() {\n      this.reset();\n      axeStarted = true;\n      this.mark('mark_axe_start');\n    },\n\n    /**\n     * @member {Function} end Concludes performance timing, compares start/end marks\n     */\n    end() {\n      this.mark('mark_axe_end');\n      this.measure('axe', 'mark_axe_start', 'mark_axe_end', true);\n      this.logMeasures('axe');\n      this.clearMark('mark_axe_start', 'mark_axe_end');\n      axeStarted = false;\n    },\n\n    /**\n     * @member {Function} auditStart Starts an audit for a page or frame\n     */\n    auditStart() {\n      if (!axeStarted) {\n        this.reset(); // We're in a frame\n      }\n      this.mark('mark_audit_start');\n    },\n\n    /**\n     * @member {Function} auditEnd Ends an audit for a page or frame, logs measurement of start/end marks\n     */\n    auditEnd() {\n      this.mark('mark_audit_end');\n      this.measure(\n        'audit_start_to_end',\n        'mark_audit_start',\n        'mark_audit_end',\n        true\n      );\n      // log audit/rule measures\n      this.logMeasures();\n      this.clearMark('mark_audit_start', 'mark_audit_end');\n    },\n\n    /**\n     * @member {Function} mark Shims creating a new named time stamp, called a mark\n     * @param {String} markName String name to record how long it took to get there.\n     * A mark that already exists will be overwritten.\n     *\n     */\n    mark(markName) {\n      if (window.performance?.mark) {\n        window.performance.mark(markName);\n      }\n    },\n\n    /**\n     * @member {Function} measure Shims creating a measure to compare two marks, which can be logged\n     * @param {String} measureName String name to log what is being compared.\n     * Measures that already exist will be overwritten with a new time stamp.\n     * @param {String} startMark String name of mark to start measuring\n     * @param {String} endMark String name of mark to end measuring\n     */\n    measure(measureName, startMark, endMark, keepMarks = false) {\n      if (!window.performance?.measure) {\n        return;\n      }\n      try {\n        window.performance.measure(measureName, startMark, endMark);\n      } catch (e) {\n        this._log(e);\n      }\n      if (!keepMarks) {\n        this.clearMark(startMark, endMark);\n      }\n    },\n\n    /**\n     * @member {Function} logMeasures Logs previously captured measures in chronological order.\n     * Starts from the most recent start()/auditStart() call.\n     * @param {String|undefined} measureName If provided, will only log up to the first matching measure.\n     */\n    logMeasures(measureName) {\n      const last = arr => (Array.isArray(arr) ? arr[arr.length - 1] : arr);\n      const logMeasure = req => {\n        this._log('Measure ' + req.name + ' took ' + req.duration + 'ms');\n      };\n      if (\n        !window.performance?.getEntriesByType ||\n        !window.performance?.getEntriesByName\n      ) {\n        return;\n      }\n      // only output measures that were started after axe started, otherwise\n      // we get measures made by the page before axe ran (which is confusing)\n      const axeStart =\n        last(window.performance.getEntriesByName('mark_axe_start')) ||\n        last(window.performance.getEntriesByName('mark_audit_start'));\n      if (!axeStart) {\n        this._log('Axe must be started before using performanceTimer');\n        return;\n      }\n\n      const measures = window.performance\n        .getEntriesByType('measure')\n        .filter(measure => measure.startTime >= axeStart.startTime);\n      for (let i = 0; i < measures.length; ++i) {\n        const req = measures[i];\n        if (req.name === measureName) {\n          logMeasure(req);\n          return;\n        } else if (!measureName) {\n          logMeasure(req);\n        }\n      }\n    },\n\n    /**\n     * @member {Function} timeElapsed Records time since last audit\n     * @return {DOMTimeStamp}\n     */\n    timeElapsed() {\n      const currentTime = now();\n      return currentTime - axeStartTime;\n    },\n\n    /**\n     * @member {Function} clearMark Clears a mark\n     * @param {String} markName String name of mark to clear\n     */\n    clearMark(...markNames) {\n      if (!window.performance?.clearMarks) {\n        return;\n      }\n\n      for (const markName of markNames) {\n        window.performance.clearMarks(markName);\n      }\n    },\n\n    /**\n     * @member {Function} reset Resets the time for a new audit\n     */\n    reset() {\n      axeStartTime = now();\n    },\n\n    /**\n     * Logs the message, available to override in testing\n     * @private\n     * @param {String} message\n     */\n    _log(message) {\n      log(message);\n    }\n  };\n})();\n\nexport default performanceTimer;\n"
  },
  {
    "path": "lib/core/utils/pollyfill-elements-from-point.js",
    "content": "// Spelled incorrectly intentionally (backwards compatibility).\nexport function pollyfillElementsFromPoint() {\n  if (document.elementsFromPoint) return document.elementsFromPoint;\n  if (document.msElementsFromPoint) return document.msElementsFromPoint;\n\n  const usePointer = (function () {\n    const element = document.createElement('x');\n    element.style.cssText = 'pointer-events:auto';\n    return element.style.pointerEvents === 'auto';\n  })();\n\n  const cssProp = usePointer ? 'pointer-events' : 'visibility';\n  const cssDisableVal = usePointer ? 'none' : 'hidden';\n\n  const style = document.createElement('style');\n  style.innerHTML = usePointer\n    ? '* { pointer-events: all }'\n    : '* { visibility: visible }';\n\n  return function (x, y) {\n    let current, i, d;\n    const elements = [];\n    const previousPointerEvents = [];\n\n    // startup\n    document.head.appendChild(style);\n\n    while (\n      (current = document.elementFromPoint(x, y)) &&\n      elements.indexOf(current) === -1\n    ) {\n      // push the element and its current style\n      elements.push(current);\n\n      previousPointerEvents.push({\n        value: current.style.getPropertyValue(cssProp),\n        priority: current.style.getPropertyPriority(cssProp)\n      });\n\n      // add \"pointer-events: none\", to get to the underlying element\n      current.style.setProperty(cssProp, cssDisableVal, 'important');\n    }\n\n    // Due to negative index, documentElement could actually not be the last,\n    // so we'll simply move it to the end\n    if (elements.indexOf(document.documentElement) < elements.length - 1) {\n      elements.splice(elements.indexOf(document.documentElement), 1);\n      elements.push(document.documentElement);\n    }\n\n    // restore the previous pointer-events values\n    for (\n      i = previousPointerEvents.length;\n      !!(d = previousPointerEvents[--i]);\n    ) {\n      elements[i].style.setProperty(\n        cssProp,\n        d.value ? d.value : '',\n        d.priority\n      );\n    }\n\n    // teardown;\n    document.head.removeChild(style);\n\n    return elements;\n  };\n}\n\nif (typeof window.addEventListener === 'function') {\n  document.elementsFromPoint = pollyfillElementsFromPoint();\n}\n"
  },
  {
    "path": "lib/core/utils/preload-cssom.js",
    "content": "import getStyleSheetFactory from './get-stylesheet-factory';\nimport uniqueArray from './unique-array';\nimport getRootNode from './get-root-node';\nimport parseStylesheet from './parse-stylesheet';\nimport querySelectorAllFilter from './query-selector-all-filter';\n\n/**\n * Given a rootNode - construct CSSOM\n * -> get all source nodes (document & document fragments) within given root node\n * -> recursively call `parseStylesheets` to resolve styles for each node\n *\n * @method preloadCssom\n * @memberof `axe.utils`\n * @param {Object} options composite options object\n * @property {Array<String>} options.assets array of preloaded assets requested, eg: [`cssom`]\n * @property {Number} options.timeout timeout\n * @property {Object} options.treeRoot (optional) the DOM tree to be inspected\n * @returns {Promise}\n */\n// TODO: es-modules_tree\nfunction preloadCssom({ treeRoot = axe._tree[0] }) {\n  /**\n   * get all `document` and `documentFragment` with in given `tree`\n   */\n  const rootNodes = getAllRootNodesInTree(treeRoot);\n\n  if (!rootNodes.length) {\n    return Promise.resolve();\n  }\n\n  const dynamicDoc = document.implementation.createHTMLDocument(\n    'Dynamic document for loading cssom'\n  );\n\n  const convertDataToStylesheet = getStyleSheetFactory(dynamicDoc);\n\n  return getCssomForAllRootNodes(rootNodes, convertDataToStylesheet).then(\n    assets => flattenAssets(assets)\n  );\n}\n\nexport default preloadCssom;\n\n/**\n * Returns am array of source nodes containing `document` and `documentFragment` in a given `tree`.\n *\n * @param {Object} treeRoot tree\n * @returns {Array<Object>} array of objects, which each object containing a root and an optional `shadowId`\n */\nfunction getAllRootNodesInTree(tree) {\n  const ids = [];\n\n  const rootNodes = querySelectorAllFilter(tree, '*', node => {\n    if (ids.includes(node.shadowId)) {\n      return false;\n    }\n    ids.push(node.shadowId);\n    return true;\n  }).map(node => {\n    return {\n      shadowId: node.shadowId,\n      rootNode: getRootNode(node.actualNode)\n    };\n  });\n\n  return uniqueArray(rootNodes, []);\n}\n\n/**\n * Process CSSOM on all root nodes\n *\n * @param {Array<Object>} rootNodes array of root nodes, where node  is an enhanced `document` or `documentFragment` object returned from `getAllRootNodesInTree`\n * @param {Function} convertDataToStylesheet fn to convert given data to Stylesheet object\n * @returns {Promise}\n */\nfunction getCssomForAllRootNodes(rootNodes, convertDataToStylesheet) {\n  const promises = [];\n\n  rootNodes.forEach(({ rootNode, shadowId }, index) => {\n    const sheets = getStylesheetsOfRootNode(\n      rootNode,\n      shadowId,\n      convertDataToStylesheet\n    );\n    if (!sheets) {\n      return Promise.all(promises);\n    }\n\n    const rootIndex = index + 1;\n    const parseOptions = {\n      rootNode,\n      shadowId,\n      convertDataToStylesheet,\n      rootIndex\n    };\n    /**\n     * Note:\n     * `importedUrls` - keeps urls of already imported stylesheets, to prevent re-fetching\n     * eg: nested, cyclic or cross referenced `@import` urls\n     */\n    const importedUrls = [];\n\n    const p = Promise.all(\n      sheets.map((sheet, sheetIndex) => {\n        const priority = [rootIndex, sheetIndex];\n\n        return parseStylesheet(sheet, parseOptions, priority, importedUrls);\n      })\n    );\n\n    promises.push(p);\n  });\n\n  return Promise.all(promises);\n}\n\n/**\n * Flatten CSSOM assets\n *\n * @param {Array.<Object[]>} assets nested assets (varying depth)\n * @returns {Array<Object>} Array of CSSOM object\n */\nfunction flattenAssets(assets) {\n  return assets.reduce(\n    (acc, val) =>\n      Array.isArray(val) ? acc.concat(flattenAssets(val)) : acc.concat(val),\n    []\n  );\n}\n\n/**\n * Get stylesheet(s) for root\n *\n * @param {Object} options.rootNode `document` or `documentFragment`\n * @param {String} options.shadowId an id if undefined denotes that given root is a document fragment/ shadowDOM\n * @param {Function} options.convertDataToStylesheet a utility function to generate a style sheet from given data (text)\n * @returns {Array<Object>} an array of stylesheets\n */\nfunction getStylesheetsOfRootNode(rootNode, shadowId, convertDataToStylesheet) {\n  let sheets;\n\n  // nodeType === 11  -> DOCUMENT_FRAGMENT\n  if (rootNode.nodeType === 11 && shadowId) {\n    sheets = getStylesheetsFromDocumentFragment(\n      rootNode,\n      convertDataToStylesheet\n    );\n  } else {\n    sheets = getStylesheetsFromDocument(rootNode);\n  }\n\n  return filterStylesheetsWithSameHref(sheets);\n}\n\n/**\n * Get stylesheets from `documentFragment`\n *\n * @property {Object} options.rootNode `documentFragment`\n * @property {Function} options.convertDataToStylesheet a utility function to generate a stylesheet from given data\n * @returns {Array<Object>}\n */\nfunction getStylesheetsFromDocumentFragment(rootNode, convertDataToStylesheet) {\n  return (\n    Array.from(rootNode.children)\n      .filter(filerStyleAndLinkAttributesInDocumentFragment)\n      // Reducer to convert `<style></style>` and `<link>` references to `CSSStyleSheet` object\n      .reduce((out, node) => {\n        const nodeName = node.nodeName.toUpperCase();\n        const data = nodeName === 'STYLE' ? node.textContent : node;\n        const isLink = nodeName === 'LINK';\n        const stylesheet = convertDataToStylesheet({\n          data,\n          isLink,\n          root: rootNode\n        });\n        // prevent error in jsdom with style elements not having a `sheet` property\n        // @see https://github.com/jsdom/jsdom/issues/3179\n        if (stylesheet.sheet) {\n          out.push(stylesheet.sheet);\n        }\n        return out;\n      }, [])\n  );\n}\n\n/**\n * Get stylesheets from `document`\n * -> filter out stylesheet that are `media=print`\n *\n * @param {Object} rootNode `document`\n * @returns {Array<Object>}\n */\nfunction getStylesheetsFromDocument(rootNode) {\n  return Array.from(rootNode.styleSheets).filter(sheet => {\n    if (!sheet.media) {\n      return false;\n    }\n\n    return filterMediaIsPrint(sheet.media.mediaText);\n  });\n}\n\n/**\n * Get all `<style></style>` and `<link>` attributes\n * -> limit to only `style` or `link` attributes with `rel=stylesheet` and `media != print`\n *\n * @param {Object} node HTMLElement\n * @returns {Boolean}\n */\nfunction filerStyleAndLinkAttributesInDocumentFragment(node) {\n  const nodeName = node.nodeName.toUpperCase();\n  const linkHref = node.getAttribute('href');\n  const linkRel = node.getAttribute('rel');\n  const isLink =\n    nodeName === 'LINK' &&\n    linkHref &&\n    linkRel &&\n    node.rel.toUpperCase().includes('STYLESHEET');\n  const isStyle = nodeName === 'STYLE';\n  return isStyle || (isLink && filterMediaIsPrint(node.media));\n}\n\n/**\n * Exclude `link[rel='stylesheet]` attributes where `media=print`\n *\n * @param {String} media media value eg: 'print'\n * @returns {Boolean}\n */\nfunction filterMediaIsPrint(media) {\n  if (!media) {\n    return true;\n  }\n  return !media.toUpperCase().includes('PRINT');\n}\n\n/**\n * Exclude any duplicate `stylesheets`, that share the same `href`\n *\n * @param {Array<Object>} sheets stylesheets\n * @returns {Array<Object>}\n */\nfunction filterStylesheetsWithSameHref(sheets) {\n  const hrefs = [];\n  return sheets.filter(sheet => {\n    if (!sheet.href) {\n      // include sheets without `href`\n      return true;\n    }\n    // if `href` is present, ensure they are not duplicates\n    if (hrefs.includes(sheet.href)) {\n      return false;\n    }\n    hrefs.push(sheet.href);\n    return true;\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/preload-media.js",
    "content": "import querySelectorAllFilter from './query-selector-all-filter';\n\n/**\n * Given a rootNode\n * -> get all HTMLMediaElement's and ensure their metadata is loaded\n *\n * @method preloadMedia\n * @memberof axe.utils\n * @property {Object} options.treeRoot (optional) the DOM tree to be inspected\n */\n// TODO: es-modules_tree\nfunction preloadMedia({ treeRoot = axe._tree[0] }) {\n  const mediaVirtualNodes = querySelectorAllFilter(\n    treeRoot,\n    /**\n     * Only concern ourselves with media that autoplays as the no-autoplay-audio rule\n     * is the only rule that uses this information\n     */\n    'video[autoplay], audio[autoplay]',\n    ({ actualNode }) => {\n      /**\n       * Ignore media that won't load no matter how long we wait (i.e. preload=none).\n       *\n       * Although the spec says that the autoplay attribute can override the preload\n       * attribute, it depends on the browser settings (if autoplay is allowed) and\n       * operating system (e.g. Android does not preload autoplay media even when\n       * autoplay is allowed).\n       *\n       * We can identify preload=none media that won't load if the networkState is\n       * idle and the readyState is 0. If the browser is currently loading the media\n       * (networkState) or if the media is already loaded (readyState) that means the\n       * preload attribute was ignored.\n       *\n       * @see https://github.com/dequelabs/axe-core/issues/4665\n       * @see https://html.spec.whatwg.org/multipage/media.html#attr-media-preload\n       */\n      if (\n        actualNode.preload === 'none' &&\n        actualNode.readyState === 0 &&\n        actualNode.networkState !== actualNode.NETWORK_LOADING\n      ) {\n        return false;\n      }\n\n      /**\n       * Ignore media nodes which are `paused` or `muted` as the no-autoplay-audio\n       * rule matcher ignores them\n       */\n      if (\n        actualNode.hasAttribute('paused') ||\n        actualNode.hasAttribute('muted')\n      ) {\n        return false;\n      }\n\n      /**\n       * This is to safe-gaurd against empty `src` values which can get resolved `window.location`, thus never preloading as the URL is not a media asset\n       */\n      if (actualNode.hasAttribute('src')) {\n        return !!actualNode.getAttribute('src');\n      }\n\n      /**\n       * The `src` on <source> element is essential for `audio` and `video` elements\n       */\n      const sourceWithSrc = Array.from(\n        actualNode.getElementsByTagName('source')\n      ).filter(source => !!source.getAttribute('src'));\n      if (sourceWithSrc.length <= 0) {\n        return false;\n      }\n\n      return true;\n    }\n  );\n\n  return Promise.all(\n    mediaVirtualNodes.map(({ actualNode }) => isMediaElementReady(actualNode))\n  );\n}\n\nexport default preloadMedia;\n\n/**\n * Ensures a media element's metadata is loaded\n * @param {HTMLMediaElement} elm elm\n * @returns {Promise}\n */\nfunction isMediaElementReady(elm) {\n  return new Promise(resolve => {\n    /**\n     * See - https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState\n     */\n    if (elm.readyState > 0) {\n      resolve(elm);\n    }\n\n    function onMediaReady() {\n      elm.removeEventListener('loadedmetadata', onMediaReady);\n      resolve(elm);\n    }\n\n    /**\n     * Given media is not ready, wire up listener for `loadedmetadata`\n     * See - https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event\n     */\n    elm.addEventListener('loadedmetadata', onMediaReady);\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/preload.js",
    "content": "import preloadCssom from './preload-cssom';\nimport preloadMedia from './preload-media';\nimport uniqueArray from './unique-array';\nimport constants from '../constants';\n\n/**\n * Returns a Promise with results of all requested preload(able) assets. eg: ['cssom'].\n *\n * @param {Object} options run configuration options (or defaults) passed via axe.run\n * @return {Object} Promise\n */\nexport default function preload(options) {\n  const preloadFunctionsMap = {\n    cssom: preloadCssom,\n    media: preloadMedia\n  };\n\n  if (!shouldPreload(options)) {\n    return Promise.resolve();\n  }\n\n  return new Promise((resolve, reject) => {\n    const { assets, timeout } = getPreloadConfig(options);\n\n    /**\n     * Start `timeout` timer for preloading assets\n     * -> reject if allowed time expires.\n     */\n    const preloadTimeout = setTimeout(\n      () => reject(new Error(`Preload assets timed out.`)),\n      timeout\n    );\n\n    /**\n     * Fetch requested `assets`\n     */\n    Promise.all(\n      assets.map(asset =>\n        preloadFunctionsMap[asset](options).then(results => {\n          return {\n            [asset]: results\n          };\n        })\n      )\n    )\n      .then(results => {\n        /**\n         * Combine array of results into an object map\n         *\n         * From ->\n         *  [{cssom: [...], aom: [...]}]\n         * To ->\n         *  {\n         *    cssom: [...]\n         *    aom: [...]\n         *  }\n         */\n        const preloadAssets = results.reduce((out, result) => {\n          return {\n            ...out,\n            ...result\n          };\n        }, {});\n\n        clearTimeout(preloadTimeout);\n        resolve(preloadAssets);\n      })\n      .catch(err => {\n        clearTimeout(preloadTimeout);\n        reject(err);\n      });\n  });\n}\n\n/**\n * Validated the preload object\n * @param {Object | boolean} preloadObj configuration object or boolean passed via the options parameter to axe.run\n * @return {boolean}\n * @private\n */\nfunction isValidPreloadObject(preloadObj) {\n  return typeof preloadObj === 'object' && Array.isArray(preloadObj.assets);\n}\n\n/**\n * Returns a boolean which decides if preload is configured\n * @param {Object} options run configuration options (or defaults) passed via axe.run\n * @return {boolean} defaults to true\n */\nexport function shouldPreload(options) {\n  if (!options || options.preload === undefined || options.preload === null) {\n    return true; // by default `preload` requested assets eg: ['cssom']\n  }\n  if (typeof options.preload === 'boolean') {\n    return options.preload;\n  }\n  return isValidPreloadObject(options.preload);\n}\n\n/**\n * Constructs a configuration object representing the preload requested assets & timeout\n * @param {Object} options run configuration options (or defaults) passed via axe.run\n * @return {Object} configuration\n */\nexport function getPreloadConfig(options) {\n  const { assets, timeout } = constants.preload;\n  const config = {\n    assets,\n    timeout\n  };\n\n  // if no `preload` is configured via `options` - return default config\n  if (!options.preload) {\n    return config;\n  }\n\n  // if type is boolean\n  if (typeof options.preload === 'boolean') {\n    return config;\n  }\n\n  // check if requested assets to preload are valid items\n  const areRequestedAssetsValid = options.preload.assets.every(a =>\n    assets.includes(a.toLowerCase())\n  );\n\n  if (!areRequestedAssetsValid) {\n    throw new Error(\n      `Requested assets, not supported. ` +\n        `Supported assets are: ${assets.join(', ')}.`\n    );\n  }\n\n  // unique assets to load, in case user had requested same asset type many times.\n  config.assets = uniqueArray(\n    options.preload.assets.map(a => a.toLowerCase()),\n    []\n  );\n\n  if (\n    options.preload.timeout &&\n    typeof options.preload.timeout === 'number' &&\n    !isNaN(options.preload.timeout)\n  ) {\n    config.timeout = options.preload.timeout;\n  }\n  return config;\n}\n"
  },
  {
    "path": "lib/core/utils/process-message.js",
    "content": "import { incompleteFallbackMessage } from '../reporters/helpers';\n\nconst dataRegex = /\\$\\{\\s?data\\s?\\}/g;\n\n/**\n * Replace a placeholder with the value of a data string or object\n * @param {String} str\n * @param {String|Object} data\n */\nfunction substitute(str, data) {\n  // replace all instances of ${ data } with the value of the string\n  if (typeof data === 'string') {\n    return str.replace(dataRegex, data);\n  }\n\n  // replace all instances of ${ data[prop] } with the value of the property\n  for (const prop in data) {\n    if (data.hasOwnProperty(prop)) {\n      const regex = new RegExp('\\\\${\\\\s?data\\\\.' + prop + '\\\\s?}', 'g');\n      const replace =\n        typeof data[prop] === 'undefined' ? '' : String(data[prop]);\n      str = str.replace(regex, replace);\n    }\n  }\n\n  return str;\n}\n\n/**\n * Process a metadata message.\n * @param {String|Object} message\n * @param {Object} data\n * @return {String}\n */\nfunction processMessage(message, data) {\n  if (!message) {\n    return;\n  }\n\n  // data as array\n  if (Array.isArray(data)) {\n    data.values = data.join(', ');\n\n    if (\n      typeof message.singular === 'string' &&\n      typeof message.plural === 'string'\n    ) {\n      const str = data.length === 1 ? message.singular : message.plural;\n      return substitute(str, data);\n    }\n\n    // no singular/plural message so just pass data\n    return substitute(message, data);\n  }\n\n  // message is a string that uses data as a string or object properties\n  if (typeof message === 'string') {\n    return substitute(message, data);\n  }\n\n  // message is an object that uses value of data to determine message\n  if (typeof data === 'string') {\n    const str = message[data];\n    return substitute(str, data);\n  }\n\n  // message uses value of data property to determine message\n  let str = message.default || incompleteFallbackMessage();\n\n  if (data && data.messageKey && message[data.messageKey]) {\n    str = message[data.messageKey];\n  }\n\n  return processMessage(str, data);\n}\n\nexport default processMessage;\n"
  },
  {
    "path": "lib/core/utils/publish-metadata.js",
    "content": "import processMessage from './process-message';\nimport clone from './clone';\nimport findBy from './find-by';\nimport extendMetaData from './extend-meta-data';\nimport incompleteFallbackMessage from '../reporters/helpers/incomplete-fallback-msg';\n\n/**\n * Publish metadata from axe._audit.data\n * @param  {RuleResult} result Result to publish to\n * @private\n */\nexport default function publishMetaData(ruleResult) {\n  // TODO: es-modules_audit\n  const checksData = axe._audit.data.checks || {};\n  const rulesData = axe._audit.data.rules || {};\n  const rule = findBy(axe._audit.rules, 'id', ruleResult.id) || {};\n\n  ruleResult.tags = clone(rule.tags || []);\n\n  const shouldBeTrue = extender(checksData, true, rule);\n  const shouldBeFalse = extender(checksData, false, rule);\n  ruleResult.nodes.forEach(detail => {\n    detail.any.forEach(shouldBeTrue);\n    detail.all.forEach(shouldBeTrue);\n    detail.none.forEach(shouldBeFalse);\n  });\n  extendMetaData(ruleResult, clone(rulesData[ruleResult.id] || {}));\n}\n\n/**\n * Construct incomplete message from check.data\n * @param  {Object} checkData Check result with reason specified\n * @param  {Object} messages Source data object with message options\n * @return  {String}\n * @private\n */\nfunction getIncompleteReason(checkData, messages) {\n  function getDefaultMsg(message) {\n    if (message.incomplete && message.incomplete.default) {\n      // fall back to the default message if no reason specified\n      return message.incomplete.default;\n    } else {\n      return incompleteFallbackMessage();\n    }\n  }\n  if (checkData && checkData.missingData) {\n    try {\n      const msg = messages.incomplete[checkData.missingData[0].reason];\n      if (!msg) {\n        throw new Error();\n      }\n      return msg;\n    } catch {\n      if (typeof checkData.missingData === 'string') {\n        // return a string with the appropriate reason\n        return messages.incomplete[checkData.missingData];\n      } else {\n        return getDefaultMsg(messages);\n      }\n    }\n  } else if (checkData && checkData.messageKey) {\n    return messages.incomplete[checkData.messageKey];\n  } else {\n    return getDefaultMsg(messages);\n  }\n}\n\n/**\n * Extend checksData with the correct result message\n * @param  {Object} checksData The check result data\n * @param  {Boolean} shouldBeTrue Result of pass/fail check run\n * @param  {Object} rule The rule metadata\n * @return {Function}\n * @private\n */\nfunction extender(checksData, shouldBeTrue, rule) {\n  return check => {\n    const sourceData = checksData[check.id] || {};\n    const messages = sourceData.messages || {};\n    const data = Object.assign({}, sourceData);\n    delete data.messages;\n    if (!rule.reviewOnFail && check.result === undefined) {\n      // handle old doT template\n      if (\n        typeof messages.incomplete === 'object' &&\n        !Array.isArray(check.data)\n      ) {\n        data.message = getIncompleteReason(check.data, messages);\n      }\n\n      // fallback to new process message style\n      if (!data.message) {\n        data.message = messages.incomplete;\n      }\n    } else {\n      data.message =\n        check.result === shouldBeTrue ? messages.pass : messages.fail;\n    }\n\n    // don't process doT template functions\n    if (typeof data.message !== 'function') {\n      data.message = processMessage(data.message, check.data);\n    }\n\n    extendMetaData(check, data);\n  };\n}\n"
  },
  {
    "path": "lib/core/utils/query-selector-all-filter.js",
    "content": "import cache from '../base/cache';\nimport { matchesExpression, convertSelector } from './matches';\nimport { getNodesMatchingExpression } from './selector-cache';\n\nfunction createLocalVariables(\n  vNodes,\n  anyLevel,\n  thisLevel,\n  parentShadowId,\n  recycledLocalVariable\n) {\n  const retVal = recycledLocalVariable || {};\n\n  retVal.vNodes = vNodes;\n  retVal.vNodesIndex = 0;\n  retVal.anyLevel = anyLevel;\n  retVal.thisLevel = thisLevel;\n  retVal.parentShadowId = parentShadowId;\n\n  return retVal;\n}\n\nfunction matchExpressions(domTree, expressions, filter) {\n  /**\n   * Allocating new objects in createLocalVariables is quite expensive given\n   * that matchExpressions is in the hot path.\n   *\n   * Keep track of previously allocated objects to avoid useless allocations\n   * and garbage collection. This is intentionally shared between calls of\n   * matchExpressions.\n   */\n  const recycledLocalVariables = cache.get(\n    'qsa.recycledLocalVariables',\n    () => []\n  );\n\n  const stack = [];\n  const vNodes = Array.isArray(domTree) ? domTree : [domTree];\n  let currentLevel = createLocalVariables(\n    vNodes,\n    expressions,\n    null,\n    domTree[0].shadowId,\n    recycledLocalVariables.pop()\n  );\n  const result = [];\n\n  while (currentLevel.vNodesIndex < currentLevel.vNodes.length) {\n    const vNode = currentLevel.vNodes[currentLevel.vNodesIndex++];\n    let childOnly = null; // we will add hierarchical '>' selectors here\n    let childAny = null;\n    const combinedLength =\n      (currentLevel.anyLevel?.length || 0) +\n      (currentLevel.thisLevel?.length || 0);\n    let added = false;\n    // see if node matches\n    for (let i = 0; i < combinedLength; i++) {\n      const exp =\n        i < (currentLevel.anyLevel?.length || 0)\n          ? currentLevel.anyLevel[i]\n          : currentLevel.thisLevel[i - (currentLevel.anyLevel?.length || 0)];\n      if (\n        (!exp[0].id || vNode.shadowId === currentLevel.parentShadowId) &&\n        matchesExpression(vNode, exp[0])\n      ) {\n        if (exp.length === 1) {\n          if (!added && (!filter || filter(vNode))) {\n            result.push(vNode);\n            added = true;\n          }\n        } else {\n          const rest = exp.slice(1);\n          if ([' ', '>'].includes(rest[0].combinator) === false) {\n            throw new Error(\n              'axe.utils.querySelectorAll does not support the combinator: ' +\n                exp[1].combinator\n            );\n          }\n          if (rest[0].combinator === '>') {\n            // add the rest to the childOnly array\n            (childOnly = childOnly || []).push(rest);\n          } else {\n            // add the rest to the childAny array\n            (childAny = childAny || []).push(rest);\n          }\n        }\n      }\n      if (\n        (!exp[0].id || vNode.shadowId === currentLevel.parentShadowId) &&\n        currentLevel.anyLevel?.includes(exp)\n      ) {\n        (childAny = childAny || []).push(exp);\n      }\n    }\n\n    if (vNode.children && vNode.children.length) {\n      stack.push(currentLevel);\n      currentLevel = createLocalVariables(\n        vNode.children,\n        childAny,\n        childOnly,\n        vNode.shadowId,\n        recycledLocalVariables.pop()\n      );\n    }\n    // check for \"return\"\n    while (\n      currentLevel.vNodesIndex === currentLevel.vNodes.length &&\n      stack.length\n    ) {\n      recycledLocalVariables.push(currentLevel);\n      currentLevel = stack.pop();\n    }\n  }\n  return result;\n}\n\n/**\n * querySelectorAllFilter implements querySelectorAll on the virtual DOM with\n * ability to filter the returned nodes using an optional supplied filter function\n *\n * @method querySelectorAllFilter\n * @memberof axe.utils\n * @param {NodeList} domTree flattened tree collection to search\n * @param {String} selector String containing one or more CSS selectors separated by commas\n * @param {Function} filter function (optional)\n * @return {Array} Elements matched by any of the selectors and filtered by the filter function\n */\nfunction querySelectorAllFilter(domTree, selector, filter) {\n  domTree = Array.isArray(domTree) ? domTree : [domTree];\n  const expressions = convertSelector(selector);\n\n  // see if the passed in node is the root node of the tree and can\n  // find nodes using the cache rather than looping through the\n  // the entire tree\n  const nodes = getNodesMatchingExpression(domTree, expressions, filter);\n  if (nodes) {\n    return nodes;\n  }\n\n  // if the selector cache is not set up or if not passed the\n  // top level node we default back to parsing the whole tree\n  return matchExpressions(domTree, expressions, filter);\n}\n\nexport default querySelectorAllFilter;\n"
  },
  {
    "path": "lib/core/utils/query-selector-all.js",
    "content": "import querySelectorAllFilter from './query-selector-all-filter';\n\n/**\n * querySelectorAll implementation that operates on the flattened tree (supports shadow DOM)\n * @method querySelectorAll\n * @memberof axe.utils\n * @param\t{NodeList} domTree flattened tree collection to search\n * @param\t{String} selector String containing one or more CSS selectors separated by commas\n * @return {NodeList} Elements matched by any of the selectors\n */\nexport function querySelectorAll(domTree, selector) {\n  return querySelectorAllFilter(domTree, selector);\n}\n\nexport default querySelectorAll;\n"
  },
  {
    "path": "lib/core/utils/queue.js",
    "content": "import log from '../log';\n\nfunction noop() {}\nfunction funcGuard(f) {\n  if (typeof f !== 'function') {\n    throw new TypeError('Queue methods require functions as arguments');\n  }\n}\n\n/**\n * Create an asynchronous \"queue\", list of functions to be invoked in parallel, but not necessarily returned in order\n * @return {Queue} The newly generated \"queue\"\n */\nfunction queue() {\n  const tasks = [];\n  let started = 0;\n  let remaining = 0; // number of tasks not yet finished\n  let completeQueue = noop;\n  let complete = false;\n  let err;\n\n  // By default, wait until the next tick,\n  // if no catch was set, throw to console.\n  const defaultFail = e => {\n    err = e;\n    setTimeout(() => {\n      if (err !== undefined && err !== null) {\n        log('Uncaught error (of queue)', err);\n      }\n    }, 1);\n  };\n  let failed = defaultFail;\n\n  function createResolve(i) {\n    return r => {\n      tasks[i] = r;\n      remaining -= 1;\n      if (!remaining && completeQueue !== noop) {\n        complete = true;\n        completeQueue(tasks);\n      }\n    };\n  }\n\n  function abort(msg) {\n    // reset tasks\n    completeQueue = noop;\n\n    // notify catch\n    failed(msg);\n    // return unfinished work\n    return tasks;\n  }\n\n  function pop() {\n    const length = tasks.length;\n    for (; started < length; started++) {\n      const task = tasks[started];\n\n      try {\n        task.call(null, createResolve(started), abort);\n      } catch (e) {\n        abort(e);\n      }\n    }\n  }\n\n  const q = {\n    /**\n     * Defer a function that may or may not run asynchronously.\n     *\n     * First parameter should be the function to execute with subsequent\n     * parameters being passed as arguments to that function\n     */\n    defer(fn) {\n      if (typeof fn === 'object' && fn.then && fn.catch) {\n        const defer = fn;\n        fn = (resolve, reject) => {\n          defer.then(resolve).catch(reject);\n        };\n      }\n      funcGuard(fn);\n      if (err !== undefined) {\n        return;\n      } else if (complete) {\n        throw new Error('Queue already completed');\n      }\n\n      tasks.push(fn);\n      ++remaining;\n      pop();\n      return q;\n    },\n\n    /**\n     * The callback to execute once all \"deferred\" functions have completed.  Will only be invoked once.\n     * @param  {Function} f The callback, receives an array of the return/callbacked\n     * values of each of the \"deferred\" functions\n     */\n    then(fn) {\n      funcGuard(fn);\n      if (completeQueue !== noop) {\n        throw new Error('queue `then` already set');\n      }\n      if (!err) {\n        completeQueue = fn;\n        if (!remaining) {\n          complete = true;\n          completeQueue(tasks);\n        }\n      }\n      return q;\n    },\n\n    catch: function (fn) {\n      funcGuard(fn);\n      if (failed !== defaultFail) {\n        throw new Error('queue `catch` already set');\n      }\n      if (!err) {\n        failed = fn;\n      } else {\n        fn(err);\n        err = null;\n      }\n      return q;\n    },\n    /**\n     * Abort the \"queue\" and prevent `then` function from firing\n     * @param  {Function} fn The callback to execute; receives an array of the results which have completed\n     */\n    abort: abort\n  };\n  return q;\n}\n\nexport default queue;\n"
  },
  {
    "path": "lib/core/utils/respondable.js",
    "content": "import { v4 as createUuid } from './uuid';\nimport assert from './assert';\nimport { setDefaultFrameMessenger } from './frame-messenger';\n\nlet closeHandler;\nlet postMessage;\n\nconst topicHandlers = {};\n\n/**\n * Post a message to a window who may or may not respond to it.\n * @param  {Window}   win      The window to post the message to\n * @param  {String}   topic    The topic of the message\n * @param  {Object}   message  The message content\n * @param  {Boolean}  keepalive Whether to allow multiple responses - default is false\n * @param  {Function} replyHandler The function to invoke when/if the message is responded to\n */\nexport default function respondable(\n  win,\n  topic,\n  message,\n  keepalive,\n  replyHandler\n) {\n  const data = {\n    topic,\n    message,\n    channelId: `${createUuid()}:${createUuid()}`,\n    keepalive\n  };\n\n  return postMessage(win, data, replyHandler);\n}\n\n/**\n * Handle incoming window messages\n * @param  {Object} data\n * @param {Function} responder\n */\nfunction messageListener(data, responder) {\n  const { topic, message, keepalive } = data;\n  const topicHandler = topicHandlers[topic];\n  if (!topicHandler) {\n    return;\n  }\n\n  try {\n    topicHandler(message, keepalive, responder);\n  } catch (error) {\n    axe.log(error);\n    responder(error, keepalive);\n  }\n}\n\n/**\n * Update how respondable communicates with iframes.\n * @param {Function} frameHandler  Object with open, post, and close functions\n */\nrespondable.updateMessenger = function updateMessenger({ open, post }) {\n  assert(typeof open === 'function', 'open callback must be a function');\n  assert(typeof post === 'function', 'post callback must be a function');\n\n  if (closeHandler) {\n    closeHandler();\n  }\n\n  const close = open(messageListener);\n\n  if (close) {\n    assert(\n      typeof close === 'function',\n      'open callback must return a cleanup function'\n    );\n    closeHandler = close;\n  } else {\n    closeHandler = null;\n  }\n\n  postMessage = post;\n};\n\n/**\n * Subscribe to messages sent via the `respondable` module.\n *\n * Axe._load uses this to listen for messages from other frames\n *\n * @param  {String}   topic    The topic to listen to\n * @param  {Function} topicHandler The function to invoke when a message is received\n */\nrespondable.subscribe = function subscribe(topic, topicHandler) {\n  assert(\n    typeof topicHandler === 'function',\n    'Subscriber callback must be a function'\n  );\n  assert(!topicHandlers[topic], `Topic ${topic} is already registered to.`);\n\n  topicHandlers[topic] = topicHandler;\n};\n\n/**\n * checks if the current context is inside a frame\n * @return {Boolean}\n */\nrespondable.isInFrame = function isInFrame(win = window) {\n  return !!win.frameElement;\n};\n\nsetDefaultFrameMessenger(respondable);\n"
  },
  {
    "path": "lib/core/utils/rule-error.js",
    "content": "import serializeError from './serialize-error';\n\nexport default class RuleError extends Error {\n  constructor({ error, ruleId, method, errorNode }) {\n    super();\n    this.name = error.name ?? 'RuleError';\n    this.message = error.message;\n    this.stack = error.stack;\n    if (error.cause) {\n      this.cause = serializeError(error.cause);\n    }\n    if (ruleId) {\n      this.ruleId = ruleId;\n      this.message += ` Skipping ${this.ruleId} rule.`;\n    }\n    if (method) {\n      this.method = method;\n    }\n    if (errorNode) {\n      this.errorNode = errorNode;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/core/utils/rule-should-run.js",
    "content": "/**\n * Check if a rule matches the value of runOnly type=tag\n * @private\n * @param  {object} rule\n * @param  {object}\trunOnly Value of runOnly with type=tags\n * @return {bool}\n */\nfunction matchTags(rule, runOnly) {\n  let include, exclude;\n  const defaultExclude =\n    // TODO: es-modules_audit\n    axe._audit && axe._audit.tagExclude ? axe._audit.tagExclude : [];\n\n  // normalize include/exclude\n  if (runOnly.hasOwnProperty('include') || runOnly.hasOwnProperty('exclude')) {\n    // Wrap include and exclude if it's not already an array\n    include = runOnly.include || [];\n    include = Array.isArray(include) ? include : [include];\n\n    exclude = runOnly.exclude || [];\n    exclude = Array.isArray(exclude) ? exclude : [exclude];\n    // add defaults, unless mentioned in include\n    exclude = exclude.concat(\n      defaultExclude.filter(tag => {\n        return include.indexOf(tag) === -1;\n      })\n    );\n\n    // Otherwise, only use the include value, ignore exclude\n  } else {\n    include = Array.isArray(runOnly) ? runOnly : [runOnly];\n    // exclude the defaults not included\n    exclude = defaultExclude.filter(tag => {\n      return include.indexOf(tag) === -1;\n    });\n  }\n\n  const matching = include.some(tag => {\n    return rule.tags.indexOf(tag) !== -1;\n  });\n  if (matching || (include.length === 0 && rule.enabled !== false)) {\n    return exclude.every(tag => {\n      return rule.tags.indexOf(tag) === -1;\n    });\n  } else {\n    return false;\n  }\n}\n\n/**\n * Determines whether a rule should run\n * @param  {Rule}    rule     The rule to test\n * @param  {Context} context  The context of the Audit\n * @param  {Object}  options  Options object\n * @return {Boolean}\n */\nfunction ruleShouldRun(rule, context, options) {\n  const runOnly = options.runOnly || {};\n  const ruleOptions = (options.rules || {})[rule.id];\n\n  // Never run page level rules if the context is not on the page\n  if (rule.pageLevel && !context.page) {\n    return false;\n\n    // First, runOnly type rule overrides anything else\n  } else if (runOnly.type === 'rule') {\n    return runOnly.values.indexOf(rule.id) !== -1;\n\n    // Second, if options.rules[rule].enabled is set, it overrides all\n  } else if (ruleOptions && typeof ruleOptions.enabled === 'boolean') {\n    return ruleOptions.enabled;\n\n    // Third, if tags are set, look at those\n  } else if (runOnly.type === 'tag' && runOnly.values) {\n    return matchTags(rule, runOnly.values);\n\n    // If nothing is set, only check for default excludes\n  } else {\n    return matchTags(rule, []);\n  }\n}\n\nexport default ruleShouldRun;\n"
  },
  {
    "path": "lib/core/utils/select.js",
    "content": "import contains from './contains';\nimport querySelectorAllFilter from './query-selector-all-filter';\nimport isNodeInContext from './is-node-in-context';\n\n/**\n * Selects elements which match `selector` that are included and excluded via the `Context` object\n * @param  {String} selector  CSS selector of the HTMLElements to select\n * @param  {Context} context  The \"resolved\" context object, @see Context\n * @return {Array}            Matching virtual DOM nodes sorted by DOM order\n */\nexport default function select(selector, context) {\n  let result = [];\n  let candidate;\n  if (axe._selectCache) {\n    // if used outside of run, it will still work\n    for (let j = 0, l = axe._selectCache.length; j < l; j++) {\n      // First see whether the item exists in the cache\n      const item = axe._selectCache[j];\n      if (item.selector === selector) {\n        return item.result;\n      }\n    }\n  }\n\n  const outerIncludes = getOuterIncludes(context.include);\n  const isInContext = getContextFilter(context);\n  for (let i = 0; i < outerIncludes.length; i++) {\n    candidate = outerIncludes[i];\n    const nodes = querySelectorAllFilter(candidate, selector, isInContext);\n    result = mergeArrayUniques(result, nodes);\n  }\n  if (axe._selectCache) {\n    axe._selectCache.push({\n      selector: selector,\n      result: result\n    });\n  }\n  return result;\n}\n\n/**\n * reduces the includes list to only the outermost includes\n * @private\n * @param {Array} the array of include nodes\n * @return {Array} the modified array of nodes\n */\nfunction getOuterIncludes(includes) {\n  return includes.reduce((res, el) => {\n    if (!res.length || !contains(res[res.length - 1], el)) {\n      res.push(el);\n    }\n    return res;\n  }, []);\n}\n\n/**\n * Return a filter method to test if a node is in context; or\n *  null if the node is always included.\n * @private\n * @param {Context}\n * @return {Function|null}\n */\nfunction getContextFilter(context) {\n  // Since we're starting from included nodes,\n  // if nothing is excluded, we can skip the filter step.\n  if (!context.exclude || context.exclude.length === 0) {\n    return null;\n  }\n  return node => isNodeInContext(node, context);\n}\n\n/**\n * Merge the unique items from Array 1 into 2, or from 2 into 1 (whichever is longest)\n * @private\n * @param  {Array} Arr1\n * @param  {Array} Arr2\n */\nfunction mergeArrayUniques(arr1, arr2) {\n  if (arr1.length === 0) {\n    return arr2;\n  }\n  if (arr1.length < arr2.length) {\n    // switch so the comparison is shortest\n    const temp = arr1;\n    arr1 = arr2;\n    arr2 = temp;\n  }\n  for (let i = 0, l = arr2.length; i < l; i++) {\n    if (!arr1.includes(arr2[i])) {\n      arr1.push(arr2[i]);\n    }\n  }\n  return arr1;\n}\n"
  },
  {
    "path": "lib/core/utils/selector-cache.js",
    "content": "import { matchesExpression } from './matches';\nimport tokenList from './token-list';\n\n// since attribute names can't contain whitespace, this will be\n// a reserved list for ids so we can perform virtual id lookups\nconst idsKey = ' [idsMap]';\n\n/**\n * Get nodes from the selector cache that match the selector.\n * @param {VirtualTree[]} domTree flattened tree collection to search\n * @param {Object} expressions\n * @param {Function} filter function (optional)\n * @return {Mixed} Array of nodes that match the selector or undefined if the selector map is not setup\n */\nexport function getNodesMatchingExpression(domTree, expressions, filter) {\n  // check to see if the domTree is the root and has the selector\n  // map. if not we just return and let our QSA code do the finding\n  const selectorMap = domTree[0]._selectorMap;\n  if (!selectorMap) {\n    return;\n  }\n\n  const shadowId = domTree[0].shadowId;\n\n  // if the selector uses a global selector with a combinator\n  // (e.g. A *, A > *) it's actually faster to use our QSA code than\n  // getting all nodes and using matchesExpression\n  for (let i = 0; i < expressions.length; i++) {\n    if (\n      expressions[i].length > 1 &&\n      expressions[i].some(expression => isGlobalSelector(expression))\n    ) {\n      return;\n    }\n  }\n\n  // it turned out to be more performant to use a Set to generate a\n  // unique list of nodes rather than an array and array.includes\n  // (~3 seconds total on a benchmark site)\n  const nodeSet = new Set();\n\n  expressions.forEach(expression => {\n    const matchingNodes = findMatchingNodes(expression, selectorMap, shadowId);\n    matchingNodes?.nodes?.forEach(node => {\n      // for complex selectors we need to verify that the node\n      // actually matches the entire selector since we only have\n      // nodes that partially match the last part of the selector\n      if (\n        matchingNodes.isComplexSelector &&\n        !matchesExpression(node, expression)\n      ) {\n        return;\n      }\n\n      nodeSet.add(node);\n    });\n  });\n\n  // Sets in ie11 do not work with Array.from without a polyfill\n  //(missing `.entries`), but do have forEach\n  let matchedNodes = [];\n  nodeSet.forEach(node => matchedNodes.push(node));\n\n  if (filter) {\n    matchedNodes = matchedNodes.filter(filter);\n  }\n\n  return matchedNodes.sort((a, b) => a.nodeIndex - b.nodeIndex);\n}\n\n/**\n * Add nodes to the passed in Set that match just a part of the selector in order to speed up traversing the entire tree.\n * @param {Object} expression Selector Expression\n * @param {Object} selectorMap Selector map cache\n * @param {String} shadowId ShadowID of the root node\n */\nfunction findMatchingNodes(expression, selectorMap, shadowId) {\n  // use the last part of the expression to find nodes as it's more\n  // specific. e.g. for `body h1` use `h1` and not `body`\n  const exp = expression[expression.length - 1];\n  let nodes = null;\n\n  // a complex selector is one that will require using\n  // matchesExpression to determine if it matches. these include\n  // pseudo selectors (:not), combinators (A > B), and any\n  // attribute value ([class=foo]).\n  let isComplexSelector =\n    expression.length > 1 || !!exp.pseudos || !!exp.classes;\n\n  if (isGlobalSelector(exp)) {\n    nodes = selectorMap['*'];\n  } else {\n    if (exp.id) {\n      // a selector must match all parts, otherwise we can just exit early\n      if (\n        !selectorMap[idsKey] ||\n        !Object.hasOwn(selectorMap[idsKey], exp.id) ||\n        !selectorMap[idsKey][exp.id]?.length\n      ) {\n        return;\n      }\n\n      // when using id selector (#one) we only find nodes that\n      // match the shadowId of the root\n      nodes = selectorMap[idsKey][exp.id].filter(\n        node => node.shadowId === shadowId\n      );\n    }\n\n    if (exp.tag && exp.tag !== '*') {\n      if (!selectorMap[exp.tag]?.length) {\n        return;\n      }\n\n      const cachedNodes = selectorMap[exp.tag];\n      nodes = nodes ? getSharedValues(cachedNodes, nodes) : cachedNodes;\n    }\n\n    if (exp.classes) {\n      if (!selectorMap['[class]']?.length) {\n        return;\n      }\n\n      const cachedNodes = selectorMap['[class]'];\n      nodes = nodes ? getSharedValues(cachedNodes, nodes) : cachedNodes;\n    }\n\n    if (exp.attributes) {\n      for (let i = 0; i < exp.attributes.length; i++) {\n        const attr = exp.attributes[i];\n\n        // an attribute selector that looks for a specific value is\n        // a complex selector\n        if (attr.type === 'attrValue') {\n          isComplexSelector = true;\n        }\n\n        if (!selectorMap[`[${attr.key}]`]?.length) {\n          return;\n        }\n\n        const cachedNodes = selectorMap[`[${attr.key}]`];\n        nodes = nodes ? getSharedValues(cachedNodes, nodes) : cachedNodes;\n      }\n    }\n  }\n\n  return { nodes, isComplexSelector };\n}\n\n/**\n * Non-tag selectors use `*` for the tag name so a global selector won't have any other properties of the expression. Pseudo selectors that use `*` (e.g. `*:not([class])`) will still be considered a global selector since we don't cache anything for pseudo selectors and will rely on filtering with matchesExpression.\n * @param {Object} expression Selector Expression\n * @returns {Boolean}\n */\nfunction isGlobalSelector(expression) {\n  return (\n    expression.tag === '*' &&\n    !expression.attributes &&\n    !expression.id &&\n    !expression.classes\n  );\n}\n\n/**\n * Find all nodes in A that are also in B.\n * @param {Mixed[]} a\n * @param {Mixed[]} b\n * @returns {Mixed[]}\n */\nfunction getSharedValues(a, b) {\n  return a.filter(node => b.includes(node));\n}\n\n/**\n * Save a selector and vNode to the selectorMap.\n * @param {String} key\n * @param {VirtualNode} vNode\n * @param {Object} map\n */\nfunction cacheSelector(key, vNode, map) {\n  if (!Object.hasOwn(map, key)) {\n    map[key] = [];\n  }\n  map[key].push(vNode);\n}\n\n/**\n * Cache selector information about a VirtalNode.\n * @param {VirtualNode} vNode\n */\nexport function cacheNodeSelectors(vNode, selectorMap) {\n  if (vNode.props.nodeType !== 1) {\n    return;\n  }\n\n  cacheSelector(vNode.props.nodeName, vNode, selectorMap);\n  cacheSelector('*', vNode, selectorMap);\n\n  vNode.attrNames.forEach(attrName => {\n    // element ids are the only values we'll match\n    if (attrName === 'id') {\n      selectorMap[idsKey] = selectorMap[idsKey] || {};\n      tokenList(vNode.attr(attrName)).forEach(value => {\n        cacheSelector(value, vNode, selectorMap[idsKey]);\n      });\n    }\n\n    cacheSelector(`[${attrName}]`, vNode, selectorMap);\n  });\n}\n"
  },
  {
    "path": "lib/core/utils/send-command-to-frame.js",
    "content": "import getSelector from './get-selector';\nimport respondable from './respondable';\nimport log from '../log';\n\n/**\n * Sends a command to an instance of axe in the specified frame\n * @param  {Element}  node       The frame element to send the message to\n * @param  {Object}   parameters Parameters to pass to the frame\n * @param  {Function} callback   Function to call when results from the frame has returned\n */\nexport default function sendCommandToFrame(node, parameters, resolve, reject) {\n  const win = node.contentWindow;\n  const pingWaitTime = parameters.options?.pingWaitTime ?? 500;\n  if (!win) {\n    log('Frame does not have a content window', node);\n    resolve(null);\n    return;\n  }\n\n  // Skip ping\n  if (pingWaitTime === 0) {\n    callAxeStart(node, parameters, resolve, reject);\n    return;\n  }\n\n  // give the frame .5s to respond to 'axe.ping', else log failed response\n  let timeout = setTimeout(() => {\n    // This double timeout is important for allowing iframes to respond\n    // DO NOT REMOVE\n    timeout = setTimeout(() => {\n      if (!parameters.debug) {\n        resolve(null);\n      } else {\n        reject(err('No response from frame', node));\n      }\n    }, 0);\n  }, pingWaitTime);\n\n  // send 'axe.ping' to the frame\n  respondable(win, 'axe.ping', null, undefined, () => {\n    clearTimeout(timeout);\n    callAxeStart(node, parameters, resolve, reject);\n  });\n}\n\nfunction callAxeStart(node, parameters, resolve, reject) {\n  // Give axe 60s (or user-supplied value) to respond to 'axe.start'\n  const frameWaitTime = parameters.options?.frameWaitTime ?? 60000;\n  const win = node.contentWindow;\n  const timeout = setTimeout(function collectResultFramesTimeout() {\n    reject(err('Axe in frame timed out', node));\n  }, frameWaitTime);\n\n  // send 'axe.start' and send the callback if it responded\n  respondable(win, 'axe.start', parameters, undefined, data => {\n    clearTimeout(timeout);\n    if (data instanceof Error === false) {\n      resolve(data);\n    } else {\n      reject(data);\n    }\n  });\n}\n\nfunction err(message, node) {\n  let selector;\n  // TODO: es-modules_tree\n  if (axe._tree) {\n    selector = getSelector(node);\n  }\n  return new Error(message + ': ' + (selector || node));\n}\n"
  },
  {
    "path": "lib/core/utils/serialize-error.js",
    "content": "import constants from '../constants';\n\n/**\n * Serializes an error to a JSON object\n * @param e - The error to serialize\n * @returns A JSON object representing the error\n */\nexport default function serializeError(err, iteration = 0) {\n  if (typeof err !== 'object' || err === null) {\n    return { message: String(err) };\n  }\n  const serial = {};\n  for (const prop of constants.serializableErrorProps) {\n    if (['string', 'number', 'boolean'].includes(typeof err[prop])) {\n      serial[prop] = err[prop];\n    }\n  }\n  // Recursively serialize cause up to 10 levels deep\n  if (err.cause) {\n    serial.cause =\n      iteration < 10 ? serializeError(err.cause, iteration + 1) : '...';\n  }\n  return serial;\n}\n"
  },
  {
    "path": "lib/core/utils/set-scroll-state.js",
    "content": "/**\n * set the scroll position of an element\n */\nfunction setScroll(elm, top, left) {\n  if (elm === window) {\n    return elm.scroll(left, top);\n  } else {\n    elm.scrollTop = top;\n    elm.scrollLeft = left;\n  }\n}\n\n/**\n * set the scroll position of all items in the scrollState array\n * @deprecated\n */\nexport function setScrollState(scrollState) {\n  scrollState.forEach(({ elm, top, left }) => setScroll(elm, top, left));\n}\n\nexport default setScrollState;\n"
  },
  {
    "path": "lib/core/utils/shadow-select-all.js",
    "content": "/**\n * Find elements to match a selector.\n * Use an array of selectors to reach into shadow DOM trees\n *\n * @param {string|string[]} selector String or array of strings with a CSS selector\n * @param {Document} doc Optional document node\n * @returns {Node[]}\n */\nexport default function shadowSelectAll(selectors, doc = document) {\n  // Spread to avoid mutating the input\n  const selectorArr = Array.isArray(selectors) ? [...selectors] : [selectors];\n  if (selectors.length === 0) {\n    return [];\n  }\n  return selectAllRecursive(selectorArr, doc);\n}\n\n/* Find elements in shadow or light DOM trees, using an array of selectors */\nfunction selectAllRecursive([selectorStr, ...restSelector], doc) {\n  const elms = doc.querySelectorAll(selectorStr);\n  if (restSelector.length === 0) {\n    return Array.from(elms);\n  }\n  const selected = [];\n  for (const elm of elms) {\n    if (elm?.shadowRoot) {\n      selected.push(...selectAllRecursive(restSelector, elm.shadowRoot));\n    }\n  }\n  return selected;\n}\n"
  },
  {
    "path": "lib/core/utils/shadow-select.js",
    "content": "/**\n * Find the first element to match a selector.\n * Use an array of selectors to reach into shadow DOM trees\n *\n * @param {string|string[]} selector String or array of strings with a CSS selector\n * @param {Document} doc Optional document node\n * @returns {Element|Null}\n */\nexport default function shadowSelect(selectors) {\n  // Spread to avoid mutating the input\n  const selectorArr = Array.isArray(selectors) ? [...selectors] : [selectors];\n  return selectRecursive(selectorArr, document);\n}\n\n/* Find an element in shadow or light DOM trees, using an axe selector */\nfunction selectRecursive(selectors, doc) {\n  const selectorStr = selectors.shift();\n  const elm = selectorStr ? doc.querySelector(selectorStr) : null;\n  if (selectors.length === 0) {\n    return elm;\n  }\n  if (!elm?.shadowRoot) {\n    return null;\n  }\n  return selectRecursive(selectors, elm.shadowRoot);\n}\n"
  },
  {
    "path": "lib/core/utils/to-array.js",
    "content": "/**\n * Converts array-like (numerical indicies and `length` property) structures to actual, real arrays\n * @param\t{Mixed} thing Array-like thing to convert\n * @return {Array}\n */\nfunction toArray(thing) {\n  return Array.prototype.slice.call(thing);\n}\n\nexport default toArray;\n"
  },
  {
    "path": "lib/core/utils/token-list.js",
    "content": "/**\n * Converts space delimited token list to an Array\n * @method tokenList\n * @memberof axe.utils\n * @param  {String} str\n * @return {Array}\n */\nfunction tokenList(str) {\n  return (str || '')\n    .trim()\n    .replace(/\\s{2,}/g, ' ')\n    .split(' ');\n}\n\nexport default tokenList;\n"
  },
  {
    "path": "lib/core/utils/unique-array.js",
    "content": "/**\n * Creates an array without duplicate values from 2 array inputs\n * @param {Array} arr1 First array\n * @param {Array} arr2 Second array\n * @return {Array}\n */\nfunction uniqueArray(arr1, arr2) {\n  return arr1.concat(arr2).filter((elem, pos, arr) => {\n    return arr.indexOf(elem) === pos;\n  });\n}\n\nexport default uniqueArray;\n"
  },
  {
    "path": "lib/core/utils/uuid.js",
    "content": "/*eslint no-bitwise: 0, eqeqeq: 0, curly: 0, strict: 0, no-eq-null: 0, no-shadow: 0, no-undef: 0 */\n//\t\t uuid.js\n//\n//\t\t Copyright (c) 2010-2012 Robert Kieffer\n//\t\t MIT License - http://opensource.org/licenses/mit-license.php\nlet uuid;\n// Unique ID creation requires a high quality random # generator.\tWe feature\n// detect to determine the best RNG source, normalizing to a function that\n// returns 128-bits of randomness, since that's what's usually required\nlet _rng;\n\n// Allow for MSIE11 msCrypto\nlet _crypto = window.crypto || window.msCrypto;\n\nif (!_rng && _crypto && _crypto.getRandomValues) {\n  // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto\n  //\n  // Moderately fast, high quality\n  let _rnds8 = new Uint8Array(16);\n  _rng = function whatwgRNG() {\n    _crypto.getRandomValues(_rnds8);\n    return _rnds8;\n  };\n}\n\nif (!_rng) {\n  // Math.random()-based (RNG)\n  //\n  // If all else fails, use Math.random().\tIt's fast, but is of unspecified\n  // quality.\n  let _rnds = new Array(16);\n  _rng = () => {\n    for (let i = 0, r; i < 16; i++) {\n      if ((i & 0x03) === 0) r = Math.random() * 0x100000000;\n      _rnds[i] = (r >>> ((i & 0x03) << 3)) & 0xff;\n    }\n\n    return _rnds;\n  };\n}\n\n// Buffer class to use\nlet BufferClass = typeof window.Buffer == 'function' ? window.Buffer : Array;\n\n// Maps for number <-> hex string conversion\nlet _byteToHex = [];\nlet _hexToByte = {};\nfor (let i = 0; i < 256; i++) {\n  _byteToHex[i] = (i + 0x100).toString(16).substr(1);\n  _hexToByte[_byteToHex[i]] = i;\n}\n\n// **`parse()` - Parse a UUID into it's component bytes**\nfunction parse(s, buf, offset) {\n  let i = (buf && offset) || 0,\n    ii = 0;\n\n  buf = buf || [];\n  s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {\n    if (ii < 16) {\n      // Don't overflow!\n      buf[i + ii++] = _hexToByte[oct];\n    }\n  });\n\n  // Zero out remaining bytes if string was short\n  while (ii < 16) {\n    buf[i + ii++] = 0;\n  }\n\n  return buf;\n}\n\n// **`unparse()` - Convert UUID byte array (ala parse()) into a string**\nfunction unparse(buf, offset) {\n  let i = offset || 0,\n    bth = _byteToHex;\n  return (\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    '-' +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    '-' +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    '-' +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    '-' +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]] +\n    bth[buf[i++]]\n  );\n}\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\n\n// random #'s we need to init node and clockseq\nlet _seedBytes = _rng();\n\n// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\nlet _nodeId = [\n  _seedBytes[0] | 0x01,\n  _seedBytes[1],\n  _seedBytes[2],\n  _seedBytes[3],\n  _seedBytes[4],\n  _seedBytes[5]\n];\n\n// Per 4.2.2, randomize (14 bit) clockseq\nlet _clockseq = ((_seedBytes[6] << 8) | _seedBytes[7]) & 0x3fff;\n\n// Previous uuid creation time\nlet _lastMSecs = 0,\n  _lastNSecs = 0;\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v1(options, buf, offset) {\n  let i = (buf && offset) || 0;\n  let b = buf || [];\n\n  options = options || {};\n\n  let clockseq = options.clockseq != null ? options.clockseq : _clockseq;\n\n  // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n  // (1582-10-15 00:00).\tJSNumbers aren't precise enough for this, so\n  // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n  // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n  let msecs = options.msecs != null ? options.msecs : new Date().getTime();\n\n  // Per 4.2.1.2, use count of uuid's generated during the current clock\n  // cycle to simulate higher resolution clock\n  let nsecs = options.nsecs != null ? options.nsecs : _lastNSecs + 1;\n\n  // Time since last uuid creation (in msecs)\n  let dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000;\n\n  // Per 4.2.1.2, Bump clockseq on clock regression\n  if (dt < 0 && options.clockseq == null) {\n    clockseq = (clockseq + 1) & 0x3fff;\n  }\n\n  // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n  // time interval\n  if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {\n    nsecs = 0;\n  }\n\n  // Per 4.2.1.2 Throw error if too many uuids are requested\n  if (nsecs >= 10000) {\n    throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");\n  }\n\n  _lastMSecs = msecs;\n  _lastNSecs = nsecs;\n  _clockseq = clockseq;\n\n  // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n  msecs += 12219292800000;\n\n  // `time_low`\n  let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n  b[i++] = (tl >>> 24) & 0xff;\n  b[i++] = (tl >>> 16) & 0xff;\n  b[i++] = (tl >>> 8) & 0xff;\n  b[i++] = tl & 0xff;\n\n  // `time_mid`\n  let tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff;\n  b[i++] = (tmh >>> 8) & 0xff;\n  b[i++] = tmh & 0xff;\n\n  // `time_high_and_version`\n  b[i++] = ((tmh >>> 24) & 0xf) | 0x10; // include version\n  b[i++] = (tmh >>> 16) & 0xff;\n\n  // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n  b[i++] = (clockseq >>> 8) | 0x80;\n\n  // `clock_seq_low`\n  b[i++] = clockseq & 0xff;\n\n  // `node`\n  let node = options.node || _nodeId;\n  for (let n = 0; n < 6; n++) {\n    b[i + n] = node[n];\n  }\n\n  return buf ? buf : unparse(b);\n}\n\n// **`v4()` - Generate random UUID**\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v4(options, buf, offset) {\n  // Deprecated - 'format' argument, as supported in v1.2\n  let i = (buf && offset) || 0;\n\n  if (typeof options == 'string') {\n    buf = options == 'binary' ? new BufferClass(16) : null;\n    options = null;\n  }\n  options = options || {};\n\n  let rnds = options.random || (options.rng || _rng)();\n\n  // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n  rnds[6] = (rnds[6] & 0x0f) | 0x40;\n  rnds[8] = (rnds[8] & 0x3f) | 0x80;\n\n  // Copy bytes to buffer, if provided\n  if (buf) {\n    for (let ii = 0; ii < 16; ii++) {\n      buf[i + ii] = rnds[ii];\n    }\n  }\n\n  return buf || unparse(rnds);\n}\n\n// Export public API\nuuid = v4;\nuuid.v1 = v1;\nuuid.v4 = v4;\nuuid.parse = parse;\nuuid.unparse = unparse;\nuuid.BufferClass = BufferClass;\n\n// assign a unique id to this axe instance\naxe._uuid = v1();\n\nexport { v1, v4, parse, unparse, BufferClass };\nexport default v4;\n"
  },
  {
    "path": "lib/core/utils/valid-input-type.js",
    "content": "/**\n * Returns array of valid input type values\n * @method validInputTypes\n * @memberof axe.utils\n * @return {Array<Sting>}\n */\nfunction validInputTypes() {\n  // Reference - https://html.spec.whatwg.org/multipage/input.html#the-input-element\n  return [\n    'hidden',\n    'text',\n    'search',\n    'tel',\n    'url',\n    'email',\n    'password',\n    'date',\n    'month',\n    'week',\n    'time',\n    'datetime-local',\n    'number',\n    'range',\n    'color',\n    'checkbox',\n    'radio',\n    'file',\n    'submit',\n    'image',\n    'reset',\n    'button'\n  ];\n}\n\nexport default validInputTypes;\n"
  },
  {
    "path": "lib/core/utils/valid-langs.js",
    "content": "/*\nIn order to reduce the file size of axe-core, this file was specially encoded. Normally, an array of strings of each valid ISO 639-1/2 code is 20kb gzipped. Encoding the codes is only 2kb gzipped.\n\nThe encoding is a trie data structure. Normally storing a trie as a true binary tree would take up more space than just an array of strings. However, there is a technique to store large amounts of data as a nested array of integers (borrowed from the Js13kGames game jam - https://keithclark.github.io/ZzFXM/) that gzipps extremely well.\n\nThe encoding was generated by storing the trie of strings as a sequence of nested arrays, where each index represents the letter (a = 1, b = 2, etc.). For example, 'aaa' is stored as `[,[,[,1]]`. We can also use the 0 index to know if `aa` is valid by padding the end of the string with '`' (which gives 0 index as `'`'.charCodeAt(0) - 96` = 0). For example, both `aaa` and `aa` are stored as `[,[,[1,1]]]` (where `a` is not a valid lang).\n\nIf we need to edit the list of langs, use the code below on the page https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry:\n\nconst str = document.querySelector('pre').innerHTML;\nconst langs = new Set();\nconst encodedLangs = [];\n\nstr.split('%%').forEach(language => {\n  const properties = language.split('\\n');\n  for (let i = 0; i < properties.length; i++) {\n    const property = properties[i];\n    const match = property.match(/(?<type>\\w+): (?<value>\\w+)/);\n    if (!match) continue;\n\n    const { type, value } = match.groups;\n    if (type === 'Type' && value !== 'language') return;\n    if (type === 'Subtag') {\n      langs.add(value);\n    }\n    if (type === 'Deprecated') {\n      langs.delete(value);\n    }\n  }\n});\n\nArray.from(langs).forEach(lang => {\n  lang = lang.padEnd(3, '`');\n  let array = encodedLangs;\n  lang.split('').forEach((char, i) => {\n    // index is 1 indexed\n    const index = char.charCodeAt(0) - 96;\n\n    if (i < lang.length - 1) {\n      array[index] = array[index] || [];\n      array = array[index];\n    }\n    else {\n      array[index] = 1\n    }\n  });\n});\n\nJSON.stringify(encodedLangs).replace(/null/g, '');\n*/\n\n// prettier-ignore\n\nconst langs = [,[,[1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,,1,1,1,1,1,1,,1],[1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,1,1,,1,1,,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,1,1,,1,1,1,1],[1,1,1,1,1,1,,,,,,1,1,1,1,,,1,1,1,,1,,1,,1,1],[1,1,1,,1,1,,1,1,1,,1,,,1,1,1,,,1,1,1,,,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,,,,1,1,1,,1,1,1,1,1,1,,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1],[,1,,,,,,1,,1,,,,,1,,1,,,1,1,1,,1,,,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,,1,1,1,1,,,1,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,1,1,,,1,,,,1,1,1,1,,1,,1,,1,,,,,,1],[1,,1,1,1,1,,,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[1,,1,,1,,,,,1,,1,1,1,1,1,,,,1,1,1,1],[,1,1,1,1,1,,1,1,1,,1,,1,1,1,,,1,1,1,1,1,1,1,1],[,,1,,,1,,1,,,,1,1,1,,,,,,,,,,,1],[1,1,1,1,1,1,,1,1,1,,1,1,,1,1,1,1,1,1,1,1,,,1,1,1],[1,1,1,1,1,,,1,,,1,,,1,1,1,,,,,1,,,,,,1]],[,[1,1,1,1,1,1,1,1,1,1,1,,1,,1,1,1,,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,,,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,,1,1,,1,1,1,1,1,1,1,,1],[,1,,1,1,1,,1,1,,1,,1,1,1,1,1,1,1,1],[,1,,1,1,1,1,1,1,1,1,,,1,1,1,,,1,1,,,,,,1,1],[1,1,1,,,,,1,,,,1,1,,1,,,,,,1,,,,,1],[,1,,,1,,,1,,,,,,1],[,1,,1,,,,1,,,,1],[1,,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,,1,,,1,1,1,1],[,1,1,1,1,1,,,1,,,1,,1,1,,1,,1,,,,,1,,1],[,1,,,,1,,,1,1,,1,,1,1,1,1,,1,1,,,1,,,1],[,1,1,,,,,,1,,,,1,1,1,1,,1,1,1,1,1,1,,1,1,1],[,1,,1,1,1,,,1,1,1,1,1,1,,1,,,,1,1,1,,1,,1],[,1,,1,,1,,1,,1,,1,1,1,1,1,,,1,1,1],[,1,1,1,,,,1,1,1,,1,1,,,1,1,1,1,1,1,1,,1,1],[1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,,,1,1,1,1,1,1,1],[,1,1,1,,1,1,1,,1,,,,,1,1,1,,,1,,1,,,1,1],[,,,,1,,,,,,,,,,,,,,,,,1],[1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,1,1,1,,1,1,,,,1,1,1,1,1,,,1,1,1,,,,1,1],[1,1,1,1,,,,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[1,,,,,,,1,,,,,,,1],[,1,1,,1,1,,1,,,,,,,,,,,,,1],[,,,,,,,,1],[1,1,1,,,,,,,,,,,,,1],[,,,,,,,,1,,,1,,,1,1,,,,,1]],[,[1,1,,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,1],[,1,1,,1,1,1,1,,1,1,,1,1,1,1,1,1,1,,1,1,1,1,,1],[,,,1,,,,,,,,,,,,,,,1],[,1,,,1,1,,1,,1,1,,,,1,1,,,1,1,,,,1],[1,,,1,1,1,1,1,1,1,,1,1,1,1,,1,1,1,1,,,1,,,,1],,[,1,1,1,1,1,,1,1,1,,1,1,,1,1,,,1,1,1,1,,1,1,,1],[,1,,,1,,,1,,1,,,1,1,1,1,,,1,1,,1,1,1,1],[,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[,1,1,1,1,1,1,,,1,1,1,1,1,1,1,,,1,,,1,,1],[,1,,,,,,1,,,,1,1,,,,,,1,1,,,,,1],[,,,,,,,1,,,,1,,1,1],[,1,1,1,1,1,1,1,,,,1,1,1,1,1,,,1,1,,1,1,1,1,1],[,1,,,1,1,,1,,1,1,1,,,1,1,,,1,,1,1,1,1,,1],[,1,1,1,,1,1,,1,1,,1,1,,1,1,1,1,1,1,1,,1,1,1,1,1],[,,,,,,,,,,,,,,,,1],,[,1,1,1,1,1,,1,1,1,,,1,,1,1,,1,1,1,1,1,,1,,1],[,,1,,,1,,,1,1,,1,1,,1,1,,1,,,,,,,,,1],[,1,1,,1,,,,1,1,,1,,1,1,1,1,,1,1,1,1,,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1],[1,1],[,1,,,,,,,,,,1,1,,,,,,1,1,,1,,1,,1,1],,[,1,1,,1,,,1,,1,,,,1,1,1,,,1,,,1,,,,1],[1,1,,,1,1,,1,,,,,1,,1]],[,[,1],[,,,1,,,,1,,,,1,,,,1,,,1,,,1],[,,,,,,,,,,,,,,,,,,1,1,,,,,,1],,[1,,,,,1],[,1,,,,1,,,,1],[,1,,,,,,,,,,,1,1,,1,,,,,,,,,1,1],[,,,,,,,,,,,,,,,,,,,1,,1],[,,,,,,,,,,,,,,,,1,,,,1,,1],[,1],[,1,,1,,1,,1,,1,,1,1,1,,1,1,,1,,,,,,,1],[1,,,,,1,,,1,1,,1,,1,,1,1,,,,,1,,,1],[,1,1,,,1,,1,,1,,1,,1,1,1,1,1,,1,,1,,1,1,1,1],[1,1,1,1,1,,1,,1,,,,1,1,1,1,,1,1,,,1,1,1,1],[1,,,,,,,,,,,,,,,,,,,,1],[,,,,,,,,,1],,[,1,,,,,,1,1,1,,1,,,,1,,,1,1,1,,,1],[1,,,,,1,,1,1,1,,1,1,1,1,1,,1,,1,,1,,,1,1],[1,,1,1,,,,,1,,,,,,1,1,,,1,1,1,1,,,1,,1],[1,,,,1,,,,,,,,,,,,,1],[,,,,,1,,,1,,,,,,1],[,,,,,,,,,,,,,,,1],[,,,,,,,,,,,,,,,,,,,,1],[,1,,,,,,,,,,,,,,1],[,1,,,,1]],[,[1,1,1,,1,,1,1,1,1,1,1,1,1,1,,1,,1,,1,1,,,1,1,1],[,,,,,,,,,,,,1],[,,,,,,,,,,,,,,,,,,,1],,[,,,,,,,,,,,,,,,,,,1],[1,,,,,,,,,1,,,,1],[,,,,,,,,,,,,,,,,,,1],,[1,1,,,,1,1,,,,,,1,,,,1,,1,,1,1,,1],[1],[,,,,,,,,,,,1,,,,,,,,,,,1],[,1,,,,,,,1,1,,,1,,1,,,,1,,,,,,,1],[,,,,,,,,,,,,,,,,1,,,,,1],[,,1,,,,,1,,1],[1,,,,1,,,,,1,,,,1,1,,,,1,1,,,,,1],[,,,,,1],[,,,,,,,,,,,,,,,,,,,1],[1,,,1,1,,,,,,,1,,1,,1,1,1,1,1,1],[,,,,,1,,,,,,,1,,,,,,,1],,[,,1,1,1,1,1,,1,1,1,,,1,1,,,1,1,,1,1,1,,,1],[,,,,,,,,,,,,,,,,,,1],[,1,,,,1],,[1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1],[,,,1,1,1,1,,,,,,1,,1,,,,1,,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,,1],[,1,1,1,1,,1,1,1,1,1,1,1,1,,,,1,,1,,,1,1,1,1,1],[,,,,,,,,,,,1,,,,,,,,,1,,,,1],[,1,1,,1,1,,1,,,,1,1,,1,1,,,1,,1,1,,1],[,1,,1,,1,,,1,,,1,1,,1,1,,,1,1,1],[,1,1,1,1,1,,1,1,1,,,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[,,,,,,,,,1,,1,,1,1,,,,1,,,1],[,1,,,1,1,,,,,,,,,1,1,1,,,,,1],[1,,1,1,1,,,,1,1,1,1,1,,,1,,,1,,,1,,1,,1],[,1,1,,1,1,,1,1,,,,1,1,1,,,1,1,,,1,1,1,1,1,1],[1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,,1,1,,1,1,,1,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,,,1,,,,,,,,,1],[,1,,,,,,,,1,,,,,1,,,,1,,,1],[,1,1,1,1,,,1,1,1,1,1,,1,,1,,1,1,1,1,1,1,1,1,1,1],[,,,,,1,,1,,,,,1,1,1,1,1,,,1,,,,1],[,1,,,,,,,,1,,,,,,,,,,,,1],[1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1],[1,1,,1,,1,1,,,,1,,1,1,1,1,1,,1,1,,,,,,1],[,1,1,1,1,1,1,1,,1,1,,,1,1,,,,1,,1,1,,1,1],[,,,,,,,,,,,,,,,,,,,,,,,,1],[,1,1,,1,1,1,1,,1,,,1,1,1,1,,,1,,,,,,,1,1],[,1,,,,,,,,1,,,,,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1,1],[,1,1,,,,,,,,,,,,1,1,,,,,,1],[,1,,,,,,,1],[,,,,,,,,,,,,,,1,,,,,1,,,,,,1],[1,1,,,1,,,1,1,1,,,,1],,[,,,,,,,,,,,,,1,,,,,,,,,,1],[,,,,,,,,,1,,,,,,,,,1,,,,,,,1],[1,1,1,,1,,1,1,1,1,1,1,1,1,,1,,,1,,1,,,1,1],[,,,,,,,,,1],[,1,,,,1,,,1,,,1,,,1,,,,,1],[,1,1,,1,1,,,,,,,,,,,,,,,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[,1,,,1,1,,1,1,1,1,,,1,1,1,,,,1,,1],[1,1,1,1,1,1,,,1,1,1,1,1,1,,1,1,,1,1,1,,1,1,,1,1],[,,,,,,,,,,,,,,,1,,,,1],,[1,1,,1,,1,,,,,,1,,1,,1,1,,1,,1,1,,1,1,,1],[,,1,,,,,,1,,,,1,,1,,,,,1],[1,,,,,,,,,1,,,,,,1,,,,1,,1,,,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,,,1,,1,,,,,,1,,,1,,,,,,,,1],[,1,,1,,,,,,,,,,,,1],,[1,1,,,,,,,,,,,,,,,,,,,,,,1,1],[1]],[,[1,,,,,,,,,1,,,,,1,,1,,1],[,1,1,,1,1,,1,1,1,,,1,1,1,,,,1,,,1,,,,1],[,1,,,,,,,1,,,,1,,,,,,1],[1,1,1,1,1,1,,,,1,,,,,,,,,1,1,1,1],[1],[,1,1,,,1,1,,,,,1,,1,,,,,,,,1,,,,1],[1,,1,,,1,,1,,,,,1,1,1,1,,,,1,,,,1],[,,1,,,,,,,1,,,,,,,1,,,,,,,1],[1,,,,,,,,,,,,,,1,,,,1],[,,,1,,1,,,,,1,,,,1,1,,,,1],[1,,,,,1,,,1,1,,1,1,,,1,1,,1,1,1,,1,1,1,,1],[,1,1,,,,,1,,1,,1,1,1,,1,1,,,1,,1,1,1],[,1,,,,1,,,,1,,,1,,1,1,,,1,1,1,,,,,1],[1,,1,1,,1,,1,1,,1,,1,1,1,1,1,,,1,1,,,,,,1],[1,,,,,,,,,,,,,,,,,,1,,,1,,1],[,,,,,,,,,1,,,,,,1],[,,,,,,,,,,,,,,,,,,,,,1,,1],[,1,,,,1,,,1,1,,1,,,1,1,,,1,,,1,,,1,1],[1,1,,1,1,1,,1,1,1,,1,,1,1,1,,,1,,1,1,1],[1,,1,1,1,1,,,,1,,1,1,1,,1,,,1,1,1,,1,1,1,1,1],[1,,,,,,,,,,,,,1],[,,1,,,,,,,,,,,,,,,,,,,,1],[1,,,,,,,,,,,1,,1,,1,,,,1],[,,,1,,,,,,,,,1],[,1,,,,,,,,,,,,,,1,,,,,,,,,1],[,,,,,,,,1,1,,,,1,,,,,1,,,,,,,,1]],[,[1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,,1,1,1,1,1,,,1,1,1],[,,,,,1,,,,1,1,1,,1,1,1,,,1,,1,1,,1],[,,,,,,,,,,,,,,,,,,,1,1],[,1,,,,,,1,,,,,,,,,,,,,1],[,,1,,,1,,1,1,1,,1,1,,1,,,,1,,1,1],,[,,1,,,1,,,,,,1,,,,1],[,,,,,,,,,1,,,,,,,,,,1],[1,1,1,1,1,1,,1,1,1,,,1,1,,1,,1,,,1,1,1,,,1],[,,,,,1,,,,,,,,,,,,,1],[,1,,,,,,,,,,,,1,,1,1,,1,1,,1],[,,,,,1,,,,,,,,,,,,,,1],[,1,1,1,1,,,,,1,,,1,,1,,,,1,1,,,,1,1],[,1,,,1,,,1,,1,1,,1,,,,,,,1],[,,1,,1,,,1,,,,,,,,,,,1,1,,,,1],[,1,,,,,,,,,,,,,,,,,1,,,,,,1],[,,,,,,,,,,,,,,,,,,1],[,1,1,,,,,,,,,,,,,,,,1,,1,1],[,,,,,,,,,,,,1],,[,1,1,1,1,,,,1,1,,1,1,1,1,1,1,,1,1,1,1,,1,,1],[1,,,,1,,,,,,,,,,1],[1,,,,,,,,,1],,[,1,,,,1,,,,,,,,,,,,,,,,,,,,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,,,,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,,1,1,,1,1,1,,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,,1,1,1,1,1,1,1,1,1,1,,,1,1,1,,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,,1,,1,1,1,1],[1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1],[,,,1,1,1,1,,1,,,,1,1,,,1,1,,1],[,1,1,,1,,,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,,,,,,,,,,,,1],[1,1,1,,,,,1,1,1,,1,1,1,1,1,,1,1,1,1,1,,,,,1],[,1,,,,,,,1,1,,,1,1,1,,1,,,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1,1,,1,1,1,1,1,1],[,1,,,,1,,,,1,,,1,,,,1,,,,,,,1,1],[,1,1,1,1,1,,,1,1,1,,1,1,1,1,,,1,1,1,1,,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,,1,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1,1,1],[1,1,1,,1,,,1,1,1,1,,1,1,1,1,,,,1,,1,,1,,,1],[1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,,,1,,,,,,,,,1,1,,,,,,,,,1],[,,,,,,,,,,,,,,,,,,1],[,1,,1,,1,,1,,1,,1,1,1,1,1,,,1,,1,,1,,,,1],[,1,1,1,1,1,,1,1,1,,,1,1,1,1,1,,1,1,1,,1,1,,1],[1,,,1,,,,1,1,1,,,,,1,1,,,,1,,1],[1,1,,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[1,1,,,,,,,,1,,1,1,,,,,,,1,,1],[,1,,,,1,,1,1,,,,1,1,,1,,,,1,1,1,,1],[,,,,,,,,,,,,,1],[,1,,,,,,1,,,,,,,1],[,,,,,,,,1,,,,1,,1,,,,,,,,,,,,1]],[,[,1,1,,1,1,1,1,,1,1,1,,1,1,,1,1,,1,1,1,1,1,1,,1],[,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1],[,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,,1],[1,1,1,1,1,,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1],[,1,,,1,,,,,,,,1,,,,,,1,,,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,,,,1,1,1,,1,1,1,1,,,1,1,1,1,,,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,,1],[1,1,,1,,1,,1,,1,1,1,1,1,1,1,,1,1,,,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1],[,1,1,,,,,1,1,1,,,1,,1,1,,,,1,,1,,,1,1],[,,,,,,,1,,,,1,1,1,1,1,,1,,,1,,,,,1],[1,1,1,1,,1,1,1,,1,,1,1,1,1,,1,,1,,1,1,,,1,,1],[,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,,,1,1,,1,,1,1,1,,1,,1,1,,1,1,,1,,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,,,,,,,1,,,,,1,,1],[,1,1,1,,1,,1,,1,,,,1,,1,,,1,,,,,1,1,1],[,1,,,1,1,,1,,1,,1,1,1,1,1,,1,1,,,1,,,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,,,,,1,,1,,1,,,,,1,1,,1,,,,1,1]],[,[,1,,1,,,,,,,,1,,,,,,,1,,,,1],[,,,,,,,,,1,,1,1,1,,1,,,1,,1,1],[1,1,,,,,,,1,,,,,1,,1,,,,,,1],[,1,,,,,,,,,,1,,,,,,,,,1,1],,[,,,,,,,,,,,,,,,1,,,,1,,1],[,,1,1,,1,,1,,,,,,,,1,,,,,,1],[,,,,,,,,,,,,,,,,,,,,1,1],[,1,,,,1,,,,,,,,,1],[1,,1,1,,,,1,,,,,,,,,1,,,1,,,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,,,1,1,,1,1,,1,,1],[,1,,,1,1,,,,,,1,,1,,1,,,1,,1,1],[1,1,1,1,,1,,1,,1,,1,1,,1,1,1,1,1,,1,1,1,1,1,1],[,1,1,,,1,,1,,1,1,1,,,1,1,1,,1,1,1,1,,1,1],[,,,,1,,,1,,,,,,,1,,,,1,1],[,1,,,,,,,,,,1,,1,,1,,,,,1,,,,,1],,[1,1,,1,,1,,1,1,,,,,,1,1,,,1,1,1,1,1,1,1,1,1],[1,1,,1,,,,,,1,,,,,1,1,1,,,,1,1,,,1],[,1,1,,1,1,,,,1,,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1],[,1,1,,,1,,,,1,,,,1,1],[,,,,1],[,,,,,,,,,1,,,1],,[,,1,,1,,,,,,,,,1,,,,,,,,,,,,1],[,,,,,,,,,,,,,1]],[,[1,1,1,1,1,1,1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,,1,1,,1,1,1,1,1,,,1,1,1,1,1,,1,1,1,1,1,,,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,,1,,,,,1],[,1,,1,,,,,,1,,,,,1,1,,,,,1,1],[,1,1,,1,1,1,1,1,1,1,1,1,1,,1,1,1,,1,,,1,,1,1,1],[,1,,,,1,,,,,,,1],[,1,,,1,,,1,,1,,1,1,,1,,,,,1,,1,,,,1,1],[,1,,,1,,,1,1,1,1,1,1,1,1,1,,1,1,,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,1,1,1,1],[,,,,,,,,,,,,,,,,,,,,1],[,1,1,1,,,,1,1,,,,,,1,1,1,,1,1,1,1],[1,1,1,1,1,1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1,,1,1],[,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1],[,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,,1,1,1,1,1,1,1,,1,,1,1,1,1,1,,1,1,,1,1,1,1,1],[,1,,,,1,,,,1,,1,1,1,1,1,1,1,1,1,1,1],[,1,,,,1,,,,,,,,1,,,,,,,,,,1],[,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1],[1,1,,1,1,1,,1,1,1,,,1,1,1,1,1,1,1,1,1,1,,1,,1],[1,1,,,,,,,1,1,,,,,1,1,1,1,1,,1,1,1,1,,1],[,1,1,1,1,1,1,1,,1,1,1,,1,,1,1,1,1,,1,1,,1,1,1,1],,[,1,1,,,,,1,,1,,,,1,1,1,,,1,,,,,1],[,,,,,,,,,,,,,1],[,,,,,1,,,,,,,,1,1,,,,,1,,1,,,1,1],[,,,,,1,,,1,,,,,,1]],[,[,1],,,,,,,,,,,,,,,,,,,,[1,1,1,1,1,,1,1,1,1,,1,1,1,1,,1,1,1,1,,,1,1,1,1,1],[,1,,1,,1,,,1,1,1,,1,1,1,1,1,,,1,,,,1,,1,1],[,1,,1,,1,,,1,,,,,1,,,,,,1,1],[,1,,1,,,,,1,,,,1,,1,1,1,1,1,1,1,1,,1],[,1,,,,,,,,,,,,,,,1]],[,[,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,1,,,,,,,,,1,1,,,,1],[,,,,,,1],[,,1],[,1,1,,,1,,1,,1,1,,1,1,1,,,,1,1,1,,,,,1],,[,1,,,,1,,,,,,1,,,1,,,,1,1,,1],[,,,,,,,1,,,,,,,,,1],[,1,1,,,1,1,,,,,,1,1,1,,,,1,,1,1],[,,,,,,,1,,1,,,,,,,,,,1],[,1,1,,,,,,1,1,,,,1,,,,,,,1,,,1],,[1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,,1,,,1,,,,,1,,1,,1,,1,,,,,1],[1,1,1,1,1,1,1,1,,,,,1,1,,1,1,,1,,,1,,1],[,,,,,,,,,,,,,,1,,,,,,1],,[,,,,,,,,,1,,,,1,,1,,,,,1],[,,1,,,,,,,1,,1,1,1,1,,,,,,,,,1],[,,,1,,,,,1,,,,,1,,,,,,1,,,,1],[1,,1,1,,1,1,1,1,1,,1,,,,1,1,1,,,1,1,,,,1,1],,[1,1,,,,,,,,,,1,1,1,,1,,,1],[,,,,1,,,,,,,,,,,,,,,,,,,1],[,,,,,,,,,,,,,,1,,,,,1,,1],[,,,,,,,,1]],[,[1,1,1,1,1,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,,,1,1,1,1,1,,1,1,,1,1,1,1,,1,1,1,1,1,1],[1,1,1,1,,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[,,1,,,1,,,,,,,,1,,,,,,1,,,,1],[1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,,1,1,1,1],[1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,,,,1,1,1,1,1,1,,1,1,1,1,,1],[1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,,1,1,1,1,1,1,1,1,,1,1,1,,1,1,1,1,1,1,,1,1,1,1],[1,1,1,1,1,,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[1,,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1],[1,1,1,1,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1],[,,1,1,1,1,,1,,1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[1,1,,,,,,,1,,1,1,,1,1,1,,1,1,1,1,1,,,1],[1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1],[1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1],[1,1,1,1,,1,,1,,1,1,1,1,1,,1,,1,1,1,1,,1,1,1,1,1],[1,1,1,1,,1,,,,,,1,,1,,,,,1,1,,,,,1],[1,,1,1,,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,1,1,,1,,1,,,,1,1,1,1,1,,,1,1,,1,,1],[,1,1,1,1,,,,,1,,1,1,1,1,1,,,1,1,,,,1,1,1],[,1,1,1,1,1,,1,,,,,1,,1,,1,,,1,,,1,1,,1]],[,[1,1,1,1,1,1,1,1,,1,1,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,,1,1,1,,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,,1,1],[1,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,,,,,,,,1,,,,,1,1,,,1,,1],[1,1,1,1,1,1,1,1,1,1,1,,,,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,,1,1,1,1,,1,1,,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1],[1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1],[,1,,,,,,1,,1,1,,1,1,1,1,1,,,1,,1,,1],[1,1,1,,1,1,1,1,,,,1,1,1,1,,1,1,1,1,1,1,1,1,1,,1],[1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,,1,1,1,1,1,1],[,1,,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1],[,,1,,,,,,,,,,1,1,1,1,1,1,1,,1,1,,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,,1,1,1,1,1,1,1,1],[,1,,,1,1,,,,1,,1,1,1,1,1,,,,1,1,1,,1,1,1],[1,1,1,1,1,1,1,1,1,,,,1,1,1,1,1,1,1,,1,1,,1,1,1],[,1,1,1,,1,,1,1,1,1,,,1,1,1,,1,1,1,1,1,,,1,1],[1,1,,,,1,,,1,1,1,,1,,1,,1,,1,1,1,1,1,,1,1,1],[,1,,,,,,,1,,1,,1,1,1,1,,,,,,,,,1]],[,[,,,,,,,,,,,,,1,1,,,,1],[,1,,,,,,,,1,,,1,,,,,,1,,,1,,,,1],,[,1,,,,1,,1,,1,1,,1,1,,,,,,,,1],[,,,,,,,,,,,,,,,,,,,1],[,,,,,,,,,1],[1,1,1,,,1,,,1,,,,,,1,1,,,,,,,,,,1],[,1,,,,,,,,,,,,,1],[,,,,,,,,,,,,,,,,,,,1,,,1],[,,,,,,,,,1],[1,1,,,,,,1,1,1,,1,1,,,,1,1,,1,,1,1,1,,1],[,1,1,1,,1,1,,,1,,1,1,1,1,,,,,,,1,,1,,1],[,1,1,1,1,,,1,,1,,,,1,1,1,1,,1,1,,1],[,1,,,1,1,,1,,1,,1,,1,1,,1,,1,,,1,,,1,,1],[,,,,,,,,,,,1,,,1],[,,,,,,,,,1,,,,,,,,,,,,,1],,[1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,,1,1,1,1,1,1,1],[,1,,,,,,,1,1,,1,,,,,1,,,1,,1],[,1,,,,1,,,1,,,,,,,,1,,1,,,1],[,,,,,,,,,,,,,1,1,,,,1,,,1],[,,,,,1,,,1,,,,1],[,1],,[,1],[1,,,,,,,,,,,,,,1,,,,,1]],[,[,1,,,,1,1,1,1,1,1,,1,1,1,1,1,,1,1,,1,1,,,1],[,,1,,,,,,,,,1],,,[1,,,1,1,,,,,,,,1,1,,1,1,,1],,[,,,,,,,,,,,,,,,,,,1,,1],,[1,,,1,1,,1,1,,,,,1,,1,,,,,1,1,,1],[,,,,,,,,,,,1],[,1,,,,,,,,1,1,1,1,1,1,1,1,,,,1,1,,,,,1],[,,,,,,,,,,,,,,,,1,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,,,1,1,1,1,,1,1,1,1,1,1],[,,,,,,,,,,,1,,1,,,1],[1,,,,,,,,,,,,,,,,,,1,,1],,,[,1,,,,,,,,,,,,,,1,,,,1,1],[,,,,,,,,,1,,,1,,1,,,,,,,,1],[,,,,,,,,,,,,,,,1],[,,,,,,,,,,,,,1,1,,,,,,1],,[,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,,1,1,,1,1,1,1,1,1,,,1,1,1,1,1,,1,1],[,1,,,,,,,,1],[,,,,1,,,1,,,1,1,,,,,,,,,1,1,,,,1],[,1,,1,1,,,1,1,1,,,,1,1,1,1,,1,1,1,1,,1],[,,,,,,,1],[,1,1,,,,,1,,1,,,,,,1,,,,,,1,,1,,1],[,1,,,,,,1,,,,1,,,,,,,,,,1],[,,1,1,,1,1,1,1,1,1,1,1,1,1,,,,1,,1,1,1,1,,1],[,1,,,,,,,,1],[,1,1,,1,,,,,,,,1,,,,,,1,,,1,,1,,1],[,1,,1,,1,,1,1,1,,1,1,1,,1,,,1,1,,1,1,1,1,1],[,1,1,1,1,1,,1,1,1,,,,1,1,1,,,,1,1,,,1,1],[,,1,1,1,1,,1,,1,,1,,1,1,1,1,,,,,1,,1,,1],[1,1,1,1,1,1,1,1,,1,,1,,1,1,1,,,1,1,,,,1,,1],[,,,1],,[,1,1,,1,,,1,1,1,,1,1,1,1,1,1,,1,1,,1,1,1,1,1,1],[,1,,,,,,1,,1,,1,,,,,,,1,1,,1,1],[,,1,,,,1,,1,1,,1,,1,,,,,,,,,,1],[,1,1,,1,,,,1,,,,1,1,1,,,,1,,1,1,1,,1,1],,[,1,1,,,,,,,,,,,,,1,,,1,,,,,1],[,1,,,,,,,,,,,,,,,,,,,,,,1],[,1,1,,,,,,,1,,,,1,1,,,,1,,,,,,,1]],[,[,1,1,1,1,1,,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1],[,1,1,1,1,1,,1,,1,1,,,1,1,1,1,,1,,,,,1,1,1],[,,1,1,,1,,1,1,,,,1,1,1,1,,,1,,1,1,1,1,,1],[,1,,1,,,,,,,,1,,1,,1,,1,,,,,,,,1],[,,1,,1,,,1,,,,,1,1,,,1,,1,1,1,1],[,1],[,1,1,,1,,1,1,,1,,,1,1,1,,,,1,,,1,,1],[1,1,,1,1,1,,,,,,,,1,,,,,1,,1,1,1],[,1,1,,,,,,,1,,,1,,1,,1,,1,1,,,1,,,1],[,,1,,,,,,,,,,,,,,,,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,1,1,1,,1,,1,,,,,1,1,1,,,1,,1,,,,1],[,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,,,1,1,1,1,1,,1,1,1,,1,1,1,1,1,,,,1,1],[,,,1,1,,,1,,1,,1,,1,1,1,1,,1,,,,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,,,,,,,,,,,,,,,,,,1],[,1,1,,1,1,,1,,1,,,,1,1,,,1,1,,1,1,,1],[,1,1,1,1,1,,,1,1,1,,1,1,1,1,1,1,1,1,,1,1,,,1],[,1,1,1,1,1,,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1,,1,1],[,1,1,,1,,,1,,,1,,1,1,1,1,1,,1,,1,1],[,,,,,1,,,,1,,,,,1,1,,,,1],[,1,,1,1,1,,1,,,1,1,1,,,1,,,1,,1,,,1],[,,1,,,,,,,,,1,,1,,,,,1,,1],[,1,1,,,,,,,,1,1,1,,,,,,,,1,,,,,1],[,,,,,,,,1,,,,,1,,,1]],[,[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,1,,1,1,,,1,1,1,1,1,1,1,1,,,,,,,,,1,1],[,,,,,,,,1,,,,1,,1,,1,,1],[,1,,,1,1,,1,,,,1,,,,,,,,1],[,1,,1,,1,,,,1,1,,1,,1,,,,1,1,1,1,1,,,1],,[,1,,,,,,,,1,,,1,1,,,1,,1,1,,1,,1],[,1,,,1,,,,,,,,1,,,,,,,1],[1,1,,,,,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,,1,1,1],,[,1,,,,,,1,1,1,,1,1,1,1,1,,,1,,1,1,,,,1],[,1,1,,,1,,1,,1,,,1,1,1,1,,,1,,,1,,,,1],[,1,1,1,1,1,,1,1,1,,1,1,1,1,1,1,1,1,1,1,,,,1,,1],[,1,1,,1,1,,1,1,,,1,1,,1,1,,1,,1,,1],[1,,1,,,,,1,,1,,1,1,1,1,,,,,1,1,,,,1,1],[,1,1,,,,,1,1,,,1,,1,1,1,1,,,,,,,,,,1],,[,1,1,,,1,,,,1,,1,1,1,1,1,,,,1,,,,1,,1],[,,,1,1,,,1,,,,,1,1,1,1,1,,1,1,,,,,,1],[,1,,,,,,,,,,,1,,,,1,,,,,,,1,,1],[,1,1,1,1,1,1,1,,1,1,1,1,1,1,,1,1,1,,1,1,,1,1,1,1],[,1,,,,,,,,,,,,,,,,,,,1],[,1,,,,,,1,,,,,1,,1,,,1,1,,1,1,,1],[,1,,,,,,1,,,,,1,1,,,,,,,,1,,,,1],[,,,,,,,,,,,,,,,,,,1,,,1,,,,,1],[,,,,,,,1,,,,1]],[,[1,1,1,1,1,1,1,1,1,1,1,1,1,1,,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,1,,1,,,,,,,1,,,,,,,,1,1,,1],[,1,,,1,,,,1],[,,,,,,,,,,1],[,1,,,,,,1,1,,,,,1,1],,[,1,1,,,,,,1,,,,,1,1,,,,1],[1,,1,,1,,,,,1,,,,,1,,,,,,,,,1,1],[,1,1,,,,,,,,,1,1,1,1,,,,1,,,,,1,,,1],,[,1,1,,1,,,1,1,,,1,,,1,1,1,,1,,1,1,1,,,,1],[,1,,,,1,,,,,1,,,1,1,,,1,,1,,1,,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,1,,,1,1,,1,,,,1,,,,,,,,1],[,,,1,,,,,1,,,,,1,,1,,1,1,1],[,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[,,,,,1],[,1,,,,,,1,,,,,,,1,1,1,,,1],[,1,,,,,,,,,,1,1,1,,,,,1,,,1],[,,,,,1,,1,,,,,1,1,1,,1,1,,1,1,1,,,1,1],[1,1,,,,,,,1,,,,,1,1,,,,,,,,,,,1],,[,1],[,,,,,,,,,,,,,,,,,,,,,,,,1],[,,1,,,,,1,,,1,,,,1,,1],[,1,,,,,,,,,1]]];\n\n/**\n * Determine if a string is a valid language code\n * @method isValidLang\n * @memberof axe.utils\n * @param {String} lang String to test if a valid language code\n * @returns {Boolean}\n */\nfunction isValidLang(lang) {\n  let array = langs;\n\n  // padEnd is not supported in IE11\n  while (lang.length < 3) {\n    lang += '`';\n  }\n  for (let i = 0; i <= lang.length - 1; i++) {\n    const index = lang.charCodeAt(i) - 96;\n    array = array[index];\n    if (!array) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n/**\n * Returns array of valid language codes\n * @deprecated\n * @method validLangs\n * @memberof axe.utils\n * @return {Array<String>} Valid language codes\n */\nexport function validLangs(langArray) {\n  // account for our external API tests passing non-array things\n  langArray = Array.isArray(langArray) ? langArray : langs;\n\n  let codes = [];\n  langArray.forEach((lang, index) => {\n    const char = String.fromCharCode(index + 96).replace('`', '');\n    if (Array.isArray(lang)) {\n      codes = codes.concat(validLangs(lang).map(newLang => char + newLang));\n    } else {\n      codes.push(char);\n    }\n  });\n\n  return codes;\n}\n\nexport default isValidLang;\n"
  },
  {
    "path": "lib/intro.stub",
    "content": "/*! axe v<%= pkg.version %>\n * Copyright (c) 2015 - <%= grunt.template.today(\"yyyy\") %> Deque Systems, Inc.\n *\n * Your use of this Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * This entire copyright notice must appear in every copy of this file you\n * distribute or in any file that contains substantial portions of this source\n * code.\n */\n(function axeFunction (window) {\n  // A window reference is required to access the axe object in a \"global\".\n  var global = window;\n  var document = window.document;\n"
  },
  {
    "path": "lib/misc/any-failure-summary.json",
    "content": "{\n  \"type\": \"any\",\n  \"metadata\": {\n    \"failureMessage\": \"Fix any of the following:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n  }\n}\n"
  },
  {
    "path": "lib/misc/incomplete-fallback.json",
    "content": "{\n  \"incompleteFallbackMessage\": \"axe couldn't tell the reason. Time to break out the element inspector!\"\n}\n"
  },
  {
    "path": "lib/misc/none-failure-summary.json",
    "content": "{\n  \"type\": \"none\",\n  \"metadata\": {\n    \"failureMessage\": \"Fix all of the following:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n  }\n}\n"
  },
  {
    "path": "lib/outro.stub",
    "content": "\n}( typeof window === 'object' ? window : this ));\n"
  },
  {
    "path": "lib/rules/accesskeys.json",
    "content": "{\n  \"id\": \"accesskeys\",\n  \"impact\": \"serious\",\n  \"selector\": \"[accesskey]\",\n  \"excludeHidden\": false,\n  \"tags\": [\"cat.keyboard\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure every accesskey attribute value is unique\",\n    \"help\": \"accesskey attribute value should be unique\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"accesskeys\"]\n}\n"
  },
  {
    "path": "lib/rules/area-alt.json",
    "content": "{\n  \"id\": \"area-alt\",\n  \"impact\": \"critical\",\n  \"selector\": \"map area[href]\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag244\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.2.4.4\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-1.1.2\"\n  ],\n  \"actIds\": [\"c487ae\"],\n  \"metadata\": {\n    \"description\": \"Ensure <area> elements of image maps have alternative text\",\n    \"help\": \"Active <area> elements must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\"non-empty-alt\", \"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-allowed-attr-matches.js",
    "content": "function ariaAllowedAttrMatches(node, virtualNode) {\n  const aria = /^aria-/;\n  const attrs = virtualNode.attrNames;\n  if (attrs.length) {\n    for (let i = 0, l = attrs.length; i < l; i++) {\n      if (aria.test(attrs[i])) {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\nexport default ariaAllowedAttrMatches;\n"
  },
  {
    "path": "lib/rules/aria-allowed-attr.json",
    "content": "{\n  \"id\": \"aria-allowed-attr\",\n  \"impact\": \"critical\",\n  \"matches\": \"aria-allowed-attr-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"5c01ea\"],\n  \"metadata\": {\n    \"description\": \"Ensure an element's role supports its ARIA attributes\",\n    \"help\": \"Elements must only use supported ARIA attributes\"\n  },\n  \"all\": [\"aria-allowed-attr\"],\n  \"any\": [],\n  \"none\": [\"aria-unsupported-attr\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-allowed-role-matches.js",
    "content": "import { getExplicitRole } from '../commons/aria';\n\nfunction ariaAllowedRoleMatches(node, virtualNode) {\n  return (\n    getExplicitRole(virtualNode, {\n      dpub: true,\n      fallback: true\n    }) !== null\n  );\n}\n\nexport default ariaAllowedRoleMatches;\n"
  },
  {
    "path": "lib/rules/aria-allowed-role.json",
    "content": "{\n  \"id\": \"aria-allowed-role\",\n  \"impact\": \"minor\",\n  \"excludeHidden\": false,\n  \"selector\": \"[role]\",\n  \"matches\": \"aria-allowed-role-matches\",\n  \"tags\": [\"cat.aria\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure role attribute has an appropriate value for the element\",\n    \"help\": \"ARIA role should be appropriate for the element\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-allowed-role\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-braille-equivalent.json",
    "content": "{\n  \"id\": \"aria-braille-equivalent\",\n  \"reviewOnFail\": true,\n  \"impact\": \"serious\",\n  \"selector\": \"[aria-brailleroledescription], [aria-braillelabel]\",\n  \"tags\": [\"cat.aria\", \"wcag2a\", \"wcag412\", \"EN-301-549\", \"EN-9.4.1.2\"],\n  \"metadata\": {\n    \"description\": \"Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent\",\n    \"help\": \"aria-braille attributes must have a non-braille equivalent\"\n  },\n  \"all\": [\"braille-roledescription-equivalent\", \"braille-label-equivalent\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-command-name.json",
    "content": "{\n  \"id\": \"aria-command-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"link\\\"], [role=\\\"button\\\"], [role=\\\"menuitem\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.9.1\"\n  ],\n  \"actIds\": [\"97a4e1\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA button, link and menuitem has an accessible name\",\n    \"help\": \"ARIA commands must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-conditional-attr.json",
    "content": "{\n  \"id\": \"aria-conditional-attr\",\n  \"impact\": \"serious\",\n  \"matches\": \"aria-allowed-attr-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"5c01ea\"],\n  \"metadata\": {\n    \"description\": \"Ensure ARIA attributes are used as described in the specification of the element's role\",\n    \"help\": \"ARIA attributes must be used as specified for the element's role\"\n  },\n  \"all\": [\"aria-conditional-attr\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-deprecated-role.json",
    "content": "{\n  \"id\": \"aria-deprecated-role\",\n  \"impact\": \"minor\",\n  \"selector\": \"[role]\",\n  \"matches\": \"no-empty-role-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"674b10\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements do not use deprecated roles\",\n    \"help\": \"Deprecated ARIA roles must not be used\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"deprecatedrole\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-dialog-name.json",
    "content": "{\n  \"id\": \"aria-dialog-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"dialog\\\"], [role=\\\"alertdialog\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\"cat.aria\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA dialog and alertdialog node has an accessible name\",\n    \"help\": \"ARIA dialog and alertdialog nodes should have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-has-attr-matches.js",
    "content": "function ariaHasAttrMatches(node, virtualNode) {\n  const aria = /^aria-/;\n\n  return virtualNode.attrNames.some(attr => {\n    return aria.test(attr);\n  });\n}\n\nexport default ariaHasAttrMatches;\n"
  },
  {
    "path": "lib/rules/aria-hidden-body.json",
    "content": "{\n  \"id\": \"aria-hidden-body\",\n  \"impact\": \"critical\",\n  \"selector\": \"body\",\n  \"excludeHidden\": false,\n  \"matches\": \"is-initiator-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-10.8.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure aria-hidden=\\\"true\\\" is not present on the document body.\",\n    \"help\": \"aria-hidden=\\\"true\\\" must not be present on the document body\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-hidden-body\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-hidden-focus-matches.js",
    "content": "import { getComposedParent } from '../commons/dom';\n\n/**\n * Only match the outer-most `aria-hidden=true` element\n * @param {HTMLElement} el the HTMLElement to verify\n * @return {Boolean}\n */\nfunction shouldMatchElement(el) {\n  if (!el) {\n    return true;\n  }\n  if (el.getAttribute('aria-hidden') === 'true') {\n    return false;\n  }\n  return shouldMatchElement(getComposedParent(el));\n}\n\nfunction ariaHiddenFocusMatches(node) {\n  return shouldMatchElement(getComposedParent(node));\n}\n\nexport default ariaHiddenFocusMatches;\n"
  },
  {
    "path": "lib/rules/aria-hidden-focus.json",
    "content": "{\n  \"id\": \"aria-hidden-focus\",\n  \"impact\": \"serious\",\n  \"selector\": \"[aria-hidden=\\\"true\\\"]\",\n  \"matches\": \"aria-hidden-focus-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.name-role-value\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-10.8.1\"\n  ],\n  \"actIds\": [\"6cfa84\"],\n  \"metadata\": {\n    \"description\": \"Ensure aria-hidden elements are not focusable nor contain focusable elements\",\n    \"help\": \"ARIA hidden element must not be focusable or contain focusable elements\"\n  },\n  \"all\": [\n    \"focusable-modal-open\",\n    \"focusable-disabled\",\n    \"focusable-not-tabbable\"\n  ],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-input-field-name.json",
    "content": "{\n  \"id\": \"aria-input-field-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"combobox\\\"], [role=\\\"listbox\\\"], [role=\\\"searchbox\\\"], [role=\\\"slider\\\"], [role=\\\"spinbutton\\\"], [role=\\\"textbox\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.1.1\"\n  ],\n  \"actIds\": [\"e086e5\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA input field has an accessible name\",\n    \"help\": \"ARIA input fields must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": [\"no-implicit-explicit-label\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-meter-name.json",
    "content": "{\n  \"id\": \"aria-meter-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"meter\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"RGAAv4\",\n    \"RGAA-11.1.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA meter node has an accessible name\",\n    \"help\": \"ARIA meter nodes must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-progressbar-name.json",
    "content": "{\n  \"id\": \"aria-progressbar-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"progressbar\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"RGAAv4\",\n    \"RGAA-11.1.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA progressbar node has an accessible name\",\n    \"help\": \"ARIA progressbar nodes must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-prohibited-attr.json",
    "content": "{\n  \"id\": \"aria-prohibited-attr\",\n  \"impact\": \"serious\",\n  \"matches\": \"aria-allowed-attr-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"5c01ea\"],\n  \"metadata\": {\n    \"description\": \"Ensure ARIA attributes are not prohibited for an element's role\",\n    \"help\": \"Elements must only use permitted ARIA attributes\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"aria-prohibited-attr\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-required-attr.json",
    "content": "{\n  \"id\": \"aria-required-attr\",\n  \"impact\": \"critical\",\n  \"selector\": \"[role]\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"4e8ab6\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements with ARIA roles have all required ARIA attributes\",\n    \"help\": \"Required ARIA attributes must be provided\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-required-attr\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-required-children-matches.js",
    "content": "import { requiredOwned, getExplicitRole } from '../commons/aria';\n\nfunction ariaRequiredChildrenMatches(node, virtualNode) {\n  const role = getExplicitRole(virtualNode, { dpub: true });\n  return !!requiredOwned(role);\n}\n\nexport default ariaRequiredChildrenMatches;\n"
  },
  {
    "path": "lib/rules/aria-required-children.json",
    "content": "{\n  \"id\": \"aria-required-children\",\n  \"impact\": \"critical\",\n  \"selector\": \"[role]\",\n  \"matches\": \"aria-required-children-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.1\"\n  ],\n  \"actIds\": [\"bc4a75\", \"ff89c9\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements with an ARIA role that require child roles contain them\",\n    \"help\": \"Certain ARIA roles must contain particular children\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-required-children\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-required-parent-matches.js",
    "content": "import { requiredContext, getExplicitRole } from '../commons/aria';\n\nfunction ariaRequiredParentMatches(node, virtualNode) {\n  const role = getExplicitRole(virtualNode);\n  return !!requiredContext(role);\n}\n\nexport default ariaRequiredParentMatches;\n"
  },
  {
    "path": "lib/rules/aria-required-parent.json",
    "content": "{\n  \"id\": \"aria-required-parent\",\n  \"impact\": \"critical\",\n  \"selector\": \"[role]\",\n  \"matches\": \"aria-required-parent-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.1\"\n  ],\n  \"actIds\": [\"ff89c9\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements with an ARIA role that require parent roles are contained by them\",\n    \"help\": \"Certain ARIA roles must be contained by particular parents\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-required-parent\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-roledescription.json",
    "content": "{\n  \"id\": \"aria-roledescription\",\n  \"impact\": \"serious\",\n  \"selector\": \"[aria-roledescription]\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"deprecated\"\n  ],\n  \"enabled\": false,\n  \"metadata\": {\n    \"description\": \"Ensure aria-roledescription is only used on elements with an implicit or explicit role\",\n    \"help\": \"aria-roledescription must be on elements with a semantic role\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-roledescription\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-roles.json",
    "content": "{\n  \"id\": \"aria-roles\",\n  \"impact\": \"critical\",\n  \"selector\": \"[role]\",\n  \"matches\": \"no-empty-role-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"674b10\"],\n  \"metadata\": {\n    \"description\": \"Ensure all elements with a role attribute use a valid value\",\n    \"help\": \"ARIA roles used must conform to valid values\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"invalidrole\", \"abstractrole\", \"unsupportedrole\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-tab-name.json",
    "content": "{\n  \"id\": \"aria-tab-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"tab\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA tab node has an accessible name\",\n    \"help\": \"ARIA tab nodes must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-text.json",
    "content": "{\n  \"id\": \"aria-text\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=text]\",\n  \"tags\": [\"cat.aria\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure role=\\\"text\\\" is used on elements with no focusable descendants\",\n    \"help\": \"\\\"role=text\\\" should have no focusable descendants\"\n  },\n  \"all\": [],\n  \"any\": [\"no-focusable-content\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-toggle-field-name.json",
    "content": "{\n  \"id\": \"aria-toggle-field-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"checkbox\\\"], [role=\\\"menuitemcheckbox\\\"], [role=\\\"menuitemradio\\\"], [role=\\\"radio\\\"], [role=\\\"switch\\\"], [role=\\\"option\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"e086e5\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA toggle field has an accessible name\",\n    \"help\": \"ARIA toggle fields must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": [\"no-implicit-explicit-label\"]\n}\n"
  },
  {
    "path": "lib/rules/aria-tooltip-name.json",
    "content": "{\n  \"id\": \"aria-tooltip-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"tooltip\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\"cat.aria\", \"wcag2a\", \"wcag412\", \"EN-301-549\", \"EN-9.4.1.2\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA tooltip node has an accessible name\",\n    \"help\": \"ARIA tooltip nodes must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-treeitem-name.json",
    "content": "{\n  \"id\": \"aria-treeitem-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"treeitem\\\"]\",\n  \"matches\": \"no-naming-method-matches\",\n  \"tags\": [\"cat.aria\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure every ARIA treeitem node has an accessible name\",\n    \"help\": \"ARIA treeitem nodes should have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-valid-attr-value.json",
    "content": "{\n  \"id\": \"aria-valid-attr-value\",\n  \"impact\": \"critical\",\n  \"matches\": \"aria-has-attr-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"6a7281\"],\n  \"metadata\": {\n    \"description\": \"Ensure all ARIA attributes have valid values\",\n    \"help\": \"ARIA attributes must conform to valid values\"\n  },\n  \"all\": [\"aria-valid-attr-value\", \"aria-errormessage\", \"aria-level\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/aria-valid-attr.json",
    "content": "{\n  \"id\": \"aria-valid-attr\",\n  \"impact\": \"critical\",\n  \"matches\": \"aria-has-attr-matches\",\n  \"tags\": [\n    \"cat.aria\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"5f99a7\"],\n  \"metadata\": {\n    \"description\": \"Ensure attributes that begin with aria- are valid ARIA attributes\",\n    \"help\": \"ARIA attributes must conform to valid names\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-valid-attr\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/audio-caption.json",
    "content": "{\n  \"id\": \"audio-caption\",\n  \"impact\": \"critical\",\n  \"selector\": \"audio\",\n  \"enabled\": false,\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.time-and-media\",\n    \"wcag2a\",\n    \"wcag121\",\n    \"EN-301-549\",\n    \"EN-9.1.2.1\",\n    \"section508\",\n    \"section508.22.a\",\n    \"deprecated\"\n  ],\n  \"actIds\": [\"2eb176\", \"afb423\"],\n  \"metadata\": {\n    \"description\": \"Ensure <audio> elements have captions\",\n    \"help\": \"<audio> elements must have a captions track\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"caption\"]\n}\n"
  },
  {
    "path": "lib/rules/autocomplete-matches.js",
    "content": "import { sanitize } from '../commons/text';\nimport standards from '../standards';\nimport { isVisibleToScreenReaders, isVisibleOnScreen } from '../commons/dom';\nimport { parseTabindex } from '../core/utils';\nimport { getExplicitRole } from '../commons/aria';\n\nfunction autocompleteMatches(node, virtualNode) {\n  const autocomplete = virtualNode.attr('autocomplete');\n  if (!autocomplete || sanitize(autocomplete) === '') {\n    return false;\n  }\n\n  const nodeName = virtualNode.props.nodeName;\n  if (['textarea', 'input', 'select'].includes(nodeName) === false) {\n    return false;\n  }\n\n  // The element has a `readonly` or `aria-readonly=\"true\"` attribute\n  const ariaReadonly = virtualNode.attr('aria-readonly') || 'false';\n  if (\n    virtualNode.hasAttr('readonly') ||\n    ariaReadonly.toLowerCase() === 'true'\n  ) {\n    return false;\n  }\n\n  // The element is an `input` element a `type` of `hidden`, `button`, `submit` or `reset`\n  const excludedInputTypes = ['submit', 'reset', 'button', 'hidden'];\n  if (\n    nodeName === 'input' &&\n    excludedInputTypes.includes(virtualNode.props.type)\n  ) {\n    return false;\n  }\n\n  // The element has a `disabled` or `aria-disabled=\"true\"` attribute\n  const ariaDisabled = virtualNode.attr('aria-disabled') || 'false';\n  if (\n    virtualNode.hasAttr('disabled') ||\n    ariaDisabled.toLowerCase() === 'true'\n  ) {\n    return false;\n  }\n\n  // The element has `tabindex=\"-1\"` and has a [[semantic role]] that is\n  //   not a [widget](https://www.w3.org/TR/wai-aria-1.1/#widget_roles)\n  const role = getExplicitRole(virtualNode);\n  const tabIndex = parseTabindex(virtualNode.attr('tabindex'));\n  if (tabIndex < 0 && virtualNode.hasAttr('role')) {\n    const roleDef = standards.ariaRoles[role];\n    if (roleDef === undefined || roleDef.type !== 'widget') {\n      return false;\n    }\n  }\n\n  // The element is **not** visible on the page or exposed to assistive technologies\n  if (\n    tabIndex < 0 &&\n    virtualNode.actualNode &&\n    !isVisibleOnScreen(virtualNode) &&\n    !isVisibleToScreenReaders(virtualNode)\n  ) {\n    return false;\n  }\n\n  return true;\n}\n\nexport default autocompleteMatches;\n"
  },
  {
    "path": "lib/rules/autocomplete-valid.json",
    "content": "{\n  \"id\": \"autocomplete-valid\",\n  \"impact\": \"serious\",\n  \"matches\": \"autocomplete-matches\",\n  \"tags\": [\n    \"cat.forms\",\n    \"wcag21aa\",\n    \"wcag135\",\n    \"EN-301-549\",\n    \"EN-9.1.3.5\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.13.1\"\n  ],\n  \"actIds\": [\"73f2c2\"],\n  \"metadata\": {\n    \"description\": \"Ensure the autocomplete attribute is correct and suitable for the form field\",\n    \"help\": \"autocomplete attribute must be used correctly\"\n  },\n  \"all\": [\"autocomplete-valid\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/avoid-inline-spacing.json",
    "content": "{\n  \"id\": \"avoid-inline-spacing\",\n  \"impact\": \"serious\",\n  \"selector\": \"[style]\",\n  \"matches\": \"is-visible-on-screen-matches\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag21aa\",\n    \"wcag1412\",\n    \"EN-301-549\",\n    \"EN-9.1.4.12\",\n    \"ACT\"\n  ],\n  \"actIds\": [\"24afc2\", \"9e45ec\", \"78fd32\"],\n  \"metadata\": {\n    \"description\": \"Ensure that text spacing set through style attributes can be adjusted with custom stylesheets\",\n    \"help\": \"Inline text spacing must be adjustable with custom stylesheets\"\n  },\n  \"all\": [\n    \"important-letter-spacing\",\n    \"important-word-spacing\",\n    \"important-line-height\"\n  ],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/blink.json",
    "content": "{\n  \"id\": \"blink\",\n  \"impact\": \"serious\",\n  \"selector\": \"blink\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.time-and-media\",\n    \"wcag2a\",\n    \"wcag222\",\n    \"section508\",\n    \"section508.22.j\",\n    \"TTv5\",\n    \"TT2.b\",\n    \"EN-301-549\",\n    \"EN-9.2.2.2\",\n    \"RGAAv4\",\n    \"RGAA-13.8.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure <blink> elements are not used\",\n    \"help\": \"<blink> elements are deprecated and must not be used\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"is-on-screen\"]\n}\n"
  },
  {
    "path": "lib/rules/button-name.json",
    "content": "{\n  \"id\": \"button-name\",\n  \"impact\": \"critical\",\n  \"selector\": \"button\",\n  \"matches\": \"no-explicit-name-required-matches\",\n  \"tags\": [\n    \"cat.name-role-value\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.9.1\"\n  ],\n  \"actIds\": [\"97a4e1\", \"m6b1q3\"],\n  \"metadata\": {\n    \"description\": \"Ensure buttons have discernible text\",\n    \"help\": \"Buttons must have discernible text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"button-has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"implicit-label\",\n    \"explicit-label\",\n    \"presentational-role\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/bypass-matches.js",
    "content": "import isInitiatorMatches from './is-initiator-matches';\n\nfunction bypassMatches(node, virtualNode, context) {\n  // the top level window should have an anchor\n  if (isInitiatorMatches(node, virtualNode, context)) {\n    return !!node.querySelector('a[href]');\n  }\n\n  // all iframes do not need an anchor but should be checked for bypass\n  // elements (headings, landmarks, etc.)\n  return true;\n}\n\nexport default bypassMatches;\n"
  },
  {
    "path": "lib/rules/bypass.json",
    "content": "{\n  \"id\": \"bypass\",\n  \"impact\": \"serious\",\n  \"selector\": \"html:not(html *)\",\n  \"pageLevel\": true,\n  \"matches\": \"bypass-matches\",\n  \"reviewOnFail\": true,\n  \"tags\": [\n    \"cat.keyboard\",\n    \"wcag2a\",\n    \"wcag241\",\n    \"section508\",\n    \"section508.22.o\",\n    \"TTv5\",\n    \"TT9.a\",\n    \"EN-301-549\",\n    \"EN-9.2.4.1\",\n    \"RGAAv4\",\n    \"RGAA-12.7.1\"\n  ],\n  \"actIds\": [\"cf77f2\", \"047fe0\", \"b40fd1\", \"3e12e1\", \"ye5d6e\"],\n  \"metadata\": {\n    \"description\": \"Ensure each page has at least one mechanism for a user to bypass navigation and jump straight to the content\",\n    \"help\": \"Page must have means to bypass repeated blocks\"\n  },\n  \"all\": [],\n  \"any\": [\"internal-link-present\", \"header-present\", \"landmark\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/color-contrast-enhanced.json",
    "content": "{\n  \"id\": \"color-contrast-enhanced\",\n  \"impact\": \"serious\",\n  \"matches\": \"color-contrast-matches\",\n  \"excludeHidden\": false,\n  \"enabled\": false,\n  \"tags\": [\"cat.color\", \"wcag2aaa\", \"wcag146\", \"ACT\"],\n  \"actIds\": [\"09o5cg\"],\n  \"metadata\": {\n    \"description\": \"Ensure the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds\",\n    \"help\": \"Elements must meet enhanced color contrast ratio thresholds\"\n  },\n  \"all\": [],\n  \"any\": [\"color-contrast-enhanced\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/color-contrast-matches.js",
    "content": "/* global document */\nimport { getAccessibleRefs } from '../commons/aria';\nimport {\n  findUpVirtual,\n  visuallyOverlaps,\n  getRootNode,\n  isInert,\n  getOverflowHiddenAncestors\n} from '../commons/dom';\nimport {\n  visibleVirtual,\n  removeUnicode,\n  sanitize,\n  isIconLigature\n} from '../commons/text';\nimport { rectsOverlap } from '../commons/math';\nimport { isDisabled } from '../commons/forms';\nimport { getNodeFromTree, querySelectorAll, tokenList } from '../core/utils';\n\nfunction colorContrastMatches(node, virtualNode) {\n  const { nodeName, type: inputType } = virtualNode.props;\n\n  // Don't test options, color contrast doesn't work well on these\n  if (nodeName === 'option') {\n    return false;\n  }\n  // Don't test empty select elements\n  if (nodeName === 'select' && !node.options.length) {\n    return false;\n  }\n\n  // some input types don't have text, so the rule shouldn't be applied\n  const nonTextInput = [\n    'hidden',\n    'range',\n    'color',\n    'checkbox',\n    'radio',\n    'image'\n  ];\n  if (nodeName === 'input' && nonTextInput.includes(inputType)) {\n    return false;\n  }\n\n  if (isDisabled(virtualNode) || isInert(virtualNode)) {\n    return false;\n  }\n\n  // form elements that don't have direct child text nodes need to check that\n  // the text indent has not been changed and moved the text away from the\n  // control\n  const formElements = ['input', 'select', 'textarea'];\n  if (formElements.includes(nodeName)) {\n    const style = window.getComputedStyle(node);\n    const textIndent = parseInt(style.getPropertyValue('text-indent'), 10);\n\n    if (textIndent) {\n      // since we can't get the actual bounding rect of the text node, we'll\n      // use the current nodes bounding rect and adjust by the text-indent to\n      // see if it still overlaps the node\n      let rect = node.getBoundingClientRect();\n      rect = {\n        top: rect.top,\n        bottom: rect.bottom,\n        left: rect.left + textIndent,\n        right: rect.right + textIndent\n      };\n\n      if (!visuallyOverlaps(rect, node)) {\n        return false;\n      }\n    }\n    // Match all form fields, regardless of if they have text\n    return true;\n  }\n  const nodeParentLabel = findUpVirtual(virtualNode, 'label');\n  if (nodeName === 'label' || nodeParentLabel) {\n    const labelNode = nodeParentLabel || node;\n    const labelVirtual = nodeParentLabel\n      ? getNodeFromTree(nodeParentLabel)\n      : virtualNode;\n\n    // explicit label of disabled control\n    if (labelNode.htmlFor) {\n      const doc = getRootNode(labelNode);\n      const explicitControl = doc.getElementById(labelNode.htmlFor);\n      const explicitControlVirtual =\n        explicitControl && getNodeFromTree(explicitControl);\n\n      if (explicitControlVirtual && isDisabled(explicitControlVirtual)) {\n        return false;\n      }\n    }\n\n    // implicit label of disabled control\n    const query =\n      'input:not(' +\n      '[type=\"hidden\"],' +\n      '[type=\"image\"],' +\n      '[type=\"button\"],' +\n      '[type=\"submit\"],' +\n      '[type=\"reset\"]' +\n      '), select, textarea';\n    const implicitControl = querySelectorAll(labelVirtual, query)[0];\n\n    if (implicitControl && isDisabled(implicitControl)) {\n      return false;\n    }\n  }\n\n  const ariaLabelledbyControls = [];\n  let ancestorNode = virtualNode;\n  while (ancestorNode) {\n    // Find any ancestor (including itself) that is used with aria-labelledby\n    if (ancestorNode.props.id) {\n      const virtualControls = getAccessibleRefs(ancestorNode)\n        .filter(control => {\n          return tokenList(\n            control.getAttribute('aria-labelledby') || ''\n          ).includes(ancestorNode.props.id);\n        })\n        .map(control => getNodeFromTree(control));\n\n      ariaLabelledbyControls.push(...virtualControls);\n    }\n    ancestorNode = ancestorNode.parent;\n  }\n\n  if (\n    ariaLabelledbyControls.length > 0 &&\n    ariaLabelledbyControls.every(isDisabled)\n  ) {\n    return false;\n  }\n\n  if (!hasRealTextChildren(virtualNode)) {\n    return false;\n  }\n\n  if (!parseFloat(virtualNode.getComputedStylePropertyValue('font-size'))) {\n    return false;\n  }\n\n  const range = document.createRange();\n  const childNodes = virtualNode.children;\n  for (let index = 0; index < childNodes.length; index++) {\n    const child = childNodes[index];\n    if (\n      child.actualNode.nodeType === 3 &&\n      sanitize(child.actualNode.nodeValue) !== ''\n    ) {\n      range.selectNodeContents(child.actualNode);\n    }\n  }\n\n  const rects = Array.from(range.getClientRects());\n  const clippingAncestors = getOverflowHiddenAncestors(virtualNode);\n  return rects.some(rect => {\n    //check to see if the rectangle impinges\n    const overlaps = visuallyOverlaps(rect, node);\n\n    if (!clippingAncestors.length) {\n      return overlaps;\n    }\n\n    const withinOverflow = clippingAncestors.some(overflowNode => {\n      return rectsOverlap(rect, overflowNode.boundingClientRect);\n    });\n\n    return overlaps && withinOverflow;\n  });\n}\n\nexport default colorContrastMatches;\n\nconst removeUnicodeOptions = {\n  emoji: true,\n  nonBmp: false,\n  punctuations: true\n};\n\nfunction hasRealTextChildren(virtualNode) {\n  const visibleText = visibleVirtual(virtualNode, false, true);\n  if (\n    visibleText === '' ||\n    removeUnicode(visibleText, removeUnicodeOptions) === ''\n  ) {\n    return false;\n  }\n  return virtualNode.children.some(\n    vChild => vChild.props.nodeName === '#text' && !isIconLigature(vChild)\n  );\n}\n"
  },
  {
    "path": "lib/rules/color-contrast.json",
    "content": "{\n  \"id\": \"color-contrast\",\n  \"impact\": \"serious\",\n  \"matches\": \"color-contrast-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.color\",\n    \"wcag2aa\",\n    \"wcag143\",\n    \"TTv5\",\n    \"TT13.c\",\n    \"EN-301-549\",\n    \"EN-9.1.4.3\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-3.2.1\"\n  ],\n  \"actIds\": [\"afw4f7\", \"09o5cg\"],\n  \"metadata\": {\n    \"description\": \"Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds\",\n    \"help\": \"Elements must meet minimum color contrast ratio thresholds\"\n  },\n  \"all\": [],\n  \"any\": [\"color-contrast\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/css-orientation-lock.json",
    "content": "{\n  \"id\": \"css-orientation-lock\",\n  \"impact\": \"serious\",\n  \"selector\": \"html:not(html *)\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag134\",\n    \"wcag21aa\",\n    \"EN-301-549\",\n    \"EN-9.1.3.4\",\n    \"RGAAv4\",\n    \"RGAA-13.9.1\",\n    \"experimental\"\n  ],\n  \"actIds\": [\"b33eff\"],\n  \"metadata\": {\n    \"description\": \"Ensure content is not locked to any specific display orientation, and the content is operable in all display orientations\",\n    \"help\": \"CSS Media queries must not lock display orientation\"\n  },\n  \"all\": [\"css-orientation-lock\"],\n  \"any\": [],\n  \"none\": [],\n  \"preload\": true\n}\n"
  },
  {
    "path": "lib/rules/data-table-large-matches.js",
    "content": "import { isDataTable, toArray } from '../commons/table';\n\nfunction dataTableLargeMatches(node) {\n  if (isDataTable(node)) {\n    const tableArray = toArray(node);\n    return (\n      tableArray.length >= 3 &&\n      tableArray[0].length >= 3 &&\n      tableArray[1].length >= 3 &&\n      tableArray[2].length >= 3\n    );\n  }\n\n  return false;\n}\n\nexport default dataTableLargeMatches;\n"
  },
  {
    "path": "lib/rules/data-table-matches.js",
    "content": "import { isDataTable } from '../commons/table';\n\nfunction dataTableMatches(node) {\n  return isDataTable(node);\n}\n\nexport default dataTableMatches;\n"
  },
  {
    "path": "lib/rules/definition-list.json",
    "content": "{\n  \"id\": \"definition-list\",\n  \"impact\": \"serious\",\n  \"selector\": \"dl\",\n  \"matches\": \"no-role-matches\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.3\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure <dl> elements are structured correctly\",\n    \"help\": \"<dl> elements must only directly contain properly-ordered <dt> and <dd> groups, <script>, <template> or <div> elements\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"structured-dlitems\", \"only-dlitems\"]\n}\n"
  },
  {
    "path": "lib/rules/dlitem.json",
    "content": "{\n  \"id\": \"dlitem\",\n  \"impact\": \"serious\",\n  \"selector\": \"dd, dt\",\n  \"matches\": \"no-role-matches\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.3\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure <dt> and <dd> elements are contained by a <dl>\",\n    \"help\": \"<dt> and <dd> elements must be contained by a <dl>\"\n  },\n  \"all\": [],\n  \"any\": [\"dlitem\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/document-title.json",
    "content": "{\n  \"id\": \"document-title\",\n  \"impact\": \"serious\",\n  \"selector\": \"html:not(html *)\",\n  \"matches\": \"is-initiator-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag242\",\n    \"TTv5\",\n    \"TT12.a\",\n    \"EN-301-549\",\n    \"EN-9.2.4.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-8.5.1\"\n  ],\n  \"actIds\": [\"2779a5\"],\n  \"metadata\": {\n    \"description\": \"Ensure each HTML document contains a non-empty <title> element\",\n    \"help\": \"Documents must have <title> element to aid in navigation\"\n  },\n  \"all\": [],\n  \"any\": [\"doc-has-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/duplicate-id-active-matches.js",
    "content": "import { getRootNode, isFocusable } from '../commons/dom';\nimport { isAccessibleRef } from '../commons/aria';\nimport { escapeSelector } from '../core/utils';\n\nfunction duplicateIdActiveMatches(node) {\n  const id = node.getAttribute('id').trim();\n  const idSelector = `*[id=\"${escapeSelector(id)}\"]`;\n  const idMatchingElms = Array.from(\n    getRootNode(node).querySelectorAll(idSelector)\n  );\n\n  return !isAccessibleRef(node) && idMatchingElms.some(isFocusable);\n}\n\nexport default duplicateIdActiveMatches;\n"
  },
  {
    "path": "lib/rules/duplicate-id-active.json",
    "content": "{\n  \"id\": \"duplicate-id-active\",\n  \"impact\": \"serious\",\n  \"selector\": \"[id]\",\n  \"matches\": \"duplicate-id-active-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\"cat.parsing\", \"wcag2a-obsolete\", \"wcag411\", \"deprecated\"],\n  \"enabled\": false,\n  \"actIds\": [\"3ea0c8\"],\n  \"metadata\": {\n    \"description\": \"Ensure every id attribute value of active elements is unique\",\n    \"help\": \"IDs of active elements must be unique\"\n  },\n  \"all\": [],\n  \"any\": [\"duplicate-id-active\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/duplicate-id-aria-matches.js",
    "content": "import { isAccessibleRef } from '../commons/aria';\n\nfunction duplicateIdAriaMatches(node) {\n  return isAccessibleRef(node);\n}\n\nexport default duplicateIdAriaMatches;\n"
  },
  {
    "path": "lib/rules/duplicate-id-aria.json",
    "content": "{\n  \"id\": \"duplicate-id-aria\",\n  \"impact\": \"critical\",\n  \"selector\": \"[id]\",\n  \"matches\": \"duplicate-id-aria-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.parsing\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-8.2.1\"\n  ],\n  \"reviewOnFail\": true,\n  \"actIds\": [\"3ea0c8\"],\n  \"metadata\": {\n    \"description\": \"Ensure every id attribute value used in ARIA and in labels is unique\",\n    \"help\": \"IDs used in ARIA and labels must be unique\"\n  },\n  \"all\": [],\n  \"any\": [\"duplicate-id-aria\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/duplicate-id-misc-matches.js",
    "content": "import { getRootNode, isFocusable } from '../commons/dom';\nimport { isAccessibleRef } from '../commons/aria';\nimport { escapeSelector } from '../core/utils';\n\nfunction duplicateIdMiscMatches(node) {\n  const id = node.getAttribute('id').trim();\n  const idSelector = `*[id=\"${escapeSelector(id)}\"]`;\n  const idMatchingElms = Array.from(\n    getRootNode(node).querySelectorAll(idSelector)\n  );\n\n  return (\n    !isAccessibleRef(node) && idMatchingElms.every(elm => !isFocusable(elm))\n  );\n}\n\nexport default duplicateIdMiscMatches;\n"
  },
  {
    "path": "lib/rules/duplicate-id.json",
    "content": "{\n  \"id\": \"duplicate-id\",\n  \"impact\": \"minor\",\n  \"selector\": \"[id]\",\n  \"matches\": \"duplicate-id-misc-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\"cat.parsing\", \"wcag2a-obsolete\", \"wcag411\", \"deprecated\"],\n  \"enabled\": false,\n  \"actIds\": [\"3ea0c8\"],\n  \"metadata\": {\n    \"description\": \"Ensure every id attribute value is unique\",\n    \"help\": \"id attribute value must be unique\"\n  },\n  \"all\": [],\n  \"any\": [\"duplicate-id\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/empty-heading.json",
    "content": "{\n  \"id\": \"empty-heading\",\n  \"impact\": \"minor\",\n  \"selector\": \"h1, h2, h3, h4, h5, h6, [role=\\\"heading\\\"]\",\n  \"matches\": \"heading-matches\",\n  \"tags\": [\"cat.name-role-value\", \"best-practice\"],\n  \"actIds\": [\"ffd0e9\"],\n  \"metadata\": {\n    \"description\": \"Ensure headings have discernible text\",\n    \"help\": \"Headings should not be empty\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/empty-table-header.json",
    "content": "{\n  \"id\": \"empty-table-header\",\n  \"impact\": \"minor\",\n  \"selector\": \"th:not([role]), [role=\\\"rowheader\\\"], [role=\\\"columnheader\\\"]\",\n  \"tags\": [\"cat.name-role-value\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure table headers have discernible text\",\n    \"help\": \"Table header text should not be empty\"\n  },\n  \"all\": [],\n  \"any\": [\"has-visible-text\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/focus-order-semantics.json",
    "content": "{\n  \"id\": \"focus-order-semantics\",\n  \"impact\": \"minor\",\n  \"selector\": \"div, h1, h2, h3, h4, h5, h6, [role=heading], p, span\",\n  \"matches\": \"inserted-into-focus-order-matches\",\n  \"tags\": [\n    \"cat.keyboard\",\n    \"best-practice\",\n    \"RGAAv4\",\n    \"RGAA-12.8.1\",\n    \"experimental\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure elements in the focus order have a role appropriate for interactive content\",\n    \"help\": \"Elements in the focus order should have an appropriate role\"\n  },\n  \"all\": [],\n  \"any\": [\"has-widget-role\", \"valid-scrollable-semantics\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/form-field-multiple-labels.json",
    "content": "{\n  \"id\": \"form-field-multiple-labels\",\n  \"impact\": \"moderate\",\n  \"selector\": \"input, select, textarea\",\n  \"matches\": \"label-matches\",\n  \"tags\": [\n    \"cat.forms\",\n    \"wcag2a\",\n    \"wcag332\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.3.3.2\",\n    \"RGAAv4\",\n    \"RGAA-11.2.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure form field does not have multiple label elements\",\n    \"help\": \"Form field must not have multiple label elements\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"multiple-label\"]\n}\n"
  },
  {
    "path": "lib/rules/frame-focusable-content-matches.js",
    "content": "function frameFocusableContentMatches(node, virtualNode, context) {\n  return (\n    !context.initiator &&\n    !context.focusable &&\n    context.size?.width * context.size?.height > 1\n  );\n}\n\nexport default frameFocusableContentMatches;\n"
  },
  {
    "path": "lib/rules/frame-focusable-content.json",
    "content": "{\n  \"id\": \"frame-focusable-content\",\n  \"impact\": \"serious\",\n  \"selector\": \"html:not(html *)\",\n  \"matches\": \"frame-focusable-content-matches\",\n  \"tags\": [\n    \"cat.keyboard\",\n    \"wcag2a\",\n    \"wcag211\",\n    \"TTv5\",\n    \"TT4.a\",\n    \"EN-301-549\",\n    \"EN-9.2.1.1\",\n    \"RGAAv4\",\n    \"RGAA-7.3.2\"\n  ],\n  \"actIds\": [\"akn7bn\"],\n  \"metadata\": {\n    \"description\": \"Ensure <frame> and <iframe> elements with focusable content do not have tabindex=-1\",\n    \"help\": \"Frames with focusable content must not have tabindex=-1\"\n  },\n  \"all\": [],\n  \"any\": [\"frame-focusable-content\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/frame-tested.json",
    "content": "{\n  \"id\": \"frame-tested\",\n  \"impact\": \"critical\",\n  \"selector\": \"html:not(html *), frame, iframe\",\n  \"tags\": [\"cat.structure\", \"best-practice\", \"review-item\"],\n  \"metadata\": {\n    \"description\": \"Ensure <iframe> and <frame> elements contain the axe-core script\",\n    \"help\": \"Frames should be tested with axe-core\"\n  },\n  \"all\": [\"frame-tested\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/frame-title-has-text-matches.js",
    "content": "import { sanitize } from '../commons/text';\n\nfunction frameTitleHasTextMatches(node) {\n  const title = node.getAttribute('title');\n  return !!sanitize(title);\n}\n\nexport default frameTitleHasTextMatches;\n"
  },
  {
    "path": "lib/rules/frame-title-unique.json",
    "content": "{\n  \"id\": \"frame-title-unique\",\n  \"impact\": \"serious\",\n  \"selector\": \"frame[title], iframe[title]\",\n  \"matches\": \"frame-title-has-text-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT12.d\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-2.2.1\"\n  ],\n  \"actIds\": [\"4b1c6c\"],\n  \"metadata\": {\n    \"description\": \"Ensure <iframe> and <frame> elements contain a unique title attribute\",\n    \"help\": \"Frames must have a unique title attribute\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"unique-frame-title\"],\n  \"reviewOnFail\": true\n}\n"
  },
  {
    "path": "lib/rules/frame-title.json",
    "content": "{\n  \"id\": \"frame-title\",\n  \"impact\": \"serious\",\n  \"selector\": \"frame, iframe\",\n  \"matches\": \"no-negative-tabindex-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.i\",\n    \"TTv5\",\n    \"TT12.d\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-2.1.1\"\n  ],\n  \"actIds\": [\"cae760\"],\n  \"metadata\": {\n    \"description\": \"Ensure <iframe> and <frame> elements have an accessible name\",\n    \"help\": \"Frames must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"non-empty-title\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"presentational-role\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/has-implicit-chromium-role-matches.js",
    "content": "import { getImplicitRole } from '../commons/aria';\n\nfunction hasImplicitChromiumRoleMatches(node, virtualNode) {\n  return getImplicitRole(virtualNode, { chromium: true }) !== null;\n}\n\nexport default hasImplicitChromiumRoleMatches;\n"
  },
  {
    "path": "lib/rules/heading-matches.js",
    "content": "import { getRole } from '../commons/aria';\n\nexport default function headingMatches(node, virtualNode) {\n  return getRole(virtualNode) === 'heading';\n}\n"
  },
  {
    "path": "lib/rules/heading-order.json",
    "content": "{\n  \"id\": \"heading-order\",\n  \"impact\": \"moderate\",\n  \"selector\": \"h1, h2, h3, h4, h5, h6, [role=heading]\",\n  \"matches\": \"heading-matches\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the order of headings is semantically correct\",\n    \"help\": \"Heading levels should only increase by one\"\n  },\n  \"all\": [],\n  \"any\": [\"heading-order\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/hidden-content.json",
    "content": "{\n  \"id\": \"hidden-content\",\n  \"impact\": \"minor\",\n  \"selector\": \"*\",\n  \"excludeHidden\": false,\n  \"tags\": [\"cat.structure\", \"best-practice\", \"experimental\", \"review-item\"],\n  \"metadata\": {\n    \"description\": \"Inform users about hidden content.\",\n    \"help\": \"Hidden content on the page should be analyzed\"\n  },\n  \"all\": [],\n  \"any\": [\"hidden-content\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/html-has-lang.json",
    "content": "{\n  \"id\": \"html-has-lang\",\n  \"impact\": \"serious\",\n  \"selector\": \"html:not(html *)\",\n  \"matches\": \"is-initiator-matches\",\n  \"tags\": [\n    \"cat.language\",\n    \"wcag2a\",\n    \"wcag311\",\n    \"TTv5\",\n    \"TT11.a\",\n    \"EN-301-549\",\n    \"EN-9.3.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-8.3.1\"\n  ],\n  \"actIds\": [\"b5c3f8\"],\n  \"metadata\": {\n    \"description\": \"Ensure every HTML document has a lang attribute\",\n    \"help\": \"<html> element must have a lang attribute\"\n  },\n  \"all\": [],\n  \"any\": [\"has-lang\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/html-lang-valid.json",
    "content": "{\n  \"id\": \"html-lang-valid\",\n  \"impact\": \"serious\",\n  \"selector\": \"html[lang]:not([lang=\\\"\\\"]):not(html *), html[xml\\\\:lang]:not([xml\\\\:lang=\\\"\\\"]):not(html *)\",\n  \"tags\": [\n    \"cat.language\",\n    \"wcag2a\",\n    \"wcag311\",\n    \"TTv5\",\n    \"TT11.a\",\n    \"EN-301-549\",\n    \"EN-9.3.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-8.4.1\"\n  ],\n  \"actIds\": [\"bf051a\"],\n  \"metadata\": {\n    \"description\": \"Ensure the lang attribute of the <html> element has a valid value\",\n    \"help\": \"<html> element must have a valid value for the lang attribute\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"valid-lang\"]\n}\n"
  },
  {
    "path": "lib/rules/html-namespace-matches.js",
    "content": "import svgNamespaceMatches from './svg-namespace-matches';\n\nfunction htmlNamespaceMatches(node, virtualNode) {\n  return !svgNamespaceMatches(node, virtualNode);\n}\n\nexport default htmlNamespaceMatches;\n"
  },
  {
    "path": "lib/rules/html-xml-lang-mismatch.json",
    "content": "{\n  \"id\": \"html-xml-lang-mismatch\",\n  \"impact\": \"moderate\",\n  \"selector\": \"html[lang][xml\\\\:lang]:not(html *)\",\n  \"matches\": \"xml-lang-mismatch-matches\",\n  \"tags\": [\n    \"cat.language\",\n    \"wcag2a\",\n    \"wcag311\",\n    \"EN-301-549\",\n    \"EN-9.3.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-8.3.1\"\n  ],\n  \"actIds\": [\"5b7ae0\"],\n  \"metadata\": {\n    \"description\": \"Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page\",\n    \"help\": \"HTML elements with lang and xml:lang must have the same base language\"\n  },\n  \"all\": [\"xml-lang-mismatch\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/identical-links-same-purpose-matches.js",
    "content": "import { accessibleTextVirtual } from '../commons/text';\nimport { getRole } from '../commons/aria';\n\nfunction identicalLinksSamePurposeMatches(node, virtualNode) {\n  const hasAccName = !!accessibleTextVirtual(virtualNode);\n  if (!hasAccName) {\n    return false;\n  }\n\n  const role = getRole(node);\n  if (role && role !== 'link') {\n    return false;\n  }\n\n  return true;\n}\n\nexport default identicalLinksSamePurposeMatches;\n"
  },
  {
    "path": "lib/rules/identical-links-same-purpose.json",
    "content": "{\n  \"id\": \"identical-links-same-purpose\",\n  \"impact\": \"minor\",\n  \"selector\": \"a[href], area[href], [role=\\\"link\\\"]\",\n  \"excludeHidden\": false,\n  \"enabled\": false,\n  \"matches\": \"identical-links-same-purpose-matches\",\n  \"tags\": [\"cat.semantics\", \"wcag2aaa\", \"wcag249\"],\n  \"actIds\": [\"b20e66\"],\n  \"metadata\": {\n    \"description\": \"Ensure that links with the same accessible name serve a similar purpose\",\n    \"help\": \"Links with the same name must have a similar purpose\"\n  },\n  \"all\": [\"identical-links-same-purpose\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/image-alt.json",
    "content": "{\n  \"id\": \"image-alt\",\n  \"impact\": \"critical\",\n  \"selector\": \"img\",\n  \"matches\": \"no-explicit-name-required-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT7.a\",\n    \"TT7.b\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-1.1.1\"\n  ],\n  \"actIds\": [\"23a2a8\"],\n  \"metadata\": {\n    \"description\": \"Ensure <img> elements have alternative text or a role of none or presentation\",\n    \"help\": \"Images must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-alt\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"presentational-role\"\n  ],\n  \"none\": [\"alt-space-value\"]\n}\n"
  },
  {
    "path": "lib/rules/img-redundant-alt.json",
    "content": "{\n  \"id\": \"image-redundant-alt\",\n  \"impact\": \"minor\",\n  \"selector\": \"img\",\n  \"tags\": [\"cat.text-alternatives\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure image alternative is not repeated as text\",\n    \"help\": \"Alternative text of images should not be repeated as text\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"duplicate-img-label\"]\n}\n"
  },
  {
    "path": "lib/rules/input-button-name.json",
    "content": "{\n  \"id\": \"input-button-name\",\n  \"impact\": \"critical\",\n  \"selector\": \"input[type=\\\"button\\\"], input[type=\\\"submit\\\"], input[type=\\\"reset\\\"]\",\n  \"matches\": \"no-explicit-name-required-matches\",\n  \"tags\": [\n    \"cat.name-role-value\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.9.1\"\n  ],\n  \"actIds\": [\"97a4e1\"],\n  \"metadata\": {\n    \"description\": \"Ensure input buttons have discernible text\",\n    \"help\": \"Input buttons must have discernible text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"non-empty-if-present\",\n    \"non-empty-value\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"implicit-label\",\n    \"explicit-label\",\n    \"presentational-role\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/input-image-alt.json",
    "content": "{\n  \"id\": \"input-image-alt\",\n  \"impact\": \"critical\",\n  \"selector\": \"input[type=\\\"image\\\"]\",\n  \"matches\": \"no-explicit-name-required-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT7.a\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-1.1.3\"\n  ],\n  \"actIds\": [\"59796f\"],\n  \"metadata\": {\n    \"description\": \"Ensure <input type=\\\"image\\\"> elements have alternative text\",\n    \"help\": \"Image buttons must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"non-empty-alt\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"implicit-label\",\n    \"explicit-label\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/inserted-into-focus-order-matches.js",
    "content": "import { insertedIntoFocusOrder } from '../commons/dom';\n\nfunction insertedIntoFocusOrderMatches(node) {\n  return insertedIntoFocusOrder(node);\n}\n\nexport default insertedIntoFocusOrderMatches;\n"
  },
  {
    "path": "lib/rules/is-initiator-matches.js",
    "content": "function isInitiatorMatches(node, virtualNode, context) {\n  return context.initiator;\n}\n\nexport default isInitiatorMatches;\n"
  },
  {
    "path": "lib/rules/is-visible-matches.js",
    "content": "import { isVisibleOnScreen } from '../commons/dom';\n\n// @deprecated use isVisibleOnScreenMatches\nexport default function hasVisibleTextMatches(node) {\n  return isVisibleOnScreen(node);\n}\n"
  },
  {
    "path": "lib/rules/is-visible-on-screen-matches.js",
    "content": "import { isVisibleOnScreen } from '../commons/dom';\n\nexport default function isVisibleOnScreenMatches(node, virtualNode) {\n  return isVisibleOnScreen(virtualNode);\n}\n"
  },
  {
    "path": "lib/rules/label-content-name-mismatch-matches.js",
    "content": "import { getRole, arialabelText, arialabelledbyText } from '../commons/aria';\nimport {\n  getAriaRolesSupportingNameFromContent,\n  getAriaRolesByType\n} from '../commons/standards';\nimport { sanitize, visibleVirtual } from '../commons/text';\n\nfunction labelContentNameMismatchMatches(node, virtualNode) {\n  /**\n   * Applicability:\n   * Rule applies to any element that has\n   * a) a semantic role that is `widget` that supports name from content\n   * b) has visible text content\n   * c) has accessible name (eg: `aria-label`)\n   */\n  const role = getRole(node);\n  if (!role) {\n    return false;\n  }\n\n  const widgetRoles = getAriaRolesByType('widget');\n  const isWidgetType = widgetRoles.includes(role);\n  if (!isWidgetType) {\n    return false;\n  }\n\n  const rolesWithNameFromContents = getAriaRolesSupportingNameFromContent();\n  if (!rolesWithNameFromContents.includes(role)) {\n    return false;\n  }\n\n  /**\n   * if no `aria-label` or `aria-labelledby` attribute - ignore `node`\n   */\n  if (\n    !sanitize(arialabelText(virtualNode)) &&\n    !sanitize(arialabelledbyText(node))\n  ) {\n    return false;\n  }\n\n  /**\n   * if no `contentText` - ignore `node`\n   */\n  if (!sanitize(visibleVirtual(virtualNode))) {\n    return false;\n  }\n\n  return true;\n}\n\nexport default labelContentNameMismatchMatches;\n"
  },
  {
    "path": "lib/rules/label-content-name-mismatch.json",
    "content": "{\n  \"id\": \"label-content-name-mismatch\",\n  \"impact\": \"serious\",\n  \"matches\": \"label-content-name-mismatch-matches\",\n  \"tags\": [\n    \"cat.semantics\",\n    \"wcag21a\",\n    \"wcag253\",\n    \"EN-301-549\",\n    \"EN-9.2.5.3\",\n    \"RGAAv4\",\n    \"RGAA-6.1.5\",\n    \"experimental\"\n  ],\n  \"actIds\": [\"2ee8b8\"],\n  \"metadata\": {\n    \"description\": \"Ensure that elements labelled through their content must have their visible text as part of their accessible name\",\n    \"help\": \"Elements must have their visible text as part of their accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\"label-content-name-mismatch\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/label-matches.js",
    "content": "function labelMatches(node, virtualNode) {\n  if (\n    virtualNode.props.nodeName !== 'input' ||\n    virtualNode.hasAttr('type') === false\n  ) {\n    return true;\n  }\n\n  const type = virtualNode.attr('type').toLowerCase();\n  return (\n    ['hidden', 'image', 'button', 'submit', 'reset'].includes(type) === false\n  );\n}\n\nexport default labelMatches;\n"
  },
  {
    "path": "lib/rules/label-title-only.json",
    "content": "{\n  \"id\": \"label-title-only\",\n  \"impact\": \"serious\",\n  \"selector\": \"input, select, textarea\",\n  \"matches\": \"label-matches\",\n  \"tags\": [\"cat.forms\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure that every form element has a visible label and is not solely labeled using hidden labels, or the title or aria-describedby attributes\",\n    \"help\": \"Form elements should have a visible label\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"title-only\"]\n}\n"
  },
  {
    "path": "lib/rules/label.json",
    "content": "{\n  \"id\": \"label\",\n  \"impact\": \"critical\",\n  \"selector\": \"input, textarea\",\n  \"matches\": \"label-matches\",\n  \"tags\": [\n    \"cat.forms\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.n\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.1.1\"\n  ],\n  \"actIds\": [\"e086e5\"],\n  \"metadata\": {\n    \"description\": \"Ensure every form element has a label\",\n    \"help\": \"Form elements must have labels\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"implicit-label\",\n    \"explicit-label\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"non-empty-placeholder\",\n    \"presentational-role\"\n  ],\n  \"none\": [\"hidden-explicit-label\"]\n}\n"
  },
  {
    "path": "lib/rules/landmark-banner-is-top-level.json",
    "content": "{\n  \"id\": \"landmark-banner-is-top-level\",\n  \"impact\": \"moderate\",\n  \"selector\": \"header:not([role]), [role=banner]\",\n  \"matches\": \"landmark-has-body-context-matches\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the banner landmark is at top level\",\n    \"help\": \"Banner landmark should not be contained in another landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"landmark-is-top-level\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-complementary-is-top-level.json",
    "content": "{\n  \"id\": \"landmark-complementary-is-top-level\",\n  \"impact\": \"moderate\",\n  \"selector\": \"aside:not([role]), [role=complementary]\",\n  \"tags\": [\"cat.semantics\", \"best-practice\", \"deprecated\"],\n  \"enabled\": false,\n  \"metadata\": {\n    \"description\": \"Ensure the complementary landmark or aside is at top level\",\n    \"help\": \"Aside should not be contained in another landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"landmark-is-top-level\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-contentinfo-is-top-level.json",
    "content": "{\n  \"id\": \"landmark-contentinfo-is-top-level\",\n  \"impact\": \"moderate\",\n  \"selector\": \"footer:not([role]), [role=contentinfo]\",\n  \"matches\": \"landmark-has-body-context-matches\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the contentinfo landmark is at top level\",\n    \"help\": \"Contentinfo landmark should not be contained in another landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"landmark-is-top-level\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-has-body-context-matches.js",
    "content": "import { findUpVirtual } from '../commons/dom';\n\nfunction landmarkHasBodyContextMatches(node, virtualNode) {\n  const nativeScopeFilter = 'article, aside, main, nav, section';\n\n  // Filter elements that, within certain contexts, don't map their role.\n  // e.g. a <header> inside a <main> is not a banner, but in the <body> context it is\n  return (\n    node.hasAttribute('role') || !findUpVirtual(virtualNode, nativeScopeFilter)\n  );\n}\n\nexport default landmarkHasBodyContextMatches;\n"
  },
  {
    "path": "lib/rules/landmark-main-is-top-level.json",
    "content": "{\n  \"id\": \"landmark-main-is-top-level\",\n  \"impact\": \"moderate\",\n  \"selector\": \"main:not([role]), [role=main]\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the main landmark is at top level\",\n    \"help\": \"Main landmark should not be contained in another landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"landmark-is-top-level\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-no-duplicate-banner.json",
    "content": "{\n  \"id\": \"landmark-no-duplicate-banner\",\n  \"impact\": \"moderate\",\n  \"selector\": \"header:not([role]), [role=banner]\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the document has at most one banner landmark\",\n    \"help\": \"Document should not have more than one banner landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"page-no-duplicate-banner\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-no-duplicate-contentinfo.json",
    "content": "{\n  \"id\": \"landmark-no-duplicate-contentinfo\",\n  \"impact\": \"moderate\",\n  \"selector\": \"footer:not([role]), [role=contentinfo]\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the document has at most one contentinfo landmark\",\n    \"help\": \"Document should not have more than one contentinfo landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"page-no-duplicate-contentinfo\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-no-duplicate-main.json",
    "content": "{\n  \"id\": \"landmark-no-duplicate-main\",\n  \"impact\": \"moderate\",\n  \"selector\": \"main:not([role]), [role=main]\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the document has at most one main landmark\",\n    \"help\": \"Document should not have more than one main landmark\"\n  },\n  \"all\": [],\n  \"any\": [\"page-no-duplicate-main\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-one-main.json",
    "content": "{\n  \"id\": \"landmark-one-main\",\n  \"impact\": \"moderate\",\n  \"selector\": \"html:not(html *)\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the document has a main landmark\",\n    \"help\": \"Document should have one main landmark\"\n  },\n  \"all\": [\"page-has-main\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/landmark-unique-matches.js",
    "content": "import { isVisibleToScreenReaders } from '../commons/dom';\nimport { getRole } from '../commons/aria';\nimport { getAriaRolesByType } from '../commons/standards';\nimport { accessibleTextVirtual } from '../commons/text';\n\nexport default function landmarkUniqueMatches(node, virtualNode) {\n  return (\n    isLandmarkVirtual(virtualNode) && isVisibleToScreenReaders(virtualNode)\n  );\n}\n\nfunction isLandmarkVirtual(vNode) {\n  const landmarkRoles = getAriaRolesByType('landmark');\n  const role = getRole(vNode);\n  if (!role) {\n    return false;\n  }\n\n  const { nodeName } = vNode.props;\n\n  if (nodeName === 'section' || nodeName === 'form') {\n    const accessibleText = accessibleTextVirtual(vNode);\n    return !!accessibleText;\n  }\n\n  return landmarkRoles.indexOf(role) >= 0 || role === 'region';\n}\n"
  },
  {
    "path": "lib/rules/landmark-unique.json",
    "content": "{\n  \"id\": \"landmark-unique\",\n  \"impact\": \"moderate\",\n  \"selector\": \"[role=banner], [role=complementary], [role=contentinfo], [role=main], [role=navigation], [role=region], [role=search], [role=form], form, footer, header, aside, main, nav, section\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure landmarks are unique\",\n    \"help\": \"Landmarks should have a unique role or role/label/title (i.e. accessible name) combination\"\n  },\n  \"matches\": \"landmark-unique-matches\",\n  \"all\": [],\n  \"any\": [\"landmark-is-unique\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/layout-table-matches.js",
    "content": "import { isDataTable } from '../commons/table';\nimport { isFocusable } from '../commons/dom';\n\nfunction dataTableMatches(node) {\n  return !isDataTable(node) && !isFocusable(node);\n}\n\nexport default dataTableMatches;\n"
  },
  {
    "path": "lib/rules/link-in-text-block-matches.js",
    "content": "import { sanitize } from '../commons/text';\nimport { isVisibleOnScreen, isInTextBlock } from '../commons/dom';\nimport { getExplicitRole } from '../commons/aria';\n\nfunction linkInTextBlockMatches(node) {\n  const text = sanitize(node.innerText);\n  const role = getExplicitRole(node);\n\n  if (role && role !== 'link') {\n    return false;\n  }\n  if (!text) {\n    return false;\n  }\n  if (!isVisibleOnScreen(node)) {\n    return false;\n  }\n\n  return isInTextBlock(node);\n}\n\nexport default linkInTextBlockMatches;\n"
  },
  {
    "path": "lib/rules/link-in-text-block.json",
    "content": "{\n  \"id\": \"link-in-text-block\",\n  \"impact\": \"serious\",\n  \"selector\": \"a[href], [role=link]\",\n  \"matches\": \"link-in-text-block-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.color\",\n    \"wcag2a\",\n    \"wcag141\",\n    \"TTv5\",\n    \"TT13.a\",\n    \"EN-301-549\",\n    \"EN-9.1.4.1\",\n    \"RGAAv4\",\n    \"RGAA-10.6.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure links are distinguished from surrounding text in a way that does not rely on color\",\n    \"help\": \"Links must be distinguishable without relying on color\"\n  },\n  \"all\": [],\n  \"any\": [\"link-in-text-block\", \"link-in-text-block-style\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/link-name.json",
    "content": "{\n  \"id\": \"link-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"a[href]\",\n  \"tags\": [\n    \"cat.name-role-value\",\n    \"wcag2a\",\n    \"wcag244\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.2.4.4\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-6.2.1\"\n  ],\n  \"actIds\": [\"c487ae\"],\n  \"metadata\": {\n    \"description\": \"Ensure links have discernible text\",\n    \"help\": \"Links must have discernible text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": [\"focusable-no-name\"]\n}\n"
  },
  {
    "path": "lib/rules/list.json",
    "content": "{\n  \"id\": \"list\",\n  \"impact\": \"serious\",\n  \"selector\": \"ul, ol\",\n  \"matches\": \"no-role-matches\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure that lists are structured correctly\",\n    \"help\": \"<ul> and <ol> must only directly contain <li>, <script> or <template> elements\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"only-listitems\"]\n}\n"
  },
  {
    "path": "lib/rules/listitem.json",
    "content": "{\n  \"id\": \"listitem\",\n  \"impact\": \"serious\",\n  \"selector\": \"li\",\n  \"matches\": \"no-role-matches\",\n  \"tags\": [\n    \"cat.structure\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.3.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure <li> elements are used semantically\",\n    \"help\": \"<li> elements must be contained in a <ul> or <ol>\"\n  },\n  \"all\": [],\n  \"any\": [\"listitem\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/marquee.json",
    "content": "{\n  \"id\": \"marquee\",\n  \"impact\": \"serious\",\n  \"selector\": \"marquee\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.parsing\",\n    \"wcag2a\",\n    \"wcag222\",\n    \"TTv5\",\n    \"TT2.b\",\n    \"EN-301-549\",\n    \"EN-9.2.2.2\",\n    \"RGAAv4\",\n    \"RGAA-13.8.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure <marquee> elements are not used\",\n    \"help\": \"<marquee> elements are deprecated and must not be used\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"is-on-screen\"]\n}\n"
  },
  {
    "path": "lib/rules/meta-refresh-no-exceptions.json",
    "content": "{\n  \"id\": \"meta-refresh-no-exceptions\",\n  \"impact\": \"minor\",\n  \"selector\": \"meta[http-equiv=\\\"refresh\\\"][content]\",\n  \"excludeHidden\": false,\n  \"enabled\": false,\n  \"tags\": [\"cat.time-and-media\", \"wcag2aaa\", \"wcag224\", \"wcag325\"],\n  \"actIds\": [\"bisz58\"],\n  \"metadata\": {\n    \"description\": \"Ensure <meta http-equiv=\\\"refresh\\\"> is not used for delayed refresh\",\n    \"help\": \"Delayed refresh must not be used\"\n  },\n  \"all\": [],\n  \"any\": [\"meta-refresh-no-exceptions\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/meta-refresh.json",
    "content": "{\n  \"id\": \"meta-refresh\",\n  \"impact\": \"critical\",\n  \"selector\": \"meta[http-equiv=\\\"refresh\\\"][content]\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.time-and-media\",\n    \"wcag2a\",\n    \"wcag221\",\n    \"TTv5\",\n    \"TT8.a\",\n    \"EN-301-549\",\n    \"EN-9.2.2.1\",\n    \"RGAAv4\",\n    \"RGAA-13.1.2\"\n  ],\n  \"actIds\": [\"bc659a\", \"bisz58\"],\n  \"metadata\": {\n    \"description\": \"Ensure <meta http-equiv=\\\"refresh\\\"> is not used for delayed refresh\",\n    \"help\": \"Delayed refresh under 20 hours must not be used\"\n  },\n  \"all\": [],\n  \"any\": [\"meta-refresh\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/meta-viewport-large.json",
    "content": "{\n  \"id\": \"meta-viewport-large\",\n  \"impact\": \"minor\",\n  \"selector\": \"meta[name=\\\"viewport\\\"]\",\n  \"matches\": \"is-initiator-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\"cat.sensory-and-visual-cues\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure <meta name=\\\"viewport\\\"> can scale a significant amount\",\n    \"help\": \"Users should be able to zoom and scale the text up to 500%\"\n  },\n  \"all\": [],\n  \"any\": [\"meta-viewport-large\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/meta-viewport.json",
    "content": "{\n  \"id\": \"meta-viewport\",\n  \"impact\": \"moderate\",\n  \"selector\": \"meta[name=\\\"viewport\\\"]\",\n  \"matches\": \"is-initiator-matches\",\n  \"excludeHidden\": false,\n  \"tags\": [\n    \"cat.sensory-and-visual-cues\",\n    \"wcag2aa\",\n    \"wcag144\",\n    \"EN-301-549\",\n    \"EN-9.1.4.4\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-10.4.2\"\n  ],\n  \"actIds\": [\"b4f0c3\"],\n  \"metadata\": {\n    \"description\": \"Ensure <meta name=\\\"viewport\\\"> does not disable text scaling and zooming\",\n    \"help\": \"Zooming and scaling must not be disabled\"\n  },\n  \"all\": [],\n  \"any\": [\"meta-viewport\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/nested-interactive-matches.js",
    "content": "import { getRole } from '../commons/aria';\nimport standards from '../standards';\n\nfunction nestedInteractiveMatches(node, virtualNode) {\n  const role = getRole(virtualNode);\n  if (!role) {\n    return false;\n  }\n\n  return !!standards.ariaRoles[role].childrenPresentational;\n}\n\nexport default nestedInteractiveMatches;\n"
  },
  {
    "path": "lib/rules/nested-interactive.json",
    "content": "{\n  \"id\": \"nested-interactive\",\n  \"impact\": \"serious\",\n  \"matches\": \"nested-interactive-matches\",\n  \"tags\": [\n    \"cat.keyboard\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"RGAAv4\",\n    \"RGAA-7.1.1\"\n  ],\n  \"actIds\": [\"307n5z\"],\n  \"metadata\": {\n    \"description\": \"Ensure interactive controls are not nested as they are not always announced by screen readers or can cause focus problems for assistive technologies\",\n    \"help\": \"Interactive controls must not be nested\"\n  },\n  \"all\": [],\n  \"any\": [\"no-focusable-content\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/no-autoplay-audio-matches.js",
    "content": "function noAutoplayAudioMatches(node) {\n  /**\n   * Ignore media nodes without `currenSrc`\n   * \tNotes:\n   * \t- https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/currentSrc\n   * \t- https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/src\n   */\n  if (!node.currentSrc) {\n    return false;\n  }\n\n  /**\n   * Ignore media nodes which are `paused` or `muted`\n   */\n  if (node.hasAttribute('paused') || node.hasAttribute('muted')) {\n    return false;\n  }\n\n  return true;\n}\n\nexport default noAutoplayAudioMatches;\n"
  },
  {
    "path": "lib/rules/no-autoplay-audio.json",
    "content": "{\n  \"id\": \"no-autoplay-audio\",\n  \"impact\": \"moderate\",\n  \"excludeHidden\": false,\n  \"selector\": \"audio[autoplay], video[autoplay]\",\n  \"matches\": \"no-autoplay-audio-matches\",\n  \"reviewOnFail\": true,\n  \"tags\": [\n    \"cat.time-and-media\",\n    \"wcag2a\",\n    \"wcag142\",\n    \"TTv5\",\n    \"TT2.a\",\n    \"EN-301-549\",\n    \"EN-9.1.4.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-4.10.1\"\n  ],\n  \"actIds\": [\"80f0bf\"],\n  \"metadata\": {\n    \"description\": \"Ensure <video> or <audio> elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio\",\n    \"help\": \"<video> or <audio> elements must not play automatically\"\n  },\n  \"preload\": true,\n  \"all\": [\"no-autoplay-audio\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/no-empty-role-matches.js",
    "content": "function noEmptyRoleMatches(node, virtualNode) {\n  if (!virtualNode.hasAttr('role')) {\n    return false;\n  }\n\n  if (!virtualNode.attr('role').trim()) {\n    return false;\n  }\n\n  return true;\n}\n\nexport default noEmptyRoleMatches;\n"
  },
  {
    "path": "lib/rules/no-explicit-name-required-matches.js",
    "content": "import { isFocusable } from '../commons/dom';\nimport { getExplicitRole } from '../commons/aria';\nimport ariaRoles from '../standards/aria-roles';\n\n/**\n * Filter out elements with an explicit role that does not require an accessible name and is not focusable\n */\nfunction noExplicitNameRequired(node, virtualNode) {\n  const role = getExplicitRole(virtualNode);\n  if (!role || ['none', 'presentation'].includes(role)) {\n    return true;\n  }\n\n  const { accessibleNameRequired } = ariaRoles[role] || {};\n  if (accessibleNameRequired || isFocusable(virtualNode)) {\n    return true;\n  }\n\n  return false;\n}\n\nexport default noExplicitNameRequired;\n"
  },
  {
    "path": "lib/rules/no-naming-method-matches.js",
    "content": "import getExplicitRole from '../commons/aria/get-explicit-role';\nimport isComboboxPopup from '../commons/aria/is-combobox-popup';\nimport getElementSpec from '../commons/standards/get-element-spec';\nimport querySelectorAll from '../core/utils/query-selector-all';\n\n/**\n * Filter out elements that have a naming method (i.e. img[alt], table > caption, etc.)\n */\nfunction noNamingMethodMatches(node, virtualNode) {\n  const { namingMethods } = getElementSpec(virtualNode);\n  if (namingMethods && namingMethods.length !== 0) {\n    return false;\n  }\n  // Additionally, ignore combobox that get their name from a descendant input:\n  if (\n    getExplicitRole(virtualNode) === 'combobox' &&\n    querySelectorAll(virtualNode, 'input:not([type=\"hidden\"])').length\n  ) {\n    return false;\n  }\n  // Ignore listboxes that are referenced by a combobox\n  // Other roles don't require a name at all, or require one anyway\n  if (isComboboxPopup(virtualNode, { popupRoles: ['listbox'] })) {\n    return false;\n  }\n  return true;\n}\n\nexport default noNamingMethodMatches;\n"
  },
  {
    "path": "lib/rules/no-negative-tabindex-matches.js",
    "content": "import { parseTabindex } from '../core/utils';\n\nfunction noNegativeTabindexMatches(node, virtualNode) {\n  const tabindex = parseTabindex(virtualNode.attr('tabindex'));\n  return tabindex === null || tabindex >= 0;\n}\n\nexport default noNegativeTabindexMatches;\n"
  },
  {
    "path": "lib/rules/no-role-matches.js",
    "content": "function noRoleMatches(node, vNode) {\n  return !vNode.attr('role');\n}\n\nexport default noRoleMatches;\n"
  },
  {
    "path": "lib/rules/not-html-matches.js",
    "content": "/**\n * @deprecated; use :not(html) instead\n */\nfunction notHtmlMatches(node, virtualNode) {\n  return virtualNode.props.nodeName !== 'html';\n}\n\nexport default notHtmlMatches;\n"
  },
  {
    "path": "lib/rules/object-alt.json",
    "content": "{\n  \"id\": \"object-alt\",\n  \"impact\": \"serious\",\n  \"selector\": \"object[data]\",\n  \"matches\": \"object-is-loaded-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"section508\",\n    \"section508.22.a\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"RGAAv4\",\n    \"RGAA-1.1.6\"\n  ],\n  \"actIds\": [\"8fc3b6\"],\n  \"metadata\": {\n    \"description\": \"Ensure <object> elements have alternative text\",\n    \"help\": \"<object> elements must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"presentational-role\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/object-is-loaded-matches.js",
    "content": "import noExplicitNameRequired from './no-explicit-name-required-matches';\n\nexport default (node, vNode) =>\n  [noExplicitNameRequired, objectHasLoaded].every(fn => fn(node, vNode));\n\n/**\n * Test if an object loaded content; assume yes if we can't prove otherwise\n *\n * @param {Element} node\n * @param {VirtualNode} vNode\n * @returns {boolean}\n */\nfunction objectHasLoaded(node) {\n  if (!node?.ownerDocument?.createRange) {\n    return true; // Assume it did\n  }\n  // There's no ready\n  const range = node.ownerDocument.createRange();\n  range.setStart(node, 0);\n  range.setEnd(node, node.childNodes.length);\n  return range.getClientRects().length === 0;\n}\n"
  },
  {
    "path": "lib/rules/p-as-heading-matches.js",
    "content": "function pAsHeadingMatches(node) {\n  const children = Array.from(node.parentNode.childNodes);\n  const nodeText = node.textContent.trim();\n  const isSentence = /[.!?:;](?![.!?:;])/g;\n\n  // Check that there is text, and it is not more than a single sentence\n  if (nodeText.length === 0 || (nodeText.match(isSentence) || []).length >= 2) {\n    return false;\n  }\n\n  // Grab sibling p element following the current node\n  const siblingsAfter = children\n    .slice(children.indexOf(node) + 1)\n    .filter(\n      elm => elm.nodeName.toUpperCase() === 'P' && elm.textContent.trim() !== ''\n    );\n\n  return siblingsAfter.length !== 0;\n}\n\nexport default pAsHeadingMatches;\n"
  },
  {
    "path": "lib/rules/p-as-heading.json",
    "content": "{\n  \"id\": \"p-as-heading\",\n  \"impact\": \"serious\",\n  \"selector\": \"p\",\n  \"matches\": \"p-as-heading-matches\",\n  \"tags\": [\n    \"cat.semantics\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-9.1.3\",\n    \"experimental\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure bold, italic text and font-size is not used to style <p> elements as a heading\",\n    \"help\": \"Styled <p> elements must not be used as headings\"\n  },\n  \"all\": [\"p-as-heading\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/page-has-heading-one.json",
    "content": "{\n  \"id\": \"page-has-heading-one\",\n  \"impact\": \"moderate\",\n  \"selector\": \"html:not(html *)\",\n  \"tags\": [\"cat.semantics\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure that the page, or at least one of its frames contains a level-one heading\",\n    \"help\": \"Page should contain a level-one heading\"\n  },\n  \"all\": [\"page-has-heading-one\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/presentation-role-conflict-matches.js",
    "content": "import { getImplicitRole } from '../commons/aria';\n\n/**\n * @deprecated Will be removed in axe-core 5.0.0\n */\nfunction presentationRoleConflictMatches(node, virtualNode) {\n  return getImplicitRole(virtualNode, { chromiumRoles: true }) !== null;\n}\n\nexport default presentationRoleConflictMatches;\n"
  },
  {
    "path": "lib/rules/presentation-role-conflict.json",
    "content": "{\n  \"id\": \"presentation-role-conflict\",\n  \"impact\": \"minor\",\n  \"selector\": \"img[alt=''], [role=\\\"none\\\"], [role=\\\"presentation\\\"]\",\n  \"matches\": \"has-implicit-chromium-role-matches\",\n  \"tags\": [\"cat.aria\", \"best-practice\", \"ACT\"],\n  \"actIds\": [\"46ca7f\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements marked as presentational do not have global ARIA or tabindex so that all screen readers ignore them\",\n    \"help\": \"Elements marked as presentational should be consistently ignored\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"is-element-focusable\", \"has-global-aria-attribute\"]\n}\n"
  },
  {
    "path": "lib/rules/region.json",
    "content": "{\n  \"id\": \"region\",\n  \"impact\": \"moderate\",\n  \"selector\": \"body *\",\n  \"tags\": [\"cat.keyboard\", \"best-practice\", \"RGAAv4\", \"RGAA-9.2.1\"],\n  \"metadata\": {\n    \"description\": \"Ensure all page content is contained by landmarks\",\n    \"help\": \"All page content should be contained by landmarks\"\n  },\n  \"all\": [],\n  \"any\": [\"region\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/role-img-alt.json",
    "content": "{\n  \"id\": \"role-img-alt\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role='img']:not(img, area, input, object)\",\n  \"matches\": \"html-namespace-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT7.a\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-1.1.1\"\n  ],\n  \"actIds\": [\"23a2a8\"],\n  \"metadata\": {\n    \"description\": \"Ensure [role=\\\"img\\\"] elements have alternative text\",\n    \"help\": \"[role=\\\"img\\\"] elements must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\"aria-label\", \"aria-labelledby\", \"non-empty-title\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/scope-attr-valid.json",
    "content": "{\n  \"id\": \"scope-attr-valid\",\n  \"impact\": \"moderate\",\n  \"selector\": \"td[scope], th[scope]\",\n  \"tags\": [\"cat.tables\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure the scope attribute is used correctly on tables\",\n    \"help\": \"scope attribute should be used correctly\"\n  },\n  \"all\": [\"html5-scope\", \"scope-value\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/scrollable-region-focusable-matches.js",
    "content": "import hasContentVirtual, {\n  hasChildTextNodes\n} from '../commons/dom/has-content-virtual';\nimport isComboboxPopup from '../commons/aria/is-combobox-popup';\nimport { querySelectorAll, getScroll } from '../core/utils';\n\nexport default function scrollableRegionFocusableMatches(node, virtualNode) {\n  return (\n    // The element scrolls\n    getScroll(node, 13) !== undefined &&\n    // It's not a combobox popup, which commonly has keyboard focus added\n    isComboboxPopup(virtualNode) === false &&\n    // And there's something actually worth scrolling to\n    isNonEmptyElementOutsideViewableRect(virtualNode)\n  );\n}\n\nfunction isNonEmptyElementOutsideViewableRect(vNode) {\n  const boundingRect = vNode.boundingClientRect;\n\n  return querySelectorAll(vNode, '*').some(elm => {\n    // (elm, noRecursion, ignoreAria)\n    if (!hasContentVirtual(elm, true, true)) {\n      return false;\n    }\n\n    let rects = [];\n    if (hasChildTextNodes(elm)) {\n      rects.push(...getContentRects(elm));\n    } else {\n      rects = [elm.boundingClientRect];\n    }\n\n    return rects.some(rect => {\n      return (\n        rect.left < boundingRect.left ||\n        rect.right > boundingRect.right ||\n        rect.top < boundingRect.top ||\n        rect.bottom > boundingRect.bottom\n      );\n    });\n  });\n}\n\nfunction getContentRects(vNode) {\n  const range = document.createRange();\n  range.selectNodeContents(vNode.actualNode);\n  return Array.from(range.getClientRects());\n}\n"
  },
  {
    "path": "lib/rules/scrollable-region-focusable.json",
    "content": "{\n  \"id\": \"scrollable-region-focusable\",\n  \"impact\": \"serious\",\n  \"selector\": \"*:not(select,textarea)\",\n  \"matches\": \"scrollable-region-focusable-matches\",\n  \"tags\": [\n    \"cat.keyboard\",\n    \"wcag2a\",\n    \"wcag211\",\n    \"wcag213\",\n    \"TTv5\",\n    \"TT4.a\",\n    \"EN-301-549\",\n    \"EN-9.2.1.1\",\n    \"EN-9.2.1.3\",\n    \"RGAAv4\",\n    \"RGAA-7.3.2\"\n  ],\n  \"actIds\": [\"0ssw9k\"],\n  \"metadata\": {\n    \"description\": \"Ensure elements that have scrollable content are accessible by keyboard in Safari\",\n    \"help\": \"Scrollable region must have keyboard access\"\n  },\n  \"all\": [],\n  \"any\": [\"focusable-content\", \"focusable-element\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/select-name.json",
    "content": "{\n  \"id\": \"select-name\",\n  \"impact\": \"critical\",\n  \"selector\": \"select\",\n  \"tags\": [\n    \"cat.forms\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.n\",\n    \"TTv5\",\n    \"TT5.c\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-11.1.1\"\n  ],\n  \"actIds\": [\"e086e5\"],\n  \"metadata\": {\n    \"description\": \"Ensure select element has an accessible name\",\n    \"help\": \"Select element must have an accessible name\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"implicit-label\",\n    \"explicit-label\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\",\n    \"presentational-role\"\n  ],\n  \"none\": [\"hidden-explicit-label\"]\n}\n"
  },
  {
    "path": "lib/rules/server-side-image-map.json",
    "content": "{\n  \"id\": \"server-side-image-map\",\n  \"impact\": \"minor\",\n  \"selector\": \"img[ismap]\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag211\",\n    \"section508\",\n    \"section508.22.f\",\n    \"TTv5\",\n    \"TT4.a\",\n    \"EN-301-549\",\n    \"EN-9.2.1.1\",\n    \"RGAAv4\",\n    \"RGAA-1.1.4\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure that server-side image maps are not used\",\n    \"help\": \"Server-side image maps must not be used\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"exists\"]\n}\n"
  },
  {
    "path": "lib/rules/skip-link-matches.js",
    "content": "import { isSkipLink, isOffscreen } from '../commons/dom';\n\nfunction skipLinkMatches(node) {\n  return isSkipLink(node) && isOffscreen(node);\n}\n\nexport default skipLinkMatches;\n"
  },
  {
    "path": "lib/rules/skip-link.json",
    "content": "{\n  \"id\": \"skip-link\",\n  \"impact\": \"moderate\",\n  \"selector\": \"a[href^=\\\"#\\\"], a[href^=\\\"/#\\\"]\",\n  \"matches\": \"skip-link-matches\",\n  \"tags\": [\"cat.keyboard\", \"best-practice\", \"RGAAv4\", \"RGAA-12.7.1\"],\n  \"metadata\": {\n    \"description\": \"Ensure all skip links have a focusable target\",\n    \"help\": \"The skip-link target should exist and be focusable\"\n  },\n  \"all\": [],\n  \"any\": [\"skip-link\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/summary-interactive-matches.js",
    "content": "export default function summaryIsInteractiveMatches(_, virtualNode) {\n  // Summary only interactive if its real DOM parent is a details element\n  const parent = virtualNode.parent;\n  if (parent.props.nodeName !== 'details' || isSlottedElm(virtualNode)) {\n    return false;\n  }\n  // Only the first summary element is interactive\n  const firstSummary = parent.children.find(\n    child => child.props.nodeName === 'summary'\n  );\n  if (firstSummary !== virtualNode) {\n    return false;\n  }\n  return true;\n}\n\nfunction isSlottedElm(vNode) {\n  // Normally this wouldn't be enough, but since we know parent is a details\n  // element, we can ignore edge cases like slot being the real parent\n  const domParent = vNode.actualNode?.parentElement;\n  return domParent && domParent !== vNode.parent.actualNode;\n}\n"
  },
  {
    "path": "lib/rules/summary-name.json",
    "content": "{\n  \"id\": \"summary-name\",\n  \"impact\": \"serious\",\n  \"selector\": \"summary\",\n  \"matches\": \"summary-interactive-matches\",\n  \"tags\": [\n    \"cat.name-role-value\",\n    \"wcag2a\",\n    \"wcag412\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT6.a\",\n    \"EN-301-549\",\n    \"EN-9.4.1.2\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure summary elements have discernible text\",\n    \"help\": \"Summary elements must have discernible text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"has-visible-text\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/svg-img-alt.json",
    "content": "{\n  \"id\": \"svg-img-alt\",\n  \"impact\": \"serious\",\n  \"selector\": \"[role=\\\"img\\\"], [role=\\\"graphics-symbol\\\"], svg[role=\\\"graphics-document\\\"]\",\n  \"matches\": \"svg-namespace-matches\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag111\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT7.a\",\n    \"EN-301-549\",\n    \"EN-9.1.1.1\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-1.1.5\"\n  ],\n  \"actIds\": [\"7d6734\"],\n  \"metadata\": {\n    \"description\": \"Ensure <svg> elements with an img, graphics-document or graphics-symbol role have accessible text\",\n    \"help\": \"<svg> elements with an img role must have alternative text\"\n  },\n  \"all\": [],\n  \"any\": [\n    \"svg-non-empty-title\",\n    \"aria-label\",\n    \"aria-labelledby\",\n    \"non-empty-title\"\n  ],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/svg-namespace-matches.js",
    "content": "import { closest } from '../core/utils';\n\nfunction svgNamespaceMatches(node, virtualNode) {\n  try {\n    const nodeName = virtualNode.props.nodeName;\n\n    if (nodeName === 'svg') {\n      return true;\n    }\n\n    // element is svg namespace if its parent is an svg element\n    return !!closest(virtualNode, 'svg');\n  } catch {\n    return false;\n  }\n}\n\nexport default svgNamespaceMatches;\n"
  },
  {
    "path": "lib/rules/tabindex.json",
    "content": "{\n  \"id\": \"tabindex\",\n  \"impact\": \"serious\",\n  \"selector\": \"[tabindex]\",\n  \"tags\": [\"cat.keyboard\", \"best-practice\"],\n  \"metadata\": {\n    \"description\": \"Ensure tabindex attribute values are not greater than 0\",\n    \"help\": \"Elements should not have tabindex greater than zero\"\n  },\n  \"all\": [],\n  \"any\": [\"tabindex\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/table-duplicate-name.json",
    "content": "{\n  \"id\": \"table-duplicate-name\",\n  \"impact\": \"minor\",\n  \"selector\": \"table\",\n  \"tags\": [\"cat.tables\", \"best-practice\", \"RGAAv4\", \"RGAA-5.2.1\"],\n  \"metadata\": {\n    \"description\": \"Ensure the <caption> element does not contain the same text as the summary attribute\",\n    \"help\": \"Tables should not have the same summary and caption\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"same-caption-summary\"]\n}\n"
  },
  {
    "path": "lib/rules/table-fake-caption.json",
    "content": "{\n  \"id\": \"table-fake-caption\",\n  \"impact\": \"serious\",\n  \"selector\": \"table\",\n  \"matches\": \"data-table-matches\",\n  \"tags\": [\n    \"cat.tables\",\n    \"experimental\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"section508\",\n    \"section508.22.g\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-5.4.1\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure that tables with a caption use the <caption> element.\",\n    \"help\": \"Data or header cells must not be used to give caption to a data table.\"\n  },\n  \"all\": [\"caption-faked\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/table-or-grid-role-matches.js",
    "content": "import { getRole } from '../commons/aria';\n\nexport default function tableOrGridRoleMatches(_, vNode) {\n  const role = getRole(vNode);\n  return ['treegrid', 'grid', 'table'].includes(role);\n}\n"
  },
  {
    "path": "lib/rules/target-size.json",
    "content": "{\n  \"id\": \"target-size\",\n  \"impact\": \"serious\",\n  \"selector\": \"*\",\n  \"enabled\": false,\n  \"matches\": \"widget-not-inline-matches\",\n  \"tags\": [\"cat.sensory-and-visual-cues\", \"wcag22aa\", \"wcag258\"],\n  \"metadata\": {\n    \"description\": \"Ensure touch targets have sufficient size and space\",\n    \"help\": \"All touch targets must be 24px large, or leave sufficient space\"\n  },\n  \"all\": [],\n  \"any\": [\"target-size\", \"target-offset\"],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/td-has-header.json",
    "content": "{\n  \"id\": \"td-has-header\",\n  \"impact\": \"critical\",\n  \"selector\": \"table\",\n  \"matches\": \"data-table-large-matches\",\n  \"tags\": [\n    \"cat.tables\",\n    \"experimental\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"section508\",\n    \"section508.22.g\",\n    \"TTv5\",\n    \"TT14.b\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-5.7.4\"\n  ],\n  \"metadata\": {\n    \"description\": \"Ensure that each non-empty data cell in a <table> larger than 3 by 3  has one or more table headers\",\n    \"help\": \"Non-empty <td> elements in larger <table> must have an associated table header\"\n  },\n  \"all\": [\"td-has-header\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/td-headers-attr.json",
    "content": "{\n  \"id\": \"td-headers-attr\",\n  \"impact\": \"serious\",\n  \"selector\": \"table\",\n  \"matches\": \"table-or-grid-role-matches\",\n  \"tags\": [\n    \"cat.tables\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"section508\",\n    \"section508.22.g\",\n    \"TTv5\",\n    \"TT14.b\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-5.7.4\"\n  ],\n  \"actIds\": [\"a25f45\"],\n  \"metadata\": {\n    \"description\": \"Ensure that each cell in a table that uses the headers attribute refers only to other <th> elements in that table\",\n    \"help\": \"Table cell headers attributes must refer to other <th> elements in the same table\"\n  },\n  \"all\": [\"td-headers-attr\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/th-has-data-cells.json",
    "content": "{\n  \"id\": \"th-has-data-cells\",\n  \"impact\": \"serious\",\n  \"selector\": \"table\",\n  \"matches\": \"data-table-matches\",\n  \"tags\": [\n    \"cat.tables\",\n    \"wcag2a\",\n    \"wcag131\",\n    \"section508\",\n    \"section508.22.g\",\n    \"TTv5\",\n    \"TT14.b\",\n    \"EN-301-549\",\n    \"EN-9.1.3.1\",\n    \"RGAAv4\",\n    \"RGAA-5.7.1\"\n  ],\n  \"actIds\": [\"d0f69e\"],\n  \"metadata\": {\n    \"description\": \"Ensure that <th> elements and elements with role=columnheader/rowheader have data cells they describe\",\n    \"help\": \"Table headers in a data table must refer to data cells\"\n  },\n  \"all\": [\"th-has-data-cells\"],\n  \"any\": [],\n  \"none\": []\n}\n"
  },
  {
    "path": "lib/rules/valid-lang.json",
    "content": "{\n  \"id\": \"valid-lang\",\n  \"impact\": \"serious\",\n  \"selector\": \"[lang]:not(html), [xml\\\\:lang]:not(html)\",\n  \"tags\": [\n    \"cat.language\",\n    \"wcag2aa\",\n    \"wcag312\",\n    \"TTv5\",\n    \"TT11.b\",\n    \"EN-301-549\",\n    \"EN-9.3.1.2\",\n    \"ACT\",\n    \"RGAAv4\",\n    \"RGAA-8.8.1\"\n  ],\n  \"actIds\": [\"de46e4\"],\n  \"metadata\": {\n    \"description\": \"Ensure lang attributes have valid values\",\n    \"help\": \"lang attribute must have a valid value\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"valid-lang\"]\n}\n"
  },
  {
    "path": "lib/rules/video-caption.json",
    "content": "{\n  \"id\": \"video-caption\",\n  \"impact\": \"critical\",\n  \"selector\": \"video\",\n  \"tags\": [\n    \"cat.text-alternatives\",\n    \"wcag2a\",\n    \"wcag122\",\n    \"section508\",\n    \"section508.22.a\",\n    \"TTv5\",\n    \"TT17.a\",\n    \"EN-301-549\",\n    \"EN-9.1.2.2\",\n    \"RGAAv4\",\n    \"RGAA-4.3.1\"\n  ],\n  \"actIds\": [\"eac66b\"],\n  \"metadata\": {\n    \"description\": \"Ensure <video> elements have captions\",\n    \"help\": \"<video> elements must have captions\"\n  },\n  \"all\": [],\n  \"any\": [],\n  \"none\": [\"caption\"]\n}\n"
  },
  {
    "path": "lib/rules/widget-not-inline-matches.js",
    "content": "import { getRoleType } from '../commons/aria';\nimport { isFocusable, isInTabOrder, isInTextBlock } from '../commons/dom';\nimport svgNamespaceMatches from './svg-namespace-matches';\nimport { memoize } from '../core/utils';\n\nexport default function widgetNotInline(node, vNode) {\n  return matchesFns.every(fn => fn(node, vNode));\n}\n\nconst matchesFns = [\n  (node, vNode) => isWidgetType(vNode),\n  (node, vNode) => isNotAreaElement(vNode),\n  (node, vNode) => !svgNamespaceMatches(node, vNode),\n  (node, vNode) => isFocusable(vNode),\n  // Skip nested widgets with tabindex=-1\n  (node, vNode) => isInTabOrder(vNode) || !hasWidgetAncestorInTabOrder(vNode),\n  node => !isInTextBlock(node, { noLengthCompare: true })\n];\n\nfunction isWidgetType(vNode) {\n  return getRoleType(vNode) === 'widget';\n}\n\nfunction isNotAreaElement(vNode) {\n  return vNode.props.nodeName !== 'area';\n}\n\nconst hasWidgetAncestorInTabOrder = memoize(\n  function hasWidgetAncestorInTabOrderMemoized(vNode) {\n    if (!vNode?.parent) {\n      return false;\n    }\n    if (isWidgetType(vNode.parent) && isInTabOrder(vNode.parent)) {\n      return true;\n    }\n    return hasWidgetAncestorInTabOrderMemoized(vNode.parent);\n  }\n);\n"
  },
  {
    "path": "lib/rules/window-is-top-matches.js",
    "content": "// @deprecated\nfunction windowIsTopMatches(node) {\n  return (\n    node.ownerDocument.defaultView.self === node.ownerDocument.defaultView.top\n  );\n}\n\nexport default windowIsTopMatches;\n"
  },
  {
    "path": "lib/rules/xml-lang-mismatch-matches.js",
    "content": "import { getBaseLang, isValidLang } from '../core/utils';\n\nfunction xmlLangMismatchMatches(node) {\n  // using -> \"selector\": \"html[lang][xml\\\\:lang]\" to narrow down html with lang and xml:lang attributes\n\n  // get primary base language for each of the attributes\n  const primaryLangValue = getBaseLang(node.getAttribute('lang'));\n  const primaryXmlLangValue = getBaseLang(node.getAttribute('xml:lang'));\n\n  // ensure that the value specified is valid lang for both `lang` and `xml:lang`\n  return isValidLang(primaryLangValue) && isValidLang(primaryXmlLangValue);\n}\n\nexport default xmlLangMismatchMatches;\n"
  },
  {
    "path": "lib/standards/aria-attrs.js",
    "content": "// Source: https://www.w3.org/TR/wai-aria-1.1/#states_and_properties\nconst ariaAttrs = {\n  'aria-activedescendant': {\n    type: 'idref',\n    allowEmpty: true\n  },\n  'aria-atomic': {\n    type: 'boolean',\n    global: true\n  },\n  'aria-autocomplete': {\n    type: 'nmtoken',\n    values: ['inline', 'list', 'both', 'none']\n  },\n  'aria-braillelabel': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-brailleroledescription': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-busy': {\n    type: 'boolean',\n    global: true\n  },\n  'aria-checked': {\n    type: 'nmtoken',\n    values: ['false', 'mixed', 'true', 'undefined']\n  },\n  'aria-colcount': {\n    type: 'int',\n    minValue: -1\n  },\n  'aria-colindex': {\n    type: 'int',\n    minValue: 1\n  },\n  'aria-colspan': {\n    type: 'int',\n    minValue: 1\n  },\n  'aria-controls': {\n    type: 'idrefs',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-current': {\n    type: 'nmtoken',\n    allowEmpty: true,\n    values: ['page', 'step', 'location', 'date', 'time', 'true', 'false'],\n    global: true\n  },\n  'aria-describedby': {\n    type: 'idrefs',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-description': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-details': {\n    type: 'idref',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-disabled': {\n    type: 'boolean',\n    global: true\n  },\n  'aria-dropeffect': {\n    type: 'nmtokens',\n    values: ['copy', 'execute', 'link', 'move', 'none', 'popup'],\n    global: true\n  },\n  'aria-errormessage': {\n    type: 'idref',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-expanded': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined']\n  },\n  'aria-flowto': {\n    type: 'idrefs',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-grabbed': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined'],\n    global: true\n  },\n  'aria-haspopup': {\n    type: 'nmtoken',\n    allowEmpty: true,\n    values: ['true', 'false', 'menu', 'listbox', 'tree', 'grid', 'dialog'],\n    global: true\n  },\n  'aria-hidden': {\n    type: 'nmtoken',\n    values: ['true', 'false', 'undefined'],\n    global: true\n  },\n  'aria-invalid': {\n    type: 'nmtoken',\n    values: ['grammar', 'false', 'spelling', 'true'],\n    global: true\n  },\n  'aria-keyshortcuts': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-label': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-labelledby': {\n    type: 'idrefs',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-level': {\n    type: 'int',\n    minValue: 1\n  },\n  'aria-live': {\n    type: 'nmtoken',\n    values: ['assertive', 'off', 'polite'],\n    global: true\n  },\n  'aria-modal': {\n    type: 'boolean'\n  },\n  'aria-multiline': {\n    type: 'boolean'\n  },\n  'aria-multiselectable': {\n    type: 'boolean'\n  },\n  'aria-orientation': {\n    type: 'nmtoken',\n    values: ['horizontal', 'undefined', 'vertical']\n  },\n  'aria-owns': {\n    type: 'idrefs',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-placeholder': {\n    type: 'string',\n    allowEmpty: true\n  },\n  'aria-posinset': {\n    type: 'int',\n    minValue: 1\n  },\n  'aria-pressed': {\n    type: 'nmtoken',\n    values: ['false', 'mixed', 'true', 'undefined']\n  },\n  'aria-readonly': {\n    type: 'boolean'\n  },\n  'aria-relevant': {\n    type: 'nmtokens',\n    values: ['additions', 'all', 'removals', 'text'],\n    global: true\n  },\n  'aria-required': {\n    type: 'boolean'\n  },\n  'aria-roledescription': {\n    type: 'string',\n    allowEmpty: true,\n    global: true\n  },\n  'aria-rowcount': {\n    type: 'int',\n    minValue: -1\n  },\n  'aria-rowindex': {\n    type: 'int',\n    minValue: 1\n  },\n  'aria-rowspan': {\n    type: 'int',\n    minValue: 0\n  },\n  'aria-selected': {\n    type: 'nmtoken',\n    values: ['false', 'true', 'undefined']\n  },\n  'aria-setsize': {\n    type: 'int',\n    minValue: -1\n  },\n  'aria-sort': {\n    type: 'nmtoken',\n    values: ['ascending', 'descending', 'none', 'other']\n  },\n  'aria-valuemax': {\n    type: 'decimal'\n  },\n  'aria-valuemin': {\n    type: 'decimal'\n  },\n  'aria-valuenow': {\n    type: 'decimal'\n  },\n  'aria-valuetext': {\n    type: 'string',\n    allowEmpty: true\n  }\n};\n\nexport default ariaAttrs;\n"
  },
  {
    "path": "lib/standards/aria-roles.js",
    "content": "// Source: https://www.w3.org/TR/wai-aria-1.1/#roles\n// Source for abstract roles (types): https://www.w3.org/TR/wai-aria/#abstract_roles\n\n/* easiest way to see allowed roles is to filter out the global ones\n   from the list of inherited states and properties. The dpub spec\n   does not have the global list so you'll need to copy over from\n   the wai-aria one:\n\n  const globalAttrs = Array.from(\n    document.querySelectorAll('#global_states li')\n  ).map(li => li.textContent.replace(/\\s*\\(.*\\)/, ''));\n\n  const globalRoleAttrs = Array.from(\n    document.querySelectorAll('.role-inherited li')\n  ).filter(li => globalAttrs.includes(li.textContent.replace(/\\s*\\(.*\\)/, '')))\n\n  globalRoleAttrs.forEach(li => li.style.display = 'none');\n*/\nconst ariaRoles = {\n  alert: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  alertdialog: {\n    type: 'window',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded', 'aria-modal'],\n    superclassRole: ['alert', 'dialog'],\n    accessibleNameRequired: true\n  },\n  application: {\n    // Note: spec difference\n    type: 'landmark',\n    // Note: aria-expanded is not in the 1.1 spec but is\n    // consistently supported in ATs and was added in 1.2\n    allowedAttrs: ['aria-activedescendant', 'aria-expanded'],\n    superclassRole: ['structure'],\n    accessibleNameRequired: true\n  },\n  article: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-posinset', 'aria-setsize', 'aria-expanded'],\n    superclassRole: ['document']\n  },\n  banner: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  blockquote: {\n    type: 'structure',\n    superclassRole: ['section']\n  },\n  button: {\n    type: 'widget',\n    allowedAttrs: ['aria-expanded', 'aria-pressed'],\n    superclassRole: ['command'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  caption: {\n    type: 'structure',\n    requiredContext: ['figure', 'table', 'grid', 'treegrid'],\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  cell: {\n    type: 'structure',\n    requiredContext: ['row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-colindex',\n      'aria-colspan',\n      'aria-rowindex',\n      'aria-rowspan',\n      'aria-expanded'\n    ],\n    superclassRole: ['section'],\n    nameFromContent: true\n  },\n  checkbox: {\n    type: 'widget',\n    // Note: aria-required is not in the 1.1 spec but is\n    // consistently supported in ATs and was added in 1.2\n    requiredAttrs: ['aria-checked'],\n    allowedAttrs: ['aria-readonly', 'aria-expanded', 'aria-required'],\n    superclassRole: ['input'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  code: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  columnheader: {\n    type: 'structure',\n    requiredContext: ['row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-sort',\n      'aria-colindex',\n      'aria-colspan',\n      'aria-expanded',\n      'aria-readonly',\n      'aria-required',\n      'aria-rowindex',\n      'aria-rowspan',\n      'aria-selected'\n    ],\n    superclassRole: ['cell', 'gridcell', 'sectionhead'],\n    // Note: spec difference\n    accessibleNameRequired: false,\n    nameFromContent: true\n  },\n  combobox: {\n    // Note: spec difference\n    type: 'widget',\n    requiredAttrs: ['aria-expanded', 'aria-controls'],\n    allowedAttrs: [\n      'aria-owns',\n      'aria-autocomplete',\n      'aria-readonly',\n      'aria-required',\n      'aria-activedescendant',\n      'aria-orientation'\n    ],\n    superclassRole: ['select'],\n    accessibleNameRequired: true\n  },\n  command: {\n    type: 'abstract',\n    superclassRole: ['widget']\n  },\n  complementary: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  composite: {\n    type: 'abstract',\n    superclassRole: ['widget']\n  },\n  contentinfo: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  comment: {\n    type: 'structure',\n    allowedAttrs: ['aria-level', 'aria-posinset', 'aria-setsize'],\n    superclassRole: ['article']\n  },\n  definition: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  deletion: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  dialog: {\n    type: 'window',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded', 'aria-modal'],\n    superclassRole: ['window'],\n    accessibleNameRequired: true\n  },\n  directory: {\n    type: 'structure',\n    deprecated: true,\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['list'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  document: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['structure']\n  },\n  emphasis: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  feed: {\n    type: 'structure',\n    requiredOwned: ['article'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['list']\n  },\n  figure: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  form: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  grid: {\n    type: 'composite',\n    requiredOwned: ['rowgroup', 'row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-level',\n      'aria-multiselectable',\n      'aria-readonly',\n      'aria-activedescendant',\n      'aria-colcount',\n      'aria-expanded',\n      'aria-rowcount'\n    ],\n    superclassRole: ['composite', 'table'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  gridcell: {\n    type: 'widget',\n    requiredContext: ['row'],\n    allowedAttrs: [\n      'aria-readonly',\n      'aria-required',\n      'aria-selected',\n      'aria-colindex',\n      'aria-colspan',\n      'aria-expanded',\n      'aria-rowindex',\n      'aria-rowspan'\n    ],\n    superclassRole: ['cell', 'widget'],\n    nameFromContent: true\n  },\n  group: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-activedescendant', 'aria-expanded'],\n    superclassRole: ['section']\n  },\n  heading: {\n    type: 'structure',\n    requiredAttrs: ['aria-level'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['sectionhead'],\n    // Note: spec difference\n    accessibleNameRequired: false,\n    nameFromContent: true\n  },\n  img: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    accessibleNameRequired: true,\n    childrenPresentational: true\n  },\n  input: {\n    type: 'abstract',\n    superclassRole: ['widget']\n  },\n  insertion: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  landmark: {\n    type: 'abstract',\n    superclassRole: ['section']\n  },\n  link: {\n    type: 'widget',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['command'],\n    accessibleNameRequired: true,\n    nameFromContent: true\n  },\n  list: {\n    type: 'structure',\n    requiredOwned: ['listitem'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  listbox: {\n    // Note: spec difference\n    type: 'widget',\n    requiredOwned: ['group', 'option'],\n    allowedAttrs: [\n      'aria-multiselectable',\n      'aria-readonly',\n      'aria-required',\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-orientation'\n    ],\n    superclassRole: ['select'],\n    accessibleNameRequired: true\n  },\n  listitem: {\n    type: 'structure',\n    requiredContext: ['list'],\n    allowedAttrs: [\n      'aria-level',\n      'aria-posinset',\n      'aria-setsize',\n      'aria-expanded'\n    ],\n    superclassRole: ['section'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  log: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  main: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  marquee: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  math: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    childrenPresentational: true\n  },\n  menu: {\n    type: 'composite',\n    // Note: spec difference (menu & separator as required owned)\n    requiredOwned: [\n      'group',\n      'menuitemradio',\n      'menuitem',\n      'menuitemcheckbox',\n      'menu',\n      'separator'\n    ],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-orientation'\n    ],\n    superclassRole: ['select']\n  },\n  menubar: {\n    type: 'composite',\n    // Note: spec difference (menu & separator as required owned)\n    requiredOwned: [\n      'group',\n      'menuitemradio',\n      'menuitem',\n      'menuitemcheckbox',\n      'menu',\n      'separator'\n    ],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-orientation'\n    ],\n    superclassRole: ['menu']\n  },\n  menuitem: {\n    type: 'widget',\n    requiredContext: ['menu', 'menubar', 'group'],\n    // Note: aria-expanded is not in the 1.1 spec but is\n    // consistently supported in ATs and was added in 1.2\n    allowedAttrs: ['aria-posinset', 'aria-setsize', 'aria-expanded'],\n    superclassRole: ['command'],\n    accessibleNameRequired: true,\n    nameFromContent: true\n  },\n  menuitemcheckbox: {\n    type: 'widget',\n    requiredContext: ['menu', 'menubar', 'group'],\n    requiredAttrs: ['aria-checked'],\n    allowedAttrs: [\n      'aria-expanded',\n      'aria-posinset',\n      'aria-readonly',\n      'aria-setsize'\n    ],\n    superclassRole: ['checkbox', 'menuitem'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  menuitemradio: {\n    type: 'widget',\n    requiredContext: ['menu', 'menubar', 'group'],\n    requiredAttrs: ['aria-checked'],\n    allowedAttrs: [\n      'aria-expanded',\n      'aria-posinset',\n      'aria-readonly',\n      'aria-setsize'\n    ],\n    superclassRole: ['menuitemcheckbox', 'radio'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  meter: {\n    type: 'structure',\n    requiredAttrs: ['aria-valuenow'],\n    allowedAttrs: ['aria-valuemax', 'aria-valuemin', 'aria-valuetext'],\n    superclassRole: ['range'],\n    accessibleNameRequired: true,\n    childrenPresentational: true\n  },\n  mark: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  navigation: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  none: {\n    type: 'structure',\n    superclassRole: ['structure'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  note: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  option: {\n    type: 'widget',\n    requiredContext: ['group', 'listbox'],\n    // Note: since the option role has an implicit\n    // aria-selected value it is not required to be added by\n    // the user\n    allowedAttrs: [\n      'aria-selected',\n      'aria-checked',\n      'aria-posinset',\n      'aria-setsize'\n    ],\n    superclassRole: ['input'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  paragraph: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  presentation: {\n    type: 'structure',\n    superclassRole: ['structure'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  progressbar: {\n    type: 'widget',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-expanded',\n      'aria-valuemax',\n      'aria-valuemin',\n      'aria-valuenow',\n      'aria-valuetext'\n    ],\n    superclassRole: ['range'],\n    accessibleNameRequired: true,\n    childrenPresentational: true\n  },\n  radio: {\n    type: 'widget',\n    // Note: aria-required is not in the 1.1 or 1.2 specs but is\n    // consistently supported in ATs on the individual radio element\n    requiredAttrs: ['aria-checked'],\n    allowedAttrs: ['aria-posinset', 'aria-setsize', 'aria-required'],\n    superclassRole: ['input'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  radiogroup: {\n    type: 'composite',\n    // Note: spec difference (no required owned)\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-readonly',\n      'aria-required',\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-orientation'\n    ],\n    superclassRole: ['select'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  range: {\n    type: 'abstract',\n    superclassRole: ['widget']\n  },\n  region: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  roletype: {\n    type: 'abstract',\n    superclassRole: []\n  },\n  row: {\n    type: 'structure',\n    requiredContext: ['grid', 'rowgroup', 'table', 'treegrid'],\n    requiredOwned: ['cell', 'columnheader', 'gridcell', 'rowheader'],\n    allowedAttrs: [\n      'aria-colindex',\n      'aria-level',\n      'aria-rowindex',\n      'aria-selected',\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-posinset',\n      'aria-setsize'\n    ],\n    superclassRole: ['group', 'widget'],\n    nameFromContent: true\n  },\n  rowgroup: {\n    type: 'structure',\n    requiredContext: ['grid', 'table', 'treegrid'],\n    requiredOwned: ['row'],\n    superclassRole: ['structure'],\n    nameFromContent: true\n  },\n  rowheader: {\n    type: 'structure',\n    requiredContext: ['row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-sort',\n      'aria-colindex',\n      'aria-colspan',\n      'aria-expanded',\n      'aria-readonly',\n      'aria-required',\n      'aria-rowindex',\n      'aria-rowspan',\n      'aria-selected'\n    ],\n    superclassRole: ['cell', 'gridcell', 'sectionhead'],\n    // Note: spec difference\n    accessibleNameRequired: false,\n    nameFromContent: true\n  },\n  scrollbar: {\n    type: 'widget',\n    requiredAttrs: ['aria-valuenow'],\n    // Note: since the scrollbar role has implicit\n    // aria-orientation, aria-valuemax, aria-valuemin values it\n    // is not required to be added by the user\n    //\n    // Note: because aria-controls is not well supported we will not\n    // make it a required attribute even though it is required in the\n    // spec\n    allowedAttrs: [\n      'aria-controls',\n      'aria-orientation',\n      'aria-valuemax',\n      'aria-valuemin',\n      'aria-valuetext'\n    ],\n    superclassRole: ['range'],\n    childrenPresentational: true\n  },\n  search: {\n    type: 'landmark',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  searchbox: {\n    type: 'widget',\n    allowedAttrs: [\n      'aria-activedescendant',\n      'aria-autocomplete',\n      'aria-multiline',\n      'aria-placeholder',\n      'aria-readonly',\n      'aria-required'\n    ],\n    superclassRole: ['textbox'],\n    accessibleNameRequired: true\n  },\n  section: {\n    type: 'abstract',\n    superclassRole: ['structure'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  sectionhead: {\n    type: 'abstract',\n    superclassRole: ['structure'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  select: {\n    type: 'abstract',\n    superclassRole: ['composite', 'group']\n  },\n  separator: {\n    type: 'structure',\n    requiredAttrs: ['aria-valuenow'],\n    // Note: since the separator role has implicit\n    // aria-orientation, aria-valuemax, aria-valuemin, and\n    // values it is not required to be added by\n    // the user\n    allowedAttrs: [\n      'aria-valuemax',\n      'aria-valuemin',\n      'aria-orientation',\n      'aria-valuetext'\n    ],\n    superclassRole: ['structure', 'widget'],\n    childrenPresentational: true\n  },\n  slider: {\n    type: 'widget',\n    requiredAttrs: ['aria-valuenow'],\n    // Note: since the slider role has implicit\n    // aria-orientation, aria-valuemax, aria-valuemin values it\n    // is not required to be added by the user\n    // Note: aria-required is not in the 1.1 or 1.2 specs but is\n    // consistently supported in ATs\n    allowedAttrs: [\n      'aria-valuemax',\n      'aria-valuemin',\n      'aria-orientation',\n      'aria-readonly',\n      'aria-required',\n      'aria-valuetext'\n    ],\n    superclassRole: ['input', 'range'],\n    accessibleNameRequired: true,\n    childrenPresentational: true\n  },\n  spinbutton: {\n    type: 'widget',\n    // Note: since the spinbutton role has implicit\n    // aria-valuenow, aria-valuemax, aria-valuemin values it\n    // is not required to be added by the user\n    allowedAttrs: [\n      'aria-valuemax',\n      'aria-valuemin',\n      'aria-readonly',\n      'aria-required',\n      'aria-activedescendant',\n      'aria-valuetext',\n      'aria-valuenow'\n    ],\n    superclassRole: ['composite', 'input', 'range'],\n    accessibleNameRequired: true\n  },\n  status: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  strong: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  structure: {\n    type: 'abstract',\n    superclassRole: ['roletype']\n  },\n  subscript: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  superscript: {\n    type: 'structure',\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  switch: {\n    type: 'widget',\n    requiredAttrs: ['aria-checked'],\n    allowedAttrs: ['aria-expanded', 'aria-readonly', 'aria-required'],\n    superclassRole: ['checkbox'],\n    accessibleNameRequired: true,\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  suggestion: {\n    type: 'structure',\n    requiredOwned: ['insertion', 'deletion'],\n    superclassRole: ['section'],\n    prohibitedAttrs: ['aria-label', 'aria-labelledby']\n  },\n  tab: {\n    type: 'widget',\n    requiredContext: ['tablist'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-posinset',\n      'aria-selected',\n      'aria-setsize',\n      'aria-expanded'\n    ],\n    superclassRole: ['sectionhead', 'widget'],\n    nameFromContent: true,\n    childrenPresentational: true\n  },\n  table: {\n    type: 'structure',\n    requiredOwned: ['rowgroup', 'row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-colcount', 'aria-rowcount', 'aria-expanded'],\n    // NOTE: although the spec says this is not named from contents,\n    // the accessible text acceptance tests (#139 and #140) require\n    // table be named from content (we even had to special case\n    // table in commons/aria/named-from-contents)\n    superclassRole: ['section'],\n    // Note: spec difference\n    accessibleNameRequired: false,\n    nameFromContent: true\n  },\n  tablist: {\n    type: 'composite',\n    requiredOwned: ['tab'],\n    // NOTE: aria-expanded is from the 1.0 spec but is still\n    // consistently supported in ATs\n    allowedAttrs: [\n      'aria-level',\n      'aria-multiselectable',\n      'aria-orientation',\n      'aria-activedescendant',\n      'aria-expanded'\n    ],\n    superclassRole: ['composite']\n  },\n  tabpanel: {\n    // Spec ambiguity: Aria 1.1 and 1.2 both say that tabpanel rolls up to\n    // structure via its section superclass, but also include it as a widget\n    // in §5.3.2 Widget Roles.\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  term: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    // Note: spec difference\n    nameFromContent: true\n  },\n  text: {\n    type: 'structure',\n    superclassRole: ['section'],\n    nameFromContent: true\n  },\n  textbox: {\n    type: 'widget',\n    allowedAttrs: [\n      'aria-activedescendant',\n      'aria-autocomplete',\n      'aria-multiline',\n      'aria-placeholder',\n      'aria-readonly',\n      'aria-required'\n    ],\n    superclassRole: ['input'],\n    accessibleNameRequired: true\n  },\n  time: {\n    type: 'structure',\n    superclassRole: ['section']\n  },\n  timer: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['status']\n  },\n  toolbar: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-orientation',\n      'aria-activedescendant',\n      'aria-expanded'\n    ],\n    superclassRole: ['group'],\n    accessibleNameRequired: true\n  },\n  tooltip: {\n    type: 'structure',\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section'],\n    nameFromContent: true\n  },\n  tree: {\n    type: 'composite',\n    requiredOwned: ['group', 'treeitem'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-multiselectable',\n      'aria-required',\n      'aria-activedescendant',\n      'aria-expanded',\n      'aria-orientation'\n    ],\n    superclassRole: ['select'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  treegrid: {\n    type: 'composite',\n    requiredOwned: ['rowgroup', 'row'],\n    // Spec difference: Aria-expanded removed in 1.2\n    allowedAttrs: [\n      'aria-activedescendant',\n      'aria-colcount',\n      'aria-expanded',\n      'aria-level',\n      'aria-multiselectable',\n      'aria-orientation',\n      'aria-readonly',\n      'aria-required',\n      'aria-rowcount'\n    ],\n    superclassRole: ['grid', 'tree'],\n    // Note: spec difference\n    accessibleNameRequired: false\n  },\n  treeitem: {\n    type: 'widget',\n    requiredContext: ['group', 'tree'],\n    allowedAttrs: [\n      'aria-checked',\n      'aria-expanded',\n      'aria-level',\n      'aria-posinset',\n      'aria-selected',\n      'aria-setsize'\n    ],\n    superclassRole: ['listitem', 'option'],\n    accessibleNameRequired: true,\n    nameFromContent: true\n  },\n  widget: {\n    type: 'abstract',\n    superclassRole: ['roletype']\n  },\n  window: {\n    type: 'abstract',\n    superclassRole: ['roletype']\n  }\n};\n\nexport default ariaRoles;\n"
  },
  {
    "path": "lib/standards/css-colors.js",
    "content": "// Source: https://drafts.csswg.org/css-color/#valdef-color-rebeccapurple\nconst cssColors = {\n  aliceblue: [240, 248, 255],\n  antiquewhite: [250, 235, 215],\n  aqua: [0, 255, 255],\n  aquamarine: [127, 255, 212],\n  azure: [240, 255, 255],\n  beige: [245, 245, 220],\n  bisque: [255, 228, 196],\n  black: [0, 0, 0],\n  blanchedalmond: [255, 235, 205],\n  blue: [0, 0, 255],\n  blueviolet: [138, 43, 226],\n  brown: [165, 42, 42],\n  burlywood: [222, 184, 135],\n  cadetblue: [95, 158, 160],\n  chartreuse: [127, 255, 0],\n  chocolate: [210, 105, 30],\n  coral: [255, 127, 80],\n  cornflowerblue: [100, 149, 237],\n  cornsilk: [255, 248, 220],\n  crimson: [220, 20, 60],\n  cyan: [0, 255, 255],\n  darkblue: [0, 0, 139],\n  darkcyan: [0, 139, 139],\n  darkgoldenrod: [184, 134, 11],\n  darkgray: [169, 169, 169],\n  darkgreen: [0, 100, 0],\n  darkgrey: [169, 169, 169],\n  darkkhaki: [189, 183, 107],\n  darkmagenta: [139, 0, 139],\n  darkolivegreen: [85, 107, 47],\n  darkorange: [255, 140, 0],\n  darkorchid: [153, 50, 204],\n  darkred: [139, 0, 0],\n  darksalmon: [233, 150, 122],\n  darkseagreen: [143, 188, 143],\n  darkslateblue: [72, 61, 139],\n  darkslategray: [47, 79, 79],\n  darkslategrey: [47, 79, 79],\n  darkturquoise: [0, 206, 209],\n  darkviolet: [148, 0, 211],\n  deeppink: [255, 20, 147],\n  deepskyblue: [0, 191, 255],\n  dimgray: [105, 105, 105],\n  dimgrey: [105, 105, 105],\n  dodgerblue: [30, 144, 255],\n  firebrick: [178, 34, 34],\n  floralwhite: [255, 250, 240],\n  forestgreen: [34, 139, 34],\n  fuchsia: [255, 0, 255],\n  gainsboro: [220, 220, 220],\n  ghostwhite: [248, 248, 255],\n  gold: [255, 215, 0],\n  goldenrod: [218, 165, 32],\n  gray: [128, 128, 128],\n  green: [0, 128, 0],\n  greenyellow: [173, 255, 47],\n  grey: [128, 128, 128],\n  honeydew: [240, 255, 240],\n  hotpink: [255, 105, 180],\n  indianred: [205, 92, 92],\n  indigo: [75, 0, 130],\n  ivory: [255, 255, 240],\n  khaki: [240, 230, 140],\n  lavender: [230, 230, 250],\n  lavenderblush: [255, 240, 245],\n  lawngreen: [124, 252, 0],\n  lemonchiffon: [255, 250, 205],\n  lightblue: [173, 216, 230],\n  lightcoral: [240, 128, 128],\n  lightcyan: [224, 255, 255],\n  lightgoldenrodyellow: [250, 250, 210],\n  lightgray: [211, 211, 211],\n  lightgreen: [144, 238, 144],\n  lightgrey: [211, 211, 211],\n  lightpink: [255, 182, 193],\n  lightsalmon: [255, 160, 122],\n  lightseagreen: [32, 178, 170],\n  lightskyblue: [135, 206, 250],\n  lightslategray: [119, 136, 153],\n  lightslategrey: [119, 136, 153],\n  lightsteelblue: [176, 196, 222],\n  lightyellow: [255, 255, 224],\n  lime: [0, 255, 0],\n  limegreen: [50, 205, 50],\n  linen: [250, 240, 230],\n  magenta: [255, 0, 255],\n  maroon: [128, 0, 0],\n  mediumaquamarine: [102, 205, 170],\n  mediumblue: [0, 0, 205],\n  mediumorchid: [186, 85, 211],\n  mediumpurple: [147, 112, 219],\n  mediumseagreen: [60, 179, 113],\n  mediumslateblue: [123, 104, 238],\n  mediumspringgreen: [0, 250, 154],\n  mediumturquoise: [72, 209, 204],\n  mediumvioletred: [199, 21, 133],\n  midnightblue: [25, 25, 112],\n  mintcream: [245, 255, 250],\n  mistyrose: [255, 228, 225],\n  moccasin: [255, 228, 181],\n  navajowhite: [255, 222, 173],\n  navy: [0, 0, 128],\n  oldlace: [253, 245, 230],\n  olive: [128, 128, 0],\n  olivedrab: [107, 142, 35],\n  orange: [255, 165, 0],\n  orangered: [255, 69, 0],\n  orchid: [218, 112, 214],\n  palegoldenrod: [238, 232, 170],\n  palegreen: [152, 251, 152],\n  paleturquoise: [175, 238, 238],\n  palevioletred: [219, 112, 147],\n  papayawhip: [255, 239, 213],\n  peachpuff: [255, 218, 185],\n  peru: [205, 133, 63],\n  pink: [255, 192, 203],\n  plum: [221, 160, 221],\n  powderblue: [176, 224, 230],\n  purple: [128, 0, 128],\n  rebeccapurple: [102, 51, 153],\n  red: [255, 0, 0],\n  rosybrown: [188, 143, 143],\n  royalblue: [65, 105, 225],\n  saddlebrown: [139, 69, 19],\n  salmon: [250, 128, 114],\n  sandybrown: [244, 164, 96],\n  seagreen: [46, 139, 87],\n  seashell: [255, 245, 238],\n  sienna: [160, 82, 45],\n  silver: [192, 192, 192],\n  skyblue: [135, 206, 235],\n  slateblue: [106, 90, 205],\n  slategray: [112, 128, 144],\n  slategrey: [112, 128, 144],\n  snow: [255, 250, 250],\n  springgreen: [0, 255, 127],\n  steelblue: [70, 130, 180],\n  tan: [210, 180, 140],\n  teal: [0, 128, 128],\n  thistle: [216, 191, 216],\n  tomato: [255, 99, 71],\n  turquoise: [64, 224, 208],\n  violet: [238, 130, 238],\n  wheat: [245, 222, 179],\n  white: [255, 255, 255],\n  whitesmoke: [245, 245, 245],\n  yellow: [255, 255, 0],\n  yellowgreen: [154, 205, 50]\n};\n\nexport default cssColors;\n"
  },
  {
    "path": "lib/standards/dpub-roles.js",
    "content": "// Source https://www.w3.org/TR/dpub-aria-1.0/\nconst dpubRoles = {\n  'doc-abstract': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-acknowledgments': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-afterword': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-appendix': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-backlink': {\n    type: 'link',\n    allowedAttrs: ['aria-expanded'],\n    nameFromContent: true,\n    superclassRole: ['link']\n  },\n  'doc-biblioentry': {\n    type: 'listitem',\n    allowedAttrs: [\n      'aria-expanded',\n      'aria-level',\n      'aria-posinset',\n      'aria-setsize'\n    ],\n    superclassRole: ['listitem'],\n    deprecated: true\n  },\n  'doc-bibliography': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-biblioref': {\n    type: 'link',\n    allowedAttrs: ['aria-expanded'],\n    nameFromContent: true,\n    superclassRole: ['link']\n  },\n  'doc-chapter': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-colophon': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-conclusion': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-cover': {\n    type: 'img',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['img']\n  },\n  'doc-credit': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-credits': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-dedication': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-endnote': {\n    type: 'listitem',\n    allowedAttrs: [\n      'aria-expanded',\n      'aria-level',\n      'aria-posinset',\n      'aria-setsize'\n    ],\n    superclassRole: ['listitem'],\n    deprecated: true\n  },\n  'doc-endnotes': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-epigraph': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-epilogue': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-errata': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-example': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-footnote': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-foreword': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-glossary': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-glossref': {\n    type: 'link',\n    allowedAttrs: ['aria-expanded'],\n    nameFromContent: true,\n    superclassRole: ['link']\n  },\n  'doc-index': {\n    type: 'navigation',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['navigation']\n  },\n  'doc-introduction': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-noteref': {\n    type: 'link',\n    allowedAttrs: ['aria-expanded'],\n    nameFromContent: true,\n    superclassRole: ['link']\n  },\n  'doc-notice': {\n    type: 'note',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['note']\n  },\n  'doc-pagebreak': {\n    type: 'separator',\n    allowedAttrs: ['aria-expanded', 'aria-orientation'],\n    superclassRole: ['separator'],\n    childrenPresentational: true\n  },\n  'doc-pagelist': {\n    type: 'navigation',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['navigation']\n  },\n  'doc-part': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-preface': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-prologue': {\n    type: 'landmark',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['landmark']\n  },\n  'doc-pullquote': {\n    type: 'none',\n    superclassRole: ['none']\n  },\n  'doc-qna': {\n    type: 'section',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['section']\n  },\n  'doc-subtitle': {\n    type: 'sectionhead',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['sectionhead']\n  },\n  'doc-tip': {\n    type: 'note',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['note']\n  },\n  'doc-toc': {\n    type: 'navigation',\n    allowedAttrs: ['aria-expanded'],\n    superclassRole: ['navigation']\n  }\n};\n\nexport default dpubRoles;\n"
  },
  {
    "path": "lib/standards/graphics-roles.js",
    "content": "// Source: https://www.w3.org/TR/graphics-aria-1.0/\n\nconst graphicsRoles = {\n  'graphics-document': {\n    type: 'structure',\n    superclassRole: ['document'],\n    accessibleNameRequired: true\n  },\n  'graphics-object': {\n    type: 'structure',\n    superclassRole: ['group'],\n    nameFromContent: true\n  },\n  'graphics-symbol': {\n    type: 'structure',\n    superclassRole: ['img'],\n    accessibleNameRequired: true,\n    childrenPresentational: true\n  }\n};\n\nexport default graphicsRoles;\n"
  },
  {
    "path": "lib/standards/html-elms.js",
    "content": "// Source: https://www.w3.org/TR/html-aria/#allowed-aria-roles-states-and-properties\n// Source: https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings\n// Source https://html.spec.whatwg.org/multipage/dom.html#content-models\n// Source https://dom.spec.whatwg.org/#dom-element-attachshadow\nconst htmlElms = {\n  a: {\n    // Note: variants work by matching the node against the\n    // `matches` attribute. if the variant matches AND has the\n    // desired property (contentTypes, etc.) then we use it,\n    // otherwise we move on to the next matching variant\n    variant: {\n      href: {\n        matches: '[href]',\n        contentTypes: ['interactive', 'phrasing', 'flow'],\n        allowedRoles: [\n          'button',\n          'checkbox',\n          'menuitem',\n          'menuitemcheckbox',\n          'menuitemradio',\n          'option',\n          'radio',\n          'switch',\n          'tab',\n          'treeitem',\n          'doc-backlink',\n          'doc-biblioref',\n          'doc-glossref',\n          'doc-noteref'\n        ],\n        namingMethods: ['subtreeText']\n      },\n      // Note: the default variant is a special variant and is\n      // used as the last match if none of the other variants\n      // match or have the desired attribute\n      default: {\n        contentTypes: ['phrasing', 'flow'],\n        allowedRoles: true\n      }\n    }\n  },\n  abbr: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  address: {\n    contentTypes: ['flow'],\n    allowedRoles: true\n  },\n  area: {\n    variant: {\n      href: {\n        matches: '[href]',\n        allowedRoles: false\n      },\n      default: {\n        allowedRoles: ['button', 'link']\n      }\n    },\n    contentTypes: ['phrasing', 'flow'],\n    namingMethods: ['altText']\n  },\n  article: {\n    contentTypes: ['sectioning', 'flow'],\n    allowedRoles: [\n      'feed',\n      'presentation',\n      'none',\n      'document',\n      'application',\n      'main',\n      'region'\n    ],\n    shadowRoot: true\n  },\n  aside: {\n    contentTypes: ['sectioning', 'flow'],\n    allowedRoles: [\n      'feed',\n      'note',\n      'presentation',\n      'none',\n      'region',\n      'search',\n      'doc-dedication',\n      'doc-example',\n      'doc-footnote',\n      'doc-glossary',\n      'doc-pullquote',\n      'doc-tip'\n    ]\n  },\n  audio: {\n    variant: {\n      controls: {\n        matches: '[controls]',\n        contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']\n      },\n      default: {\n        contentTypes: ['embedded', 'phrasing', 'flow']\n      }\n    },\n    // Note: if the property applies regardless of variants it is\n    // placed at the top level instead of the default variant\n    allowedRoles: ['application'],\n    chromiumRole: 'Audio'\n  },\n  b: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  base: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  bdi: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  bdo: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  blockquote: {\n    contentTypes: ['flow'],\n    allowedRoles: true,\n    shadowRoot: true\n  },\n  body: {\n    allowedRoles: false,\n    shadowRoot: true\n  },\n  br: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: ['presentation', 'none'],\n    namingMethods: ['titleText', 'singleSpace']\n  },\n  button: {\n    contentTypes: ['interactive', 'phrasing', 'flow'],\n    allowedRoles: [\n      'checkbox',\n      'combobox',\n      'gridcell',\n      'link',\n      'menuitem',\n      'menuitemcheckbox',\n      'menuitemradio',\n      'option',\n      'radio',\n      'separator',\n      'slider',\n      'switch',\n      'tab',\n      'treeitem'\n    ],\n    // 5.4 button Element\n    namingMethods: ['subtreeText']\n  },\n  canvas: {\n    allowedRoles: true,\n    contentTypes: ['embedded', 'phrasing', 'flow'],\n    chromiumRole: 'Canvas'\n  },\n  caption: {\n    allowedRoles: false\n  },\n  cite: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  code: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  col: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  colgroup: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  data: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  datalist: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true,\n    implicitAttrs: {\n      // Note: even though the value of aria-multiselectable is based\n      // on the attributes, we don't currently need to know the\n      // precise value. however, this allows us to make the attribute\n      // future proof in case we ever do need to know it\n      'aria-multiselectable': 'false'\n    }\n  },\n  dd: {\n    allowedRoles: false\n  },\n  del: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  dfn: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  details: {\n    contentTypes: ['interactive', 'flow'],\n    allowedRoles: false\n  },\n  dialog: {\n    contentTypes: ['flow'],\n    allowedRoles: ['alertdialog']\n  },\n  div: {\n    contentTypes: ['flow'],\n    allowedRoles: true,\n    shadowRoot: true\n  },\n  dl: {\n    contentTypes: ['flow'],\n    allowedRoles: ['group', 'list', 'presentation', 'none'],\n    chromiumRole: 'DescriptionList'\n  },\n  dt: {\n    allowedRoles: ['listitem']\n  },\n  em: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  embed: {\n    contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'],\n    allowedRoles: ['application', 'document', 'img', 'presentation', 'none'],\n    chromiumRole: 'EmbeddedObject'\n  },\n  fieldset: {\n    contentTypes: ['flow'],\n    allowedRoles: ['none', 'presentation', 'radiogroup'],\n    // 5.5 fieldset and legend Elements\n    namingMethods: ['fieldsetLegendText']\n  },\n  figcaption: {\n    allowedRoles: ['group', 'none', 'presentation']\n  },\n  figure: {\n    contentTypes: ['flow'],\n    // Note: technically you're allowed no role when a figcaption\n    // descendant, but we can't match that so we'll go with any role\n    allowedRoles: true,\n    // 5.9 figure and figcaption Elements\n    namingMethods: ['figureText', 'titleText']\n  },\n  footer: {\n    contentTypes: ['flow'],\n    allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'],\n    shadowRoot: true\n  },\n  form: {\n    contentTypes: ['flow'],\n    allowedRoles: ['form', 'search', 'none', 'presentation']\n  },\n  h1: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '1'\n    }\n  },\n  h2: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '2'\n    }\n  },\n  h3: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '3'\n    }\n  },\n  h4: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '4'\n    }\n  },\n  h5: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '5'\n    }\n  },\n  h6: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: ['none', 'presentation', 'tab', 'doc-subtitle'],\n    shadowRoot: true,\n    implicitAttrs: {\n      'aria-level': '6'\n    }\n  },\n  head: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  header: {\n    contentTypes: ['flow'],\n    allowedRoles: ['group', 'none', 'presentation', 'doc-footnote'],\n    shadowRoot: true\n  },\n  hgroup: {\n    contentTypes: ['heading', 'flow'],\n    allowedRoles: true\n  },\n  hr: {\n    contentTypes: ['flow'],\n    allowedRoles: ['none', 'presentation', 'doc-pagebreak'],\n    namingMethods: ['titleText', 'singleSpace']\n  },\n  html: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  i: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  iframe: {\n    contentTypes: ['interactive', 'embedded', 'phrasing', 'flow'],\n    allowedRoles: ['application', 'document', 'img', 'none', 'presentation'],\n    chromiumRole: 'Iframe'\n  },\n  img: {\n    variant: {\n      nonEmptyAlt: {\n        matches: [\n          {\n            // Because <img role=\"none\" alt=\"foo\" /> has no accessible name:\n            attributes: {\n              alt: '/.+/'\n            }\n          },\n          {\n            hasAccessibleName: true\n          }\n        ],\n        allowedRoles: [\n          'button',\n          'checkbox',\n          'link',\n          'math',\n          'menuitem',\n          'menuitemcheckbox',\n          'menuitemradio',\n          'meter',\n          'option',\n          'progressbar',\n          'radio',\n          'scrollbar',\n          'separator',\n          'slider',\n          'switch',\n          'tab',\n          'treeitem',\n          'doc-cover'\n        ]\n      },\n      usemap: {\n        matches: '[usemap]',\n        contentTypes: ['interactive', 'embedded', 'flow']\n      },\n      default: {\n        // Note: allow role presentation and none on image with no\n        // alt as a way to prevent axe from flagging the image as\n        // needing an alt\n        allowedRoles: ['presentation', 'none'],\n        // Note: spec change (do not count as phrasing), because browsers\n        // insert a space between an img's accessible name and other\n        // elements' accessible names\n        contentTypes: ['embedded', 'flow']\n      }\n    },\n    // 5.10 img Element\n    namingMethods: ['altText']\n  },\n  input: {\n    variant: {\n      button: {\n        matches: {\n          properties: {\n            type: 'button'\n          }\n        },\n        allowedRoles: [\n          'checkbox',\n          'combobox',\n          'link',\n          'menuitem',\n          'menuitemcheckbox',\n          'menuitemradio',\n          'option',\n          'radio',\n          'switch',\n          'tab'\n        ]\n      },\n      // 5.2 input type=\"button\", input type=\"submit\" and input type=\"reset\"\n      buttonType: {\n        matches: {\n          properties: {\n            type: ['button', 'submit', 'reset']\n          }\n        },\n        namingMethods: ['valueText', 'titleText', 'buttonDefaultText']\n      },\n      checkboxPressed: {\n        matches: {\n          properties: {\n            type: 'checkbox'\n          },\n          attributes: {\n            'aria-pressed': '/.*/'\n          }\n        },\n        allowedRoles: ['button', 'menuitemcheckbox', 'option', 'switch'],\n        implicitAttrs: {\n          'aria-checked': 'false'\n        }\n      },\n      checkbox: {\n        matches: {\n          properties: {\n            type: 'checkbox'\n          },\n          attributes: {\n            'aria-pressed': null\n          }\n        },\n        allowedRoles: ['menuitemcheckbox', 'option', 'switch'],\n        implicitAttrs: {\n          'aria-checked': 'false'\n        }\n      },\n      noRoles: {\n        matches: {\n          properties: {\n            // Note: types of url, search, tel, and email are listed\n            // as not allowed roles however since they are text\n            // types they should be allowed to have role=combobox\n            type: [\n              'color',\n              'date',\n              'datetime-local',\n              'file',\n              'month',\n              'number',\n              'password',\n              'range',\n              'reset',\n              'submit',\n              'time',\n              'week'\n            ]\n          }\n        },\n        allowedRoles: false\n      },\n      hidden: {\n        matches: {\n          properties: {\n            type: 'hidden'\n          }\n        },\n        // Note: spec change (do not count as phrasing)\n        contentTypes: ['flow'],\n        allowedRoles: false,\n        noAriaAttrs: true\n      },\n      image: {\n        matches: {\n          properties: {\n            type: 'image'\n          }\n        },\n        allowedRoles: [\n          'link',\n          'menuitem',\n          'menuitemcheckbox',\n          'menuitemradio',\n          'radio',\n          'switch'\n        ],\n        // 5.3 input type=\"image\"\n        namingMethods: [\n          'altText',\n          'valueText',\n          'labelText',\n          'titleText',\n          'buttonDefaultText'\n        ]\n      },\n      radio: {\n        matches: {\n          properties: {\n            type: 'radio'\n          }\n        },\n        allowedRoles: ['menuitemradio'],\n        implicitAttrs: {\n          'aria-checked': 'false'\n        }\n      },\n      textWithList: {\n        matches: {\n          properties: {\n            type: 'text'\n          },\n          attributes: {\n            list: '/.*/'\n          }\n        },\n        allowedRoles: false\n      },\n      default: {\n        // Note: spec change (do not count as phrasing)\n        contentTypes: ['interactive', 'flow'],\n        allowedRoles: ['combobox', 'searchbox', 'spinbutton'],\n        implicitAttrs: {\n          'aria-valuenow': ''\n        },\n        // 5.1 input type=\"text\", input type=\"password\", input type=\"search\", input type=\"tel\", input type=\"url\"\n        // 5.7 Other Form Elements\n        namingMethods: ['labelText', 'placeholderText']\n      }\n    }\n  },\n  ins: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  kbd: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  label: {\n    contentTypes: ['interactive', 'phrasing', 'flow'],\n    allowedRoles: false,\n    chromiumRole: 'Label'\n  },\n  legend: {\n    allowedRoles: false\n  },\n  li: {\n    allowedRoles: [\n      'menuitem',\n      'menuitemcheckbox',\n      'menuitemradio',\n      'option',\n      'none',\n      'presentation',\n      'radio',\n      'separator',\n      'tab',\n      'treeitem',\n      'doc-biblioentry',\n      'doc-endnote'\n    ],\n    implicitAttrs: {\n      'aria-setsize': '1',\n      'aria-posinset': '1'\n    }\n  },\n  link: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  main: {\n    contentTypes: ['flow'],\n    allowedRoles: false,\n    shadowRoot: true\n  },\n  map: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  math: {\n    contentTypes: ['embedded', 'phrasing', 'flow'],\n    allowedRoles: false\n  },\n  mark: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  menu: {\n    contentTypes: ['flow'],\n    allowedRoles: [\n      'directory',\n      'group',\n      'listbox',\n      'menu',\n      'menubar',\n      'none',\n      'presentation',\n      'radiogroup',\n      'tablist',\n      'toolbar',\n      'tree'\n    ]\n  },\n  meta: {\n    variant: {\n      itemprop: {\n        matches: '[itemprop]',\n        contentTypes: ['phrasing', 'flow']\n      }\n    },\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  meter: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    chromiumRole: 'progressbar'\n  },\n  nav: {\n    contentTypes: ['sectioning', 'flow'],\n    allowedRoles: [\n      'doc-index',\n      'doc-pagelist',\n      'doc-toc',\n      'menu',\n      'menubar',\n      'none',\n      'presentation',\n      'tablist'\n    ],\n    shadowRoot: true\n  },\n  noscript: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  object: {\n    variant: {\n      usemap: {\n        matches: '[usemap]',\n        contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']\n      },\n      default: {\n        contentTypes: ['embedded', 'phrasing', 'flow']\n      }\n    },\n    allowedRoles: ['application', 'document', 'img'],\n    chromiumRole: 'PluginObject'\n  },\n  ol: {\n    contentTypes: ['flow'],\n    allowedRoles: [\n      'directory',\n      'group',\n      'listbox',\n      'menu',\n      'menubar',\n      'none',\n      'presentation',\n      'radiogroup',\n      'tablist',\n      'toolbar',\n      'tree'\n    ]\n  },\n  optgroup: {\n    allowedRoles: false\n  },\n  option: {\n    allowedRoles: false,\n    implicitAttrs: {\n      'aria-selected': 'false'\n    }\n  },\n  output: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true,\n    // 5.6 output Element\n    namingMethods: ['subtreeText']\n  },\n  p: {\n    contentTypes: ['flow'],\n    allowedRoles: true,\n    shadowRoot: true\n  },\n  param: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  picture: {\n    // Note: spec change (do not count as embedded), because browsers do not hide text inside the picture element\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  pre: {\n    contentTypes: ['flow'],\n    allowedRoles: true\n  },\n  progress: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    implicitAttrs: {\n      'aria-valuemax': '100',\n      'aria-valuemin': '0',\n      'aria-valuenow': '0'\n    }\n  },\n  q: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  rp: {\n    allowedRoles: true\n  },\n  rt: {\n    allowedRoles: true\n  },\n  ruby: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  s: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  samp: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  script: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  search: {\n    contentTypes: ['flow'],\n    allowedRoles: ['form', 'group', 'none', 'presentation', 'region', 'search']\n  },\n  section: {\n    contentTypes: ['sectioning', 'flow'],\n    allowedRoles: [\n      'alert',\n      'alertdialog',\n      'application',\n      'banner',\n      'complementary',\n      'contentinfo',\n      'dialog',\n      'document',\n      'feed',\n      'group',\n      'log',\n      'main',\n      'marquee',\n      'navigation',\n      'none',\n      'note',\n      'presentation',\n      'search',\n      'status',\n      'tabpanel',\n      'doc-abstract',\n      'doc-acknowledgments',\n      'doc-afterword',\n      'doc-appendix',\n      'doc-bibliography',\n      'doc-chapter',\n      'doc-colophon',\n      'doc-conclusion',\n      'doc-credit',\n      'doc-credits',\n      'doc-dedication',\n      'doc-endnotes',\n      'doc-epigraph',\n      'doc-epilogue',\n      'doc-errata',\n      'doc-example',\n      'doc-foreword',\n      'doc-glossary',\n      'doc-index',\n      'doc-introduction',\n      'doc-notice',\n      'doc-pagelist',\n      'doc-part',\n      'doc-preface',\n      'doc-prologue',\n      'doc-pullquote',\n      'doc-qna',\n      'doc-toc'\n    ],\n    shadowRoot: true\n  },\n  select: {\n    variant: {\n      combobox: {\n        matches: {\n          attributes: {\n            multiple: null,\n            size: [null, '1']\n          }\n        },\n        allowedRoles: ['menu']\n      },\n      default: {\n        allowedRoles: false\n      }\n    },\n    contentTypes: ['interactive', 'phrasing', 'flow'],\n    implicitAttrs: {\n      'aria-valuenow': ''\n    },\n    // 5.7 Other form elements\n    namingMethods: ['labelText']\n  },\n  slot: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  small: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  source: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  span: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true,\n    shadowRoot: true\n  },\n  strong: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  style: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  svg: {\n    contentTypes: ['embedded', 'phrasing', 'flow'],\n    allowedRoles: true,\n    chromiumRole: 'SVGRoot',\n    namingMethods: ['svgTitleText']\n  },\n  sub: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  summary: {\n    allowedRoles: false,\n    // 5.8 summary Element\n    namingMethods: ['subtreeText']\n  },\n  sup: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  table: {\n    contentTypes: ['flow'],\n    allowedRoles: true,\n    // 5.11 table Element\n    namingMethods: ['tableCaptionText', 'tableSummaryText']\n  },\n  tbody: {\n    allowedRoles: true\n  },\n  template: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  textarea: {\n    contentTypes: ['interactive', 'phrasing', 'flow'],\n    allowedRoles: false,\n    implicitAttrs: {\n      'aria-valuenow': '',\n      'aria-multiline': 'true'\n    },\n    // 5.1 textarea\n    namingMethods: ['labelText', 'placeholderText']\n  },\n  tfoot: {\n    allowedRoles: true\n  },\n  thead: {\n    allowedRoles: true\n  },\n  time: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  title: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  td: {\n    allowedRoles: true\n  },\n  th: {\n    allowedRoles: true\n  },\n  tr: {\n    allowedRoles: true\n  },\n  track: {\n    allowedRoles: false,\n    noAriaAttrs: true\n  },\n  u: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  ul: {\n    contentTypes: ['flow'],\n    allowedRoles: [\n      'directory',\n      'group',\n      'listbox',\n      'menu',\n      'menubar',\n      'none',\n      'presentation',\n      'radiogroup',\n      'tablist',\n      'toolbar',\n      'tree'\n    ]\n  },\n  var: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: true\n  },\n  video: {\n    variant: {\n      controls: {\n        matches: '[controls]',\n        contentTypes: ['interactive', 'embedded', 'phrasing', 'flow']\n      },\n      default: {\n        contentTypes: ['embedded', 'phrasing', 'flow']\n      }\n    },\n    allowedRoles: ['application'],\n    chromiumRole: 'video'\n  },\n  wbr: {\n    contentTypes: ['phrasing', 'flow'],\n    allowedRoles: ['presentation', 'none']\n  }\n};\n\nexport default htmlElms;\n"
  },
  {
    "path": "lib/standards/index.js",
    "content": "import ariaAttrs from './aria-attrs';\nimport ariaRoles from './aria-roles';\nimport dpubRoles from './dpub-roles';\nimport graphicsRoles from './graphics-roles';\nimport htmlElms from './html-elms';\nimport { deepMerge } from '../core/utils';\nimport cssColors from './css-colors';\n\nconst originals = {\n  ariaAttrs,\n  ariaRoles: {\n    ...ariaRoles,\n    ...dpubRoles,\n    ...graphicsRoles\n  },\n  htmlElms,\n  cssColors\n};\nconst standards = {\n  ...originals\n};\n\nexport function configureStandards(config) {\n  Object.keys(standards).forEach(propName => {\n    if (config[propName]) {\n      standards[propName] = deepMerge(standards[propName], config[propName]);\n    }\n  });\n}\n\nexport function resetStandards() {\n  Object.keys(standards).forEach(propName => {\n    standards[propName] = originals[propName];\n  });\n}\n\nexport default standards;\n"
  },
  {
    "path": "locales/README.md",
    "content": "# Localizations\n\nWe welcome any localization for axe-core. For details on how to contribute, see the [Contributing section](../README.md#contributing) of the main README. For details on the message syntax, see [Check Message Template](../doc/check-message-template.md).\n\nTo create a new translation for axe, start by running `grunt translate --lang=<langcode>`. This will create a JSON file with the default English text in it for you to translate. Alternatively, you could copy `_template.json`.\n\nTo update an existing translation file, re-run `grunt translate --lang=<langcode>`. This will add new messages used in English and remove messages that are no longer used in English.\n\n`_template.json` is a generated file which is created every time axe is built. It's compiled using each rule's `description` and `help` properties, as well as each check's `metadata.messages` property. To update the `_template.json` file you'll need to update the corresponding [rule](../lib/rules) or [check](../lib/checks) metadata file and rebuild.\n"
  },
  {
    "path": "locales/_template.json",
    "content": "{\n  \"lang\": \"xyz\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Ensure every accesskey attribute value is unique\",\n      \"help\": \"accesskey attribute value should be unique\"\n    },\n    \"area-alt\": {\n      \"description\": \"Ensure <area> elements of image maps have alternative text\",\n      \"help\": \"Active <area> elements must have alternative text\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Ensure an element's role supports its ARIA attributes\",\n      \"help\": \"Elements must only use supported ARIA attributes\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Ensure role attribute has an appropriate value for the element\",\n      \"help\": \"ARIA role should be appropriate for the element\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Ensure aria-braillelabel and aria-brailleroledescription have a non-braille equivalent\",\n      \"help\": \"aria-braille attributes must have a non-braille equivalent\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Ensure every ARIA button, link and menuitem has an accessible name\",\n      \"help\": \"ARIA commands must have an accessible name\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Ensure ARIA attributes are used as described in the specification of the element's role\",\n      \"help\": \"ARIA attributes must be used as specified for the element's role\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Ensure elements do not use deprecated roles\",\n      \"help\": \"Deprecated ARIA roles must not be used\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Ensure every ARIA dialog and alertdialog node has an accessible name\",\n      \"help\": \"ARIA dialog and alertdialog nodes should have an accessible name\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Ensure aria-hidden=\\\"true\\\" is not present on the document body.\",\n      \"help\": \"aria-hidden=\\\"true\\\" must not be present on the document body\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Ensure aria-hidden elements are not focusable nor contain focusable elements\",\n      \"help\": \"ARIA hidden element must not be focusable or contain focusable elements\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Ensure every ARIA input field has an accessible name\",\n      \"help\": \"ARIA input fields must have an accessible name\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Ensure every ARIA meter node has an accessible name\",\n      \"help\": \"ARIA meter nodes must have an accessible name\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Ensure every ARIA progressbar node has an accessible name\",\n      \"help\": \"ARIA progressbar nodes must have an accessible name\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Ensure ARIA attributes are not prohibited for an element's role\",\n      \"help\": \"Elements must only use permitted ARIA attributes\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Ensure elements with ARIA roles have all required ARIA attributes\",\n      \"help\": \"Required ARIA attributes must be provided\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Ensure elements with an ARIA role that require child roles contain them\",\n      \"help\": \"Certain ARIA roles must contain particular children\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Ensure elements with an ARIA role that require parent roles are contained by them\",\n      \"help\": \"Certain ARIA roles must be contained by particular parents\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Ensure aria-roledescription is only used on elements with an implicit or explicit role\",\n      \"help\": \"aria-roledescription must be on elements with a semantic role\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Ensure all elements with a role attribute use a valid value\",\n      \"help\": \"ARIA roles used must conform to valid values\"\n    },\n    \"aria-tab-name\": {\n      \"description\": \"Ensure every ARIA tab node has an accessible name\",\n      \"help\": \"ARIA tab nodes must have an accessible name\"\n    },\n    \"aria-text\": {\n      \"description\": \"Ensure role=\\\"text\\\" is used on elements with no focusable descendants\",\n      \"help\": \"\\\"role=text\\\" should have no focusable descendants\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Ensure every ARIA toggle field has an accessible name\",\n      \"help\": \"ARIA toggle fields must have an accessible name\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Ensure every ARIA tooltip node has an accessible name\",\n      \"help\": \"ARIA tooltip nodes must have an accessible name\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Ensure every ARIA treeitem node has an accessible name\",\n      \"help\": \"ARIA treeitem nodes should have an accessible name\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Ensure all ARIA attributes have valid values\",\n      \"help\": \"ARIA attributes must conform to valid values\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Ensure attributes that begin with aria- are valid ARIA attributes\",\n      \"help\": \"ARIA attributes must conform to valid names\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Ensure <audio> elements have captions\",\n      \"help\": \"<audio> elements must have a captions track\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Ensure the autocomplete attribute is correct and suitable for the form field\",\n      \"help\": \"autocomplete attribute must be used correctly\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Ensure that text spacing set through style attributes can be adjusted with custom stylesheets\",\n      \"help\": \"Inline text spacing must be adjustable with custom stylesheets\"\n    },\n    \"blink\": {\n      \"description\": \"Ensure <blink> elements are not used\",\n      \"help\": \"<blink> elements are deprecated and must not be used\"\n    },\n    \"button-name\": {\n      \"description\": \"Ensure buttons have discernible text\",\n      \"help\": \"Buttons must have discernible text\"\n    },\n    \"bypass\": {\n      \"description\": \"Ensure each page has at least one mechanism for a user to bypass navigation and jump straight to the content\",\n      \"help\": \"Page must have means to bypass repeated blocks\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Ensure the contrast between foreground and background colors meets WCAG 2 AAA enhanced contrast ratio thresholds\",\n      \"help\": \"Elements must meet enhanced color contrast ratio thresholds\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds\",\n      \"help\": \"Elements must meet minimum color contrast ratio thresholds\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Ensure content is not locked to any specific display orientation, and the content is operable in all display orientations\",\n      \"help\": \"CSS Media queries must not lock display orientation\"\n    },\n    \"definition-list\": {\n      \"description\": \"Ensure <dl> elements are structured correctly\",\n      \"help\": \"<dl> elements must only directly contain properly-ordered <dt> and <dd> groups, <script>, <template> or <div> elements\"\n    },\n    \"dlitem\": {\n      \"description\": \"Ensure <dt> and <dd> elements are contained by a <dl>\",\n      \"help\": \"<dt> and <dd> elements must be contained by a <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Ensure each HTML document contains a non-empty <title> element\",\n      \"help\": \"Documents must have <title> element to aid in navigation\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Ensure every id attribute value of active elements is unique\",\n      \"help\": \"IDs of active elements must be unique\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Ensure every id attribute value used in ARIA and in labels is unique\",\n      \"help\": \"IDs used in ARIA and labels must be unique\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Ensure every id attribute value is unique\",\n      \"help\": \"id attribute value must be unique\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Ensure headings have discernible text\",\n      \"help\": \"Headings should not be empty\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Ensure table headers have discernible text\",\n      \"help\": \"Table header text should not be empty\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Ensure elements in the focus order have a role appropriate for interactive content\",\n      \"help\": \"Elements in the focus order should have an appropriate role\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Ensure form field does not have multiple label elements\",\n      \"help\": \"Form field must not have multiple label elements\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Ensure <frame> and <iframe> elements with focusable content do not have tabindex=-1\",\n      \"help\": \"Frames with focusable content must not have tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Ensure <iframe> and <frame> elements contain the axe-core script\",\n      \"help\": \"Frames should be tested with axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Ensure <iframe> and <frame> elements contain a unique title attribute\",\n      \"help\": \"Frames must have a unique title attribute\"\n    },\n    \"frame-title\": {\n      \"description\": \"Ensure <iframe> and <frame> elements have an accessible name\",\n      \"help\": \"Frames must have an accessible name\"\n    },\n    \"heading-order\": {\n      \"description\": \"Ensure the order of headings is semantically correct\",\n      \"help\": \"Heading levels should only increase by one\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Inform users about hidden content.\",\n      \"help\": \"Hidden content on the page should be analyzed\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Ensure every HTML document has a lang attribute\",\n      \"help\": \"<html> element must have a lang attribute\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Ensure the lang attribute of the <html> element has a valid value\",\n      \"help\": \"<html> element must have a valid value for the lang attribute\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page\",\n      \"help\": \"HTML elements with lang and xml:lang must have the same base language\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Ensure that links with the same accessible name serve a similar purpose\",\n      \"help\": \"Links with the same name must have a similar purpose\"\n    },\n    \"image-alt\": {\n      \"description\": \"Ensure <img> elements have alternative text or a role of none or presentation\",\n      \"help\": \"Images must have alternative text\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Ensure image alternative is not repeated as text\",\n      \"help\": \"Alternative text of images should not be repeated as text\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Ensure input buttons have discernible text\",\n      \"help\": \"Input buttons must have discernible text\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Ensure <input type=\\\"image\\\"> elements have alternative text\",\n      \"help\": \"Image buttons must have alternative text\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Ensure that elements labelled through their content must have their visible text as part of their accessible name\",\n      \"help\": \"Elements must have their visible text as part of their accessible name\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Ensure that every form element has a visible label and is not solely labeled using hidden labels, or the title or aria-describedby attributes\",\n      \"help\": \"Form elements should have a visible label\"\n    },\n    \"label\": {\n      \"description\": \"Ensure every form element has a label\",\n      \"help\": \"Form elements must have labels\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Ensure the banner landmark is at top level\",\n      \"help\": \"Banner landmark should not be contained in another landmark\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Ensure the complementary landmark or aside is at top level\",\n      \"help\": \"Aside should not be contained in another landmark\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Ensure the contentinfo landmark is at top level\",\n      \"help\": \"Contentinfo landmark should not be contained in another landmark\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Ensure the main landmark is at top level\",\n      \"help\": \"Main landmark should not be contained in another landmark\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Ensure the document has at most one banner landmark\",\n      \"help\": \"Document should not have more than one banner landmark\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Ensure the document has at most one contentinfo landmark\",\n      \"help\": \"Document should not have more than one contentinfo landmark\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Ensure the document has at most one main landmark\",\n      \"help\": \"Document should not have more than one main landmark\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Ensure the document has a main landmark\",\n      \"help\": \"Document should have one main landmark\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"Ensure landmarks are unique\",\n      \"help\": \"Landmarks should have a unique role or role/label/title (i.e. accessible name) combination\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Ensure links are distinguished from surrounding text in a way that does not rely on color\",\n      \"help\": \"Links must be distinguishable without relying on color\"\n    },\n    \"link-name\": {\n      \"description\": \"Ensure links have discernible text\",\n      \"help\": \"Links must have discernible text\"\n    },\n    \"list\": {\n      \"description\": \"Ensure that lists are structured correctly\",\n      \"help\": \"<ul> and <ol> must only directly contain <li>, <script> or <template> elements\"\n    },\n    \"listitem\": {\n      \"description\": \"Ensure <li> elements are used semantically\",\n      \"help\": \"<li> elements must be contained in a <ul> or <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Ensure <marquee> elements are not used\",\n      \"help\": \"<marquee> elements are deprecated and must not be used\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Ensure <meta http-equiv=\\\"refresh\\\"> is not used for delayed refresh\",\n      \"help\": \"Delayed refresh must not be used\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Ensure <meta http-equiv=\\\"refresh\\\"> is not used for delayed refresh\",\n      \"help\": \"Delayed refresh under 20 hours must not be used\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Ensure <meta name=\\\"viewport\\\"> can scale a significant amount\",\n      \"help\": \"Users should be able to zoom and scale the text up to 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Ensure <meta name=\\\"viewport\\\"> does not disable text scaling and zooming\",\n      \"help\": \"Zooming and scaling must not be disabled\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Ensure interactive controls are not nested as they are not always announced by screen readers or can cause focus problems for assistive technologies\",\n      \"help\": \"Interactive controls must not be nested\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Ensure <video> or <audio> elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio\",\n      \"help\": \"<video> or <audio> elements must not play automatically\"\n    },\n    \"object-alt\": {\n      \"description\": \"Ensure <object> elements have alternative text\",\n      \"help\": \"<object> elements must have alternative text\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Ensure bold, italic text and font-size is not used to style <p> elements as a heading\",\n      \"help\": \"Styled <p> elements must not be used as headings\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Ensure that the page, or at least one of its frames contains a level-one heading\",\n      \"help\": \"Page should contain a level-one heading\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Ensure elements marked as presentational do not have global ARIA or tabindex so that all screen readers ignore them\",\n      \"help\": \"Elements marked as presentational should be consistently ignored\"\n    },\n    \"region\": {\n      \"description\": \"Ensure all page content is contained by landmarks\",\n      \"help\": \"All page content should be contained by landmarks\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Ensure [role=\\\"img\\\"] elements have alternative text\",\n      \"help\": \"[role=\\\"img\\\"] elements must have alternative text\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Ensure the scope attribute is used correctly on tables\",\n      \"help\": \"scope attribute should be used correctly\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Ensure elements that have scrollable content are accessible by keyboard in Safari\",\n      \"help\": \"Scrollable region must have keyboard access\"\n    },\n    \"select-name\": {\n      \"description\": \"Ensure select element has an accessible name\",\n      \"help\": \"Select element must have an accessible name\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Ensure that server-side image maps are not used\",\n      \"help\": \"Server-side image maps must not be used\"\n    },\n    \"skip-link\": {\n      \"description\": \"Ensure all skip links have a focusable target\",\n      \"help\": \"The skip-link target should exist and be focusable\"\n    },\n    \"summary-name\": {\n      \"description\": \"Ensure summary elements have discernible text\",\n      \"help\": \"Summary elements must have discernible text\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Ensure <svg> elements with an img, graphics-document or graphics-symbol role have accessible text\",\n      \"help\": \"<svg> elements with an img role must have alternative text\"\n    },\n    \"tabindex\": {\n      \"description\": \"Ensure tabindex attribute values are not greater than 0\",\n      \"help\": \"Elements should not have tabindex greater than zero\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Ensure the <caption> element does not contain the same text as the summary attribute\",\n      \"help\": \"Tables should not have the same summary and caption\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Ensure that tables with a caption use the <caption> element.\",\n      \"help\": \"Data or header cells must not be used to give caption to a data table.\"\n    },\n    \"target-size\": {\n      \"description\": \"Ensure touch targets have sufficient size and space\",\n      \"help\": \"All touch targets must be 24px large, or leave sufficient space\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Ensure that each non-empty data cell in a <table> larger than 3 by 3  has one or more table headers\",\n      \"help\": \"Non-empty <td> elements in larger <table> must have an associated table header\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Ensure that each cell in a table that uses the headers attribute refers only to other <th> elements in that table\",\n      \"help\": \"Table cell headers attributes must refer to other <th> elements in the same table\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Ensure that <th> elements and elements with role=columnheader/rowheader have data cells they describe\",\n      \"help\": \"Table headers in a data table must refer to data cells\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Ensure lang attributes have valid values\",\n      \"help\": \"lang attribute must have a valid value\"\n    },\n    \"video-caption\": {\n      \"description\": \"Ensure <video> elements have captions\",\n      \"help\": \"<video> elements must have captions\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Abstract roles are not used\",\n      \"fail\": {\n        \"singular\": \"Abstract role cannot be directly used: ${data.values}\",\n        \"plural\": \"Abstract roles cannot be directly used: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA attributes are used correctly for the defined role\",\n      \"fail\": {\n        \"singular\": \"ARIA attribute is not allowed: ${data.values}\",\n        \"plural\": \"ARIA attributes are not allowed: ${data.values}\"\n      },\n      \"incomplete\": \"Check that there is no problem if the ARIA attribute is ignored on this element: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA role is allowed for given element\",\n      \"fail\": {\n        \"singular\": \"ARIA role ${data.values} is not allowed for given element\",\n        \"plural\": \"ARIA roles ${data.values} are not allowed for given element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA role ${data.values} must be removed when the element is made visible, as it is not allowed for the element\",\n        \"plural\": \"ARIA roles ${data.values} must be removed when the element is made visible, as they are not allowed for the element\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"Element has an aria-busy attribute\",\n      \"fail\": \"Element uses aria-busy=\\\"true\\\" while showing a loader\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"ARIA attribute is allowed\",\n      \"fail\": {\n        \"checkbox\": \"Remove aria-checked, or set it to \\\"${data.checkState}\\\" to match the real checkbox state\",\n        \"rowSingular\": \"This attribute is supported with treegrid rows, but not ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"These attributes are supported with treegrid rows, but not ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage exists and references elements visible to screen readers that use a supported aria-errormessage technique\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage value `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\",\n        \"plural\": \"aria-errormessage values `${data.values}` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\",\n        \"unsupported\": \"Multiple IDs in aria-errormessage is not widely supported in assistive technologies\",\n        \"hidden\": \"aria-errormessage value `${data.values}` cannot reference a hidden element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Ensure aria-errormessage value `${data.values}` references an existing element\",\n        \"plural\": \"Ensure aria-errormessage values `${data.values}` reference existing elements\",\n        \"idrefs\": \"Unable to determine if aria-errormessage element exists on the page: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"No aria-hidden attribute is present on document body\",\n      \"fail\": \"aria-hidden=true should not be present on the document body\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria-level values are valid\",\n      \"incomplete\": \"aria-level values greater than 6 are not supported in all screenreader and browser combinations\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"ARIA attribute is allowed\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} attributes cannot be used with role \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"${data.prohibited} attribute cannot be used with role \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"${data.prohibited} attributes cannot be used on a ${data.nodeName} with no valid role attribute.\",\n        \"noRoleSingular\": \"${data.prohibited} attribute cannot be used on a ${data.nodeName} with no valid role attribute.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"${data.prohibited} attribute is not well supported with role \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"${data.prohibited} attributes are not well supported with role \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"${data.prohibited} attribute is not well supported on a ${data.nodeName} with no valid role attribute.\",\n        \"noRolePlural\": \"${data.prohibited} attributes are not well supported on a ${data.nodeName} with no valid role attribute.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"All required ARIA attributes are present\",\n      \"fail\": {\n        \"singular\": \"Required ARIA attribute not present: ${data.values}\",\n        \"plural\": \"Required ARIA attributes not present: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Required ARIA children are present\",\n        \"aria-busy\": \"Element has an aria-busy attribute, so it is allowed to omit required children\"\n      },\n      \"fail\": {\n        \"singular\": \"Required ARIA child role not present: ${data.values}\",\n        \"plural\": \"Required ARIA children role not present: ${data.values}\",\n        \"unallowed\": \"Element has children which are not allowed: ${data.values}\",\n        \"aria-busy-fail\": \"Element has children which are not allowed: ${data.values}; Having aria-busy=\\\"true\\\" does not allow children with roles that are not allowed\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Expecting ARIA child role to be added: ${data.values}\",\n        \"plural\": \"Expecting ARIA children role to be added: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Required ARIA parent role present\",\n      \"fail\": {\n        \"singular\": \"Required ARIA parent role not present: ${data.values}\",\n        \"plural\": \"Required ARIA parents role not present: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription used on a supported semantic role\",\n      \"incomplete\": \"Check that the aria-roledescription is announced by supported screen readers\",\n      \"fail\": \"Give the element a role that supports aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA attribute is supported\",\n      \"fail\": \"ARIA attribute is not widely supported in screen readers and assistive technologies: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA attribute values are valid\",\n      \"fail\": {\n        \"singular\": \"Invalid ARIA attribute value: ${data.values}\",\n        \"plural\": \"Invalid ARIA attribute values: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"ARIA attribute element ID does not exist on the page: ${data.needsReview}\",\n        \"noIdShadow\": \"ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA attribute value is invalid and will be treated as \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}\",\n        \"empty\": \"ARIA attribute value is ignored while empty: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"ARIA attribute name is valid\",\n      \"fail\": {\n        \"singular\": \"Invalid ARIA attribute name: ${data.values}\",\n        \"plural\": \"Invalid ARIA attribute names: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabel is used on an element with accessible text\",\n      \"fail\": \"aria-braillelabel is used on an element with no accessible text\",\n      \"incomplete\": \"Unable to compute accessible text\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescription is used on an element with aria-roledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription is used on an element with no aria-roledescription\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription is used on an element with an empty aria-roledescription\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA role is not deprecated\",\n      \"fail\": \"The role used is deprecated: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Only one role value used\",\n      \"fail\": \"Use only one role value, since fallback roles are not supported in older browsers\",\n      \"incomplete\": \"Use only role 'presentation' or 'none' since they are synonymous.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"Element has global ARIA attribute: ${data.values}\",\n        \"plural\": \"Element has global ARIA attributes: ${data.values}\"\n      },\n      \"fail\": \"Element does not have global ARIA attribute\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Element has a widget role.\",\n      \"fail\": \"Element does not have a widget role.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA role is valid\",\n      \"fail\": {\n        \"singular\": \"Role must be one of the valid ARIA roles: ${data.values}\",\n        \"plural\": \"Roles must be one of the valid ARIA roles: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"Element is focusable.\",\n      \"fail\": \"Element is not focusable.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"There is no mismatch between a <label> and accessible name\",\n      \"incomplete\": \"Check that the <label> does not need be part of the ARIA ${data} field's name\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA role is supported\",\n      \"fail\": \"The role used is not widely supported in screen readers and assistive technologies: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Element has valid semantics for an element in the focus order.\",\n      \"fail\": \"Element has invalid semantics for an element in the focus order.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Element has sufficient color contrast of ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"Element has insufficient color contrast of ${data.contrastRatio} (foreground color: ${data.fgColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the foreground and shadow color (foreground color: ${data.fgColor}, text-shadow color: ${data.shadowColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the shadow color and background color (text-shadow color: ${data.shadowColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Unable to determine contrast ratio\",\n        \"bgImage\": \"Element's background color could not be determined due to a background image\",\n        \"bgGradient\": \"Element's background color could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's background color could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's background color could not be determined because it is overlapped by another element\",\n        \"fgAlpha\": \"Element's foreground color could not be determined because of alpha transparency\",\n        \"elmPartiallyObscured\": \"Element's background color could not be determined because it's partially obscured by another element\",\n        \"elmPartiallyObscuring\": \"Element's background color could not be determined because it partially overlaps other elements\",\n        \"outsideViewport\": \"Element's background color could not be determined because it's outside the viewport\",\n        \"equalRatio\": \"Element has a 1:1 contrast ratio with the background\",\n        \"shortTextContent\": \"Element content is too short to determine if it is actual text content\",\n        \"nonBmp\": \"Element content contains only non-text characters\",\n        \"pseudoContent\": \"Element's background color could not be determined due to a pseudo element\",\n        \"colorParse\": \"Could not parse color string ${data.colorParse}\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"Element has sufficient color contrast of ${data.contrastRatio}\",\n        \"hidden\": \"Element is hidden\"\n      },\n      \"fail\": {\n        \"default\": \"Element has insufficient color contrast of ${data.contrastRatio} (foreground color: ${data.fgColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the foreground and shadow color (foreground color: ${data.fgColor}, text-shadow color: ${data.shadowColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element has insufficient color contrast of ${data.contrastRatio} between the shadow color and background color (text-shadow color: ${data.shadowColor}, background color: ${data.bgColor}, font size: ${data.fontSize}, font weight: ${data.fontWeight}). Expected contrast ratio of ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Unable to determine contrast ratio\",\n        \"bgImage\": \"Element's background color could not be determined due to a background image\",\n        \"bgGradient\": \"Element's background color could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's background color could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's background color could not be determined because it is overlapped by another element\",\n        \"complexTextShadows\": \"Element's contrast could not be determined because it uses complex text shadows\",\n        \"fgAlpha\": \"Element's foreground color could not be determined because of alpha transparency\",\n        \"elmPartiallyObscured\": \"Element's background color could not be determined because it's partially obscured by another element\",\n        \"elmPartiallyObscuring\": \"Element's background color could not be determined because it partially overlaps other elements\",\n        \"outsideViewport\": \"Element's background color could not be determined because it's outside the viewport\",\n        \"equalRatio\": \"Element has a 1:1 contrast ratio with the background\",\n        \"shortTextContent\": \"Element content is too short to determine if it is actual text content\",\n        \"nonBmp\": \"Element content contains only non-text characters\",\n        \"pseudoContent\": \"Element's background color could not be determined due to a pseudo element\",\n        \"colorParse\": \"Could not parse color string ${data.colorParse}\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Links can be distinguished from surrounding text by visual styling\",\n      \"incomplete\": {\n        \"default\": \"Check if the link needs styling to distinguish it from nearby text\",\n        \"pseudoContent\": \"Check if the link's pseudo style is sufficient to distinguish it from the surrounding text\"\n      },\n      \"fail\": \"The link has no styling (such as underline) to distinguish it from the surrounding text\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Links can be distinguished from surrounding text in some way other than by color\",\n      \"fail\": {\n        \"fgContrast\": \"The link has insufficient color contrast of ${data.contrastRatio}:1 with the surrounding text. (Minimum contrast is ${data.requiredContrastRatio}:1, link text: ${data.nodeColor}, surrounding text: ${data.parentColor})\",\n        \"bgContrast\": \"The link background has insufficient color contrast of ${data.contrastRatio} (Minimum contrast is ${data.requiredContrastRatio}:1, link background color: ${data.nodeBackgroundColor}, surrounding background color: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element's foreground contrast ratio could not be determined\",\n        \"bgContrast\": \"Element's background contrast ratio could not be determined\",\n        \"bgImage\": \"Element's contrast ratio could not be determined due to a background image\",\n        \"bgGradient\": \"Element's contrast ratio could not be determined due to a background gradient\",\n        \"imgNode\": \"Element's contrast ratio could not be determined because element contains an image node\",\n        \"bgOverlap\": \"Element's contrast ratio could not be determined because of element overlap\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"The autocomplete value is on an appropriate element\",\n      \"fail\": \"The autocomplete value is inappropriate for this type of input\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"the autocomplete attribute is correctly formatted\",\n      \"fail\": \"the autocomplete attribute is incorrectly formatted\",\n      \"incomplete\": \"the autocomplete attribute has a non-standard value. Check whether any standard value could be used instead.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Accesskey attribute value is unique\",\n      \"fail\": \"Document has multiple elements with the same accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Element contains focusable elements\",\n      \"fail\": \"Element should have focusable content\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"No focusable elements contained within element\",\n      \"incomplete\": \"Check if the focusable elements immediately move the focus indicator\",\n      \"fail\": \"Focusable content should be disabled or be removed from the DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Element is focusable\",\n      \"fail\": \"Element should be focusable\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"No focusable elements while a modal is open\",\n      \"incomplete\": \"Check that focusable elements are not tabbable in the current state\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Element is not in tab order or has accessible text\",\n      \"fail\": \"Element is in tab order and does not have accessible text\",\n      \"incomplete\": \"Unable to determine if element has an accessible name\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"No focusable elements contained within element\",\n      \"incomplete\": \"Check if the focusable elements immediately move the focus indicator\",\n      \"fail\": \"Focusable content should have tabindex=\\\"-1\\\" or be removed from the DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"Element does not have focusable descendants\",\n      \"fail\": \"Element has focusable descendants\",\n      \"incomplete\": \"Could not determine if element has descendants\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"The ${data.role} landmark is at the top level.\",\n      \"fail\": \"The ${data.role} landmark is contained in another landmark.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"Element does not have focusable descendants\",\n      \"fail\": {\n        \"default\": \"Element has focusable descendants\",\n        \"notHidden\": \"Using a negative tabindex on an element inside an interactive control does not prevent assistive technologies from focusing the element (even with aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Could not determine if element has descendants\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Page has at least one level-one heading\",\n      \"fail\": \"Page must have a level-one heading\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Document has at least one main landmark\",\n      \"fail\": \"Document does not have a main landmark\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Document does not have more than one banner landmark\",\n      \"fail\": \"Document has more than one banner landmark\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Document does not have more than one contentinfo landmark\",\n      \"fail\": \"Document has more than one contentinfo landmark\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Document does not have more than one main landmark\",\n      \"fail\": \"Document has more than one main landmark\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Element does not have a tabindex greater than 0\",\n      \"fail\": \"Element has a tabindex greater than 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Element has a valid alt attribute value\",\n      \"fail\": \"Element has an alt attribute containing only a space character, which is not ignored by all screen readers\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Element does not duplicate existing text in <img> alt text\",\n      \"fail\": \"Element contains <img> element with alt text that duplicates existing text\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Element has an explicit <label>\",\n      \"fail\": \"Element does not have an explicit <label>\",\n      \"incomplete\": \"Unable to determine if form element has an explicit <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Help text (title or aria-describedby) does not duplicate label text\",\n      \"fail\": \"Help text (title or aria-describedby) text is the same as the label text\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Form element has a visible explicit <label>\",\n      \"fail\": \"Form element has explicit <label> that is hidden\",\n      \"incomplete\": \"Unable to determine if form element has explicit <label> that is hidden\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Element has an implicit (wrapped) <label>\",\n      \"fail\": \"Element does not have an implicit (wrapped) <label>\",\n      \"incomplete\": \"Unable to determine if form element has an implicit (wrapped) <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Element contains visible text as part of it's accessible name\",\n      \"fail\": \"Text inside the element is not included in the accessible name\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Form field does not have multiple label elements\",\n      \"incomplete\": \"Multiple label elements is not widely supported in assistive technologies. Ensure the first label contains all necessary information.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Form element does not solely use title attribute for its label\",\n      \"fail\": \"Only title used to generate label for form element\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Landmarks must have a unique role or role/label/title (i.e. accessible name) combination\",\n      \"fail\": \"The landmark must have a unique aria-label, aria-labelledby, or title to make landmarks distinguishable\"\n    },\n    \"has-lang\": {\n      \"pass\": \"The <html> element has a lang attribute\",\n      \"fail\": {\n        \"noXHTML\": \"The xml:lang attribute is not valid on HTML pages, use the lang attribute.\",\n        \"noLang\": \"The <html> element does not have a lang attribute\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Value of lang attribute is included in the list of valid languages\",\n      \"fail\": \"Value of lang attribute not included in the list of valid languages\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Lang and xml:lang attributes have the same base language\",\n      \"fail\": \"Lang and xml:lang attributes do not have the same base language\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Description list item has a <dl> parent element\",\n      \"fail\": \"Description list item does not have a <dl> parent element\"\n    },\n    \"listitem\": {\n      \"pass\": \"List item has a <ul>, <ol> or role=\\\"list\\\" parent element\",\n      \"fail\": {\n        \"default\": \"List item does not have a <ul>, <ol> parent element\",\n        \"roleNotValid\": \"List item parent element has a role that is not role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"dl element only has direct children that are allowed inside; <dt>, <dd>, or <div> elements\",\n      \"fail\": \"dl element has direct children that are not allowed: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"List element only has direct children that are allowed inside <li> elements\",\n      \"fail\": \"List element has direct children that are not allowed: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"When not empty, element has both <dt> and <dd> elements\",\n      \"fail\": \"When not empty, element does not have at least one <dt> element followed by at least one <dd> element\"\n    },\n    \"caption\": {\n      \"pass\": \"The multimedia element has a captions track\",\n      \"incomplete\": \"Check that captions are available for the element\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"The iframe was tested with axe-core\",\n      \"fail\": \"The iframe could not be tested with axe-core\",\n      \"incomplete\": \"The iframe still has to be tested with axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> or <audio> does not output audio for more than allowed duration or has controls mechanism\",\n      \"fail\": \"<video> or <audio> outputs audio for more than allowed duration and does not have a controls mechanism\",\n      \"incomplete\": \"Check that the <video> or <audio> does not output audio for more than allowed duration or provides a controls mechanism\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Display is operable, and orientation lock does not exist\",\n      \"fail\": \"CSS Orientation lock is applied, and makes display inoperable\",\n      \"incomplete\": \"CSS Orientation lock cannot be determined\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta> tag does not prevent significant zooming on mobile devices\",\n      \"fail\": \"<meta> tag limits zooming on mobile devices\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta> tag does not disable zooming on mobile devices\",\n      \"fail\": \"${data} on <meta> tag disables zooming on mobile devices\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"Target has sufficient space from its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px which is at least ${data.minOffset}px.\",\n        \"large\": \"Target far exceeds the minimum size of ${data.minOffset}px.\"\n      },\n      \"fail\": \"Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is this a target?\",\n        \"nonTabbableNeighbor\": \"Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of ${data.closestOffset}px instead of at least ${data.minOffset}px. Is the neighbor a target?\",\n        \"tooManyRects\": \"Could not get the target size because there are too many overlapping elements\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Control has sufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\",\n        \"obscured\": \"Control is ignored because it is fully obscured and thus not clickable\",\n        \"large\": \"Target far exceeds the minimum size of ${data.minSize}px.\"\n      },\n      \"fail\": {\n        \"default\": \"Target has insufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\",\n        \"partiallyObscured\": \"Target has insufficient size because it is partially obscured (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element with negative tabindex has insufficient size (${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is this a target?\",\n        \"contentOverflow\": \"Element size could not be accurately determined due to overflow content\",\n        \"partiallyObscured\": \"Element with negative tabindex has insufficient size because it is partially obscured (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is this a target?\",\n        \"partiallyObscuredNonTabbable\": \"Target has insufficient size because it is partially obscured by a neighbor with negative tabindex (smallest space is ${data.width}px by ${data.height}px, should be at least ${data.minSize}px by ${data.minSize}px). Is the neighbor a target?\",\n        \"tooManyRects\": \"Could not get the target size because there are too many overlapping elements\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"Page has a heading\",\n      \"fail\": \"Page does not have a heading\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Heading order valid\",\n      \"fail\": \"Heading order invalid\",\n      \"incomplete\": \"Unable to determine previous heading\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"There are no other links with the same name, that go to a different URL\",\n      \"incomplete\": \"Check that links have the same purpose, or are intentionally ambiguous.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Valid skip link found\",\n      \"fail\": \"No valid skip link found\"\n    },\n    \"landmark\": {\n      \"pass\": \"Page has a landmark region\",\n      \"fail\": \"Page does not have a landmark region\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"<meta> tag does not immediately refresh the page\",\n      \"fail\": \"<meta> tag forces timed refresh of page\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta> tag does not immediately refresh the page\",\n      \"fail\": \"<meta> tag forces timed refresh of page (less than 20 hours)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> elements are not styled as headings\",\n      \"fail\": \"Heading elements should be used instead of styled <p> elements\",\n      \"incomplete\": \"Unable to determine if <p> elements are styled as headings\"\n    },\n    \"region\": {\n      \"pass\": \"All page content is contained by landmarks\",\n      \"fail\": \"Some page content is not contained by landmarks\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Skip link target exists\",\n      \"incomplete\": \"Skip link target should become visible on activation\",\n      \"fail\": \"No skip link target\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Element's title attribute is unique\",\n      \"fail\": \"Element's title attribute is not unique\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Document has no active elements that share the same id attribute\",\n      \"fail\": \"Document has active elements with the same id attribute: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Document has no elements referenced with ARIA or labels that share the same id attribute\",\n      \"fail\": \"Document has multiple elements referenced with ARIA with the same id attribute: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Document has no static elements that share the same id attribute\",\n      \"fail\": \"Document has multiple static elements with the same id attribute: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label attribute exists and is not empty\",\n      \"fail\": \"aria-label attribute does not exist or is empty\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby attribute exists and references elements that are visible to screen readers\",\n      \"fail\": \"aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty\",\n      \"incomplete\": \"Ensure aria-labelledby references an existing element\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"No inline styles with '!important' that affect text spacing has been specified\",\n      \"fail\": {\n        \"singular\": \"Remove '!important' from inline style ${data.values}, as overriding this is not supported by most browsers\",\n        \"plural\": \"Remove '!important' from inline styles ${data.values}, as overriding this is not supported by most browsers\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Element has inner text that is visible to screen readers\",\n      \"fail\": \"Element does not have inner text that is visible to screen readers\",\n      \"incomplete\": \"Unable to determine if element has children\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Document has a non-empty <title> element\",\n      \"fail\": \"Document does not have a non-empty <title> element\"\n    },\n    \"error-occurred\": {\n      \"pass\": \"\",\n      \"incomplete\": \"Axe encountered an error; test the page for this type of problem manually\"\n    },\n    \"exists\": {\n      \"pass\": \"Element does not exist\",\n      \"incomplete\": \"Element exists\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Element has an alt attribute\",\n      \"fail\": \"Element does not have an alt attribute\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Element has text that is visible to screen readers\",\n      \"fail\": \"Element does not have text that is visible to screen readers\",\n      \"incomplete\": \"Unable to determine if element has children\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"Letter-spacing in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"letter-spacing in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"line-height in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"line-height in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"word-spacing in the style attribute is not set to !important, or meets the minimum\",\n      \"fail\": \"word-spacing in the style attribute must not use !important, or be at ${data.minValue}em (current ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Element is not visible\",\n      \"fail\": \"Element is visible\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Element has a non-empty alt attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no alt attribute\",\n        \"emptyAttr\": \"Element has an empty alt attribute\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Element does not have a value attribute\",\n        \"has-label\": \"Element has a non-empty value attribute\"\n      },\n      \"fail\": \"Element has a value attribute and the value attribute is empty\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"Element has a placeholder attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no placeholder attribute\",\n        \"emptyAttr\": \"Element has an empty placeholder attribute\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Element has a title attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no title attribute\",\n        \"emptyAttr\": \"Element has an empty title attribute\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Element has a non-empty value attribute\",\n      \"fail\": {\n        \"noAttr\": \"Element has no value attribute\",\n        \"emptyAttr\": \"Element has an empty value attribute\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"Element's default semantics were not overridden with role=\\\"none\\\" or role=\\\"presentation\\\"\",\n        \"globalAria\": \"Element's role is not presentational because it has a global ARIA attribute\",\n        \"focusable\": \"Element's role is not presentational because it is focusable\",\n        \"both\": \"Element's role is not presentational because it has a global ARIA attribute and is focusable\",\n        \"iframe\": \"Using the \\\"title\\\" attribute on an ${data.nodeName} element with a presentational role behaves inconsistently between screen readers\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"none\\\"\",\n      \"fail\": \"Element's default semantics were not overridden with role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Element's default semantics were overridden with role=\\\"presentation\\\"\",\n      \"fail\": \"Element's default semantics were not overridden with role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"Element has a child that is a title\",\n      \"fail\": {\n        \"noTitle\": \"Element has no child that is a title\",\n        \"emptyTitle\": \"Element child title is empty\"\n      },\n      \"incomplete\": \"Unable to determine element has a child that is a title\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"The first row of a table is not used as a caption\",\n      \"fail\": \"The first child of the table should be a caption instead of a table cell\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Scope attribute is only used on table header elements (<th>)\",\n      \"fail\": \"In HTML 5, scope attributes may only be used on table header elements (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Content of summary attribute and <caption> are not duplicated\",\n      \"fail\": \"Content of summary attribute and <caption> element are identical\",\n      \"incomplete\": \"Unable to determine if <table> element has a caption\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Scope attribute is used correctly\",\n      \"fail\": \"The value of the scope attribute may only be 'row' or 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"All non-empty data cells have table headers\",\n      \"fail\": \"Some non-empty data cells do not have table headers\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"The headers attribute is exclusively used to refer to other header cells in the table\",\n      \"incomplete\": \"The headers attribute is empty\",\n      \"fail\": {\n        \"cell-header-not-in-table\": \"The headers attribute is not exclusively used to refer to other header cells in the table\",\n        \"cell-header-not-th\": \"The headers attribute must refer to header cells, not data cells\",\n        \"header-refs-self\": \"The element with headers attribute refers to itself\"\n      }\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"All table header cells refer to data cells\",\n      \"fail\": \"Not all table header cells refer to data cells\",\n      \"incomplete\": \"Table data cells are missing or empty\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"All content on the page has been analyzed.\",\n      \"fail\": \"There were problems analyzing the content on this page.\",\n      \"incomplete\": \"There is hidden content on the page that was not analyzed. You will need to trigger the display of this content in order to analyze it.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Fix any of the following:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Fix all of the following:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe couldn't tell the reason. Time to break out the element inspector!\"\n}\n"
  },
  {
    "path": "locales/da.json",
    "content": "{\n  \"lang\": \"da\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"\",\n      \"help\": \"Værdien for attributten 'accesskey' skal være unik\"\n    },\n    \"area-alt\": {\n      \"description\": \"\",\n      \"help\": \"Aktive <area> elementer skal have alternativ tekst\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"\",\n      \"help\": \"Elementer må kun bruge tilladte ARIA-attributter\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributten 'role' skal være passende for elementet\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"\",\n      \"help\": \"aria-hidden='true' må ikke bruges på dokumentets <body> element\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"\",\n      \"help\": \"Elementer med ARIA-'hidden' må ikke indeholde fokuserbare elementer\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-input-felter skal have et tilgængeligt navn\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"\",\n      \"help\": \"Krævede ARIA-attributter skal være angivet\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"\",\n      \"help\": \"Bestemte ARIA-roller skal indeholde specifikke under-elementer\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"\",\n      \"help\": \"Bestemte ARIA-roller skal være under-element til specifikke over-elementer\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"\",\n      \"help\": \"Brug 'aria-roledescription' på elementer med en semantisk rolle\"\n    },\n    \"aria-roles\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-roller skal have en korrekt værdi\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"\",\n      \"help\": \"ARIA afkrydsningsfelter skal have et tilgængeligt navn\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributter skal have en korrekt værdi\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributter skal have et korrekt navn\"\n    },\n    \"audio-caption\": {\n      \"description\": \"\",\n      \"help\": \"<audio> elementer skal have en transskription ('captions track')\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"\",\n      \"help\": \"Attributten 'autocomplete' skal benyttes korrekt\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"\",\n      \"help\": \"Inline tekst-afstande skal være justerbare med brugerdefinerede stylesheets\"\n    },\n    \"blink\": {\n      \"description\": \"\",\n      \"help\": \"Elementet <blink> er udfaset og må ikke bruges\"\n    },\n    \"button-name\": {\n      \"description\": \"\",\n      \"help\": \"Knapper skal have forståelig (dvs. detekterbar) tekst\"\n    },\n    \"bypass\": {\n      \"description\": \"\",\n      \"help\": \"Sider skal have en metode til at springe over gentaget indhold\"\n    },\n    \"color-contrast\": {\n      \"description\": \"\",\n      \"help\": \"Elementer skal have tilstrækkelig farvekontrast\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"\",\n      \"help\": \"Elementer skal have tilstrækkelig farvekontrast\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"\",\n      \"help\": \"'CSS Media queries' bør ikke bruges til at låse skærmretningen ('orientation')\"\n    },\n    \"definition-list\": {\n      \"description\": \"\",\n      \"help\": \"<dl> elementer må kun direkte indeholde velsorterede <dt> og <dd> grupper, <script> eller <template> elementer\"\n    },\n    \"dlitem\": {\n      \"description\": \"\",\n      \"help\": \"<dt> og <dd> elementer skal være under-element til et <dl> element\"\n    },\n    \"document-title\": {\n      \"description\": \"\",\n      \"help\": \"Dokumenter skal have et <title> element for at bidrage til nemmere navigation\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"\",\n      \"help\": \"'id'-attributten for aktive elementer skal være unik\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"\",\n      \"help\": \"'id'-attributten brugt på ARIA-elementer og -labels skal være unik\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"\",\n      \"help\": \"Værdien for 'id'-attributen skal være unik\"\n    },\n    \"empty-heading\": {\n      \"description\": \"\",\n      \"help\": \"Overskrifter må ikke være tomme\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"\",\n      \"help\": \"Elementer i fokus-rækkefølgen bør have en 'role'-attribut, som er passende for det interaktive indhold\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"\",\n      \"help\": \"Form-feltet bør ikke have flere label-elementer\"\n    },\n    \"frame-tested\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal være testet med axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal have en unik 'title'-attribut\"\n    },\n    \"frame-title\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal have 'title'-attribut\"\n    },\n    \"heading-order\": {\n      \"description\": \"\",\n      \"help\": \"Overskriftsniveauer bør kun ændres sekventielt\"\n    },\n    \"hidden-content\": {\n      \"description\": \"\",\n      \"help\": \"Skjult indhold på siden kan ikke analyseres\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementet skal have en 'lang'-attribut\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementet skal have en korrekt værdi for 'lang'-attributten\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementer med 'lang' og 'xml:lang' skal have det samme basesprog\"\n    },\n    \"image-alt\": {\n      \"description\": \"\",\n      \"help\": \"Billeder skal have en alternativ tekst\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"\",\n      \"help\": \"Alternativ tekst til billeder (alt-tekst) bør ikke gentages som brødtekst\"\n    },\n    \"input-button-name\": {\n      \"description\": \"\",\n      \"help\": \"Input-knapper skal have en forståelig tekst\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"\",\n      \"help\": \"Billed-knapper skal have en alternativ tekst\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"\",\n      \"help\": \"Elementers synlige tekst skal være del af deres tilgængelige navn\"\n    },\n    \"label-title-only\": {\n      \"description\": \"\",\n      \"help\": \"Form-elementer bør have en synlig label\"\n    },\n    \"label\": {\n      \"description\": \"\",\n      \"help\": \"Form-elementer skal have labels\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'banner'-landmark må ikke være indeholdt i et andet landmark\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'aside'- eller 'complimentary'-landmark må ikke være indeholdt i et andet landmark\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'contentinfo'-landmark må ikke være indeholdt i et andet landmark\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'main'-landmark må ikke være indeholdt i et andet landmark\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet må ikke have mere end ét 'banner'-landmark\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet må ikke have mere end ét 'contentinfo'-landmark\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet skal have ét 'main'-landmark\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"\",\n      \"help\": \"Sikrer at landmarks er unikke\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"\",\n      \"help\": \"Links skal være fremtrædende fra den omkringliggende tekst på en måde, som ikke afhænger af farve\"\n    },\n    \"link-name\": {\n      \"description\": \"\",\n      \"help\": \"Links skal have forståelig (detekterbar) tekst\"\n    },\n    \"list\": {\n      \"description\": \"\",\n      \"help\": \"<ul> og <ol> må kun direkte indeholde <li>, <script> eller <template> elementer\"\n    },\n    \"listitem\": {\n      \"description\": \"\",\n      \"help\": \"<li> elementer skal være indeholdt i et <ul> eller <ol> element\"\n    },\n    \"marquee\": {\n      \"description\": \"\",\n      \"help\": \"<marquee> elementer er udfaset og må ikke bruges\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"\",\n      \"help\": \"Tidsindstillet 'refresh' må ikke bruges\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"\",\n      \"help\": \"Brugere bør kunne zoome og skalere tekst op til 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"\",\n      \"help\": \"Zoom og skalering må ikke være slået fra\"\n    },\n    \"object-alt\": {\n      \"description\": \"\",\n      \"help\": \"<object> elementer skal have en alternativ tekst\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"\",\n      \"help\": \"Fremhævelse med fed, kursiv og skriftstørrelse (font-size) må ikke bruges til at style <p> elementer som en overskrift\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"\",\n      \"help\": \"Siden skal indeholde en overskrift på øverste niveau\"\n    },\n    \"region\": {\n      \"description\": \"\",\n      \"help\": \"Alt indhold på siden skal være indeholdt i landmarks\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"\",\n      \"help\": \"Elementer med 'role' attributværdien 'img' skal have en alternativ tekst\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"\",\n      \"help\": \"'scope'-attributten skal bruges korrekt i tabeller\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"\",\n      \"help\": \"Sørg for, at en scrollbar region er tilgængeligt via keyboard\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"\",\n      \"help\": \"Såkaldte 'server-side image-maps' må ikke bruges\"\n    },\n    \"skip-link\": {\n      \"description\": \"\",\n      \"help\": \"Et 'skip-link' skal pege på et eksisterende og fokuserbart element\"\n    },\n    \"tabindex\": {\n      \"description\": \"\",\n      \"help\": \"Elementer bør ikke have et 'tabindex' højere end 0\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"\",\n      \"help\": \"Elementet <caption> bør ikke indeholde samme tekst som 'summary'-attributten\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"\",\n      \"help\": \"Data- eller overskrifts-celler bør ikke bruges til at beskrive indholdet af en data-tabel\"\n    },\n    \"td-has-header\": {\n      \"description\": \"\",\n      \"help\": \"Alle ikke-tomme <td> elementer i en tabel større end 3x3 bør have en tabeloverskrift (<th>)\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"\",\n      \"help\": \"Alle celler i en tabel, som  bruger 'header'-attributten må kun referere til andre celler i samme tabel\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"\",\n      \"help\": \"Alle <th> elementer og elementer med 'role=columnheader/rowheader' skal referere til de data-celler, som de beskriver\"\n    },\n    \"valid-lang\": {\n      \"description\": \"\",\n      \"help\": \"'lang'-attributten skal have en korrekt værdi\"\n    },\n    \"video-caption\": {\n      \"description\": \"\",\n      \"help\": \"<video> elementer skal have undertekster ('captions')\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Abstrakte roller er ikke brugt\",\n      \"fail\": \"Abstrakte roller bør ikke bruges\"\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA-attribut er brugt korrekt for den angivede rolle\",\n      \"fail\": {\n        \"singular\": \"ARIA-attributterne er ikke tilladt: ${data.values}\",\n        \"plural\": \"ARIA-attribut er ikke tilladt: ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA-rollen er tilladt for det givne element\",\n      \"fail\": {\n        \"singular\": \"ARIA-rollerne ${data.values} er ikke tilladt for det givne element\",\n        \"plural\": \"ARIA-rollen ${data.values} er ikke tilladt for det givne element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA-rollerne ${data.values} skal være fjernet, når elementet er synligt, da de ikke er tilladt for elementet\",\n        \"plural\": \"ARIA-rollen ${data.values} skal være fjernet, når elementet er synligt, da det ikke er tilladt for elementet\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Ingen 'aria-hidden'-attribut er at finde i dokumentets <body> element\",\n      \"fail\": \"'aria-hidden=true' bør ikke bruges på dokumentets <body> element\"\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"'aria-roledescription' bruges på en understøttet semantisk rolle\",\n      \"incomplete\": \"Tjek at 'aria-roledescription' bliver læst op af understøttet skærmlæser\",\n      \"fail\": \"Giv elementet en rolle, som understøtter 'aria-roledescription'\"\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Bruger en understøttet 'aria-errormessage'-teknik\",\n      \"fail\": {\n        \"singular\": \"'aria-errormessage'-værdier ${data.values}` bør bruge en teknik til at annoncere beskeden (fx 'aria-live', 'aria-describedby', 'role=alert', osv.)\",\n        \"plural\": \"'aria-errormessage'-værdi ${data.values}` bør bruge en teknik til at annoncere beskeden (fx 'aria-live', 'aria-describedby', 'role=alert', osv.)\"\n      }\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Elementet har en 'widget'-rolle.\",\n      \"fail\": \"Elementet har ikke en 'widget'-rolle.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA-rollen er korrekt\",\n      \"fail\": \"Rollen skal være en af de mulige ARIA-roller\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Der er uoverensstemmelse mellem <label> og det tilgængelige navn\",\n      \"incomplete\": \"Tjek at <label> elementet ikke behøver være en del af ${data}-feltets navn\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Alle krævede ARIA-attributter er tilstede\",\n      \"fail\": {\n        \"singular\": \"Krævet ARIA-attributter er ikke til stede: ${data.values}\",\n        \"plural\": \"Krævet ARIA-attribut er ikke til stede: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Krævet ARIA-under-elementer er til stede\"\n      },\n      \"fail\": {\n        \"singular\": \"Krævet ARIA-under-elementers rolle er ikke til stede: ${data.values}\",\n        \"plural\": \"Krævet ARIA-under-elements rolle er ikke til stede: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Forventer at ARIA under-elementers rolle bliver tilføjet: ${data.values}\",\n        \"plural\": \"Forventer at ARIA under-elements rolle bliver tilføjet: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Krævet ARIA-over-elements rolle er til stede\",\n      \"fail\": {\n        \"singular\": \"Krævet ARIA-over-elementers rolle er ikke til stede: ${data.values}\",\n        \"plural\": \"Krævet ARIA-over-elements rolle er ikke til stede: ${data.values}\"\n      }\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA-attribut er understøttet\",\n      \"fail\": \"ARIA-attribut er ikke bredt understøttet i skærmlæsere og tilgængelighedsteknologier:  ${data.values}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA rollen er understøttet\",\n      \"fail\": \"Den brugte rolle er ikke bredt understøttet i skærmlæsere og tilgængelighedsteknologier:  ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA-attributværdien er valid\",\n      \"fail\": {\n        \"singular\": \"Ikke-korrekt ARIA-attributværdier: ${data.values}\",\n        \"plural\": \"Ikke-korrekt ARIA-attributværdi: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA-attributternes element 'id' er ikke at finde på siden: ${data.values}\",\n        \"plural\": \"ARIA-attributtens element 'id' er ikke at finde på siden: ${data.values}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"ARIA-attributnavnene er korrekt\",\n        \"plural\": \"ARIA-attributnavnet er korrekt\"\n      },\n      \"fail\": {\n        \"singular\": \"Ikke-korrekt ARIA-attributnavne: ${data.values}\",\n        \"plural\": \"Ikke-korrekt ARIA-attributnavn: ${data.values}\"\n      }\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Elementet har korrekt semantik for et element i fokus-rækkefølgen.\",\n      \"fail\": \"Elementet har ikke korrekt semantik for et element i fokus-rækkefølgen.\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"Elementet har stor farvekontrast, den er ${data.contrastRatio}\",\n      \"fail\": \"Elementet har ikke nok farvekontrast, den er ${data.contrastRatio} (forgrundsfarve: ${data.fgColor}, baggrundsfarve: ${data.bgColor}, tekststørrelse: ${data.fontSize}, teksttykkelse: ${data.fontWeight}). Forventet kontrastforhold er ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementets baggrundsfarve kunne ikke detekteres på grund af et baggrundsbillede\",\n        \"bgGradient\": \"Elementets baggrundsfarve kunne ikke detekteres på grund af en baggrundsgradient\",\n        \"imgNode\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi elementet indeholder et billedelement\",\n        \"bgOverlap\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er overlappet af et andet element\",\n        \"fgAlpha\": \"Elementets forgrundsfarve kunne ikke detekteres på grund af dets gennemsigtighed\",\n        \"elmPartiallyObscured\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er delvist dækket af et andet element\",\n        \"elmPartiallyObscuring\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det delvist dækker et andet element\",\n        \"outsideViewport\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er udenfor sidens 'viewport'\",\n        \"equalRatio\": \"Elementet har et 1:1-kontrastforhold med baggrunden\",\n        \"shortTextContent\": \"Elementets indhold er for kort til at kunne afgøre, om indholdet ren faktisk ER tekst\",\n        \"default\": \"Kan ikke udregne kontrastforhold\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Elementet har stor farvekontrast, den er ${data.contrastRatio}\",\n      \"fail\": \"Elementet har ikke nok farvekontrast, den er ${data.contrastRatio} (forgrundsfarve: ${data.fgColor}, baggrundsfarve: ${data.bgColor}, tekststørrelse: ${data.fontSize}, teksttykkelse: ${data.fontWeight}). Forventet kontrastforhold er ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementets baggrundsfarve kunne ikke detekteres på grund af et baggrundsbillede\",\n        \"bgGradient\": \"Elementets baggrundsfarve kunne ikke detekteres på grund af en baggrundsgradient\",\n        \"imgNode\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi elementet indeholder et billedelement\",\n        \"bgOverlap\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er overlappet af et andet element\",\n        \"fgAlpha\": \"Elementets forgrundsfarve kunne ikke detekteres på grund af dets gennemsigtighed\",\n        \"elmPartiallyObscured\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er delvist dækket af et andet element\",\n        \"elmPartiallyObscuring\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det delvist dækker et andet element\",\n        \"outsideViewport\": \"Elementets baggrundsfarve kunne ikke detekteres, fordi det er udenfor sidens 'viewport'\",\n        \"equalRatio\": \"Elementet har et 1:1-kontrastforhold med baggrunden\",\n        \"shortTextContent\": \"Elementets indhold er for kort til at kunne afgøre, om indholdet ren faktisk ER tekst\",\n        \"default\": \"Kan ikke udregne kontrastforhold\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Links kan adskilles fra den omkringliggende tekst på anden måde end med farve\",\n      \"fail\": \"Links bør skille sig ud fra den omkringliggende tekst på anden måde end med farve\",\n      \"incomplete\": {\n        \"bgContrast\": \"Elementets kontrastforhold kunne ikke detekteres. Tjek for specifik 'hover'/'focus' styling\",\n        \"bgImage\": \"Elementets kontrastforhold kunne ikke detekteres på grund af et baggrundsbillede\",\n        \"bgGradient\": \"Elementets kontrastforhold kunne ikke detekteres på grund af en baggrundsgradient\",\n        \"imgNode\": \"Elementets kontrastforhold kunne ikke detekteres, fordi elementet indeholder et billedelement\",\n        \"bgOverlap\": \"Elementets kontrastforhold kunne ikke detekteres på grund af overlappende elementer\",\n        \"default\": \"Kan ikke udregne kontrastforhold\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"'autocomplete'-værdien er brugt på et passende element\",\n      \"fail\": \"'autocomplete'-værdien er ikke passende for denne type input\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"'autocomplete'-attributten er korrekt formateret\",\n      \"fail\": \"'autocomplete'-attributten er forkert formateret\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"'Accesskey' attributværdien er unik\",\n      \"fail\": \"Dokumentet har flere elementer med den samme 'accesskey'-attribut\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Elementet indeholder fokuserbare elementer\",\n      \"fail\": \"Elementet bør have fokuserbart indhold\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Ingen fokuserbare elementer indeholdt i elementet\",\n      \"fail\": \"Fokuserbart indhold bør slås fra eller fjernes fra sidens DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Elementet er fokuserbart\",\n      \"fail\": \"Elementet bør være fokuserbart\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Elementet er ikke i sidens tabulerings-rækkefølge ('tab order') eller har tilgængelig tekst\",\n      \"fail\": \"Elementet er i sidens tabulerings-rækkefølge ('tab order') og har ikke tilgængelig tekst\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Ingen fokuserbare elementer indeholdt i element\",\n      \"fail\": \"Fokuserbart indhold bør have tabindex='-1' eller fjernes fra sidens DOM\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} landmark er på det øverste niveau.\",\n      \"fail\": \"${data.role} landmark er indhold i et andet landmark.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Siden har mindst én overskrift på niveau 1\",\n      \"fail\": \"Siden skal have mindst én overskrift på niveau 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Dokumentet har mindst ét 'main'-landmark\",\n      \"fail\": \"Dokumentet har ikke et 'main'-landmark\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Dokumentet har ikke mere end ét 'banner'-landmark\",\n      \"fail\": \"Dokumentet har mere end ét 'banner-'landmark\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Dokumentet har ikke mere end ét 'contentinfo'-landmark\",\n      \"fail\": \"Dokumentet har mere end ét 'contentinfo'-landmark\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Dokumentet har ikke mere end ét 'main'-landmark\",\n      \"fail\": \"Dokumentet har mere end ét 'main'-landmark\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Elementet har ikke et 'tabindex' større end 0\",\n      \"fail\": \"Elementet har et 'tabindex' større end 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Elementet har en alt-attribut med valid værdi\",\n      \"fail\": \"Elementet har en alt-attribut, som kun indeholder et mellemrum, hvilket ikke ignoreres af alle skærmlæsere\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Elementet duplikerer ikke den eksisterende tekst fra <img> elementets alt-tekst\",\n      \"fail\": \"Elementet indeholder et <img> element med alt-tekst, som duplikerer den eksisterende tekst\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Form-elementet har en eksplicit <label>\",\n      \"fail\": \"Form-elementet har ikke en eksplicit <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Hjælpeteksten ('title' eller 'aria-describedby') duplikerer ikke label-teksten\",\n      \"fail\": \"Hjælpeteksten ('title' eller 'aria-describedby') er den samme som label-teksten\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Form-elementet har en synlig og eksplicit <label>\",\n      \"fail\": \"Form-elementet har en eksplicit <label>, som er skjult\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Form-elementet har en implicit (indeholdt i en) <label>\",\n      \"fail\": \"Form-elementet har ikke en implicit (indeholdt i en) <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Elementet indeholder synlig tekst som del af dets tilgængelige navn\",\n      \"fail\": \"Tekst i elementet er ikke inkluderet i det tilgængelige navn\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Form-feltet har ikke flere label-elementer\",\n      \"incomplete\": \"Flere label-elementer er ikke bredt understøttet i tilgængelighedsteknologier. Sørg for, at det første label-element indeholder alle de nødvendige informationer.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Form-elementet bruger ikke udelukkende 'title'-attributten som label\",\n      \"fail\": \"Kun 'title'-attributten er brugt som label for form-elementet\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Landmarks skal have en unik rolle eller 'role'/'label'/'title'-attribut-kombination (som tilgængeligt navn)\",\n      \"fail\": \"Et landmark skal have en unik 'aria-label', 'aria-labelledby', eller 'title' for at gøre landmarks adskillelige\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> elementet har en 'lang'-attribut\",\n      \"fail\": \"<html> elementet har ikke en 'lang'-attribut\"\n    },\n    \"valid-lang\": {\n      \"pass\": \"'lang'-attributtens værdi er inkluderet i listen over godkendte sprog\",\n      \"fail\": \"'lang'-attributtens værdi er ikke inkluderet i listen over godkendte sprog\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Attributterne 'lang' og 'xml:lang' har samme basissprog\",\n      \"fail\": \"Attributterne 'lang' og 'xml:lang' har ikke samme basissprog\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Beskrivelsesliste-elementet har et <dl>-over-element\",\n      \"fail\": \"Beskrivelsesliste-elementet har ikke et <dl>-over-element\"\n    },\n    \"listitem\": {\n      \"pass\": \"Elementet i listen har et <ul>, <ol> eller 'role=\\\"list\\\"'-over-element\",\n      \"fail\": \"Elementet i listen har ikke et <ul>, <ol> eller 'role=\\\"list\\\"'-over-element\"\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Elementet i listen har kun direkte under-elementer, som er tilladt i <dt> eller <dd> elementer\",\n      \"fail\": \"Elementet i listen har direkte under-elementer, som ikke er tilladt inde i <dt> eller <dd> elementer\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Elementet i listen har kun direkte under-elementer, som er tilladt i <li> elementer\",\n      \"fail\": \"Elementet i listen har direkte under-elementer, som ikke er tilladt inde i <li> elementer\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Det ikke-tomme element har både <dt> og <dd> elementer\",\n      \"fail\": \"Det ikke-tomme element har ikke mindst ét <dt> element efterfulgt af mindst ét <dd> element\"\n    },\n    \"caption\": {\n      \"pass\": \"Multimedia-elementet har et track med undertekster\",\n      \"incomplete\": \"Tjek at undertekster er tilgængelige for elementet\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Denne <iframe> blev testet af axe-core\",\n      \"fail\": \"Denne <iframe> kunne ikke testes af axe-core\",\n      \"incomplete\": \"Denne <iframe> er endnu ikke blevet testet af axe-core\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Skærmen kan styres, og sidens skærmretning er ikke låst med med 'css-orientation-lock'\",\n      \"fail\": \"Skærmretningen ('css-orientation-lock') er låst, hvilket gør sidevisning svær at styre på skærmen\",\n      \"incomplete\": \"Det kan ikke detekteres om skærmretning er låst (med 'css-orientation-lock')\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta>-tagget begrænser ikke høj zoom på mobile enheder\",\n      \"fail\": \"<meta>-tagget begrænser zoom på mobile enheder\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta>-tagget slår ikke zoom fra på mobile enheder\",\n      \"fail\": \"${data} på <meta>-tagget slår zoom fra på mobile enheder\"\n    },\n    \"header-present\": {\n      \"pass\": \"Siden har en overskrift (header)\",\n      \"fail\": \"Siden har ikke en overskrift (header)\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Rækkefølgen af overskiftsniveauer er korrekt\",\n      \"fail\": \"Rækkefølgen af overskiftsniveauer er ikke korrekt\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Passende 'skip link' fundet\",\n      \"fail\": \"Intet passende 'skip link' fundet\"\n    },\n    \"landmark\": {\n      \"pass\": \"Siden har en 'landmark' region\",\n      \"fail\": \"Siden har ikke en 'landmark' region\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta>-tagget genindlæser ikke siden med det samme\",\n      \"fail\": \"<meta>-tagget genindlæser siden med det samme\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> elementer er ikke stylet som en overskrift\",\n      \"fail\": \"Overskrifts-elementer (<h1>, <h2>, osv.) bør bruges i stedet for stylet <p> elementer\"\n    },\n    \"region\": {\n      \"pass\": \"Al indhold på siden er lagt ind under landmarks\",\n      \"fail\": \"Dele af sidens indhold er ikke lagt under et landmark\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Det element, som 'skip link' refererer til, eksisterer\",\n      \"incomplete\": \"Det element, som 'skip link' refererer til, bør blive synligt ved aktivering\",\n      \"fail\": \"Manglende 'skip link' 'target'-attribut\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Elementets 'title'-attribut er unik\",\n      \"fail\": \"Elementets 'title'-attribut er ikke unik\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Dokumentet har ingen aktive elementer, der deler den samme 'id'-attribut\",\n      \"fail\": \"Dokumentet har aktive elementer, der deler den samme 'id'-attribut: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Dokumentet har ingen elementer refereret med ARIA eller labels, der deler den samme 'id'-attribut\",\n      \"fail\": \"Dokumentet har flere elementer refereret med ARIA med den samme 'id'-attribut: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Dokumentet har ingen statiske elementer, der deler den samme 'id'-attribut\",\n      \"fail\": \"Dokumentet har flere statiske elementer med den samme 'id'-attribut\"\n    },\n    \"aria-label\": {\n      \"pass\": \"'aria-label'-attribut er til stede og er ikke tom\",\n      \"fail\": \"'aria-label'-attribut eksisterer ikke eller er tom\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"'aria-labelledby'-attribut eksisterer og refererer elementer, der er synlige for skærmlæsere\",\n      \"fail\": \"'aria-labelledby'-attribut eksisterer ikke, eller refererer elementer, som ikke eksisterer - eller refererer til elementer, der er tomme\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Ingen inline styling med '!important', som påvirker tekst-mellemrums-afstand er specificeret\",\n      \"fail\": {\n        \"singular\": \"Fjern '!important' fra inline stylings ${data.values}, da overskrivning af dette, ikke er understøttet i de fleste browsere\",\n        \"plural\": \"Fjern '!important' fra inline styling ${data.values}, da overskrivning af dette, ikke er understøttet i de fleste browsere\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Elementet har indre tekst, som er synlig for skærmlæsere\",\n      \"fail\": \"Elementet har ingen indre tekst, som er synlig for skærmlæsere\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Dokumentet har et <title> element, der ikke er tomt\",\n      \"fail\": \"Dokumentet mangler et <title> element med indhold\"\n    },\n    \"exists\": {\n      \"pass\": \"Elementet eksisterer ikke\",\n      \"fail\": \"Elementet eksisterer\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Elementet har en 'alt'-attribut\",\n      \"fail\": \"Elementet har ikke en 'alt'-attribut\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Elementet har tekst, der er synligt for skærmlæsere\",\n      \"fail\": \"Elementet har ikke tekst, der er synligt for skærmlæsere\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Elementet er ikke synligt\",\n      \"fail\": \"Elementet er synligt\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Elementet har 'alt'-attribut med indhold\",\n      \"fail\": \"Elementet har ingen 'alt'-attribut, eller 'alt'-attributten er tom\"\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Elementet har ikke en 'value'-attribut\",\n        \"has-label\": \"Elementet har en 'værdi'-attribut med indhold\"\n      },\n      \"fail\": \"Elementet har en 'value'-attribut, og 'værdi'-attributten er tom\"\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Elementet har en 'title'-attribut\",\n      \"fail\": \"Elementet har ingen 'title'-attribut, eller 'title'-attributten er tom\"\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Elementet har 'value'-attribut med indhold\",\n      \"fail\": \"Elementet har ingen 'value'-attribut, eller 'value'-attributten er tom\"\n    },\n    \"role-none\": {\n      \"pass\": \"Elementets standard semantik blev overskrevet med attributten 'role=\\\"none\\\"'\",\n      \"fail\": \"Elementets standard semantik blev ikke overskrevet med attributten 'role=\\\"none\\\"'\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Elementets standard semantik blev overskrevet med attributten 'role=\\\"presentation\\\"'\",\n      \"fail\": \"Elementets standard semantik blev ikke overskrevet med attributten 'role=\\\"presentation\\\"'\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Den første række i tabellen er ikke brugt som en beskrivelse ('caption')\",\n      \"fail\": \"Det første element i tabellen bør være en beskrivelse (<caption>) i stedet for en celle\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"'Scope'-attributten bør kun bruges på tabellens header-elementer (<th>)\",\n      \"fail\": \"'Scope'-attributten bør kun bruges på tabellens header-elementer (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Indholdet af 'summary'-attributten og <caption> elementet er ikke identisk\",\n      \"fail\": \"Indholdet af 'summary'-attributten og <caption> elementet er identisk\"\n    },\n    \"scope-value\": {\n      \"pass\": \"'scope'-attributten bruges korrekt\",\n      \"fail\": \"Værdien af 'scope'-attributten må kun være 'row' eller 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Alle data-celler med indhold har en tabel-header\",\n      \"fail\": \"Nogle data-celler med indhold har ikke tabel-headers\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Header-attributten bruges kun til at referere til andre celler i den samme tabel\",\n      \"fail\": \"Header-attributten bruges ikke kun til at referere til andre celler i den samme tabel\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Alle tabellens header-celler refererer til data-celler\",\n      \"fail\": \"Ikke alle tabellens header-celler refererer til data-celler\",\n      \"incomplete\": \"Nogle af tabellens data-celler mangler eller er tomme\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Alt indhold på siden er blevet analyseret.\",\n      \"fail\": \"Der var problemer med at analysere dele af indholdet på denne side.\",\n      \"incomplete\": \"Der er skjult indhold på siden, som ikke blev analyseret. Du skal gøre dette indhold synligt for at kunne analysere det.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Ret en af følgende:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Ret alle de følgende:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe kunne ikke give en årsag. Tid til at finde dit udviklingsværktøj frem!\"\n}\n"
  },
  {
    "path": "locales/de.json",
    "content": "{\n  \"lang\": \"de\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Stellt sicher, dass die Werte der accesskey-Attribute einzigartig sind.\",\n      \"help\": \"Der Wert des accesskey-Attributes muss einzigartig sein.\"\n    },\n    \"area-alt\": {\n      \"description\": \"Stellt sicher, dass <area>-Elemente Alternativtexte besitzen.\",\n      \"help\": \"Aktive <area>-Elemente müssen einen Alternativtext besitzen.\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Stellt sicher, dass ARIA-Attribute für die vergebene Rolle eines Elements erlaubt sind.\",\n      \"help\": \"Elemente dürfen nur erlaubte ARIA-Attribute verwenden.\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Stellt sicher, dass der Wert des role-Attributes für dieses Element geeignet ist.\",\n      \"help\": \"Der Wert des role-Attributes muss für dieses Element geeignet sein.\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Stellt sicher, dass aria-braillelabel und aria-brailleroledescription ein non-braille Äquivalent haben.\",\n      \"help\": \"aria-braille Attribute müssen ein non-braille Äquivalent haben.\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-button, -link und jedes -menuitem einen zugänglichen Namen (accessible name) hat.\",\n      \"help\": \"ARIA Befehle müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Stellt sicher, dass ARIA-Attribute wie in der Spezifikation der Rolle des Elements beschrieben verwendet werden.\",\n      \"help\": \"ARIA-Attribute müssen entsprechend der Rolle des Elements verwendet werden.\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Stellt sicher, dass die Elemente keine veralteten Rollen verwenden.\",\n      \"help\": \"Veraltete ARIA-Rollen dürfen nicht verwendet werden.\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-dialog und -alertdialog Knoten einen zugänglichen Namen (accessible name) hat.\",\n      \"help\": \"ARIA-dialog und -alertdialog Knoten müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Stellt sicher, dass aria-hidden='true' nicht am <body>-Element des Dokumentes verwendet wird.\",\n      \"help\": \"Aria-hidden='true' darf nicht für den <body> des Dokumentes verwendet werden.\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Stellt sicher, dass ARIA-hidden Elemente keine fokussierbaren Elemente beinhalten.\",\n      \"help\": \"ARIA-hidden Elemente dürfen keine fokussierbaren Elemente beinhalten.\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-input einen zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-inputs müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-meter Knoten einen zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-meter Knoten müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-progressbar Knoten einen zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-progressbar Knoten müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Stellt sicher, dass ARIA-Attribute für die Rolle eines Elements nicht verboten sind.\",\n      \"help\": \"Elemente dürfen nur erlaubte ARIA-Attribute verwenden.\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Stellt sicher, dass Elemente mit ARIA-Rollen alle erforderlichen ARIA-Attribute besitzen.\",\n      \"help\": \"Erforderliche ARIA-Attribute müssen bereitgestellt werden.\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Stellt sicher, dass Elemente mit einer ARIA-Rolle, welche bestimmte untergeordnete Rollen voraussetzten diese auch enthalten.\",\n      \"help\": \"Bestimmte ARIA-Rollen müssen spezifische, untergeordnete Kind-Rollen enthalten.\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Stellt sicher, dass Elemente mit ARIA-Rollen, welche übergeordnete Rollen voraussetzen auch in diesen enthalten sind.\",\n      \"help\": \"Bestimmte ARIA-Rollen müssen in spezifischen, übergeordneten Eltern-Rollen enthalten sein.\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Stellt sicher, dass ARIA-roledescription nur im Zusammenhang mit einer im- oder expliziten Rolle verwendet wird.\",\n      \"help\": \"Nutze aria-roledescription für Elemente mit einer semantischen Rolle.\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Stellt sicher, dass alle Elemente mit einer ARIA-Rolle auch einen gültigen Wert verwenden.\",\n      \"help\": \"Verwendete ARIA-Rollen müssen gültigen Werten entsprechen.\"\n    },\n    \"aria-text\": {\n      \"description\": \"Stellt sicher, dass role=\\\"text\\\" für Elemente verwendet wird, die keine fokussierbaren Nachkommen (descendants) haben.\",\n      \"help\": \"\\\"role=text\\\" sollte keine fokussierbaren Nachkommen (descendants) haben.\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Stellt sicher, dass jedes ARIA-toggle-Feld ein zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-toggle-Felder benötigen einen zugänglichen Namen (accessible name).\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-tooltip Knoten einen zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-tooltip-Knoten benötigen einen zugänglichen Namen (accessible name).\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Stellt sicher, dass jeder ARIA-treeitem Knoten einen zugänglichen Namen (accessible name) besitzt.\",\n      \"help\": \"ARIA-treeitem-Knoten benötigen einen zugänglichen Namen (accessible name).\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Stellt sicher, dass alle ARIA-Attribute gültige Werte verwenden.\",\n      \"help\": \"Verwendete ARIA-Attribute müssen gültigen Werten entsprechen.\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Stellt sicher, dass Attribute, welche mit aria- beginnen auch valide ARIA-Attribute sind.\",\n      \"help\": \"Verwendete ARIA-Attribute müssen gültigen Namen entsprechen.\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Stellt sicher, dass <audio>-Elemente Untertitel besitzen.\",\n      \"help\": \"<audio>-Elemente müssen eine Untertitelung (captions track) besitzen.\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Stellt sicher, dass das autocomplete-Attribut korrekt ist und für das form-Feld geeignet ist.\",\n      \"help\": \"autocomplete-Attribute müssen korrekt genutzt werden.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Stellt sicher, dass der Zeichenabstand durch benutzerdefinierte Stylesheets angepasst werden kann.\",\n      \"help\": \"Zeichenabstände müssen durch benutzerdefinierte Stylesheets anpassbar sein.\"\n    },\n    \"blink\": {\n      \"description\": \"Stellt sicher, dass keine <blink>-Elemente verwendet werden.\",\n      \"help\": \"<blink>-Elemente sind veraltet und dürfen nicht verwendet werden.\"\n    },\n    \"button-name\": {\n      \"description\": \"Stellt sicher, dass Schaltflächen wahrnehmbaren Text enthalten.\",\n      \"help\": \"Schaltflächen müssen wahrnehmbaren Text enthalten.\"\n    },\n    \"bypass\": {\n      \"description\": \"Stellt sicher, dass jede Seite mindestens ein Mittel bereitstellt, welches dem Nutzer erlaubt direkt zum Inhalt der Seite zu springen.\",\n      \"help\": \"Wiederholende Blöcke müssen vom Nutzer mit Hilfe von der Seite bereitgestellten Mitteln übersprungen werden können.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Stellt sicher, dass der Kontrast zwischen Vorder- und Hintergrundfarbe den in der WCAG 2 als AAA ausgewiesenen Kontrastgrenzwerten entspricht.\",\n      \"help\": \"Elemente müssen einen ausreichenden Farbkontrast haben.\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Stellt sicher, dass der Kontrast zwischen Vorder- und Hintergrundfarbe den in der WCAG 2 als AA ausgewiesenen Kontrastgrenzwerten entspricht.\",\n      \"help\": \"Elemente müssen einen ausreichenden Farbkontrast haben.\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Stellt sicher, dass der Inhalt nicht nur auf einer sondern auf allen spezifischen Bildschirmausrichtungen angezeigt werden kann.\",\n      \"help\": \"CSS Media Queries dürfen nicht genutzt werden um die Bildschirmausrichtung zu sperren.\"\n    },\n    \"definition-list\": {\n      \"description\": \"Stellt sicher, dass <dl>-Elemente ordnungsgemäß strukturiert sind.\",\n      \"help\": \"<dl>-Elemente dürfen unmittelbar nur korrekt verschachtelte <dt>- und <dd>-Gruppen, <script>- oder <template>-Elemente enthalten.\"\n    },\n    \"dlitem\": {\n      \"description\": \"Stellt sicher, dass <dt> und <dd>-Elemente in einem <dl>-Element enthalten sind.\",\n      \"help\": \"<dt>- und <dd>-Elemente müssen in einem <dl>-Element enthalten sein.\"\n    },\n    \"document-title\": {\n      \"description\": \"Stellt sicher, dass jedes HTML-Dokument ein nichtleeres <title>-Element besitzt.\",\n      \"help\": \"Dokumente müssen ein <title>-Element besitzen, um die Navigation zu erleichtern.\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Stellt sicher, dass jeder Wert des ID-Attributes von aktiven Elemente einzigartig ist.\",\n      \"help\": \"IDs von aktiven Elementen müssen einzigartig sein.\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Stellt sicher, dass jeder Wert des ID-Attributes, welcher in ARIA und labels genutzt wird einzigartig ist.\",\n      \"help\": \"IDs, welche in ARIA und Lables genutzt werden, müssen einzigartig sein.\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Stellt sicher, dass der Wert eines id-Attributes einzigartig ist.\",\n      \"help\": \"Der Wert des id-Attributes muss einzigartig sein.\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Stellt sicher, dass Überschriften einen wahrnehmbaren Text beinhalten.\",\n      \"help\": \"Überschriften dürfen nicht leer sein.\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Stellt sicher, dass Tabellenkopfzeilen einen wahrnehmbaren Text beinhalten.\",\n      \"help\": \"Tabellenkopfzeilen sollten nicht leer sein.\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Stellt sicher, dass Elemente in der Fokusreihenfolge eine geeignete Rolle besitzen.\",\n      \"help\": \"Elemente in der Fokusreihenfolge benötigen eine Rolle, die für interaktive Elemente geeignet ist.\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Stellt sicher, dass ein form-Feld nur ein label-Element besitzt.\",\n      \"help\": \"form-Felder sollten nur ein label-Element besitzen.\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Stellt sicher, dass <frame>- und <iframe>-Elemente mit fokussierbarem Inhalt keinen tabindex=-1 haben.\",\n      \"help\": \"Frames mit fokussierbarem Inhalt dürfen keinen tabindex=-1 haben.\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Stellt sicher, dass <iframe> und <frame>-Elemente das axe-core Script beinhalten.\",\n      \"help\": \"Frames müssen mit axe-core getestet werden.\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Stellt sicher, dass <iframe> und <frame>-Elemente ein einzigartiges title-Attribut besitzen.\",\n      \"help\": \"Frames müssen ein einzigartiges title-Attribut besitzen.\"\n    },\n    \"frame-title\": {\n      \"description\": \"Stellt sicher, dass <iframe> und <frame>-Elemente ein nichtleeres title-Attribut besitzen.\",\n      \"help\": \"Frames müssen ein nichtleeres title-Attribut besitzen.\"\n    },\n    \"heading-order\": {\n      \"description\": \"Stellt sicher, dass Überschriften in der semantisch korrekten Reihenfolge sind.\",\n      \"help\": \"Überschriftenebenen sollten nur jeweils um eins steigen.\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informiert den Nutzer über versteckten Inhalt.\",\n      \"help\": \"Versteckter Inhalt auf der Seite konnte nicht analysiert werden.\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Stellt sicher, dass jedes HTML Dokument ein lang-Attribut besitzt.\",\n      \"help\": \"Das <html>-Element muss ein lang-Attribut besitzen.\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Stellt sicher, dass das lang-Attribut des <html>-Elements einen validen Wert besitzt.\",\n      \"help\": \"Das <html>-Element muss einen gültigen Attributwert für das lang-Attribut besitzen.\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Stellt sicher, dass HTML Elemente mit validen lang und xml:lang Attributen dieselbe Angabe über die Sprache machen.\",\n      \"help\": \"HTML Elemente mit lang und xml:lang Attributen müssen dieselbe Sprache ausweisen.\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Stellt sicher, dass Links mit dem selben zugänglichen Namen (accessible name) denselben Zweck folgen.\",\n      \"help\": \"Links mit dem selben zugänglichen Namen (accessible name) verfolgen denselben Zweck.\"\n    },\n    \"image-alt\": {\n      \"description\": \"Stellt sicher, dass <img>-Elemente einen Alternativtext oder eine ARIA-Rolle mit dem Wert none oder presentation besitzen.\",\n      \"help\": \"Abbildungen müssen einen Alternativtext besitzen.\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Stellt sicher, dass Alternativtexte von Bildern nicht als Text wiederholt werden.\",\n      \"help\": \"Der Alternativtext von Bildern sollte nicht als Text wiederholt werden.\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Stellt sicher, dass Eingabeschaltflächen wahrnehmbaren Text beinhalten.\",\n      \"help\": \"Eingabeschaltflächen müssen wahrnehmbaren Text beinhalten.\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Stellt sicher, dass <input type=\\\"image\\\">-Elemente einen Alternativtext besitzen.\",\n      \"help\": \"<input type=\\\"image\\\">-Elemente müssen einen Alternativtext besitzen.\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Stellt sicher, dass Elemente, die durch ihren Inhalt beschrieben sind, auch ihren sichtbaren Text als Teil des zugänglichen Namens (accessible name) haben.\",\n      \"help\": \"Elemente müssen ihren sichtbaren Text auch als Teil des zugänglichen Namens (accessible name) haben.\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Stellt sicher, dass jedes <form>-Element nicht ausschließlich durch ein title oder aria-describedby-Attribut beschrieben sind.\",\n      \"help\": \"<form>-Elemente sollten eine sichtbare Beschriftung haben.\"\n    },\n    \"label\": {\n      \"description\": \"Stellt sicher, dass jedes <form>-Element über eine Beschriftung verfügt.\",\n      \"help\": \"<form>-Elemente müssen eine Beschriftung haben.\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Stellt sicher, dass die banner landmark sich auf der obersten Ebene befindet.\",\n      \"help\": \"Banner landmark muss sich auf der obersten Ebene befinden.\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Stellt sicher, dass die ergänzende landmark oder aside sich auf dem höchsten Level befindet.\",\n      \"help\": \"Das aside-Elemente darf sich nicht in einer anderen landmark befinden.\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Stellt sicher, dass die contentinfo landmark sich auf der obersten Ebene befindet.\",\n      \"help\": \"Contentinfo landmark muss sich auf der obersten Ebene befinden.\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Stellt sicher, dass die main landmark sich auf der obersten Ebene befindet.\",\n      \"help\": \"Main landmark ist nicht auf der obersten Ebene.\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Stellt sicher, dass das Dokument höchstens eine banner landmark besitzt.\",\n      \"help\": \"Das Dokument sollte höchstens eine banner landmark enthalten.\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Stellt sicher, dass das Dokument höchstens eine contentinfo landmark besitzt.\",\n      \"help\": \"Das Dokument sollte höchstens eine contentinfo landmark enthalten.\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Stellt sicher, dass das Dokument höchstens eine main landmark besitzt.\",\n      \"help\": \"Das Dokument sollte nur eine main landmark besitzen.\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Stellt sicher, dass das Dokument eine main landmark besitzt.\",\n      \"help\": \"Seite muss eine main landmark enthalten.\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"Stellt sicher, dass landmarks einzigartig sind.\",\n      \"help\": \"Landmarks müssen eine einzigartige role oder role/label/title Kombination (bzw. zugänglicher Name / accessible name) besitzen.\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Stellt sicher, dass Links vom umgebenden Text nicht allein durch die Farbe unterschieden werden können.\",\n      \"help\": \"Links müssen vom umgebenden Text auf eine Weise unterschieden werden können, die nicht allein auf Farbe beruht.\"\n    },\n    \"link-name\": {\n      \"description\": \"Stellt sicher, dass Links wahrnehmbaren Text beinhalten.\",\n      \"help\": \"Links müssen wahrnehmbaren Text beinhalten.\"\n    },\n    \"list\": {\n      \"description\": \"Stellt sicher, dass Listen korrekt strukturiert sind.\",\n      \"help\": \"<ul>- und <ol>-Elemente dürfen unmittelbar nur <li>-, <script>- oder <template>-Elemente enthalten.\"\n    },\n    \"listitem\": {\n      \"description\": \"Stellt sicher, dass <li>-Elemente semantisch korrekt verwendet werden.\",\n      \"help\": \"<li>-Elemente müssen in einem <ul>- oder <ol>-Element enthalten sein.\"\n    },\n    \"marquee\": {\n      \"description\": \"Stellt sicher, dass <marquee>-Elemente nicht verwendet werden.\",\n      \"help\": \"<marquee>-Elemente sind veraltet und dürfen nicht verwendet werden.\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Stellt sicher, dass <meta http-equiv=\\\"refresh\\\"> nicht für die verzögerte Aktualisierung verwendet wird.\",\n      \"help\": \"Die verzögerte Aktualisierung darf nicht verwendet werden.\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Stellt sicher, dass <meta http-equiv=\\\"refresh\\\"> nicht verwendet werden.\",\n      \"help\": \"Eine zeitgesteuerte Aktualisierung (refresh) sollte nicht verwendet werden.\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Stellt sicher, dass <meta name=\\\"viewport\\\"> nicht verhindert, dass ein signifikanter Zoom verwendet werden kann.\",\n      \"help\": \"Benutzer sollten in der Lage sein, den Text um bis zu 500% vergrößern und skalieren zu können.\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Stellt sicher, dass <meta name=\\\"viewport\\\"> Textskalierung und -zoom nicht verhindert werden.\",\n      \"help\": \"Zoomen und Skalieren darf nicht deaktiviert werden.\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Stellt sicher, dass interaktive Steuerelemente nicht verschachtelt (nested) sind, da sie nicht immer von Bildschirmlesegeräten angezeigt werden oder Probleme bei der Fokussierung von Hilfstechnologien verursachen können.\",\n      \"help\": \"Interaktive Steuerelemente dürfen nicht verschachtelt (nested) werden.\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Stellt sicher, dass <video> oder <audio> Elemente keine Töne automatisch abspielen für mehr als 3 Sekunden (autoplay) ohne eine Möglichkeit dies zu stoppen.\",\n      \"help\": \"<video> oder <audio> Elemente geben keine Töne automatisch aus.\"\n    },\n    \"object-alt\": {\n      \"description\": \"Stellt sicher, dass <object>-Elemente einen Alternativtext besitzen.\",\n      \"help\": \"<object>-Elemente müssen einen Alternativtext besitzen.\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Stellt sicher, dass <p>-Elemente nicht dafür verwendet werden um Überschriften zu formatieren.\",\n      \"help\": \"Die Schriftschnitte bold und italic sowie die Schriftgröße dürfen nicht verwendet werden, um <p>-Elemente wie Überschriften zu formatieren.\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Stellt sicher, dass die Seite oder zumindest eins der frame-Elemente eine Überschrift der ersten Ebene enthalten.\",\n      \"help\": \"Die Seite muss eine Überschrift der ersten Ebene enthalten.\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Elemente mit role=\\\"none\\\" oder role=\\\"presentation\\\" sollten kein globales ARIA-Attribute besitzen oder fokussierbar sein, damit sie von Screenreadern ignoriert werden.\",\n      \"help\": \"Elemente mit \\\"role=none\\\" oder \\\"role=presentation\\\" sollen von Screenreadern ignoriert werden.\"\n    },\n    \"region\": {\n      \"description\": \"Stellt sicher, dass jeglicher Inhalt in einer landmark region enthalten ist.\",\n      \"help\": \"Inhalte sollten in einer landmark region enthalten sein.\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Stellt sicher, dass [role='img'] Elemente einen Alternativ Text besitzen.\",\n      \"help\": \"[role='img'] Elemente haben ein Alternativtext.\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Stellt sicher, dass das scope-Attribut bei Tabellen korrekt verwendet wird.\",\n      \"help\": \"Das scope-Attribut sollte korrekt verwendet werden.\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Elemente, welche scrollbaren Inhalt besitzen sollten durch die Tastatur erreichbar und bedienbar sein.\",\n      \"help\": \"Scrollbare Regionen müssen per Tastatur erreichbar sein.\"\n    },\n    \"select-name\": {\n      \"description\": \"Stellt sicher, dass <select> Elemente einen zugänglichen Namen (accessible name) besitzen.\",\n      \"help\": \"<select> Elemente müssen einen zugänglichen Namen (accessible name) besitzen.\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Stellt sicher, dass serverseitige Imagemaps nicht verwendet werden.\",\n      \"help\": \"Serverseitige Imagemaps dürfen nicht verwendet werden.\"\n    },\n    \"skip-link\": {\n      \"description\": \"Stellt sicher, dass alle Skip-Links ein fokussierbares Ziel enthalten.\",\n      \"help\": \"Das Ziel eines Skip-Links sollte existieren und fokussierbar sein.\"\n    },\n    \"summary-name\": {\n      \"description\": \"Stellt sicher, dass die summary-Elemente einen erkennbaren Text haben\",\n      \"help\": \"summary-Elemente müssen einen erkennbaren Text haben\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Stellt sicher, dass <svg> Elemente mit einer img, graphics-document oder graphics-symbol Rolle einen zugänglichen Namen (accessible name) besitzen.\",\n      \"help\": \"<svg> Elemente mit einer img Rolle sollten einen Alternativtext besitzen.\"\n    },\n    \"tabindex\": {\n      \"description\": \"Stellt sicher, dass keine tabindex-Attribute mit einem Wert größer als null verwendet werden.\",\n      \"help\": \"Elemente sollten keinen tabindex besitzen, der größer als null ist.\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Stellt sicher, dass Tabellen nicht den gleichen Text im <caption>-Element wie im summary-Attribut enthalten.\",\n      \"help\": \"Das <caption>-Element sollte nicht den gleichen Text wie das summary-Attribut enthalten.\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Stellt sicher, dass Tabellen mit einer Beschriftung auch das <caption>-Element verwenden.\",\n      \"help\": \"Daten- oder Kopfzellen sollten nicht verwendet werden, um einer Datentabelle eine Überschrift zu geben.\"\n    },\n    \"target-size\": {\n      \"description\": \"Stellt sicher, dass Berührungsobjekte (touch targets) ausreichend groß sind und genügend Platz bieteen.\",\n      \"help\": \"Alle Berührungsobjekte (touch targets) müssen 24 Pixel groß sein oder ausreichend Platz lassen.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Stellt sicher, dass jede nichtleere Zelle einer Tabelle ein oder mehrere Tabellenköpfe haben.\",\n      \"help\": \"In Tabellen, die größer als 3 mal 3 sind, müssen alle nichtleeren <td>-Elemente einen zugehörigen Tabellenkopf haben.\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Stellt sicher, dass jede Zelle in einer Tabelle, welche das headers-Attribut verwendet, sich nur auf andere Zellen derselben Tabelle beziehen.\",\n      \"help\": \"Innerhalb eines <table>-Elementes dürfen sich Zellen, die das headers-Attribut verwenden, nur auf andere Zellen derselben Tabelle beziehen.\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Stellt sicher, dass jeder Tabellenkopf in einer Datentabelle sich auf Datenzellen bezieht.\",\n      \"help\": \"Alle <th>-Elemente sowie Elemente mit role=columnheader/rowheader müssen Datenzellen haben, die sie beschreiben.\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Stellt sicher, dass lang-Attribute gültige Werte haben.\",\n      \"help\": \"Das lang-Attribut muss einen gültigen Wert haben.\"\n    },\n    \"video-caption\": {\n      \"description\": \"Stellt sicher, dass <video>-Elemente Untertitel besitzen.\",\n      \"help\": \"<video>-Elemente müssen Untertitel besitzen.\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"abstract Rolle wird nicht verwendet.\",\n      \"fail\": {\n        \"singular\": \"abstract Rolle kann nicht so verwendet werden: ${data.values}\",\n        \"plural\": \"abstract Rollen können nicht so verwendet werden: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA-Attribute werden korrekt für die definierte Rolle verwendet.\",\n      \"fail\": {\n        \"singular\": \"Folgendes ARIA Attribut ist nicht erlaubt: ${data.values}\",\n        \"plural\": \"Folgende ARIA Attribute sind nicht erlaubt: ${data.values}\"\n      },\n      \"incomplete\": \"Prüfe, dass es kein Problem gibt, wenn das ARIA-Attribut bei diesem Element ignoriert wird: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA Rolle ist für dieses Element erlaubt.\",\n      \"fail\": {\n        \"singular\": \"ARIA Rolle ${data.values} ist nicht für dieses Element erlaubt.\",\n        \"plural\": \"ARIA Rollen ${data.values} sind nicht für dieses Element erlaubt.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA Rolle ${data.values} muss entfernt werden, wenn das Element sichtbar wird, da es nicht für dieses Element erlaubt ist.\",\n        \"plural\": \"ARIA Rollen ${data.values} müssen entfernt werden, wenn das Element sichtbar wird, da sie nicht für dieses Element erlaubt sind.\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"Element hat ein aria-busy-Attribut.\",\n      \"fail\": \"Element verwendet aria-busy=\\\"true\\\" bei der Anzeige eines Ladevorgangs (loader).\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"ARIA-Attribut ist erlaubt.\",\n      \"fail\": {\n        \"checkbox\": \"Entferne aria-checked, oder setze es auf \\\"${data.checkState}\\\", damit es dem tatsächlichen Zustand des Kontrollkästchens entspricht.\",\n        \"rowSingular\": \"Dieses Attribut wird bei treegrid-Zeilen unterstützt, aber nicht bei ${data.ownerRole}: ${data.invalidAttrs}.\",\n        \"rowPlural\": \"Diese Attribute werden von treegrid-Zeilen unterstützt, aber nicht von ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage Attribut existiert und referenziert Elemente, die sichtbar für Screen Reader sind, welche die entsprechende Technologie unterstützen.\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage Wert `${data.values}` benötigt eine Möglichkeit um entsprechend vorgeschlagen zu werden (z.B. aria-live, aria-describedby, role=alert, usw.).\",\n        \"plural\": \"aria-errormessage Werte `${data.values}` benötigen eine Möglichkeit um entsprechend vorgeschlagen zu werden (z.B. aria-live, aria-describedby, role=alert, usw.).\",\n        \"hidden\": \"aria-errormessage Wert `${data.values}` kann nicht auf ein verstecktes Element verweisen.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Stellt sicher, dass aria-errormessage Wert `${data.values}` auf ein existierendes Element verweist.\",\n        \"plural\": \"Stellt sicher, dass aria-errormessage Werte `${data.values}` zu existierenden Elementen verweisen.\",\n        \"idrefs\": \"Es konnte nicht festgestellt werden, ob das Element aria-errormessage auf der Seite existiert: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Kein aria-hidden Attribut ist im <body>-Element des Dokuments vorhanden.\",\n      \"fail\": \"Das <body>-Element des Dokumentes darf nicht das Attribut aria-hidden=\\\"true\\\" besitzen.\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria-level Werte sind gültig.\",\n      \"incomplete\": \"Aria-Level Werte größer als 6 werden nicht von allen Screenreader- und Browser-Kombinationen unterstützt.\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"ARIA-Attribut ist erlaubt\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} Attribute können nicht mit der Rolle \\\"${data.role}\\\" verwendet werden.\",\n        \"hasRoleSingular\": \"${data.prohibited} Attribut kann nicht mit der Rolle \\\"${data.role}\\\" verwendet werden.\",\n        \"noRolePlural\": \"${data.prohibited} Attribute können nicht auf ${data.nodeName} ohne gültiges role Attribut verwendet werden.\",\n        \"noRoleSingular\": \"${data.prohibited} Attribut kann nicht auf ${data.nodeName} ohne gültiges role Attribut verwendet werden.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"${data.prohibited} Attribut wird von der Rolle \\\"${data.role}\\\" nicht gut unterstützt.\",\n        \"hasRolePlural\": \"${data.prohibited} Attribute werden von der Rolle \\\"${data.role}\\\" nicht gut unterstützt.\",\n        \"noRoleSingular\": \"${data.prohibited} Attribut wird bei ${data.nodeName} ohne gültiges role Attribute nicht gut unterstützt.\",\n        \"noRolePlural\": \"${data.prohibited} Attribute werdeb bei ${data.nodeName} ohne gültiges role Attribute nicht gut unterstützt.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Alle benötigten ARIA-Attribute sind vorhanden.\",\n      \"fail\": {\n        \"singular\": \"Benötigtes ARIA Attribut nicht vorhanden: ${data.values}\",\n        \"plural\": \"Benötigte ARIA Attribute nicht vorhanden: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Alle benötigten ARIA Kinder sind vorhanden.\",\n        \"aria-busy\": \"Element hat ein aria-busy-Attribut, daher ist es erlaubt, erforderliche ARIA Kinder wegzulassen\"\n      },\n      \"fail\": {\n        \"singular\": \"Benötigte ARIA Kindrolle nicht vorhanden: ${data.values}\",\n        \"plural\": \"Benötigte ARIA Kindrollen nicht vorhanden: ${data.values}\",\n        \"unallowed\": \"Element hat Kinder, die nicht erlaubt sind: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Entsprechende ARIA Kindrolle muss hinzugefügt werden: ${data.values}\",\n        \"plural\": \"Entsprechende ARIA Kindrollen müssen hinzugefügt werden: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Alle ARIA Elternrollen sind vorhanden.\",\n      \"fail\": {\n        \"singular\": \"Benötigte ARIA Elternrolle nicht vorhanden: ${data.values}\",\n        \"plural\": \"Benötigte ARIA Elternrollen nicht vorhanden: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription mit einer unterstützten semantischen Rolle verwendet.\",\n      \"incomplete\": \"Es sollte überprüft werden ob aria-roledescription von einem Screenreader vorgelesen wird.\",\n      \"fail\": \"Das Element muss mit einer Rolle, welche aria-roledescription unterstützt, versehen werden.\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA Attribut wird unterstützt\",\n      \"fail\": \"ARIA Attribut ist nicht allgemein in Screenreadern und anderen assistiven Technologien unterstützt: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA Attributwerte sind gültig.\",\n      \"fail\": {\n        \"singular\": \"Ungültiger Wert des ARIA Attributes ${data.values}\",\n        \"plural\": \"Ungültige Werte der ARIA Attribute: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"Verwendete ID im ARIA Attribut existiert nicht auf der Seite: ${data.needsReview}\",\n        \"noIdShadow\": \"ARIA-Attribut Element-ID existiert nicht auf der Seite oder ist ein Nachkomme (descendant) eines anderen Schatten-DOM-tree: ${data.needsReview}\",\n        \"ariaCurrent\": \"Folgendes ARIA Attributwert ist ungültig und wird wie \\\"aria-current=true\\\" gesehen: ${data.needsReview}\",\n        \"idrefs\": \"Es konnte nicht festgestellt werden, ob das ARIA-Attribut element ID auf der Seite existiert: ${data.needsReview}\",\n        \"empty\": \"ARIA-Attributwert wird ignoriert, wenn leer: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"Bei der Verwendung von aria-haspopup konnte nicht festgestellt werden, ob die von aria-controls referenzierte ID auf der Seite existiert: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"Alle ARIA Attributnamen sind gültig.\",\n      \"fail\": {\n        \"singular\": \"Ungültige ARIA Attribut Name: ${data.values}\",\n        \"plural\": \"Ungültige ARIA Attribut Namen: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabel wird für ein Element mit zugänglichem Text verwendet.\",\n      \"fail\": \"aria-braillelabel wird für ein Element ohne zugänglichen Text verwendet.\",\n      \"incomplete\": \"Zugänglicher Text kann nicht berechnet werden.\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescription wird für ein Element mit aria-roledescription verwendet.\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription wird für ein Element ohne aria-roledescription verwendet.\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription wird für ein Element mit einer leeren aria-roledescription verwendet.\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA Rolle ist nicht veraltet.\",\n      \"fail\": \"Die verwendete Rolle ist veraltet: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Nur ein Wert für role genutzt.\",\n      \"fail\": \"Es sollte nur ein Wert für role benutzt werden, da Fallback-Werte in älteren Browsern nicht unterstützt werden.\",\n      \"incomplete\": \"Verwende nur die Rolle 'presentation' oder 'none', da sie synonym sind.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"Element hat globales ARIA Attribut: ${data.values}\",\n        \"plural\": \"Element hat globale ARIA Attribute: ${data.values}\"\n      },\n      \"fail\": \"Das Element hat keine globalen ARIA Attribute.\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Element hat eine widget-Rolle.\",\n      \"fail\": \"Das Element besitzt keine widget-Rolle.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA Rolle ist gültig.\",\n      \"fail\": {\n        \"singular\": \"Folgende Rolle muss eine von den validen ARIA Rollen sein: ${data.values}\",\n        \"plural\": \"Folgende Rollen müssen jeweils eine von den validen ARIA Rollen sein: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"Element ist fokussierbar.\",\n      \"fail\": \"Element ist nicht fokussierbar.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Kein Unterschied zwischen dem <label> und dem zugänglichen Namen (accessible name).\",\n      \"incomplete\": \"Überprüfe, dass das <label> nicht Teil des ARIA ${data} Feldnamens ist.\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA Rolle wird unterstützt.\",\n      \"fail\": \"Folgende Rollen werden nicht allgemein in Screenreadern und assistiven Technologien unterstützt: ${data.values}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Das Element hat eine gültige Semantik für ein Element in der Fokusreihenfolge.\",\n      \"fail\": \"Das Element hat eine ungültige Semantik für ein Element in der Fokusreihenfolge.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Das Element hat einen ausreichenden Kontrast von ${data.contrastRatio}.\",\n      \"fail\": {\n        \"default\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} (Vordergrundfarbe: ${data.fgColor}, Hintergrundfarbe: ${data.bgColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} zwischen der Vordergrund- und der Schattenfarbe (Vordergrundfarbe: ${data.fgColor}, Textschattenfarbe: ${data.shadowColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} zwischen der Schattenfarbe und der Hintergrundfarbe (Textschattenfarbe: ${data.shadowColor}, Hintergrundfarbe: ${data.bgColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Das Kontrastverhältnis konnte nicht ermittelt werden.\",\n        \"bgImage\": \"Die Hintergrundfarbe des Elementes konnte aufgrund eines Hintergrundbildes nicht bestimmt werden.\",\n        \"bgGradient\": \"Die Hintergrundfarbe des Elementes konnte aufgrund eines Hintergrundfarbverlaufes nicht bestimmt werden.\",\n        \"imgNode\": \"Die Hintergrundfarbe des Elementes konnte nicht bestimmt werden, da das Element einen Image Node enthält.\",\n        \"bgOverlap\": \"Die Hintergrundfarbe des Elementes konnte nicht bestimmt werden, da es von einem anderen Element überlagert wird.\",\n        \"fgAlpha\": \"Die Vordergrundfarbe des Elementes konnte aufgrund der Alpha-Transparenz nicht ermittelt werden.\",\n        \"elmPartiallyObscured\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es teilweise von anderen Elementen überdeckt wird.\",\n        \"elmPartiallyObscuring\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es teilweise andere Elemente überdeckt.\",\n        \"outsideViewport\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es sich außerhalb des Viewports befindet.\",\n        \"equalRatio\": \"Das Element hat einen 1:1 Kontrast mit der Hintergrundfarbe.\",\n        \"shortTextContent\": \"Der Inhalt des Elements ist zu kurz um zu bestimmen ob es sich wirklich um Textinhalt handelt.\",\n        \"nonBmp\": \"Das Element enthält ausschließlich Nicht-Text Zeichen.\",\n        \"pseudoContent\": \"Die Hintergrundfarbe konnte aufgrund eines pseudo Elementes nicht bestimmt werden.\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"Das Element hat einen ausreichenden Kontrast von ${data.contrastRatio}.\",\n        \"hidden\": \"Das Element ist versteckt.\"\n      },\n      \"fail\": {\n        \"default\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} (Vordergrundfarbe: ${data.fgColor}, Hintergrundfarbe: ${data.bgColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} zwischen der Vordergrund- und der Schattenfarbe (Vordergrundfarbe: ${data.fgColor}, Textschattenfarbe: ${data.shadowColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Das Element hat einen unzureichenden Kontrast von ${data.contrastRatio} zwischen der Schattenfarbe und der Hintergrundfarbe (Textschattenfarbe: ${data.shadowColor}, Hintergrundfarbe: ${data.bgColor}, Schriftgröße: ${data.fontSize}, Schriftstärke: ${data.fontWeight}). Erwartetes Kontrastverhältnis von ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Das Kontrastverhältnis konnte nicht ermittelt werden.\",\n        \"bgImage\": \"Die Hintergrundfarbe des Elementes konnte aufgrund eines Hintergrundbildes nicht bestimmt werden.\",\n        \"bgGradient\": \"Die Hintergrundfarbe des Elementes konnte aufgrund eines Hintergrundfarbverlaufes nicht bestimmt werden.\",\n        \"imgNode\": \"Die Hintergrundfarbe des Elementes konnte nicht bestimmt werden, da das Element einen Image Node enthält.\",\n        \"bgOverlap\": \"Die Hintergrundfarbe des Elementes konnte nicht bestimmt werden, da es von einem anderen Element überlagert wird.\",\n        \"complexTextShadows\": \"Der Kontrast des Elements konnte nicht bestimmt werden, da es komplexe Textschatten verwendet.\",\n        \"fgAlpha\": \"Die Vordergrundfarbe des Elementes konnte aufgrund der Alpha-Transparenz nicht ermittelt werden.\",\n        \"elmPartiallyObscured\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es teilweise von anderen Elementen überdeckt wird.\",\n        \"elmPartiallyObscuring\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es teilweise andere Elemente überdeckt.\",\n        \"outsideViewport\": \"Die Hintergrundfarbe des Elements konnte nicht bestimmt werden, da es sich außerhalb des Viewports befindet.\",\n        \"equalRatio\": \"Das Element hat einen 1:1 Kontrast mit der Hintergrundfarbe.\",\n        \"shortTextContent\": \"Der Inhalt des Elements ist zu kurz um zu bestimmen ob es sich wirklich um Textinhalt handelt.\",\n        \"nonBmp\": \"Das Element enthält ausschließlich Nicht-Text Zeichen.\",\n        \"pseudoContent\": \"Die Hintergrundfarbe konnte aufgrund eines pseudo Elementes nicht bestimmt werden.\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Links können durch visuelle Gestaltung vom umgebenden Text unterschieden werden.\",\n      \"incomplete\": {\n        \"default\": \"Prüfe, ob der Link ein Styling benötigt, um sich vom umgebenden Text zu unterscheiden.\",\n        \"pseudoContent\": \"Prüfe, ob der Pseudostil des Links ausreicht, um ihn vom umgebenden Text zu unterscheiden.\"\n      },\n      \"fail\": \"Der Link hat kein Styling (z.B. Unterstreichung), um ihn vom umgebenden Text zu unterscheiden.\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Links können vom umgebenenden Text auf unterschiedliche Art und Weise unterschieden werden.\",\n      \"fail\": {\n        \"fgContrast\": \"Der Link hat einen unzureichenden Kontrast von ${data.contrastRatio}:1 mit dem umgebenden Text (Mindestkontrast ist ${data.requiredContrastRatio}:1, Linktext: ${data.nodeColor}, umgebender Text: ${data.parentColor}).\",\n        \"bgContrast\": \"Der Link-Hintergrund hat einen unzureichenden Kontrast von ${data.contrastRatio} (Mindestkontrast ist ${data.requiredContrastRatio}:1, Link-Hintergrundfarbe: ${data.nodeBackgroundColor}, umgebende Hintergrundfarbe: ${data.parentBackgroundColor}).\"\n      },\n      \"incomplete\": {\n        \"default\": \"Das Kontrastverhältnis konnte nicht ermittelt werden.\",\n        \"bgContrast\": \"Das Kontrastverhältnis des Elements konnte nicht bestimmt werden. Suchen Sie nach einem bestimmten Hover/Fokus-Stil.\",\n        \"bgImage\": \"Das Kontrastverhältnis des Elements konnte aufgrund eines Hintergrundbildes nicht bestimmt werden.\",\n        \"bgGradient\": \"Das Kontrastverhältnis des Elements konnte aufgrund eines Hintergrundfarbverlaufes nicht bestimmt werden.\",\n        \"imgNode\": \"Das Kontrastverhältnis des Elements konnte nicht bestimmt werden, da das Element einen Image Node enthält.\",\n        \"bgOverlap\": \"Das Kontrastverhältnis des Elements konnte aufgrund einer Überlagerung nicht bestimmt werden.\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"Der Wert des autocomplete Attributes ist für diese Art des Eingabefeldes geeignet.\",\n      \"fail\": \"Der Wert des autocomplete Attributes ist für diese Art des Eingabefeldes nicht geeignet.\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"Der Wert des autocomplete Attributes ist korrekt formatiert.\",\n      \"fail\": \"Der Wert des autocomplete Attributes ist inkorrekt formatiert.\",\n      \"incomplete\": \"Der Wert des autocomplete Attributes hat einen Nicht-Standardwert. Prüfe, ob stattdessen ein Standardwert verwendet werden kann.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Alle accesskey-Attribute sind einzigartig.\",\n      \"fail\": \"Das Dokument enthält mehrere Elemente mit dem gleichen accesskey-Attribut.\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Das Element beeinhaltet fokussierbaren Inhalt.\",\n      \"fail\": \"Das Element beeinhaltet keinen fokussierbaren Inhalt.\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Das Element beeinhaltet keinen fokussierbaren Inhalt.\",\n      \"incomplete\": \"Prüfe, ob die fokussierbaren Elemente den Fokusindikator sofort bewegen.\",\n      \"fail\": \"Fokussierbarer Inhalt sollte deaktiviert oder vom DOM entfernt werden.\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Element ist fokussierbar.\",\n      \"fail\": \"Element sollte fokussierbar sein.\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Keine fokussierbaren Elemente während ein modaler Dialog offen ist.\",\n      \"incomplete\": \"Überprüfe ob Elemente während des derzeitigen Status fokussierbar sind.\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Das Element befindet sich nicht in der Tabreihenfolge und enthält keinen zugänglichen Text.\",\n      \"fail\": \"Das Element befindet sich in der Tabreihenfolge und enthält keinen zugänglichen Text.\",\n      \"incomplete\": \"Es ist nicht möglich herauszufinden ob Element einen zugänglichen Namen (accessible name) besitzt.\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Keine fokussierbaren Elemente innerhalb des Elements.\",\n      \"incomplete\": \"Prüfe, ob die fokussierbaren Elemente den Fokusindikator sofort bewegen.\",\n      \"fail\": \"Fokussierbare Elemente sollten mit tabindex='-1' versehen oder vom DOM entfernt werden.\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"Element hat keine fokussierbaren Nachkommen (descendants).\",\n      \"fail\": \"Element hat fokussierbare Nachkommen (descendants).\",\n      \"incomplete\": \"Es konnte nicht festgestellt werden, ob das Element Nachkommen (descendants) hat.\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"Die ${data.role} landmark befindet sich auf höchster Ebene.\",\n      \"fail\": \"Die ${data.role} landmark befindet sich innerhalb einer anderen landmark.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"Element hat keine fokussierbaren Nachkommen (descendants).\",\n      \"fail\": {\n        \"default\": \"Element hat fokussierbare Nachkommen (descendants).\",\n        \"notHidden\": \"Die Verwendung eines negativen Tabindex für ein Element innerhalb eines interaktiven Steuerelements verhindert nicht, dass assistive Technologien das Element fokussieren (selbst bei aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Es konnte nicht festgestellt werden, ob das Element Nachkommen (descendants) hat.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Die Seite besitzt mindestens eine Überschrift der ersten Ebene.\",\n      \"fail\": \"Die Seite muss eine Überschrift erster Ebene besitzen.\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Die Seite besitzt eine main landmark.\",\n      \"fail\": \"Die Seite muss eine main landmark besitzen.\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Das Dokument besitzt nicht mehr als eine banner landmark.\",\n      \"fail\": \"Das Dokument besitzt mehr als eine banner landmark.\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Das Dokument besitzt nicht mehr als eine contentinfo landmark.\",\n      \"fail\": \"Das Dokument besitzt mehr als eine contentinfo landmark.\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Das Dokument besitzt nicht mehr als eine main landmark.\",\n      \"fail\": \"Das Dokument besitzt mehr als eine main landmark.\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Das Element besitzt einen tabindex-Attributwert der nicht größer als 0 ist.\",\n      \"fail\": \"Das Element besitzt einen tabindex-Attributwert größer als 0.\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Element hat ein valides alt Attribut.\",\n      \"fail\": \"Element hat ein alt Attribut, welches ausschließlich Leerzeichen beeinhaltet, die jedoch nicht durch Screenreader ignoriert werden.\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Das Element besitzt einen Alternativtext der anderweitig vorhanden Text nicht wiederholt.\",\n      \"fail\": \"Das Element besitzt ein <img>-Element mit Alternativtext, der vorhandenen Text wiederholt.\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Das <form>-Element besitzt ein explizites <label>.\",\n      \"fail\": \"Das <form>-Element besitzt kein explizites <label>.\",\n      \"incomplete\": \"Es ist nicht möglich herauszufinden ob das <form> Element ein explizites <label> besitzt.\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Der Hilfstext (title oder aria-describedby) dupliziert den label-Text nicht.\",\n      \"fail\": \"Der Hilfstext (angegeben durch ein title- oder aria-describedby-Attribut) wiederholt den label-Text.\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Das <form> Element besitzt ein sichtbares explizites <label>.\",\n      \"fail\": \"Das <form> Element besitzt ein <label>, welches nicht sichtbar ist.\",\n      \"incomplete\": \"Nicht möglich herauszufinden ob <form> Element ein sichtbares <label> besitzt.\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Das <form>-Element besitzt ein implizites (umschlossenes) <label>-Element.\",\n      \"fail\": \"Das <form>-Element besitzt kein implizites <label>-Element.\",\n      \"incomplete\": \"Nicht möglich herauszufinden ob das <form> Element ein implizites (umschlossenes) <label> besitzt.\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Element beeinhaltet sichtbaren Text als Teil des zugänglichen Namens (accessible name).\",\n      \"fail\": \"Das Element beeinhaltet Text, welcher nicht Teil des zugänglichen Namens (accessible name) ist.\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Das <form>-Element besitzt keine multiplen <label>-Elemente.\",\n      \"incomplete\": \"Elemente mit mehreren Labeln werden in assistiven Technologien nicht allgemein unterstützt. Es sollte sichergestellt werden, dass alle relevanten Informationen im ersten Label enthalten sind.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Das <form>-Element ist nicht nur lediglich durch ein title-Attribut beschriftet.\",\n      \"fail\": \"Das <form>-Element ist lediglich durch ein title-Attribut beschriftet.\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Landmarks besitzen eine einzigartige Rolle oder Rollen/Label/Titel (zugänglicher Name / accessible name) Kombination.\",\n      \"fail\": \"Landmark muss ein einzigartiges aria-label, aria-labelledby oder einen Titel besitzen, um es von anderen zu unterscheiden.\"\n    },\n    \"has-lang\": {\n      \"pass\": \"Das <html>-Element besitzt ein lang-Attribut.\",\n      \"fail\": {\n        \"noXHTML\": \"Das xml:lang-Attribut ist auf HTML Seiten nicht valide, es sollte das lang-Attribut genutzt werden.\",\n        \"noLang\": \"Das <html>-Element besitzt kein lang-Attribut.\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Der Wert des lang-Attributes ist in der Liste der gültigen Sprachen enthalten.\",\n      \"fail\": \"Der Wert des lang-Attributes ist nicht valide.\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Das lang- und xml:lang-Attribut verweisen auf dieselbe Sprache.\",\n      \"fail\": \"Das lang- und xml:lang-Attribut verweisen nicht auf dieselbe Sprache.\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Der Definitionslisteneintrag besitzt ein <dl>-Elternelement.\",\n      \"fail\": \"Der Definitionslisteneintrag besitzt kein <dl>-Elternelement.\"\n    },\n    \"listitem\": {\n      \"pass\": \"Das Aufzählungselement besitzt ein gültiges Elternelement (<ul>, <ol> oder Element mit role=\\\"list\\\").\",\n      \"fail\": {\n        \"default\": \"Aufzählungselement besitzt kein gültiges Elternelement (<ul>, <ol>)\",\n        \"roleNotValid\": \"Aufzählungselement besitzt kein gültiges Elternelement ohne role-Attribut (<ul>, <ol>) oder mit role=\\\"list\\\".\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Das Aufzählungselement enthält Kindelemente, welche innerhalb der <dt> oder <dd>-Elemente erlaubt sind.\",\n      \"fail\": \"Das <dl>-Element enthält unerlaubte Kindelemente.\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Das Aufzählungselement besitzt Kinder, welche innerhalb eines <li>-Elements erlaubt sind.\",\n      \"fail\": \"Das Aufzählungselement besitzt Kinder, die nicht erlaubt sind: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Das Definitionslisten-Element enthält sowohl <dt> als auch <dd>-Elemente, falls es nicht leer sein sollte.\",\n      \"fail\": \"Das Definitionslisten-Element enthält kein <dt>-Element, welches von keinem <dd>-Element gefolgt wird.\"\n    },\n    \"caption\": {\n      \"pass\": \"Das Multimedia-Element besitzt eine Untertitelung (captions track).\",\n      \"incomplete\": \"Für das Element konnte keine Untertitelung (captions track) gefunden werden.\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Das iFrame konnte mit axe-core getestet werden.\",\n      \"fail\": \"Das iFrame konnte nicht mit axe-core getestet werden.\",\n      \"incomplete\": \"Das iFrame muss noch mit axe-core getestet werden.\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"Die <video> oder <audio>-Elemente geben keinen Ton über die erlaubte Zeitspanne aus oder haben Kontrollmöglichkeiten.\",\n      \"fail\": \"Die <video> oder <audio>-Elemente geben Ton über die erlaubte Zeitspanne aus oder haben keine Kontrollmöglichkeiten.\",\n      \"incomplete\": \"Es sollte überprüft werden, dass <video> oder <audio>-Elemente keinen Ton über die erlaubte Zeitspanne ausgeben oder Kontrollmöglichkeiten haben.\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Display ist bedienbar, und eine CSS-Ausrichtungssperre ist nicht vorhanden.\",\n      \"fail\": \"CSS-Ausrichtungssperre wird angewendet und macht die Anzeige unbrauchbar.\",\n      \"incomplete\": \"Der Wert der CSS-Ausrichtungssperre kann nicht ermittelt werden.\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"Der <meta>-Tag schränkt das Zoomen nicht ein.\",\n      \"fail\": \"Die viewport-Einstellungen im <meta>-Tag schränken das Zoomen auf mobilen Geräten ein.\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"Der <meta>-Tag blockiert das Zoomen auf mobilen Geräten nicht.\",\n      \"fail\": \"Die viewport-Einstellungen im <meta>-Tag blockieren das Zoomen auf mobilen Geräten.\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"Das Ziel hat genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px, der mindestens ${data.minOffset}px beträgt.\",\n        \"large\": \"Das Ziel überschreitet bei weitem die Mindestgröße von ${data.minOffset}px.\"\n      },\n      \"fail\": \"Das Ziel hat nicht genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px statt mindestens ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"Element mit negativem Tabindex hat nicht genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px statt mindestens ${data.minOffset}px. Ist dies ein Ziel?\",\n        \"nonTabbableNeighbor\": \"Das Ziel hat nicht genügend Abstand zu seinen nächsten Nachbarn. Der sichere klickbare Bereich hat einen Durchmesser von ${data.closestOffset}px statt mindestens ${data.minOffset}px. Ist der Nachbar ein Ziel?\",\n        \"tooManyRects\": \"Die Zielgröße konnte nicht ermittelt werden, da zu viele überlappende Elemente vorhanden sind.\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Das Steuerelement hat eine ausreichende Größe (${data.width}px x ${data.height}px, sollte mindestens ${data.minSize}px x ${data.minSize}px sein).\",\n        \"obscured\": \"Das Steuerelement wird ignoriert, da es vollständig verdeckt ist und daher nicht angeklickt werden kann.\",\n        \"large\": \"Das Ziel überschreitet bei weitem die Mindestgröße von ${data.minSize}px.\"\n      },\n      \"fail\": {\n        \"default\": \"Das Ziel hat eine unzureichende Größe (${data.width}px x ${data.height}px, sollte mindestens ${data.minSize}px x ${data.minSize}px sein).\",\n        \"partiallyObscured\": \"Das Ziel hat eine unzureichende Größe, weil es teilweise verdeckt ist (der kleinste Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein).\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element mit negativem Tabindex hat unzureichende Größe (${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist dies ein Ziel?\",\n        \"contentOverflow\": \"Elementgröße konnte aufgrund von Überlaufinhalten nicht genau bestimmt werden\",\n        \"partiallyObscured\": \"Element mit negativem Tabindex hat unzureichende Größe, weil es teilweise verdeckt ist (kleinster Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist dies ein Ziel?\",\n        \"partiallyObscuredNonTabbable\": \"Das Ziel hat eine unzureichende Größe, weil es teilweise von einem Nachbarn mit negativem Tabindex verdeckt wird (der kleinste Platz ist ${data.width}px mal ${data.height}px, sollte mindestens ${data.minSize}px mal ${data.minSize}px sein). Ist der Nachbar ein Ziel?\",\n        \"tooManyRects\": \"Die Zielgröße konnte nicht ermittelt werden, da zu viele überlappende Elemente vorhanden sind.\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"Die Seite besitzt eine Seitenüberschrift.\",\n      \"fail\": \"Die Seite besitzt keine Seitenüberschrift.\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Die Überschriftenstruktur ist gültig.\",\n      \"fail\": \"Die Überschriftenstruktur ist nicht valide.\",\n      \"incomplete\": \"Vorherige Überschrift kann nicht ermittelt werden.\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Es befinden sich keine Links auf der Seite, welche mit demselben Namen auf dasselbe Ziel verweisen.\",\n      \"incomplete\": \"Prüfen Sie, ob die Links den gleichen Zweck haben oder absichtlich mehrdeutig sind.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Es wurde ein gültiger Skip-Link gefunden.\",\n      \"fail\": \"Kein gültiger Skip-Link gefunden.\"\n    },\n    \"landmark\": {\n      \"pass\": \"Die Seite besitzt eine landmark region.\",\n      \"fail\": \"Die Seite besitzt keine landmark region.\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"<meta> Tag aktualisiert die Seite nicht sofort.\",\n      \"fail\": \"<meta> Tag erzwingt eine zeitgesteuerte Aktualisierung der Seite.\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Der <meta>-Tag erzwingt keine sofortige Aktualisierung der Seite.\",\n      \"fail\": \"Der <meta>-Tag erzwingt eine zeitgesteuerte Aktualisierung der Seite.\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p>-Elemente werden nicht als Überschriftenelement zweckentfremdet.\",\n      \"fail\": \"Anstelle eines Überschriftenelementes wird lediglich ein durch Formatierungen hervorgehobenes <p>-Element verwendet.\",\n      \"incomplete\": \"Es kann nicht festgestellt werden, ob <p>-Elemente als Überschriften gestylt sind.\"\n    },\n    \"region\": {\n      \"pass\": \"Jeglicher Inhalt der Seite befindet sich in einer landmark.\",\n      \"fail\": \"Der Inhalt befindet sich nicht in einer ARIA landmark.\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Das Ziel des Skip-Links existiert.\",\n      \"incomplete\": \"Der Skip-Link sollte bei Aktivierung sichtbar werden.\",\n      \"fail\": \"Es existiert kein Ziel für den Skip-Link.\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Das title-Attribut des Elements ist einzigartig.\",\n      \"fail\": \"Das title-Attribut des Elementes ist nicht einmalig.\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Dokument hat keine aktiven Elemente mit denselben ID-Attributen.\",\n      \"fail\": \"Dokument hat aktiven Elemente mit denselben ID-Attributen: ${data}.\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Dokument besitzt keine Elemente, welche mit ARIA oder Labels referenziert werden, welche die gleiche ID besitzen.\",\n      \"fail\": \"Dokument besitzt Elemente, welche mit ARIA oder Labels referenziert werden, welche folgende gleiche ID besitzen: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Das Dokument besitzt eine einzigartige ID.\",\n      \"fail\": \"Das Dokument besitzt mehrere Elemente mit demselben id-Attributwert: ${data}.\"\n    },\n    \"aria-label\": {\n      \"pass\": \"Das aria-label-Attribut existiert und ist nicht leer.\",\n      \"fail\": \"Es existiert kein aria-label-Attribut oder das Attribut ist leer.\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"Das aria-labelledby-Attribut existiert und referenziert ein Element, welches für Screen Reader sichtbar ist.\",\n      \"fail\": \"Das aria-labelledby-Attribut existiert nicht oder referenziert ein Element, das nicht existiert, nicht sichtbar oder leer ist.\",\n      \"incomplete\": \"Es sollte sichergestellt werden, dass aria-labelledby auf ein existierendes Element verweist.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Es werden keine inline-Stilangaben mit '!important' spezifiziert, welche den Textabstand beeinflussen.\",\n      \"fail\": {\n        \"singular\": \"Es sollte '!important' vom inline-Stil ${data.values} entfernt werden, da das Überschreiben in den meisten Browsern nicht erlaubt ist.\",\n        \"plural\": \"Es sollte '!important' von den inline-Stilen ${data.values} entfernt werden, da das Überschreiben in den meisten Browsern nicht erlaubt ist.\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Das Element besitzt Text, der für Screenreader sichtbar ist.\",\n      \"fail\": \"Das Element besitzt keinen Text, der für Screenreader sichtbar ist.\",\n      \"incomplete\": \"Ob das Element über Kindelemente bzw. textuelle Inhalte verfügt, kann nicht ermittelt werden.\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Das Dokument besitzt ein nichtleeres <title>-Element.\",\n      \"fail\": \"Das Dokument besitzt kein <title>-Element oder das <title>-Element ist leer.\"\n    },\n    \"exists\": {\n      \"pass\": \"Das Element existiert nicht.\",\n      \"incomplete\": \"Das Element existiert.\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Das Element besitzt ein alt-Attribut.\",\n      \"fail\": \"Das Element besitzt kein alt-Attribut.\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Das Element besitzt Text, der für Screenreader sichtbar ist.\",\n      \"fail\": \"Das Element besitzt keinen Text, der für Screenreader sichtbar ist.\",\n      \"incomplete\": \"Es ist nicht möglich zu ermitteln, ob das Element Kinder besitzt.\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"letter-spacing im style-Attribut ist nicht auf !important gesetzt oder entspricht dem Minimum.\",\n      \"fail\": \"letter-spacing im style-Attribut darf nicht !important sein oder muss ${data.minValue}em (aktuell ${data.value}em) entsprechen.\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"line-height im style-Attribut ist nicht auf !important gesetzt oder entspricht dem Minimum.\",\n      \"fail\": \"line-height im style-Attribut darf nicht !important sein oder muss ${data.minValue}em (aktuell ${data.value}em) entsprechen.\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"word-spacing im style-Attribut ist nicht auf !important gesetzt oder entspricht dem Minimum.\",\n      \"fail\": \"word-spacing im style-Attribut darf nicht !important sein oder muss ${data.minValue}em (aktuell ${data.value}em) entsprechen.\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Das Element ist nicht sichtbar.\",\n      \"fail\": \"Das Element ist sichtbar.\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Das Element hat ein nichtleeres alt-Attribut.\",\n      \"fail\": {\n        \"noAttr\": \"Das Element hat kein alt-Attribut.\",\n        \"emptyAttr\": \"Das Element hat ein leeres alt-Attribut.\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Das Element hat kein value-Attribut.\",\n        \"has-label\": \"Das Element hat ein nichtleeres value-Attribut.\"\n      },\n      \"fail\": \"Das Element besitzt ein value-Attribut und das value-Attribut ist leer.\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"Element hat ein nichtleeres Platzhalterattribut.\",\n      \"fail\": {\n        \"noAttr\": \"Element hat kein Platzhalterattribut.\",\n        \"emptyAttr\": \"Element hat ein leeres Platzhalterattribut.\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Das Element hat ein nichtleeres title-Attribut.\",\n      \"fail\": {\n        \"noAttr\": \"Element hat kein title-Attribut.\",\n        \"emptyAttr\": \"Element hat ein leeres title-Attribut.\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Das Element hat ein nichtleeres value-Attribut\",\n      \"fail\": {\n        \"noAttr\": \"Element hat kein value-Attribut.\",\n        \"emptyAttr\": \"Element hat ein leeres value-Attribut.\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"Die Standardsemantik des Elements wurden mit der Rolle \\\"${data.role}\\\" überschrieben.\",\n      \"fail\": {\n        \"default\": \"Die Standardsemantik des Elements wurden nicht mit der Rolle role=\\\"none\\\" oder role=\\\"presentation\\\" überschrieben.\",\n        \"globalAria\": \"Die Rolle des Elements ist nicht präsentativ aufgrund des globalen ARIA Attributs.\",\n        \"focusable\": \"Die Rolle des Elements ist nicht präsentativ aufgrund der Möglichkeit es zu fokussieren.\",\n        \"both\": \"Die Rolle des Elements ist nicht präsentativ aufgrund des zugewiesenen globalen ARIA Attributs und der Möglichkeit es zu fokussieren.\",\n        \"iframe\": \"Die Verwendung des \\\"title\\\"-Attributs auf einem ${data.nodeName}-Element mit einer Präsentationsrolle verhält sich inkonsistent zwischen Screenreadern.\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"Die Standard-Semantik des Elementes ist mit role=\\\"none\\\" überschrieben.\",\n      \"fail\": \"Die Standard-Semantik des Elementes ist nicht mit role=\\\"none\\\" überschrieben.\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Die Standard-Semantik des Elementes ist mit role=\\\"presentation\\\" überschrieben.\",\n      \"fail\": \"Die Standard-Semantik des Elementes ist nicht mit role=\\\"presentation\\\" überschrieben.\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"Element hat ein Kind, welches ein Titel ist.\",\n      \"fail\": {\n        \"noTitle\": \"Element hat ein Kind, welches kein Titel ist.\",\n        \"emptyTitle\": \"Das Kind des Elements, welches ein Titel ist, ist leer.\"\n      },\n      \"incomplete\": \"Es ist nicht möglich zu ermitteln ob das Element ein Kind hat, welches ein Titel ist.\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Die erste Zeile der Tabelle wird nicht als Tabellenüberschrift verwendet.\",\n      \"fail\": \"Die erste Zeile der Tabelle sollte nicht als Tabellenüberschrift verwendet werden.\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Das scope-Attribut wird nur für Tabellenkopfzellen (<th>) verwendet.\",\n      \"fail\": \"In HTML5 dürfen scope-Attribute lediglich für Tabellenkopfzellen (<th>) verwendet werden.\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Die Inhalte des summary-Attributes und des <caption>-Elementes sind nicht identisch.\",\n      \"fail\": \"Die Inhalte des summary-Attributes und des <caption>-Elementes sind identisch.\",\n      \"incomplete\": \"Es kann nicht festgestellt werden, ob das <table>-Element eine Überschrift hat.\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Das scope-Attribut wird korrekt verwendet.\",\n      \"fail\": \"Das <td>-Element besitzt ein scope-Attribut. In HTML5 dürfen scope-Attribute jedoch lediglich für Tabellenkopfzellen <th> verwendet werden.\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Alle nichtleeren Datenzellen haben eine Tabellenkopfzelle.\",\n      \"fail\": \"Nicht alle (nichtleeren) Datenzellen haben eine Tabellenkopfzelle.\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Das headers-Attribut wird ausschließlich dafür verwendet, um auf andere Kopfzellen in der Tabelle zu verweisen.\",\n      \"incomplete\": \"Das headers-Attribut ist leer.\",\n      \"fail\": {\n        \"cell-header-not-in-table\": \"Das headers-Attribut wird nicht ausschließlich dafür verwendet, um auf andere Kopfzellen in der Tabelle zu verweisen.\",\n        \"cell-header-not-th\": \"Das headers-Attribut muss auf Kopfzellen verweisen, nicht auf Datenzellen.\",\n        \"header-refs-self\": \"Das Element mit dem headers-Attribut verweist auf sich selbst.\"\n      }\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Alle Tabellenkopfzellen beziehen sich auf Datenzellen.\",\n      \"fail\": \"Nicht alle Tabellenkopfzellen beziehen sich auf Datenzellen.\",\n      \"incomplete\": \"Datenzellen der Tabelle fehlen oder sind leer.\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Jeglicher Inhalt der Seite wurde analysiert.\",\n      \"fail\": \"Beim Analysieren der Inhalte auf dieser Seite sind Probleme aufgetreten.\",\n      \"incomplete\": \"Auf der Seite befinden sich versteckte Inhalte, die nicht analysiert werden konnten. Um den Inhalt analysieren zu können, müssen Sie die Anzeige dieser Inhalte auslösen.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Korrigiere mindestens einen der folgenden Punkte:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Korrigiere alle der folgenden Punkte:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe konnte den Grund nicht ermitteln. Versuchen Sie es mit dem Element Inspector.\"\n}\n"
  },
  {
    "path": "locales/el.json",
    "content": "{\n  \"lang\": \"el\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Διασφαλίζει ότι η τιμή κάθε χαρακτηριστικού accesskey είναι μοναδική\",\n      \"help\": \"η τιμή κάθε χαρακτηριστικού accesskey πρέπει να είναι μοναδική\"\n    },\n    \"area-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <area> των χαρτών εικόνας έχουν εναλλακτικό κείμενο\",\n      \"help\": \"Τα ενεργά στοιχεία <area> πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Διασφαλίζει ότι o ρόλος του στοιχείου υποστηρίζει τα χαρακτηριστικά ARIA\",\n      \"help\": \"Τα στοιχεία πρέπει να χρησιμοποιούν μόνο υποστηριζόμενα χαρακτηριστικά ARIA\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Διασφαλίζει ότι το χαρακτηριστικό role έχει την κατάλληλη τιμή για το στοιχείο\",\n      \"help\": \"Ο ρόλος ARIA θα πρέπει να είναι κατάλληλος για το στοιχείο\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε button, link ή menuitem ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι εντολές ARIA πρέπει να έχουν ένα προσβάσιμο όνομα\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε ARIA σε κόμβο διαλόγου ή διαλόγου ειδοποίησης έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι ARIA κόμβοι διαλόγου καί διαλόγου ειδοποίησης πρέπει να έχουν ένα προσβάσιμο όνομα\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Διασφαλίζει ότι το aria-hidden='true' δεν υπάρχει στο σώμα (body) του εγγράφου.\",\n      \"help\": \"Το aria-hidden='true' δεν πρέπει να υπάρχει στο σώμα (body) του εγγράφου.\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Διασφαλίζει ότι τα κρυφά στοιχεία aria δεν είναι εστιάσιμα ούτε περιέχουν στοιχεία που μπορούν να εστιαστούν\",\n      \"help\": \"Το κρυφό στοιχείο ARIA δεν πρέπει να έχει δυνατότητα εστίασης ή να περιέχει στοιχεία με δυνατότητα εστίασης\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε πεδίο εισαγωγής ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Τα πεδία εισαγωγής ARIA πρέπει να έχουν προσβάσιμο όνομα\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε κόμβος μετρητή ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι κόμβοι μετρητών ARIA πρέπει να έχουν προσβάσιμο όνομα\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε κόμβος γραμμής προόδου ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι κόμβοι γραμμής προόδου ARIA πρέπει να έχουν ένα προσβάσιμο όνομα\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία με ρόλους ARIA έχουν όλα τα απαιτούμενα χαρακτηριστικά ARIA\",\n      \"help\": \"Πρέπει να παρέχονται τα απαιτούμενα χαρακτηριστικά ARIA\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία με ρόλο ARIA που απαιτούν θυγατρικούς ρόλους περιέχουν τα στοιχεία\",\n      \"help\": \"Ορισμένοι ρόλοι ARIA πρέπει να περιέχουν συγκεκριμένα παιδιά\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία με ρόλο ARIA που απαιτούν γονικούς ρόλους περιέχονται σε αυτά\",\n      \"help\": \"Συγκεκριμένοι ρόλοι ARIA πρέπει να περιέχονται από συγκεκριμένους γονείς\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Διασφαλίζει ότι το aria-roledescription χρησιμοποιείται μόνο σε στοιχεία με έμμεσο ή ρητό ρόλο\",\n      \"help\": \"Το aria-roledescription πρέπει να είναι σε στοιχεία με σημασιολογικό ρόλο\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Διασφαλίζει ότι όλα τα στοιχεία με χαρακτηριστικό ρόλου χρησιμοποιούν μια έγκυρη τιμή\",\n      \"help\": \"Οι ρόλοι ARIA που χρησιμοποιούνται πρέπει να συμμορφώνονται με έγκυρες τιμές\"\n    },\n    \"aria-text\": {\n      \"description\": \"Διασφαλίζει ότι το \\\"role=text\\\" χρησιμοποιείται σε στοιχεία χωρίς δυνατότητα εστίασης\",\n      \"help\": \"Το \\\"role=text\\\" δεν πρέπει να έχει εστιάσιμους απογόνους\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε πεδίο εναλλαγής ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Τα πεδία εναλλαγής ARIA πρέπει να έχουν προσβάσιμο όνομα\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε κόμβος tooltip ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι κόμβοι tooltip ARIA πρέπει να έχουν προσβάσιμο όνομα\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Διασφαλίζει ότι κάθε κόμβος treeitem ARIA έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Οι κόμβοι treeitem ARIA θα πρέπει να έχουν ένα προσβάσιμο όνομα\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Διασφαλίζει ότι όλα τα χαρακτηριστικά ARIA έχουν έγκυρες τιμές\",\n      \"help\": \"Τα χαρακτηριστικά ARIA πρέπει να είναι σύμφωνα με έγκυρες τιμές\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Διασφαλίζει ότι τα χαρακτηριστικά που ξεκινούν με aria- είναι έγκυρα χαρακτηριστικά ARIA\",\n      \"help\": \"Τα χαρακτηριστικά ARIA πρέπει να είναι σύμφωνα με έγκυρα ονόματα\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <audio> έχουν υπότιτλους\",\n      \"help\": \"Τα στοιχεία <audio> πρέπει να έχουν λεζάντες\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Διασφαλίζει ότι το χαρακτηριστικό autocomplete είναι σωστό και κατάλληλο για το πεδίο φόρμας\",\n      \"help\": \"Το χαρακτηριστικό autocomplete πρέπει να χρησιμοποιείται σωστά\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Διασφαλίζει ότι η απόσταση κειμένου που έχει οριστεί μέσω χαρακτηριστικών στυλ μπορεί να προσαρμοστεί με προσαρμοσμένα φύλλα στυλ\",\n      \"help\": \"Το ενσωματωμένο διάστημα κειμένου πρέπει να είναι ρυθμιζόμενο με προσαρμοσμένα φύλλα στυλ\"\n    },\n    \"blink\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <blink> δεν χρησιμοποιούνται\",\n      \"help\": \"Τα στοιχεία <blink> έχουν καταργηθεί και δεν πρέπει να χρησιμοποιούνται\"\n    },\n    \"button-name\": {\n      \"description\": \"Διασφαλίζει ότι τα κουμπιά έχουν ευδιάκριτο κείμενο\",\n      \"help\": \"Τα κουμπιά πρέπει να έχουν ευδιάκριτο κείμενο\"\n    },\n    \"bypass\": {\n      \"description\": \"Διασφαλίζει ότι κάθε σελίδα έχει τουλάχιστον έναν μηχανισμό για τον χρήστη να παρακάμπτει την πλοήγηση και να μεταβαίνει απευθείας στο περιεχόμενο\",\n      \"help\": \"Η σελίδα πρέπει να έχει τρόπο για την παράκαμψη επαναλαμβανόμενων μπλοκ\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Διασφαλίζει ότι η ενισχυμένη αντίθεση μεταξύ των χρωμάτων προσκηνίου και φόντου πληροί τα όρια αναλογίας αντίθεσης WCAG 2 AAA\",\n      \"help\": \"Τα στοιχεία πρέπει να έχουν επαρκώς ενισχυμένη χρωματική αντίθεση\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Διασφαλίζει ότι η αντίθεση μεταξύ των χρωμάτων προσκηνίου και φόντου πληροί τα όρια αναλογίας αντίθεσης WCAG 2 AA\",\n      \"help\": \"Τα στοιχεία πρέπει να έχουν επαρκή χρωματική αντίθεση\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Διασφαλίζει ότι το περιεχόμενο δεν είναι κλειδωμένο σε κάποιον συγκεκριμένο προσανατολισμό οθόνης και ότι είναι λειτουργικό σε όλους τους προσανατολισμούς οθόνης\",\n      \"help\": \"Τα ερωτήματα πολυμέσων CSS δεν πρέπει να κλειδώνουν τον προσανατολισμό της οθόνης\"\n    },\n    \"definition-list\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <dl> έχουν δομηθεί σωστά\",\n      \"help\": \"Τα στοιχεία <dl> πρέπει να περιέχουν μόνο σωστά διατεταγμένες ομάδες <dt> και <dd>, και <script>, <template> ή <div> στοιχεία\"\n    },\n    \"dlitem\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <dt> και <dd> περιέχονται από ένα <dl>\",\n      \"help\": \"Τα στοιχεία <dt> και <dd> πρέπει να περιέχονται από ένα <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Διασφαλίζει ότι κάθε έγγραφο HTML περιέχει ένα μη κενό στοιχείο <title>\",\n      \"help\": \"Τα έγγραφα πρέπει να διαθέτουν το στοιχείο <title> για να βοηθήσουν στην πλοήγηση\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Διασφαλίζει ότι κάθε τιμή του χαρακτηριστικού id των ενεργών στοιχείων είναι μοναδική\",\n      \"help\": \"Τα ID των ενεργών στοιχείων πρέπει να είναι μοναδικά\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Διασφαλίζει ότι κάθε τιμή χαρακτηριστικού id που χρησιμοποιείται στο ARIA και στις ετικέτες είναι μοναδική\",\n      \"help\": \"Τα ID που χρησιμοποιούνται στο ARIA και οι ετικέτες πρέπει να είναι μοναδικά\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Διασφαλίζει ότι κάθε τιμή χαρακτηριστικού id είναι μοναδική\",\n      \"help\": \"Η τιμή του χαρακτηριστικού id πρέπει να είναι μοναδική\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Διασφαλίζει ότι οι επικεφαλίδες έχουν ευδιάκριτο κείμενο\",\n      \"help\": \"Οι επικεφαλίδες δεν πρέπει να είναι κενές\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Διασφαλίζει ότι οι κεφαλίδες του πίνακα έχουν ευδιάκριτο κείμενο\",\n      \"help\": \"Το κείμενο κεφαλίδας πίνακα δεν πρέπει να είναι κενό\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία στη σειρά εστίασης έχουν ρόλο κατάλληλο για διαδραστικό περιεχόμενο\",\n      \"help\": \"Τα στοιχεία στη σειρά εστίασης πρέπει να έχουν τον κατάλληλο ρόλο\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Διασφαλίζει ότι το πεδίο της φόρμας δεν έχει πολλαπλά στοιχεία ετικέτας\",\n      \"help\": \"Το πεδίο φόρμας δεν πρέπει να έχει πολλαπλά στοιχεία ετικέτας\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <frame> και <iframe> με περιεχόμενο που μπορεί να εστιαστεί δεν έχουν tabindex=-1\",\n      \"help\": \"Τα στοιχεία <frame> και <iframe> με εστιάσιμο περιεχόμενο δεν πρέπει να έχουν tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <iframe> και <frame> περιέχουν τη δέσμη ενεργειών του axe-core\",\n      \"help\": \"Τα στοιχεία <iframe> και <frame πρέπει να ελέγχονται με το axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <iframe> και <frame> περιέχουν ένα μοναδικό χαρακτηριστικό τίτλου\",\n      \"help\": \"Τα πλαίσια πρέπει να έχουν ένα μοναδικό χαρακτηριστικό τίτλου\"\n    },\n    \"frame-title\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <iframe> και <frame> έχουν ένα προσβάσιμο όνομα\",\n      \"help\": \"Τα πλαίσια πρέπει να έχουν ένα προσβάσιμο όνομα\"\n    },\n    \"heading-order\": {\n      \"description\": \"Διασφαλίζει ότι η σειρά των επικεφαλίδων είναι σημασιολογικά σωστή\",\n      \"help\": \"Τα επίπεδα επικεφαλίδων θα πρέπει να αυξάνονται μόνο κατά ένα\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Ενημερώνει τους χρήστες για κρυφό περιεχόμενο.\",\n      \"help\": \"Το κρυμμένο περιεχόμενο στη σελίδα πρέπει να αναλυθεί\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Διασφαλίζει ότι κάθε έγγραφο HTML έχει ένα χαρακτηριστικό lang\",\n      \"help\": \"Το στοιχείο <html> πρέπει να έχει χαρακτηριστικό lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Διασφαλίζει ότι το χαρακτηριστικό lang του στοιχείου <html> έχει έγκυρη τιμή\",\n      \"help\": \"Το στοιχείο <html> πρέπει να έχει μια έγκυρη τιμή για το χαρακτηριστικό lang\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία HTML με έγκυρα χαρακτηριστικά lang και xml:lang συμφωνούν στη βασική γλώσσα της σελίδας\",\n      \"help\": \"Τα στοιχεία HTML με lang και xml:lang πρέπει να έχουν την ίδια βασική γλώσσα\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Διασφαλίζει ότι οι σύνδεσμοι με το ίδιο προσβάσιμο όνομα εξυπηρετούν παρόμοιο σκοπό\",\n      \"help\": \"Οι σύνδεσμοι με το ίδιο όνομα πρέπει να έχουν παρόμοιο σκοπό\"\n    },\n    \"image-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <img> έχουν εναλλακτικό κείμενο ή κανένα ρόλο ή παρουσίαση\",\n      \"help\": \"Οι εικόνες πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Διασφαλίζει ότι το εναλλακτικό κείμενο δεν επαναλαμβάνεται\",\n      \"help\": \"Το εναλλακτικό κείμενο εικόνων δεν πρέπει να επαναλαμβάνεται\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Διασφαλίζει ότι τα κουμπιά εισαγωγής έχουν ευδιάκριτο κείμενο\",\n      \"help\": \"Τα κουμπιά εισαγωγής πρέπει να έχουν ευδιάκριτο κείμενο\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <input type=\\\"image\\\"> έχουν εναλλακτικό κείμενο\",\n      \"help\": \"Τα κουμπιά εικόνας πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία που επισημαίνονται μέσω του περιεχομένου τους πρέπει να έχουν το ορατό κείμενό τους ως μέρος του προσβάσιμου ονόματος τους\",\n      \"help\": \"Τα στοιχεία πρέπει να έχουν το ορατό κείμενό τους ως μέρος του προσβάσιμου ονόματός τους\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Διασφαλίζει ότι κάθε στοιχείο φόρμας έχει ορατή ετικέτα και δεν επισημαίνεται αποκλειστικά με χρήση κρυφών ετικετών ή χαρακτηριστικών τίτλου ή άριας-describedby\",\n      \"help\": \"Τα στοιχεία φόρμας πρέπει να έχουν ορατή ετικέτα\"\n    },\n    \"label\": {\n      \"description\": \"Διασφαλίζει ότι κάθε στοιχείο φόρμας έχει μια ετικέτα\",\n      \"help\": \"Τα στοιχεία φόρμας πρέπει να έχουν ετικέτες\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Διασφαλίζει ότι το ορόσημο του banner βρίσκεται στο ανώτερο επίπεδο\",\n      \"help\": \"Το ορόσημο του banner δεν θα πρέπει να περιέχεται σε άλλο ορόσημο\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Εξασφαλίζει ότι το συμπληρωματικό ορόσημο ή το aside είναι στο κορυφαίο επίπεδο\",\n      \"help\": \"Το aside δεν πρέπει να περιέχεται σε άλλο ορόσημο\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Διασφαλίζει ότι το ορόσημο του contentinfo βρίσκεται στο ανώτατο επίπεδο\",\n      \"help\": \"Το ορόσημο του contentinfo δεν πρέπει να περιέχεται σε άλλο ορόσημο\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Διασφαλίζει ότι το ορόσημο main βρίσκεται σto κορυφαίο επίπεδο\",\n      \"help\": \"Το ορόσημο main δεν πρέπει να περιέχεται σε άλλο ορόσημο\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Διασφαλίζει ότι το έγγραφο έχει το πολύ ένα ορόσημο banner\",\n      \"help\": \"Το έγγραφο δεν πρέπει να έχει περισσότερα από ένα ορόσημα banner\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Διασφαλίζει ότι το έγγραφο έχει το πολύ ένα ορόσημο contentinfo\",\n      \"help\": \"Το έγγραφο δεν πρέπει να έχει περισσότερα από ένα ορόσημα contentinfo\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Διασφαλίζει ότι το έγγραφο έχει το πολύ ένα ορόσημο main\",\n      \"help\": \"Το έγγραφο δεν πρέπει να έχει περισσότερα από ένα ορόσημα main\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Διασφαλίζει ότι το έγγραφο έχει ένα ορόσημο main\",\n      \"help\": \"Το έγγραφο πρέπει να έχει ένα ορόσημο main\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Διασφαλίζει ότι τα ορόσημα είναι μοναδικά\",\n      \"description\": \"Τα ορόσημα πρέπει να έχουν μοναδικό ρόλο ή συνδυασμό ρόλου/ετικέτας/τίτλου (δηλαδή προσβάσιμο όνομα).\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Διασφαλίζει ότι οι σύνδεσμοι διακρίνονται από το περιβάλλον κείμενο με τρόπο που δεν βασίζεται στο χρώμα\",\n      \"help\": \"Οι σύνδεσμοι πρέπει να είναι διακριτοί χωρίς να βασίζονται στο χρώμα\"\n    },\n    \"link-name\": {\n      \"description\": \"Διασφαλίζει ότι οι σύνδεσμοι έχουν ευδιάκριτο κείμενο\",\n      \"help\": \"Οι σύνδεσμοι πρέπει να έχουν ευδιάκριτο κείμενο\"\n    },\n    \"list\": {\n      \"description\": \"Διασφαλίζει ότι οι λίστες είναι σωστά δομημένες\",\n      \"help\": \"Τα <ul> και <ol> πρέπει να περιέχουν μόνο στοιχεία <li>, <script> ή <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <li> χρησιμοποιούνται σημασιολογικά\",\n      \"help\": \"Τα στοιχεία <li> πρέπει να περιέχονται σε <ul> ή <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <marquee> δεν χρησιμοποιούνται\",\n      \"help\": \"Τα στοιχεία <marquee> έχουν καταργηθεί και δεν πρέπει να χρησιμοποιούνται\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Διασφαλίζει ότι το <meta http-equiv=\\\"refresh\\\"> δεν χρησιμοποιείται για καθυστερημένη ανανέωση\",\n      \"help\": \"Δεν πρέπει να χρησιμοποιείται καθυστερημένη ανανέωση\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Διασφαλίζει ότι το <meta http-equiv=\\\"refresh\\\"> δεν χρησιμοποιείται για καθυστερημένη ανανέωση\",\n      \"help\": \"Δεν πρέπει να χρησιμοποιείται καθυστερημένη ανανέωση κάτω των 20 ωρών\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Διασφαλίζει ότι το <meta name=\\\"viewport\\\"> μπορεί να κλιμακώσει σημαντικά\",\n      \"help\": \"Οι χρήστες θα πρέπει να μπορούν να μεγεθύνουν και να κλιμακώσουν το κείμενο έως και 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Διασφαλίζει ότι το <meta name=\\\"viewport\\\"> δεν απενεργοποιεί την κλιμάκωση και τη μεγέθυνση κειμένου\",\n      \"help\": \"Το ζουμ και η κλιμάκωση δεν πρέπει να απενεργοποιούνται\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Διασφαλίζει ότι τα διαδραστικά στοιχεία ελέγχου δεν είναι ένθετα, καθώς δεν ανακοινώνονται πάντα από τα προγράμματα ανάγνωσης οθόνης ή μπορεί να προκαλέσουν προβλήματα εστίασης για τις υποστηρικτικές τεχνολογίες\",\n      \"help\": \"Τα διαδραστικά στοιχεία ελέγχου δεν πρέπει να είναι ένθετα\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <video> ή <audio> δεν αναπαράγουν αυτόματα ήχο για περισσότερα από 3 δευτερόλεπτα χωρίς μηχανισμό ελέγχου για διακοπή ή σίγαση του ήχου\",\n      \"help\": \"Τα στοιχεία <video> ή <audio> δεν πρέπει να αναπαράγονται αυτόματα\"\n    },\n    \"object-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <object> έχουν εναλλακτικό κείμενο\",\n      \"help\": \"Τα στοιχεία <object> πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Διασφαλίζει ότι τα bold, italic και το font-size δε χρησιμοποιούνται για το στυλ των στοιχείων <p> ως επικεφαλίδα\",\n      \"help\": \"Τα στοιχεία <p> με στυλ δεν πρέπει να χρησιμοποιούνται ως επικεφαλίδες\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Διασφαλίζει ότι η σελίδα ή τουλάχιστον ένα από τα frame της περιέχει μια επικεφαλίδα επιπέδου 1\",\n      \"help\": \"Η σελίδα πρέπει να περιέχει μια επικεφαλίδα επιπέδου 1\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Τα στοιχεία που έχουν επισημανθεί ως παρουσίασης δε θα πρέπει να έχουν καθολικό ARIA ή tabindex για να διασφαλίζεται ότι όλα τα προγράμματα ανάγνωσης οθόνης τα αγνοούν\",\n      \"help\": \"Βεβαιωθείτε ότι τα στοιχεία που επισημαίνονται ως παρουσίασης οπωσδήποτε αγνοούνται\"\n    },\n    \"region\": {\n      \"description\": \"Εξασφαλίζει ότι όλο το περιεχόμενο της σελίδας περιλαμβάνεται από ορόσημα\",\n      \"help\": \"Όλο το περιεχόμενο της σελίδας πρέπει να περιλαμβάνεται από ορόσημα\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία [role='img'] έχουν εναλλακτικό κείμενο\",\n      \"help\": \"Τα στοιχεία [role='img'] πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Διασφαλίζει ότι το χαρακτηριστικό scope χρησιμοποιείται σωστά στους πίνακες\",\n      \"help\": \"Το χαρακτηριστικό scope θα πρέπει να χρησιμοποιείται σωστά\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία που έχουν περιεχόμενο με δυνατότητα κύλισης είναι προσβάσιμα από το πληκτρολόγιο\",\n      \"help\": \"Η περιοχή με δυνατότητα κύλισης πρέπει να είναι προσβάσιμη από το πληκτρολόγιο\"\n    },\n    \"select-name\": {\n      \"description\": \"Διασφαλίζει ότι το στοιχείο select έχει ένα προσβάσιμο όνομα\",\n      \"help\": \"Το στοιχείο select πρέπει να έχει προσβάσιμο όνομα\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Διασφαλίζει ότι δεν χρησιμοποιούνται χάρτες εικόνων από την πλευρά του διακομιστή\",\n      \"help\": \"Δεν πρέπει να χρησιμοποιούνται χάρτες εικόνων από την πλευρά του διακομιστή\"\n    },\n    \"skip-link\": {\n      \"description\": \"Βεβαιωθείτε ότι όλοι οι σύνδεσμοι παράλειψης έχουν έναν στόχο που μπορεί να εστιαστεί\",\n      \"help\": \"Ο στόχος παράβλεψης συνδέσμου θα πρέπει να υπάρχει και να μπορεί να εστιαστεί\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <svg> με ρόλο img, graphics-document ή graphics-symbol έχουν προσβάσιμο κείμενο\",\n      \"help\": \"Τα στοιχεία <svg> με ρόλο img πρέπει να έχουν εναλλακτικό κείμενο\"\n    },\n    \"tabindex\": {\n      \"description\": \"Διασφαλίζει ότι οι τιμές των χαρακτηριστικών tabindex δεν είναι μεγαλύτερες από 0\",\n      \"help\": \"Τα στοιχεία δεν πρέπει να έχουν tabindex μεγαλύτερο από μηδέν\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Διασφαλίζει ότι το στοιχείο <caption> δεν περιέχει το ίδιο κείμενο με το χαρακτηριστικό summary\",\n      \"help\": \"Οι πίνακες δεν πρέπει να έχουν την ίδια περίληψη και λεζάντα\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Διασφαλίζει ότι οι πίνακες με λεζάντα χρησιμοποιούν το στοιχείο <caption>\",\n      \"help\": \"Δεδομένα ή κελιά κεφαλίδας δεν πρέπει να χρησιμοποιούνται για να δίνεται υπότιτλος σε έναν πίνακα δεδομένων\"\n    },\n    \"target-size\": {\n      \"description\": \"Διασφαλίζει ότι ο στόχος αφής έχει επαρκές μέγεθος και χώρο\",\n      \"help\": \"Όλοι οι στόχοι αφής πρέπει να έχουν μέγεθος 24 px ή να αφήνουν αρκετό χώρο\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Διασφαλίζει ότι κάθε μη κενό κελί δεδομένων σε ένα <table> μεγαλύτερο από 3 επί 3 έχει μία ή περισσότερες κεφαλίδες πίνακα\",\n      \"help\": \"Τα μη κενά στοιχεία <td> σε μεγαλύτερο <table> πρέπει να έχουν συσχετισμένη κεφαλίδα πίνακα\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Διασφαλίζει ότι κάθε κελί σε έναν πίνακα που χρησιμοποιεί το χαρακτηριστικό headers αναφέρεται μόνο σε άλλα κελιά σε αυτόν τον πίνακα\",\n      \"help\": \"Τα κελιά πίνακα που χρησιμοποιούν το χαρακτηριστικό headers πρέπει να αναφέρονται μόνο σε κελιά του ίδιου πίνακα\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <th> και τα στοιχεία με role=columnheader/rowheader έχουν κελιά δεδομένων που περιγράφουν\",\n      \"help\": \"Οι κεφαλίδες πίνακα σε έναν πίνακα δεδομένων πρέπει να παραπέμπουν σε κελιά δεδομένων\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Διασφαλίζει ότι τα χαρακτηριστικά lang έχουν έγκυρες τιμές\",\n      \"help\": \"Το χαρακτηριστικό lang πρέπει να έχει έγκυρη τιμή\"\n    },\n    \"video-caption\": {\n      \"description\": \"Διασφαλίζει ότι τα στοιχεία <video> έχουν υπότιτλους\",\n      \"help\": \"Τα στοιχεία <video> πρέπει να έχουν υπότιτλους\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Δεν χρησιμοποιούνται αφηρημένοι ρόλοι\",\n      \"fail\": {\n        \"singular\": \"Ο αφηρημένος ρόλος δεν μπορεί να χρησιμοποιηθεί απευθείας: ${data.values}\",\n        \"plural\": \"Οι αφηρημένοι ρόλοι δεν μπορούν να χρησιμοποιηθούν απευθείας: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Τα χαρακτηριστικά ARIA χρησιμοποιούνται σωστά για τον καθορισμένο ρόλο\",\n      \"fail\": {\n        \"singular\": \"Το χαρακτηριστικό ARIA δεν επιτρέπεται: ${data.values}\",\n        \"plural\": \"Τα χαρακτηριστικά ARIA δεν επιτρέπονται: ${data.values}\"\n      },\n      \"incomplete\": \"Βεβαιωθείτε ότι δεν υπάρχει πρόβλημα αν το χαρακτηριστικό ARIA αγνοηθεί σε αυτό το στοιχείο: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"Ο ρόλος ARIA επιτρέπεται για το συγκεκριμένο στοιχείο\",\n      \"fail\": {\n        \"singular\": \"Ο ρόλος ARIA ${data.values} δεν επιτρέπεται για το συγκεκριμένο στοιχείο\",\n        \"plural\": \"Οι ρόλοι ARIA ${data.values} δεν επιτρέπονται για το συγκεκριμένο στοιχείο\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Ο ρόλος ARIA ${data.values} πρέπει να καταργηθεί όταν το στοιχείο γίνει ορατό, καθώς δεν επιτρέπεται για το στοιχείο\",\n        \"plural\": \"Οι ρόλοι ARIA ${data.values} πρέπει να καταργηθούν όταν το στοιχείο γίνει ορατό, καθώς δεν επιτρέπονται για το στοιχείο\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"Το στοιχείο έχει το χαρακτηριστικό aria-busy\",\n      \"fail\": \"Το στοιχείο δεν έχει το χαρακτηριστικό aria-busy=\\\"true\\\".\"\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Το aria-errormessage υπάρχει και παραπέμπει σε στοιχεία ορατά στους αναγνώστες οθόνης που χρησιμοποιούν μια υποστηριζόμενη τεχνική aria-errormessage\",\n      \"fail\": {\n        \"singular\": \"Η τιμή του aria-errormessage `${data.values}` πρέπει να χρησιμοποιεί μια τεχνική για να ανακοινώσει το μήνυμα (π.χ. aria-live, aria-describedby, role=alert, κ.λπ.)\",\n        \"plural\": \"Οι τιμές του aria-errormessage `${data.values}` πρέπει να χρησιμοποιούν μια τεχνική για να ανακοινώσουν το μήνυμα (π.χ. aria-live, aria-describedby, role=alert, κ.λπ.)\",\n        \"hidden\": \"Η τιμή του aria-errormessage `${data.values}` δεν μπορεί να αναφέρει ένα κρυφό στοιχείο\"\n      },\n      \"incomplete\": {\n        \"singular\": \"βεβαιωθείτε ότι η τιμή του aria-errormessage `${data.values}` αναφέρεται σε ένα υπάρχον στοιχείο\",\n        \"plural\": \"βεβαιωθείτε ότι οι τιμές του aria-errormessage `${data.values}` αναφέρονται σε υπάρχοντα στοιχεία\",\n        \"idrefs\": \"δεν είναι δυνατός ο προσδιορισμός του εάν το στοιχείο aria-errormessage υπάρχει στη σελίδα: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Δεν υπάρχει χαρακτηριστικό aria-hidden στο σώμα του εγγράφου\",\n      \"fail\": \"Το aria-hidden=true δεν πρέπει να υπάρχει στο σώμα του εγγράφου\"\n    },\n    \"aria-level\": {\n      \"pass\": \"Οι τιμές του aria-level είναι έγκυρες\",\n      \"incomplete\": \"Τιμές του aria-level μεγαλύτερες από 6 δεν υποστηρίζονται σε όλους τους συνδυασμούς προγράμματος ανάγνωσης οθόνης και προγράμματος περιήγησης\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"Το χαρακτηριστικό ARIA επιτρέπεται\",\n      \"fail\": {\n        \"hasRolePlural\": \"Τα χαρακτηριστικά ${data.prohibited} δεν μπορούν να χρησιμοποιηθούν με το ρόλο \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"Το χαρακτηριστικό ${data.prohibited} δεν μπορεί να χρησιμοποιηθεί με το ρόλο \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"Τα χαρακτηριστικά ${data.prohibited} δεν μπορούν να χρησιμοποιηθούν σε ένα ${data.nodeName} χωρίς έγκυρο χαρακτηριστικό ρόλου.\",\n        \"noRoleSingular\": \"Το χαρακτηριστικό ${data.prohibited} δεν μπορεί να χρησιμοποιηθεί σε ένα ${data.nodeName} χωρίς έγκυρο χαρακτηριστικό ρόλου.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"Το χαρακτηριστικό ${data.prohibited} δεν υποστηρίζεται καλά με τον ρόλο \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"Τα χαρακτηριστικά ${data.prohibited} δεν υποστηρίζονται καλά με τον ρόλο \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"Το χαρακτηριστικό ${data.prohibited} δεν υποστηρίζεται καλά σε ένα ${data.nodeName} χωρίς έγκυρο χαρακτηριστικό ρόλου.\",\n        \"noRolePlural\": \"Τα χαρακτηριστικά ${data.prohibited} δεν υποστηρίζονται καλά σε ένα ${data.nodeName} χωρίς έγκυρο χαρακτηριστικό ρόλου.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Υπάρχουν όλα τα απαιτούμενα χαρακτηριστικά ARIA\",\n      \"fail\": {\n        \"singular\": \"Δεν υπάρχει το απαιτούμενο χαρακτηριστικό ARIA: ${data.values}\",\n        \"plural\": \"Δεν υπάρχουν τα απαιτούμενα χαρακτηριστικά ARIA: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Οι απαιτούμενοι γόνοι ARIA είναι παρόντες\"\n      },\n      \"fail\": {\n        \"singular\": \"Ο απαιτούμενος θυγατρικός ρόλος ARIA δεν υπάρχει: ${data.values}\",\n        \"plural\": \"Οι απαιτούμενοι θυγατρικόι ρόλοι ARIA δεν υπάρχουν: ${data.values}\",\n        \"unallowed\": \"Το στοιχείο έχει γόνους που δεν επιτρέπονται ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Αναμένεται προσθήκη θυγατρικού ρόλου ARIA: ${data.values}\",\n        \"plural\": \"Αναμένεται προσθήκη θυγατρικών ρόλων ARIA: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Required ARIA parent role present\",\n      \"fail\": {\n        \"singular\": \"Ο απαιτούμενος γονικός ρόλος ARIA δεν υπάρχει: ${data.values}\",\n        \"plural\": \"Οι απαιτούμενοι ρόλοι γονέα ARIA δεν υπάρχουν: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"το aria-roledescription χρησιμοποιείται σε υποστηριζόμενο σημασιολογικό ρόλο\",\n      \"incomplete\": \"Βεβαιωθείτε ότι το aria-roledescription ανακοινώνεται από υποστηριζόμενα προγράμματα ανάγνωσης οθόνης\",\n      \"fail\": \"Δώστε στο στοιχείο έναν ρόλο που υποστηρίζει το aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"Το χαρακτηριστικό ARIA υποστηρίζεται\",\n      \"fail\": \"Το χαρακτηριστικό ARIA δεν υποστηρίζεται ευρέως σε προγράμματα ανάγνωσης οθόνης και υποστηρικτικές τεχνολογίες: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Οι τιμές του χαρακτηριστικού ARIA είναι έγκυρες\",\n      \"fail\": {\n        \"singular\": \"Μη έγκυρη τιμή χαρακτηριστικού ARIA: ${data.values}\",\n        \"plural\": \"Μη έγκυρες τιμές χαρακτηριστικών ARIA: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"Το ID του χαρακτηριστικού ARIA δεν υπάρχει στη σελίδα: ${data.needsReview}\",\n        \"noIdShadow\": \"Το ID του χαρακτηριστικού ARIA δεν υπάρχει στη σελίδα ή είναι απόγονος ενός διαφορετικού σκιώδους DOM: ${data.needsReview}\",\n        \"ariaCurrent\": \"Η τιμή χαρακτηριστικού ARIA δεν είναι έγκυρη και θα αντιμετωπιστεί ως \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Δεν είναι δυνατός ο προσδιορισμός εάν το ID του χαρακτηριστικού ARIA υπάρχει στη σελίδα: ${data.needsReview}\",\n        \"empty\": \"Η τιμή του χαρακτηριστικού ARIA αγνοείται όταν είναι κενή: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"Το όνομα χαρακτηριστικού ARIA είναι έγκυρο\",\n      \"fail\": {\n        \"singular\": \"Μη έγκυρο όνομα χαρακτηριστικού ARIA: ${data.values}\",\n        \"plural\": \"Μη έγκυρα ονόματα χαρακτηριστικών ARIA: ${data.values}\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"Ο ρόλος ARIA δεν έχει καταργηθεί\",\n      \"fail\": \"Ο ρόλος που χρησιμοποιείται έχει καταργηθεί: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Χρησιμοποιείται μόνο μια τιμή ρόλου\",\n      \"fail\": \"Χρησιμοποιήστε μόνο μία τιμή ρόλου, καθώς οι εναλλακτικοί ρόλοι δεν υποστηρίζονται σε παλαιότερα προγράμματα περιήγησης\",\n      \"incomplete\": \"Χρησιμοποιήστε μόνο τους ρόλους 'presentation' ή 'none' επειδή είναι συνώνυμοι.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"Το στοιχείο έχει καθολικό χαρακτηριστικό ARIA: ${data.values}\",\n        \"plural\": \"Το στοιχείο έχει καθολικά χαρακτηριστικά ARIA: ${data.values}\"\n      },\n      \"fail\": \"Το στοιχείο δεν έχει καθολικό χαρακτηριστικό ARIA\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Το στοιχείο έχει ρόλο widget.\",\n      \"fail\": \"Το στοιχείο δεν έχει ρόλο widget.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"Ο ρόλος ARIA είναι έγκυρος\",\n      \"fail\": {\n        \"singular\": \"Ο ρόλος πρέπει να είναι ένας από τους έγκυρους ρόλους ARIA: ${data.values}\",\n        \"plural\": \"Οι ρόλοι πρέπει να είναι ένας από τους έγκυρους ρόλους ARIA: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"Το στοιχείο μπορεί να εστιαστεί.\",\n      \"fail\": \"Το στοιχείο δεν μπορεί να εστιαστεί.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Δεν υπάρχει αναντιστοιχία μεταξύ <label> και προσβάσιμου ονόματος\",\n      \"incomplete\": \"Βεβαιωθείτε ότι το <label> δεν χρειάζεται να αποτελεί μέρος του ονόματος του πεδίου ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"Ο ρόλος του ARIA υποστηρίζεται\",\n      \"fail\": \"Ο ρόλος που χρησιμοποιείται δεν υποστηρίζεται ευρέως σε προγράμματα ανάγνωσης οθόνης και υποστηρικτικές τεχνολογίες: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Το στοιχείο έχει έγκυρη σημασιολογία για ένα στοιχείο στη σειρά εστίασης.\",\n      \"fail\": \"Το στοιχείο έχει μη έγκυρη σημασιολογία για ένα στοιχείο στη σειρά εστίασης.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Το στοιχείο έχει επαρκώς ενισχυμένη χρωματική αντίθεση ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"Το στοιχείο έχει ανεπαρκώς ενισχυμένη χρωματική αντίθεση ${data.contrastRatio} (χρώμα προσκηνίου: ${data.fgColor}, χρώμα φόντου: ${data.bgColor}, μέγεθος γραμματοσειράς: ${data.fontSize}, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Το στοιχείο έχει ανεπαρκώς ενισχυμένη χρωματική αντίθεση ${data.contrastRatio} μεταξύ του χρώματος του προσκηνίου και του χρώματος σκιάς (χρώμα προσκηνίου: ${data.fgColor}, χρώμα σκιάς κειμένου: ${data.shadowColor}, μέγεθος γραμματοσειράς: ${data.fontSize}, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Το στοιχείο έχει ανεπαρκώς ενισχυμένη χρωματική αντίθεση ${data.contrastRatio} μεταξύ του χρώματος σκιάς και του χρώματος φόντου (χρώμα σκιάς κειμένου: ${data.shadowColor}, χρώμα φόντου: ${data.bgColor}, μέγεθος γραμματοσειράς: ${data.fontSize }, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Δεν είναι δυνατός ο προσδιορισμός της αναλογίας αντίθεσης\",\n        \"bgImage\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω εικόνας φόντου\",\n        \"bgGradient\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω διαβάθμισης φόντου\",\n        \"imgNode\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή το στοιχείο περιέχει έναν κόμβο εικόνας\",\n        \"bgOverlap\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή επικαλύπτεται από άλλο στοιχείο\",\n        \"fgAlpha\": \"Το χρώμα του προσκηνίου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω της διαφάνειας άλφα\",\n        \"elmPartiallyObscured\": \"Δεν ήταν δυνατός ο προσδιορισμός του χρώματος φόντου του στοιχείου επειδή είναι μερικώς καλυμμένο από άλλο στοιχείο\",\n        \"elmPartiallyObscuring\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή επικαλύπτει εν μέρει άλλα στοιχεία\",\n        \"outsideViewport\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή βρίσκεται εκτός του viewport\",\n        \"equalRatio\": \"Το στοιχείο έχει αναλογία αντίθεσης 1:1 με το φόντο\",\n        \"shortTextContent\": \"Το περιεχόμενο του στοιχείου είναι πολύ σύντομο για να προσδιοριστεί εάν πρόκειται για πραγματικό περιεχόμενο κειμένου\",\n        \"nonBmp\": \"Το περιεχόμενο του στοιχείου περιέχει μόνο χαρακτήρες που δεν είναι κειμένου\",\n        \"pseudoContent\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω ψευδοστοιχείου\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"Το στοιχείο έχει επαρκή χρωματική αντίθεση ${data.contrastRatio}\",\n        \"hidden\": \"Το στοιχείο είναι κρυμμένο\"\n      },\n      \"fail\": {\n        \"default\": \"Το στοιχείο έχει ανεπαρκή χρωματική αντίθεση ${data.contrastRatio} (χρώμα προσκηνίου: ${data.fgColor}, χρώμα φόντου: ${data.bgColor}, μέγεθος γραμματοσειράς: ${data.fontSize}, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Το στοιχείο έχει ανεπαρκή χρωματική αντίθεση ${data.contrastRatio} μεταξύ του χρώματος προσκηνίου και σκιάς (χρώμα προσκηνίου: ${data.fgColor}, χρώμα σκιάς κειμένου: ${data.shadowColor}, μέγεθος γραμματοσειράς: ${data.fontSize}, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Το στοιχείο έχει ανεπαρκή χρωματική αντίθεση ${data.contrastRatio} μεταξύ του χρώματος σκιάς και του χρώματος φόντου (χρώμα σκιάς κειμένου: ${data.shadowColor}, χρώμα φόντου: ${data.bgColor}, μέγεθος γραμματοσειράς: ${data.fontSize}, βάρος γραμματοσειράς: ${data.fontWeight}). Αναμενόμενος λόγος αντίθεσης ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Δεν είναι δυνατός ο προσδιορισμός της αναλογίας αντίθεσης\",\n        \"bgImage\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω εικόνας φόντου\",\n        \"bgGradient\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω διαβάθμισης φόντου\",\n        \"imgNode\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή το στοιχείο περιέχει έναν κόμβο εικόνας\",\n        \"bgOverlap\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή επικαλύπτεται από άλλο στοιχείο\",\n        \"fgAlpha\": \"Το χρώμα του προσκηνίου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω διαφάνειας άλφα\",\n        \"elmPartiallyObscured\": \"Δεν ήταν δυνατός ο προσδιορισμός του χρώματος φόντου του στοιχείου επειδή είναι μερικώς καλυμμένο από άλλο στοιχείο\",\n        \"elmPartiallyObscuring\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή επικαλύπτει εν μέρει άλλα στοιχεία\",\n        \"outsideViewport\": \"Το χρώμα φόντου του στοιχείου δεν ήταν δυνατό να προσδιοριστεί επειδή βρίσκεται εκτός viewport\",\n        \"equalRatio\": \"Το στοιχείο έχει αναλογία αντίθεσης 1:1 με το φόντο\",\n        \"shortTextContent\": \"Το περιεχόμενο του στοιχείου είναι πολύ σύντομο για να προσδιοριστεί εάν πρόκειται για πραγματικό περιεχόμενο κειμένου\",\n        \"nonBmp\": \"Το περιεχόμενο του στοιχείου περιέχει μόνο χαρακτήρες εκτός κειμένου\",\n        \"pseudoContent\": \"Δεν ήταν δυνατός ο προσδιορισμός του χρώματος φόντου του στοιχείου λόγω ψευδοστοιχείου\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Οι σύνδεσμοι μπορούν να διακριθούν από το περιβάλλον κείμενο εφαρμόζοντας οπτικό στυλ\",\n      \"fail\": \"Ο σύνδεσμος δεν έχει στυλ (π.χ. underline) που να τον ξεχωρίζει από το περιβάλλον κείμενο\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Οι σύνδεσμοι μπορούν να διακριθούν από το περιβάλλον κείμενο με άλλο τρόπο εκτός από το χρώμα\",\n      \"fail\": {\n        \"fgContrast\": \"Ο σύνδεσμος έχει ανεπαρκή χρωματική αντίθεση ${data.contrastRatio}:1 με το περιβάλλον κείμενο. (Η ελάχιστη αντίθεση είναι ${data.requiredContrastRatio}:1, κείμενο συνδέσμου: ${data.nodeColor}, περιβάλλον κείμενο: ${data.parentColor})\",\n        \"bgContrast\": \"Το φόντο του συνδέσμου έχει ανεπαρκή χρωματική αντίθεση ${data.contrastRatio} (η ελάχιστη αντίθεση είναι ${data.requiredContrastRatio}:1, χρώμα φόντου συνδέσμου: ${data.nodeBackgroundColor}, χρώμα φόντου περιβάλλοντος: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Δεν ήταν δυνατός ο προσδιορισμός της αναλογίας αντίθεσης προσκηνίου του στοιχείου\",\n        \"bgContrast\": \"Δεν ήταν δυνατός ο προσδιορισμός της αναλογίας αντίθεσης φόντου του στοιχείου\",\n        \"bgImage\": \"Δεν ήταν δυνατός ο προσδιορισμός της αναλογίας αντίθεσης του στοιχείου λόγω εικόνας φόντου\",\n        \"bgGradient\": \"Η αναλογία αντίθεσης του στοιχείου δεν ήταν δυνατό να προσδιοριστεί λόγω gradient φόντου\",\n        \"imgNode\": \"Δεν ήταν δυνατός ο προσδιορισμός της αναλογίας αντίθεσης του στοιχείου επειδή το στοιχείο περιέχει έναν κόμβο εικόνας\",\n        \"bgOverlap\": \"Δεν ήταν δυνατός ο προσδιορισμός της αναλογίας αντίθεσης του στοιχείου λόγω επικάλυψης στοιχείων\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"η τιμή αυτόματης συμπλήρωσης βρίσκεται σε ένα κατάλληλο στοιχείο\",\n      \"fail\": \"η τιμή αυτόματης συμπλήρωσης είναι ακατάλληλη για αυτόν τον τύπο εισόδου\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"το χαρακτηριστικό αυτόματης συμπλήρωσης έχει μορφοποιηθεί σωστά\",\n      \"fail\": \"το χαρακτηριστικό αυτόματης συμπλήρωσης δεν έχει μορφοποιηθεί σωστά\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Η τιμή χαρακτηριστικού Accesskey είναι μοναδική\",\n      \"fail\": \"Το έγγραφο έχει πολλά στοιχεία με το ίδιο κλειδί πρόσβασης\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Το στοιχείο περιέχει στοιχεία με δυνατότητα εστίασης\",\n      \"fail\": \"Το στοιχείο πρέπει να έχει εστιασμένο περιεχόμενο\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Δεν περιέχονται στοιχεία με δυνατότητα εστίασης μέσα στο στοιχείο\",\n      \"incomplete\": \"Ελέγξτε εάν τα εστιάσιμα στοιχεία μετακινούν αμέσως την ένδειξη εστίασης\",\n      \"fail\": \"Το περιεχόμενο με δυνατότητα εστίασης θα πρέπει να απενεργοποιηθεί ή να αφαιρεθεί από το DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Το στοιχείο είναι εστιασμένο\",\n      \"fail\": \"Το στοιχείο πρέπει να είναι εστιασμένο\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Δεν υπάρχουν εστιάσιμα στοιχεία ενώ ένα modal είναι ανοιχτό\",\n      \"incomplete\": \"Βεβαιωθείτε ότι τα στοιχεία με δυνατότητα εστίασης δεν έχουν δυνατότητα tabbable στην τρέχουσα κατάσταση\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Το στοιχείο δεν είναι σε σειρά tab ή έχει προσβάσιμο κείμενο\",\n      \"fail\": \"Το στοιχείο είναι σε σειρά tab και δεν έχει προσβάσιμο κείμενο\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο έχει προσβάσιμο όνομα\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Δεν περιέχονται στοιχεία με δυνατότητα εστίασης μέσα στο στοιχείο\",\n      \"incomplete\": \"Ελέγξτε εάν τα εστιάσιμα στοιχεία μετακινούν αμέσως την ένδειξη εστίασης\",\n      \"fail\": \"Το περιεχόμενο με δυνατότητα εστίασης θα πρέπει να έχει tabindex='-1' ή να αφαιρεθεί από το DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"Το στοιχείο δεν έχει εστιάσιμους απογόνους\",\n      \"fail\": \"Το στοιχείο έχει απογόνους με δυνατότητα εστίασης\",\n      \"incomplete\": \"Δεν ήταν δυνατός ο προσδιορισμός εάν το στοιχείο έχει απογόνους\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"Το ορόσημο ${data.role} βρίσκεται στο ανώτατο επίπεδο.\",\n      \"fail\": \"Το ορόσημο ${data.role} περιέχεται σε άλλο ορόσημο.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"Το στοιχείο δεν έχει εστιάσιμους απογόνους\",\n      \"fail\": {\n        \"default\": \"Το στοιχείο έχει απογόνους με δυνατότητα εστίασης\",\n        \"notHidden\": \"Η χρήση  αρνητικού tabindex σε ένα στοιχείο μέσα σε ένα διαδραστικό στοιχείο ελέγχου δεν εμποδίζει τις υποστηρικτικές τεχνολογίες να εστιάσουν το στοιχείο (ακόμη και με το 'aria-hidden=true')\"\n      },\n      \"incomplete\": \"Δεν ήταν δυνατός ο προσδιορισμός για το εάν το στοιχείο έχει απογόνους\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Η σελίδα έχει τουλάχιστον μία επικεφαλίδα επιπέδου 1\",\n      \"fail\": \"Η σελίδα πρέπει να έχει επικεφαλίδα επιπέδου 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Το έγγραφο έχει τουλάχιστον ένα κύριο ορόσημο\",\n      \"fail\": \"Το έγγραφο δεν έχει κύριο ορόσημο\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Το έγγραφο δεν έχει περισσότερα από ένα ορόσημα banner\",\n      \"fail\": \"Το έγγραφο έχει περισσότερα από ένα ορόσημα banner\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Το έγγραφο δεν έχει περισσότερα από ένα ορόσημα contentinfo\",\n      \"fail\": \"Το έγγραφο έχει περισσότερα από ένα ορόσημα contentinfo\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Το έγγραφο δεν έχει περισσότερα από ένα κύρια ορόσημα\",\n      \"fail\": \"Το έγγραφο έχει περισσότερα από ένα κύρια ορόσημα\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Το στοιχείο δεν έχει tabindex μεγαλύτερο από 0\",\n      \"fail\": \"Το στοιχείο έχει tabindex μεγαλύτερο από 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Το στοιχείο έχει μια έγκυρη τιμή χαρακτηριστικού alt\",\n      \"fail\": \"Το στοιχείο έχει ένα χαρακτηριστικό alt που περιέχει μόνο έναν χαρακτήρα διαστήματος, το οποίο δεν αγνοείται από όλα τα προγράμματα ανάγνωσης οθόνης\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Το στοιχείο δεν αντιγράφει υπάρχον κείμενο στο πεδίο alt του <img>\",\n      \"fail\": \"Το στοιχείο περιέχει στοιχείο <img> με κείμενο alt που αντιγράφει το υπάρχον κείμενο\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Το στοιχείο φόρμας έχει ρητό <label>\",\n      \"fail\": \"Το στοιχείο φόρμας δέν έχει ρητό <label>\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο φόρμας έχει ρητό <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Το κείμενο βοήθειας (τίτλος ή aria-describedby) δεν αντιγράφει το κείμενο της ετικέτας\",\n      \"fail\": \"Το κείμενο βοήθειας (τίτλος ή aria-describedby) είναι το ίδιο με το κείμενο της ετικέτας\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Το στοιχείο φόρμας έχει ένα ορατό και ρητό <label>\",\n      \"fail\": \"Το στοιχείο φόρμας έχει ρητό <label> που είναι κρυφό\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο φόρμας έχει ρητό <label> που είναι κρυφό\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Το στοιχείο φόρμας έχει ένα σιωπηρό (wrapped) <label>\",\n      \"fail\": \"Το στοιχείο φόρμας δέν έχει ένα σιωπηρό (wrapped) <label>\",\n      \"incomplete\": \"Unable to determine if form element has an implicit (wrapped} <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Το στοιχείο περιέχει ορατό κείμενο ως μέρος του προσβάσιμου ονόματος του\",\n      \"fail\": \"Το κείμενο μέσα στο στοιχείο δεν περιλαμβάνεται στο προσβάσιμο όνομα\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Το πεδίο φόρμας δεν έχει πολλά στοιχεία ετικέτας\",\n      \"incomplete\": \"Τα πολλαπλά στοιχεία ετικετών δεν υποστηρίζονται ευρέως στις υποστηρικτικές τεχνολογίες. Βεβαιωθείτε ότι η πρώτη ετικέτα περιέχει όλες τις απαραίτητες πληροφορίες.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Το στοιχείο φόρμας δεν χρησιμοποιεί αποκλειστικά το χαρακτηριστικό τίτλου για την ετικέτα του\",\n      \"fail\": \"Μόνο ο τίτλος χρησιμοποιείται για τη δημιουργία ετικέτας για το στοιχείο φόρμας\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Τα ορόσημα πρέπει να έχουν μοναδικό ρόλο ή συνδυασμό ρόλου/ετικέτας/τίτλου (δηλαδή προσβάσιμο όνομα).\",\n      \"fail\": \"Το ορόσημο πρέπει να έχει ένα μοναδικό χαρακτηριστικό aria-label, aria-labelledby ή τίτλο για να διακρίνονται τα ορόσημα\"\n    },\n    \"has-lang\": {\n      \"pass\": \"Το στοιχείο <html> έχει ένα χαρακτηριστικό lang\",\n      \"fail\": {\n        \"noXHTML\": \"Το χαρακτηριστικό xml:lang δεν είναι έγκυρο σε σελίδες HTML, χρησιμοποιήστε το χαρακτηριστικό lang.\",\n        \"noLang\": \"Το στοιχείο <html> δεν έχει χαρακτηριστικό lang\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Η τιμή του χαρακτηριστικού lang περιλαμβάνεται στη λίστα των έγκυρων γλωσσών\",\n      \"fail\": \"Η τιμή του χαρακτηριστικού lang δεν περιλαμβάνεται στη λίστα έγκυρων γλωσσών\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Τα χαρακτηριστικά Lang και xml:lang έχουν την ίδια βασική γλώσσα\",\n      \"fail\": \"Τα χαρακτηριστικά Lang και xml:lang δεν έχουν την ίδια βασική γλώσσα\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Το στοιχείο λίστας περιγραφής έχει ένα γονικό στοιχείο <dl>\",\n      \"fail\": \"Το στοιχείο λίστας περιγραφής δεν έχει γονικό στοιχείο <dl>\"\n    },\n    \"listitem\": {\n      \"pass\": \"Το στοιχείο λίστας έχει ένα γονικό στοιχείο <ul>, <ol> ή role=\\\"list\\\".\",\n      \"fail\": {\n        \"default\": \"Το στοιχείο λίστας δεν έχει γονικό στοιχείο <ul>, <ol>\",\n        \"roleNotValid\": \"Το στοιχείο λίστας δεν έχει <ul>, <ol> γονικό στοιχείο χωρίς ρόλο ή role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Το στοιχείο dl έχει μόνο απευθείας θυγατρικά που επιτρέπεται να βρίσκονται μέσα σε στοιχεία <dt>, <dd> ή <div>\",\n      \"fail\": \"Το στοιχείο dl έχει απευθείας θυγατρικά που δεν επιτρέπονται: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Το στοιχείο λίστας έχει μόνο απευθείας θυγατρικά που επιτρέπονται μέσα σε στοιχεία <li>\",\n      \"fail\": \"Το στοιχείο λίστας έχει απευθείας θυγατρικά που δεν επιτρέπονται: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Όταν δεν είναι κενό, το στοιχείο έχει και τα δύο στοιχεία <dt> και <dd>\",\n      \"fail\": \"Όταν δεν είναι κενό, το στοιχείο δεν έχει τουλάχιστον ένα στοιχείο <dt> ακολουθούμενο από τουλάχιστον ένα στοιχείο <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"Το στοιχείο πολυμέσων έχει ένα κομμάτι υπότιτλων\",\n      \"incomplete\": \"Ελέγξτε ότι οι υπότιτλοι είναι διαθέσιμοι για το στοιχείο\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Το iframe δοκιμάστηκε με το ax-core\",\n      \"fail\": \"Το iframe δεν ήταν δυνατό να δοκιμαστεί με το axe-core\",\n      \"incomplete\": \"Το iframe πρέπει να δοκιμαστεί με το axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"Το <video> ή το <audio> δεν παράγει ήχο για μεγαλύτερη από την επιτρεπόμενη διάρκεια ή διαθέτει μηχανισμό ελέγχου\",\n      \"fail\": \"Το <video> ή το <audio> εξάγει ήχο για μεγαλύτερη από την επιτρεπόμενη διάρκεια και δεν διαθέτει μηχανισμό ελέγχου\",\n      \"incomplete\": \"Βεβαιωθείτε ότι το <video> ή το <audio> δεν παράγει ήχο για μεγαλύτερη από την επιτρεπόμενη διάρκεια ή παρέχει μηχανισμό ελέγχου\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Η οθόνη είναι λειτουργική και το κλείδωμα προσανατολισμού δεν υπάρχει\",\n      \"fail\": \"Εφαρμόζεται κλείδωμα προσανατολισμού CSS και καθιστά την οθόνη μη λειτουργική\",\n      \"incomplete\": \"Δεν είναι δυνατός ο προσδιορισμός ύπαρξης κλειδώματος προσανατολισμού CSS\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"Η ετικέτα <meta> δεν εμποδίζει τη σημαντική μεγέθυνση σε κινητές συσκευές\",\n      \"fail\": \"Η ετικέτα <meta> περιορίζει τη μεγέθυνση σε κινητές συσκευές\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"Η ετικέτα <meta> δεν απενεργοποιεί το ζουμ σε κινητές συσκευές\",\n      \"fail\": \"Η ετικέτα ${data} στην ετικέτα <meta> απενεργοποιεί το ζουμ σε κινητές συσκευές\"\n    },\n    \"target-offset\": {\n      \"pass\": \"Ο στόχος έχει επαρκή απόσταση από τους πλησιέστερους γείτονές του. Ο ασφαλής χώρος με δυνατότητα κλικ έχει διάμετρο ${data.closestOffset}px που είναι τουλάχιστον ${data.minOffset}px.\",\n      \"fail\": \"Ο στόχος δεν έχει επαρκή απόσταση από τους πλησιέστερους γείτονές του. Ο ασφαλής χώρος με δυνατότητα κλικ έχει διάμετρο ${data.closestOffset}px αντί για τουλάχιστον ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"Το στοιχείο με αρνητικό tabindex έχει ανεπαρκή χώρο προς τους πλησιέστερους γείτονές του. Ο ασφαλής χώρος με δυνατότητα κλικ έχει διάμετρο ${data.closestOffset}px αντί για τουλάχιστον ${data.minOffset}px. Είναι αυτός ο στόχος;\",\n        \"nonTabbableNeighbor\": \"Ο στόχος δεν έχει επαρκή χώρο προς τους πλησιέστερους γείτονές του. Ο ασφαλής χώρος με δυνατότητα κλικ έχει διάμετρο ${data.closestOffset}px αντί για τουλάχιστον ${data.minOffset}px. Είναι στόχος ο γείτονας;\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Το στοιχείο ελέγχου έχει επαρκές μέγεθος (${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize}px)\",\n        \"obscured\": \"Το στοιχείο ελέγχου αγνοείται επειδή είναι πλήρως κρυμμένο και επομένως δεν μπορεί να γίνει κλικ\"\n      },\n      \"fail\": {\n        \"default\": \"Ο στόχος έχει ανεπαρκές μέγεθος (${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize}px)\",\n        \"partiallyObscured\": \"Το μέγεθος του στόχου είναι ανεπαρκές επειδή είναι μερικώς κρυμμένο (το μικρότερο διάστημα είναι ${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"Το στοιχείο με αρνητικό tabindex έχει ανεπαρκές μέγεθος (${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize}px). Είναι στόχος;\",\n        \"contentOverflow\": \"Το μέγεθος του στοιχείου δεν ήταν δυνατό να προσδιοριστεί με ακρίβεια λόγω του περιεχομένου υπερχείλισης\",\n        \"partiallyObscured\": \"Το στοιχείο με αρνητικό tabindex δεν έχει επαρκές μέγεθος επειδή είναι μερικώς κρυμμένο (το μικρότερο διάστημα είναι ${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize} px). Είναι στόχος;\",\n        \"partiallyObscuredNonTabbable\": \"Ο στόχος έχει ανεπαρκές μέγεθος επειδή είναι μερικώς καλυμμένος από έναν γείτονα με αρνητικό tabindex (το μικρότερο διάστημα είναι ${data.width}px επί ${data.height}px, θα πρέπει να είναι τουλάχιστον ${data.minSize}px επί ${data.minSize}px). Είναι στόχος ο γείτονας;\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"Η σελίδα έχει επικεφαλίδα\",\n      \"fail\": \"Η σελίδα δεν έχει επικεφαλίδα\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Έγκυρη σειρά επικεφαλίδας\",\n      \"fail\": \"Μη έγκυρη σειρά επικεφαλίδας\",\n      \"incomplete\": \"Δεν είναι δυνατός ο προσδιορισμός της προηγούμενης επικεφαλίδας\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Δεν υπάρχουν άλλοι σύνδεσμοι με το ίδιο όνομα που να πηγαίνουν σε διαφορετική διεύθυνση URL\",\n      \"incomplete\": \"Ελέγξτε ότι οι σύνδεσμοι έχουν τον ίδιο σκοπό ή είναι σκόπιμα διφορούμενοι.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Βρέθηκε έγκυρο skip link\",\n      \"fail\": \"Δεν βρέθηκε έγκυρο skip link\"\n    },\n    \"landmark\": {\n      \"pass\": \"Η σελίδα έχει περιοχή ορόσημο\",\n      \"fail\": \"Η σελίδα δεν έχει περιοχή ορόσημο\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"Η ετικέτα <meta> δεν ανανεώνει αμέσως τη σελίδα\",\n      \"fail\": \"Η ετικέτα <meta> αναγκάζει σε ανανέωση της σελίδας μετά από κάποια χρονική στιγμή\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Η ετικέτα <meta> δεν ανανεώνει αμέσως τη σελίδα\",\n      \"fail\": \"Η ετικέτα <meta> αναγκάζει σε ανανέωση της σελίδας μετά από κάποια χρονική στιγμή (λιγότερο από 20 ώρες)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Τα στοιχεία <p> δεν έχουν στυλ επικεφαλίδων\",\n      \"fail\": \"Στοιχεία επικεφαλίδας πρέπει να χρησιμοποιούνται αντί για στοιχεία <p> με στυλ επικεφαλίδας\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν τα στοιχεία <p> έχουν στυλ επικεφαλίδων\"\n    },\n    \"region\": {\n      \"pass\": \"Όλο το περιεχόμενο της σελίδας περιλαμβάνεται από ορόσημα\",\n      \"fail\": \"Ορισμένο περιεχόμενο της σελίδας δεν περιλαμβάνεται από ορόσημα\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Υπάρχει στόχος για skip link\",\n      \"incomplete\": \"Ο στόχος του skip link θα πρέπει να είναι ορατός κατά την ενεργοποίηση\",\n      \"fail\": \"Δεν υπάρχει στόχος για skip link\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Το χαρακτηριστικό title του στοιχείου είναι μοναδικό\",\n      \"fail\": \"Το χαρακτηριστικό title του στοιχείου δεν είναι μοναδικό\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Το έγγραφο δεν έχει ενεργά στοιχεία που μοιράζονται το ίδιο χαρακτηριστικό id\",\n      \"fail\": \"Το έγγραφο έχει ενεργά στοιχεία με το ίδιο χαρακτηριστικό id: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Το έγγραφο δεν έχει πολλαπλά στοιχεία που αναφέρονται με ARIA ή ετικέτες που μοιράζονται το ίδιο χαρακτηριστικό id\",\n      \"fail\": \"Το έγγραφο έχει πολλαπλά στοιχεία που αναφέρονται με ARIA με το ίδιο χαρακτηριστικό id: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Το έγγραφο δεν έχει πολλαπλά στατικά στοιχεία που να μοιράζονται το ίδιο χαρακτηριστικό id\",\n      \"fail\": \"Το έγγραφο έχει πολλαπλά στατικά στοιχεία με το ίδιο χαρακτηριστικό id: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"Το χαρακτηριστικό aria-label υπάρχει και δεν είναι κενό\",\n      \"fail\": \"Το χαρακτηριστικό aria-label δεν υπάρχει ή είναι κενό\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"Το χαρακτηριστικό aria-labelledby υπάρχει και παραπέμπει σε στοιχεία που είναι ορατά στους αναγνώστες οθόνης\",\n      \"fail\": \"Το χαρακτηριστικό aria-labelledby δεν υπάρχει, αναφέρεται σε στοιχεία που δεν υπάρχουν ή παραπέμπει σε στοιχεία που είναι κενά\",\n      \"incomplete\": \"βεβαιωθείτε ότι το aria-labelledby παραπέμπει σε ένα υπάρχον στοιχείο\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Δεν έχουν καθοριστεί ενσωματωμένα στυλ με '!important' που επηρεάζουν το διάστημα κειμένου\",\n      \"fail\": {\n        \"singular\": \"Καταργήστε το '!important' από το ενσωματωμένο στυλ ${data.values}, καθώς η παράκαμψη του δεν υποστηρίζεται από τα περισσότερα προγράμματα περιήγησης\",\n        \"plural\": \"Καταργήστε το '!important' από τα ενσωματωμένα στυλ ${data.values}, καθώς η παράκαμψη τους δεν υποστηρίζεται από τα περισσότερα προγράμματα περιήγησης\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Το στοιχείο έχει εσωτερικό κείμενο που είναι ορατό στους αναγνώστες οθόνης\",\n      \"fail\": \"Το στοιχείο δεν έχει εσωτερικό κείμενο που είναι ορατό στους αναγνώστες οθόνης\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο έχει παιδιά\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Το έγγραφο έχει μη κενό στοιχείο <title>\",\n      \"fail\": \"Το έγγραφο δεν έχει μη κενό στοιχείο <title>\"\n    },\n    \"exists\": {\n      \"pass\": \"Το στοιχείο δεν υπάρχει\",\n      \"incomplete\": \"Το στοιχείο υπάρχει\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Το στοιχείο έχει χαρακτηριστικό alt\",\n      \"fail\": \"Το στοιχείο δεν έχει χαρακτηριστικό alt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Το στοιχείο έχει κείμενο που είναι ορατό στους αναγνώστες οθόνης\",\n      \"fail\": \"Το στοιχείο δεν έχει κείμενο που είναι ορατό στους αναγνώστες οθόνης\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο έχει παιδιά\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"Το letter-spacing στο χαρακτηριστικό style δεν έχει οριστεί σε !important ή πληροί το ελάχιστο\",\n      \"fail\": \"Το letter-spacing  στο χαρακτηριστικό style δεν πρέπει να χρησιμοποιεί !important ή να είναι στο ${data.minValue}em (τρέχον ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"Το line-height στο χαρακτηριστικό style δεν έχει οριστεί σε !important ή πληροί το ελάχιστο\",\n      \"fail\": \"Το line-height στο χαρακτηριστικό style δεν πρέπει να χρησιμοποιεί !important ή να είναι στο ${data.minValue}em (τρέχον ${data.value}em)\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"Το word-spacing στο χαρακτηριστικό style δεν έχει οριστεί σε !important ή πληροί το ελάχιστο\",\n      \"fail\": \"Το word-spacing στο χαρακτηριστικό style δεν πρέπει να χρησιμοποιεί !important ή να είναι στο ${data.minValue}em (τρέχον ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Το στοιχείο δεν είναι ορατό\",\n      \"fail\": \"Το στοιχείο είναι ορατό\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Το στοιχείο έχει μη κενό χαρακτηριστικό alt\",\n      \"fail\": {\n        \"noAttr\": \"Το στοιχείο δεν έχει χαρακτηριστικό alt\",\n        \"emptyAttr\": \"Το στοιχείο έχει κενό χαρακτηριστικό alt\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Το στοιχείο δεν έχει χαρακτηριστικό value\",\n        \"has-label\": \"Το στοιχείο έχει ένα χαρακτηριστικό value με μη κενή τιμή\"\n      },\n      \"fail\": \"Το στοιχείο έχει χαρακτηριστικό value αλλά είναι κενό\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"Το στοιχείο έχει χαρακτηριστικό placeholder\",\n      \"fail\": {\n        \"noAttr\": \"Το στοιχείο δεν έχει χαρακτηριστικό placeholder\",\n        \"emptyAttr\": \"Το στοιχείο έχει κενό χαρακτηριστικό placeholder\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Το στοιχείο έχει χαρακτηριστικό τίτλου\",\n      \"fail\": {\n        \"noAttr\": \"Το στοιχείο δεν έχει χαρακτηριστικό τίτλου\",\n        \"emptyAttr\": \"Το στοιχείο έχει κενό χαρακτηριστικό τίτλου\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Το στοιχείο έχει ένα χαρακτηριστικό με μη κενή τιμή\",\n      \"fail\": {\n        \"noAttr\": \"Το στοιχείο δεν έχει χαρακτηριστικό τιμής\",\n        \"emptyAttr\": \"Το στοιχείο έχει ένα χαρακτηριστικό με κενή τιμή\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου αντικαταστάθηκε με το role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου δεν αντικαταστάθηκε με role=\\\"none\\\" ή role=\\\"presentation\\\"\",\n        \"globalAria\": \"Ο ρόλος του στοιχείου δεν είναι για παρουσίαση επειδή έχει ένα καθολικό χαρακτηριστικό ARIA\",\n        \"focusable\": \"Ο ρόλος του στοιχείου δεν είναι για παρουσίαση επειδή είναι εστιασμένο\",\n        \"both\": \"Ο ρόλος του στοιχείου δεν είναι για παρουσίαση, επειδή έχει ένα καθολικό χαρακτηριστικό ARIA και είναι εστιασμένο\",\n        \"iframe\": \"Η χρήση του χαρακτηριστικού \\\"title\\\" σε ένα στοιχείο ${data.nodeName} με ρόλο παρουσίασης συμπεριφέρεται με ασυνέπεια μεταξύ των προγραμμάτων ανάγνωσης οθόνης\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου αντικαταστάθηκε με role=\\\"none\\\"\",\n      \"fail\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου δεν αντικαταστάθηκε με role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου αντικαταστάθηκε με role=\\\"presentation\\\"\",\n      \"fail\": \"Η προεπιλεγμένη σημασιολογία του στοιχείου δεν αντικαταστάθηκε με role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"Το στοιχείο έχει θυγατρικό που είναι τίτλος\",\n      \"fail\": {\n        \"noTitle\": \"Το στοιχείο δεν έχει θυγατρικό που να είναι τίτλος\",\n        \"emptyTitle\": \"Ο τίτλος του θυγατρικού στοιχείου είναι κενός\"\n      },\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί αν το στοιχείο έχει ένα θυγατρικό που είναι τίτλος\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Η πρώτη γραμμή του πίνακα δεν χρησιμοποιείται ως λεζάντα\",\n      \"fail\": \"Το πρώτο θυγατρικό του πίνακα θα πρέπει να είναι λεζάντα αντί για κελί πίνακα\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Το χαρακτηριστικό Scope χρησιμοποιείται μόνο σε στοιχεία κεφαλίδας πίνακα (<th>)\",\n      \"fail\": \"Στην HTML 5, τα χαρακτηριστικά scope μπορούν να χρησιμοποιούνται μόνο σε στοιχεία κεφαλίδας πίνακα (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Το περιεχόμενο του χαρακτηριστικού summary και του <caption> δεν είναι διπλότυπα\",\n      \"fail\": \"Το περιεχόμενο του χαρακτηριστικού summary και το στοιχείο <caption> είναι πανομοιότυπα\",\n      \"incomplete\": \"Δεν είναι δυνατό να προσδιοριστεί εάν το στοιχείο <table> έχει λεζάντα\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Το χαρακτηριστικό Scope χρησιμοποιείται σωστά\",\n      \"fail\": \"Η τιμή του χαρακτηριστικού scope μπορεί να είναι μόνο 'row' ή 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Όλα τα μη κενά κελιά δεδομένων διαθέτουν κεφαλίδες πίνακα\",\n      \"fail\": \"Ορισμένα μη κενά κελιά δεδομένων δεν διαθέτουν κεφαλίδες πίνακα\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Το χαρακτηριστικό headers χρησιμοποιείται αποκλειστικά για αναφορά σε άλλα κελιά του πίνακα\",\n      \"incomplete\": \"Το χαρακτηριστικό headers είναι κενό\",\n      \"fail\": \"Το χαρακτηριστικό headers δεν χρησιμοποιείται αποκλειστικά για αναφορά σε άλλα κελιά του πίνακα\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Όλα τα κελιά της κεφαλίδας του πίνακα αναφέρονται σε κελιά δεδομένων\",\n      \"fail\": \"Δεν αναφέρονται όλα τα κελιά της κεφαλίδας του πίνακα σε κελιά δεδομένων\",\n      \"incomplete\": \"Τα κελιά δεδομένων του πίνακα λείπουν ή είναι άδεια\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Όλο το περιεχόμενο της σελίδας έχει αναλυθεί.\",\n      \"fail\": \"Παρουσιάστηκαν προβλήματα κατά την ανάλυση του περιεχομένου σε αυτήν τη σελίδα.\",\n      \"incomplete\": \"Υπάρχει κρυφό περιεχόμενο στη σελίδα που δεν αναλύθηκε. Για να το αναλύσετε, θα πρέπει να ενεργοποιήσετε την εμφάνιση αυτού του περιεχομένου.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Διορθώστε οποιοδήποτε από τα παρακάτω:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Διορθώστε όλα τα παρακάτω:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"Το axe δεν μπορεί να πει τον λόγο. Ώρα για τον επιθεωρητή στοιχείων!\"\n}\n"
  },
  {
    "path": "locales/es.json",
    "content": "{\n  \"lang\": \"es\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Garantiza que cada valor para el atributo accesskey es único\",\n      \"help\": \"El valor del atributo accesskey debe ser único\"\n    },\n    \"area-alt\": {\n      \"description\": \"Garantiza que los elementos <area> de los mapas de imágenes tienen texto alternativo\",\n      \"help\": \"Los elementos <area> activos deben tener texto alternativo\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Garantiza que los atributos ARIA están permitidos para el rol de un elemento\",\n      \"help\": \"Los elementos solo deben usar atributos ARIA permitidos\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Garantiza que el atributo role tiene un valor apropiado para el elemento\",\n      \"help\": \"ARIA role debe ser apropiado para el elemento\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Garantiza que aria-hidden='true' no está presente en el 'body' del documento.\",\n      \"help\": \"aria-hidden='true' no debe estar presente en el 'body' del documento\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Garantiza que los elementos 'aria-hidden' no contienen elementos que admitan el foco\",\n      \"help\": \"Los elementos 'ARIA hidden' no deben contener elementos que admitan el foco\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Garantiza que cada 'ARIA input field' tiene un nombre accesible\",\n      \"help\": \"Los 'ARIA input fields' tienen un nombre accesible\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Garantiza que los elementos con 'ARIA roles' tienen todos los atributos ARIA requeridos\",\n      \"help\": \"Deben proporcionarse los atributos ARIA requeridos\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Garantiza que los elementos con un 'ARIA role' que requieren 'child roles' los contienen\",\n      \"help\": \"Ciertos 'ARIA roles' deben contener determinados hijos\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Garantiza que los elementos con un 'ARIA role' que requieren 'parent roles' están contenidos en ellos\",\n      \"help\": \"Ciertos 'ARIA roles' deben estar contenidos en determinados padres\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Garantiza que todos los elementos con un atributo role usan un valor válido\",\n      \"help\": \"Los 'ARIA roles' usados deben cumplir los requisitos para valores válidos\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Garantiza que cada 'ARIA toggle field' tiene un nombre accesible\",\n      \"help\": \"Los 'ARIA toggle fields' tienen un nombre accesible\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Garantiza que todos los atributos ARIA tienen valores válidos\",\n      \"help\": \"Los atributos ARIA deben cumplir los requisitos para valores válidos\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Garantiza que los atributos que empiezan por aria- son atributos ARIA válidos\",\n      \"help\": \"Los atributos ARIA deben cumplir los requisitos para nombres válidos\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Garantiza que los elementos <audio> tienen subtítulos\",\n      \"help\": \"Los elementos <audio> deben tener una pista de subtítulos\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Garantizar que el atributo autocomplete es correcto y adecuado para el campo de formulario\",\n      \"help\": \"El atributo autocomplete debe usarse correctamente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Garantizar que el espaciado de texto establecido mediante atributos style se puede ajustar con hojas de estilo personalizadas\",\n      \"help\": \"El espaciado de texto 'inline' debe poder ajustarse mediante hojas de estilo personalizadas\"\n    },\n    \"blink\": {\n      \"description\": \"Garantiza que no se usan elementos <blink>\",\n      \"help\": \"Los elementos <blink> están obsoletos y no deben usarse\"\n    },\n    \"button-name\": {\n      \"description\": \"Garantiza que los botones tienen texto discernible\",\n      \"help\": \"Los botones deben tener texto discernible\"\n    },\n    \"bypass\": {\n      \"description\": \"Garantiza que cada página tiene al menos un medio para que un usuario pueda saltarse la navegación y pasar directamente al contenido\",\n      \"help\": \"Las páginas deben tener medios para saltarse bloques repetidos\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Garantiza que el contraste entre colores de primer plano y fondo cumple los límites de la ratio para contraste WCAG 2 AA\",\n      \"help\": \"Los elementos deben tener un contraste de colores suficiente\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Garantiza que el contraste entre colores de primer plano y fondo cumple los límites de la ratio para contraste WCAG 2 AAA\",\n      \"help\": \"Los elementos deben tener un contraste de colores suficiente\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Garantiza que el contenido no está bloqueado en ninguna orientación de pantalla específica, y que el contenido es manejable en cualquier orientación de pantalla\",\n      \"help\": \"Las 'CSS Media queries' no se usan para bloquear la orientación de pantalla\"\n    },\n    \"definition-list\": {\n      \"description\": \"Garantiza que los elementos <dl> están estructurados correctamente\",\n      \"help\": \"Los elementos <dl> solo deben contener directamente grupos de <dt> y <dd> debidamente ordenados, o elementos <script> o <template>\"\n    },\n    \"dlitem\": {\n      \"description\": \"Garantiza que los elementos <dt> y <dd> están contenidos en un <dl>\",\n      \"help\": \"Los elementos <dt> y <dd> deben estar contenidos en un <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Garantiza que cada documento HTML tiene un elemento <title> no vacío\",\n      \"help\": \"Los documentos deben tener elementos <title> para ayudar en la navegación\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Garantiza que cada valor para el atributo id de elementos activos es único\",\n      \"help\": \"Los 'IDs' de elementos activos deben ser únicos\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Garantiza que cada valor del atributo id usado en ARIA y en 'labels' es único\",\n      \"help\": \"Los 'IDs' usados en ARIA y en 'labels' deben ser únicos\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Garantiza que cada valor para el atributo id es único\",\n      \"help\": \"El valor del atributo id debe ser único\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Garantiza que los encabezados tienen texto discernible\",\n      \"help\": \"Los encabezados no deben estar vacíos\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Garantiza que los elementos en orden de foco tienen un rol apropiado\",\n      \"help\": \"Los elementos en orden de foco necesitan un rol apropiado para contenido interactivo\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Garantiza que el campo de formulario no tiene múltiples elementos label\",\n      \"help\": \"El campo de formulario no debe tener múltiples elementos label\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Garantiza que los elementos <iframe> y <frame> contienen el script axe-core\",\n      \"help\": \"Los marcos deben probarse con axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Garantiza que los elementos <iframe> y <frame> contienen un atributo título único\",\n      \"help\": \"Los marcos deben tener un único atributo title\"\n    },\n    \"frame-title\": {\n      \"description\": \"Garantiza que los elementos <iframe> y <frame> contienen un atributo título no vacío\",\n      \"help\": \"Los marcos deben tener el atributo title\"\n    },\n    \"heading-order\": {\n      \"description\": \"Garantiza que el orden de los encabezados es semánticamente correcto\",\n      \"help\": \"El nivel de encabezados solo debería incrementarse en 1\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informa a los usuarios sobre contenido oculto.\",\n      \"help\": \"El contenido oculto de la página no se puede analizar\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Garantiza que cada documento HTML tiene un atributo lang\",\n      \"help\": \"El elemento <html> debe tener un atributo lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Garantiza que el atributo lang del elemento <html> tiene un valor válido\",\n      \"help\": \"El elemento <html> debe tener un valor válido para el atributo lang\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Garantizar que en los elementos HTML con atributos tanto lang como xml:lang válidos haya concordancia en el idioma base de la página\",\n      \"help\": \"Los elementos HTML con lang y xml:lang deben tener el mismo idioma base\"\n    },\n    \"image-alt\": {\n      \"description\": \"Garantiza que los elementos <img> tienen texto alternativo o un rol de none o presentation\",\n      \"help\": \"Las imágenes deben tener texto alternativo\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Garantiza que la alternativa a la imagen no se repite como texto\",\n      \"help\": \"El texto alternativo de las imágenes no debe repetirse como texto\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Garantizar que los 'input buttons' tienen texto discernible\",\n      \"help\": \"Los 'Input buttons' deben tener texto discernible\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Garantiza que los elementos <input type=\\\"image\\\"> tienen texto alternativo\",\n      \"help\": \"Los 'image buttons' deben tener texto alternativo\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Garantiza que, en los elementos etiquetados mediante su contenido, su texto visible debe formar parte de su nombre accesible\",\n      \"help\": \"Los elementos deben tener su texto visible como parte de su nombre accesible\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Garantiza que cada elemento de formulario no está etiquetado únicamente mediante los atributos title o aria-describedby \",\n      \"help\": \"Los elementos de formulario deben tener una etiqueta visible\"\n    },\n    \"label\": {\n      \"description\": \"Garantiza que cada elemento de formulario tiene una etiqueta\",\n      \"help\": \"Los elementos de formulario deben tener etiquetas\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Garantiza que el punto de referencia banner está en el nivel superior\",\n      \"help\": \"El punto de referencia banner no debe estar contenido en otro punto de referencia\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Garantiza que el punto de referencia complementary o aside está en el nivel superior\",\n      \"help\": \"Aside no debe estar contenido en otro punto de referencia\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Garantiza que el punto de referencia contentinfo está en el nivel superior\",\n      \"help\": \"El punto de referencia contentinfo no debe estar contenido en otro punto de referencia\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Garantiza que el punto de referencia main está en el nivel superior\",\n      \"help\": \"El punto de referencia main no debe estar contenido en otro punto de referencia\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Garantiza que el documento tiene, como mucho, un punto de referencia banner\",\n      \"help\": \"El documento no debe tener más de un punto de referencia banner\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Garantiza que el documento tiene, como mucho, un punto de referencia contentinfo\",\n      \"help\": \"El documento no debe tener más de un punto de referencia contentinfo\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Garantiza que el documento solo tiene un punto de referencia main y que cada marco incorporado en la página tiene, como mucho, un punto de referencia main\",\n      \"help\": \"El documento debe tener un punto de referencia main\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Garantiza que los puntos de referencia son únicos\",\n      \"description\": \"Los puntos de referencia deben tener una única combinación de role o role/label/title (es decir, un nombre accesible único)\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Los enlaces pueden distinguirse sin depender del color\",\n      \"help\": \"Los enlaces deben distinguirse del texto adyacente por un medio que no dependa del color\"\n    },\n    \"link-name\": {\n      \"description\": \"Garantiza que los enlaces tienen texto discernible\",\n      \"help\": \"Los enlaces deben tener texto discernible\"\n    },\n    \"list\": {\n      \"description\": \"Garantiza que las listas están estructuradas correctamente\",\n      \"help\": \"<ul> y <ol> solo deben contener directamente elementos <li>, <script> o <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Garantiza que los elementos <li> se utilizan semánticamente\",\n      \"help\": \"Los elementos <li> deben estar contenidos en un <ul> o un <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Garantiza que no se usan elementos <marquee>\",\n      \"help\": \"Los elementos <marquee> están obsoletos y no deben usarse\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Garantiza que no se usa <meta http-equiv=\\\"refresh\\\">\",\n      \"help\": \"El refresco programado no debe existir\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Garantizar que <meta name=\\\"viewport\\\"> puede ampliarse en grado significativo\",\n      \"help\": \"Los usuarios deben poder hacer zum y ampliar el texto hasta 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Garantiza que <meta name=\\\"viewport\\\"> no impide la ampliación y el zum en el texto\",\n      \"help\": \"No debe impedirse el zum y la ampliación\"\n    },\n    \"object-alt\": {\n      \"description\": \"Garantiza que los elementos <object> tienen texto alternativo\",\n      \"help\": \"Los elementos <object> deben tener texto alternativo\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Garantizar que los elementos p no se usan para diseñar encabezados\",\n      \"help\": \"No se usa texto en negrita, cursiva o tamaño de fuente para dar estilo de encabezados a elementos p\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Garantizar que la página, o al menos uno de sus marcos, contiene un encabezado de nivel 1\",\n      \"help\": \"La página debe contener un encabezado de nivel 1\"\n    },\n    \"region\": {\n      \"description\": \"Garantiza que todo el contenido de la página está incluido en puntos de referencia\",\n      \"help\": \"Todo el contenido de la página debe estar incluido en puntos de referencia\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Garantiza que los elementos [role='img'] tienen texto alternativo\",\n      \"help\": \"Los elementos [role='img'] tienen un texto alternativo\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Garantiza que el atributo scope se usa correctamente en las tablas\",\n      \"help\": \"El atributo scope debería usarse correctamente\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Los elementos que tienen contenido que puede desplazarse verticalmente (en 'scroll') deberían ser accesibles mediante el teclado\",\n      \"help\": \"Asegurar que la región de desplazamiento vertical ('scroll') tiene acceso por teclado\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Garantiza que no se usan mapas de imágenes del lado del servidor\",\n      \"help\": \"No deben usarse mapas de imágenes del lado del servidor\"\n    },\n    \"skip-link\": {\n      \"description\": \"Garantizar que todos los enlaces de salto ('skip') tienen un destino que admite el foco\",\n      \"help\": \"El destino del enlace de salto ('skip') debería existir y admitir el foco\"\n    },\n    \"tabindex\": {\n      \"description\": \"Garantiza que los valores del atributo tabindex no son mayores que 0\",\n      \"help\": \"Los elementos no deberían tener un tabindex mayor que 0\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Garantizar que las tablas no tienen el mismo summary y caption\",\n      \"help\": \"El elemento <caption> no debería contener el mismo texto que el atributo summary\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Garantizar que las tablas con título usan el elemento <caption>.\",\n      \"help\": \"Las celdas de datos o de encabezados no deberían usarse para dar título a una tabla de datos.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Garantizar que cada celda de datos no vacía de una tabla grande tiene uno o más encabezados de tabla\",\n      \"help\": \"Todos los elementos td no vacíos de una tabla mayor que 3 por 3 deben tener un encabezado de tabla asociado\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Garantizar que cada celda que use los encabezados en una tabla haga referencia a otra celda de esa tabla\",\n      \"help\": \"Todas las celdas de un elemento table que usen el atributo headers deben hacer referencia solo a otras celdas de esa misma tabla\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Garantizar que cada encabezado de tabla en una tabla de datos hace referencia a celdas de datos\",\n      \"help\": \"Todos los elementos th y elementos con role=columnheader/rowheader deben tener las celdas de datos que describen\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Garantiza que los atributos lang tienen valores válidos\",\n      \"help\": \"El atributo lang debe tener un valor válido\"\n    },\n    \"video-caption\": {\n      \"description\": \"Garantiza que los elementos <video> tienen subtítulos\",\n      \"help\": \"Los elementos <video> deben tener subtítulos\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"No se usan 'abstract roles'\",\n      \"fail\": \"Los 'abstract roles' no se pueden usar directamente\"\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Los atributos ARIA se usan correctamente para el rol definido\",\n      \"fail\": {\n        \"singular\": \"En ARIA, atributos no están permitidos : ${data.values}\",\n        \"plural\": \"En ARIA, atributono está permitido : ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"El rol ARIA está permitido para el elemento proporcionado\",\n      \"fail\": {\n        \"singular\": \"En ARIA, roles ${data.values} no están permitidos para el elemento proporcionado\",\n        \"plural\": \"En ARIA, role ${data.values}  no está permitido para el elemento proporcionado\"\n      },\n      \"incomplete\": {\n        \"singular\": \"En ARIA, hay que eliminar roles ${data.values} cuando el elemento se haga visible, ya que no están permitidos para el elemento\",\n        \"plural\": \"En ARIA, hay que eliminar role ${data.values} cuando el elemento se haga visible, ya que no está permitido para el elemento\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"No hay ningún atributo aria-hidden presente en el 'body' del documento\",\n      \"fail\": \"aria-hidden=true no debe estar presente en el 'body' del documento\"\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Usa una técnica admitida para aria-errormessage\",\n      \"fail\": {\n        \"singular\": \"En aria-errormessage, valores  ${data.values}`, se debe usar una técnica para anunciar el mensaje (p. ej., aria-live, aria-describedby, role=alert, etc.)\",\n        \"plural\": \"En aria-errormessage, valor  ${data.values}`, se debe usar una técnica para anunciar el mensaje (p. ej., aria-live, aria-describedby, role=alert, etc.)\"\n      }\n    },\n    \"has-widget-role\": {\n      \"pass\": \"El elemento tiene un rol de widget.\",\n      \"fail\": \"El elemento no tiene un rol de widget.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"El rol ARIA es válido\",\n      \"fail\": \"El rol debe ser uno de los roles ARIA válidos\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"No hay discordancia entre un <label> y el nombre accesible\",\n      \"incomplete\": \"Comprobar que el <label> no necesita ser parte del ARIA ${data} para el nombre del campo\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Todos los atributos ARIA requeridos están presentes\",\n      \"fail\": {\n        \"singular\": \"Atributos requeridos no presentes: ${data.values}\",\n        \"plural\": \"Atributo requerido no presente: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Los hijos ARIA requeridos están presentes\"\n      },\n      \"fail\": {\n        \"singular\": \"Rol de hijos requerido en ARIA no presente: ${data.values}\",\n        \"plural\": \"Rol de hijo requerido en ARIA no presente: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Esperando que se añada rol ARIA para hijos: ${data.values}\",\n        \"plural\": \"Esperando que se añada rol ARIA para hijo: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Rol de padre requerido en ARIA presente\",\n      \"fail\": {\n        \"singular\": \"Rol de ARIA requerido para padre s no presente: ${data.values}\",\n        \"plural\": \"Rol de ARIA requerido para padre  no presente: ${data.values}\"\n      }\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"El atributo ARIA está admitido\",\n      \"fail\": \"El atributo ARIA no está ampliamente admitido en lectores de pantalla y tecnologías de apoyo:  ${data.values}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"El rol ARIA está admitido\",\n      \"fail\": \"El rol usado no está ampliamente admitido en lectores de pantalla y tecnologías de apoyo:  ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Los valores de los atributos ARIA son válidos\",\n      \"fail\": {\n        \"singular\": \"Valores no válidos para atributo ARIA: ${data.values}\",\n        \"plural\": \"Valor no válido para atributo ARIA: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Atributos ARIA ID de elemento no existe en la página: ${data.values}\",\n        \"plural\": \"Atributo ARIA ID de elemento no existe en la página: ${data.values}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"Nombres de atributos ARIA válidos\",\n        \"plural\": \"Nombrede atributo ARIA válido\"\n      },\n      \"fail\": {\n        \"singular\": \"Nombres de atributos ARIA no válidos:  ${data.values}\",\n        \"plural\": \"Nombre de atributo ARIA no válido:  ${data.values}\"\n      }\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"El elemento tiene una semántica válida para un elemento en orden de foco.\",\n      \"fail\": \"El elemento tiene una semántica no válida para un elemento en orden de foco.\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"El elemento tiene un contraste de color suficiente de ${data.contrastRatio}\",\n      \"fail\": \"El elemento tiene un contraste de color insuficiente de ${data.contrastRatio} (color de primer plano: ${data.fgColor}, color de fondo: ${data.bgColor}, tamaño de fuente: ${data.fontSize}, grosor de fuente: ${data.fontWeight}). Ratio de contraste esperado: ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"El color de fondo del elemento no se pudo determinar debido a una imagen de fondo\",\n        \"bgGradient\": \"El color de fondo del elemento no se pudo determinar debido a un degradado de fondo\",\n        \"imgNode\": \"El color de fondo del elemento no se pudo determinar porque el elemento contiene un nodo de imagen\",\n        \"bgOverlap\": \"El color de fondo no se pudo determinar porque tiene otro elemento superpuesto\",\n        \"fgAlpha\": \"El color de fondo no se pudo determinar debido a una transparencia alfa\",\n        \"elmPartiallyObscured\": \"El color de fondo no se pudo determinar porque está parcialmente oculto por otro elemento\",\n        \"elmPartiallyObscuring\": \"El color de fondo del elemento no se pudo determinar porque se superpone parcialmente a otros elementos\",\n        \"outsideViewport\": \"El color de fondo del elemento no se pudo determinar porque está fuera de la ventana gráfica ('viewport')\",\n        \"equalRatio\": \"El elemento tiene una ratio de contraste 1:1 con el fondo\",\n        \"shortTextContent\": \"El contenido del elemento es demasiado corto para determinar si es contenido de texto propiamente dicho\",\n        \"default\": \"Imposible determinar la ratio de contraste\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"El elemento tiene un contraste de color suficiente de ${data.contrastRatio}\",\n      \"fail\": \"El elemento tiene un contraste de color insuficiente de ${data.contrastRatio} (color de primer plano: ${data.fgColor}, color de fondo: ${data.bgColor}, tamaño de fuente: ${data.fontSize}, grosor de fuente: ${data.fontWeight}). Ratio de contraste esperado: ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"El color de fondo del elemento no se pudo determinar debido a una imagen de fondo\",\n        \"bgGradient\": \"El color de fondo del elemento no se pudo determinar debido a un degradado de fondo\",\n        \"imgNode\": \"El color de fondo del elemento no se pudo determinar porque el elemento contiene un nodo de imagen\",\n        \"bgOverlap\": \"El color de fondo no se pudo determinar porque tiene otro elemento superpuesto\",\n        \"fgAlpha\": \"El color de fondo no se pudo determinar debido a una transparencia alfa\",\n        \"elmPartiallyObscured\": \"El color de fondo no se pudo determinar porque está parcialmente oculto por otro elemento\",\n        \"elmPartiallyObscuring\": \"El color de fondo del elemento no se pudo determinar porque se superpone parcialmente a otros elementos\",\n        \"outsideViewport\": \"El color de fondo del elemento no se pudo determinar porque está fuera de la ventana gráfica ('viewport')\",\n        \"equalRatio\": \"El elemento tiene una ratio de contraste 1:1 con el fondo\",\n        \"shortTextContent\": \"El contenido del elemento es demasiado corto para determinar si es contenido de texto propiamente dicho\",\n        \"default\": \"Imposible determinar la ratio de contraste\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Los enlaces se pueden distinguir respecto al texto adyacente de forma ajena al color\",\n      \"fail\": \"Es necesario distinguir los enlaces respecto al texto adyacente de una forma ajena al color\",\n      \"incomplete\": {\n        \"bgContrast\": \"No se pudo determinar la ratio de contraste del elemento. Comprobar si existe un estilo hover/focus distinto\",\n        \"bgImage\": \"La ratio de contraste del elemento no se pudo determinar debido a una imagen de fondo\",\n        \"bgGradient\": \"La ratio de contraste del elemento no se pudo determinar debido a un degradado de fondo\",\n        \"imgNode\": \"La ratio de contraste del elemento no se pudo determinar porque el elemento contiene un nodo de imagen\",\n        \"bgOverlap\": \"La ratio de contraste del elemento no se pudo determinar debido a superposición de elementos\",\n        \"default\": \"Imposible determinar la ratio de contraste\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"el valor de autocomplete está en un elemento apropiado\",\n      \"fail\": \"el valor de autocomplete es inapropiado para este tipo de input\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"el atributo autocomplete está formateado correctamente\",\n      \"fail\": \"el atributo autocomplete está formateado incorrectamente\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"El valor del atributo accesskey es único\",\n      \"fail\": \"El documento tiene múltiples elementos con el mismo accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"El elemento contiene elementos que admiten el foco\",\n      \"fail\": \"El elemento debería tener contenido que admita el foco\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Dentro del elemento no hay elementos que admitan el foco\",\n      \"fail\": \"El contenido que admita el foco debería ser desactivado o eliminado del DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"El elemento admite el foco\",\n      \"fail\": \"El elemento debería admitir el foco\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"El elemento no está en orden de tabulación o tiene texto accesible\",\n      \"fail\": \"El elemento está en orden de tabulación y no tiene texto accesible\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Dentro del elemento no hay elementos que admitan el foco\",\n      \"fail\": \"El contenido que admita el foco debería tener tabindex='-1' o ser eliminado del DOM\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"El punto de referencia ${data.role} está en el nivel superior.\",\n      \"fail\": \"El punto de referencia ${data.role} está contenido en otro punto de referencia.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"La página tiene al menos un encabezado de nivel 1\",\n      \"fail\": \"La página debe tener un encabezado de nivel 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"El documento tiene al menos un punto de referencia main\",\n      \"fail\": \"El documento no tiene punto de referencia main\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"El documento no tiene más de un punto de referencia banner\",\n      \"fail\": \"El documento tiene más de un punto de referencia banner\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"El documento no tiene más de un punto de referencia contentinfo\",\n      \"fail\": \"El documento tiene más de un punto de referencia contentinfo\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"El documento no tiene más de un punto de referencia main\",\n      \"fail\": \"El documento tiene más de un punto de referencia main\"\n    },\n    \"tabindex\": {\n      \"pass\": \"El elemento no tiene un tabindex mayor que 0\",\n      \"fail\": \"El elemento tiene un tabindex mayor que 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"El elemento tiene un valor válido para el atributo alt\",\n      \"fail\": \"El elemento tiene un atributo alt que contiene solo un carácter de espacio, que no es ignorado por todos los lectores de pantalla\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"El elemento no duplica texto existente en el texto alternativo de <img>\",\n      \"fail\": \"El elemento contiene un elemento <img> con texto alternativo que duplica texto existente\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"El elemento de formulario tiene un <label> explícito\",\n      \"fail\": \"El elemento de formulario no tiene un <label> explícito\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"El texto de ayuda (title o aria-describedby) no duplica el texto de label\",\n      \"fail\": \"El texto de ayuda (title o aria-describedby) es el mismo que el texto de label\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"El elemento de formulario tiene un <label> explícito visible\",\n      \"fail\": \"El elemento de formulario tiene un <label> explícito oculto\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"El elemento de formulario tiene un <label> implícito ('envuelto')\",\n      \"fail\": \"El elemento de formulario no tiene un <label> implícito ('envuelto')\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"El elemento contiene texto visible como parte de su nombre accesible\",\n      \"fail\": \"El texto contenido en el elemento no está incluido en el nombre accesible\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"El campo de formulario no tiene múltiples elementos label\",\n      \"fail\": \"Múltiples elementos label no son ampliamente admitidos en las tecnologías de apoyo\"\n    },\n    \"title-only\": {\n      \"pass\": \"El elemento de formulario no usa únicamente el atributo title para su etiqueta\",\n      \"fail\": \"Solo se usó title para generar la etiqueta de un elemento de formulario\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Los puntos de referencia deben tener una combinación única de role o role/label/title (es decir, un nombre accesible único)\",\n      \"fail\": \"El punto de referencia debe tener un aria-label, aria-labelledby o title único para que los puntos de referencia sean distinguibles\"\n    },\n    \"has-lang\": {\n      \"pass\": \"El elemento <html> tiene un atributo lang\",\n      \"fail\": \"El elemento <html> no tiene un atributo lang\"\n    },\n    \"valid-lang\": {\n      \"pass\": \"El valor del atributo lang está incluido en la lista de idiomas válidos\",\n      \"fail\": \"Valor del atributo lang no incluido en la lista de idiomas válidos\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Los atributos lang y xml:lang tienen el mismo idioma base\",\n      \"fail\": \"Los atributos lang y xml:lang no tienen el mismo idioma base\"\n    },\n    \"dlitem\": {\n      \"pass\": \"El elemento de lista de descripción tiene un elemento <dl> padre\",\n      \"fail\": \"El elemento de lista de descripción no tiene un elemento <dl> padre\"\n    },\n    \"listitem\": {\n      \"pass\": \"El elemento de lista tiene un elemento padre <ul>, <ol> o role=\\\"list\\\"\",\n      \"fail\": \"El elemento de lista no tiene un elemento padre <ul>, <ol> o role=\\\"list\\\"\"\n    },\n    \"only-dlitems\": {\n      \"pass\": \"El elemento de lista solo tiene hijos directos que están permitidos dentro de elementos <dt> o <dd>\",\n      \"fail\": \"El elemento de lista tiene hijos directos que no están permitidos dentro de elementos <dt> o <dd>\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"El elemento de lista solo tiene hijos directos que están permitidos dentro de elementos <li>\",\n      \"fail\": \"El elemento de lista tiene hijos directos que no están permitidos dentro de elementos <li>\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Cuando no está vacío, el elemento tiene tanto elementos <dt> como <dd>\",\n      \"fail\": \"Cuando no está vacío, el elemento no tiene al menos un elemento <dt> seguido por, al menos, un elemento <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"El elemento multimedia tiene una pista de subtítulos\",\n      \"incomplete\": \"Comprobar que hay disponibles subtítulos para el elemento\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"El marco incorporado fue probado con axe-core\",\n      \"fail\": \"El marco incorporado no se pudo probar con axe-core\",\n      \"incomplete\": \"El marco incorporado aún tiene que probarse con axe-core\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"La pantalla es manejable y no existe bloqueo de orientación\",\n      \"fail\": \"Se aplica bloqueo de orientación CSS y hace que la pantalla sea inmanejable\",\n      \"incomplete\": \"No se puede determinar  si hay bloqueo de orientación CSS\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"La etiqueta <meta> no impide un zum significativo en dispositivos móviles\",\n      \"fail\": \"La etiqueta <meta> limita el zum en dispositivos móviles\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"La etiqueta <meta> no impide el zum en dispositivos móviles\",\n      \"fail\": \"${data} en la etiqueta <meta> impide el zum en dispositivos móviles\"\n    },\n    \"header-present\": {\n      \"pass\": \"La página tiene un 'header'\",\n      \"fail\": \"La página no tiene un 'header'\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Horden de encabezados válido\",\n      \"fail\": \"Orden de encabezados no válido\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Encontrado enlace de salto ('skip') válido\",\n      \"fail\": \"No se han encontrado enlaces de salto ('skip') válidos\"\n    },\n    \"landmark\": {\n      \"pass\": \"La página tiene una región punto de referencia\",\n      \"fail\": \"La página no tiene una región punto de referencia\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"La etiqueta <meta> no refresca la página inmediatamente\",\n      \"fail\": \"La etiqueta <meta> fuerza el refresco programado de la página\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Los elementos <p> no se han diseñado como encabezados\",\n      \"fail\": \"Deberían usarse elementos de encabezado en vez de elementos <p> con estilos\"\n    },\n    \"region\": {\n      \"pass\": \"Todo el contenido de la página está incluido en puntos de referencia\",\n      \"fail\": \"La página tiene contenido no incluido en puntos de referencia\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Existe el destino del enlace de salto ('skip')\",\n      \"incomplete\": \"El destino del enlace de salto ('skip') debería volverse visible en la activación\",\n      \"fail\": \"No hay destino para el enlace de salto ('skip')\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"El atributo title del elemento es único\",\n      \"fail\": \"El atributo title del elemento no es único\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"El documento no tiene elementos activos que compartan el mismo atributo id\",\n      \"fail\": \"El documento tiene elementos activos con el mismo atributo id: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"El documento no tiene elementos referidos con ARIA o etiquetas que compartan el mismo atributo id\",\n      \"fail\": \"El documento tiene múltiples elementos referidos con ARIA con el mismo atributo id: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"El documento no tiene elementos estáticos que compartan el mismo atributo id\",\n      \"fail\": \"El documento tiene múltiples elementos estáticos con el mismo atributo id\"\n    },\n    \"aria-label\": {\n      \"pass\": \"El atributo aria-label existe y no está vacío\",\n      \"fail\": \"El atributo aria-label no existe o está vacío\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"El atributo aria-labelledby existe y hace referencia a elementos visibles para lectores de pantalla\",\n      \"fail\": \"El atributo aria-labelledby no existe, hace referencia a elementos inexistentes o hace referencia a elementos vacíos\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"No se han especificado estilos 'inline' con '!important' que afecten al espaciado de texto\",\n      \"fail\": {\n        \"singular\": \"Eliminar '!important' de inline styles ${data.values}, porque su anulación no está admitida en la mayoría de navegadores\",\n        \"plural\": \"Eliminar '!important' de inline style ${data.values}, porque su anulación no está admitida en la mayoría de navegadores\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"El elemento tiene texto interno visible para lectores de pantalla\",\n      \"fail\": \"El elemento no tiene texto interno visible para lectores de pantalla\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"El documento tiene un elemento <title> no vacío\",\n      \"fail\": \"El documento no tiene un elemento <title> no vacío\"\n    },\n    \"exists\": {\n      \"pass\": \"El elemento no existe\",\n      \"fail\": \"El elemento existe\"\n    },\n    \"has-alt\": {\n      \"pass\": \"El elemento tiene un atributo alt\",\n      \"fail\": \"El elemento no tiene un atributo alt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"El elemento tiene texto visible para lectores de pantalla\",\n      \"fail\": \"El elemento no tiene texto visible para lectores de pantalla\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"El elemento no es visible\",\n      \"fail\": \"El elemento es visible\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"El elemento tiene un atributo alt no vacío\",\n      \"fail\": \"El elemento no tiene atributo alt o el atributo alt está vacío\"\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"El elemento no tiene un atributo de valor\",\n        \"has-label\": \"El elemento tiene un atributo de valor no vacío\"\n      },\n      \"fail\": \"El elemento tiene un atributo de valor y el atributo de valor está vacío\"\n    },\n    \"non-empty-title\": {\n      \"pass\": \"El elemento tiene un atributo title\",\n      \"fail\": \"El elemento no tiene atributo title o el atributo title está vacío\"\n    },\n    \"non-empty-value\": {\n      \"pass\": \"El elemento tiene un atributo de valor no vacío\",\n      \"fail\": \"El elemento no tiene un atributo de valor o el atributo de valor está vacío\"\n    },\n    \"role-none\": {\n      \"pass\": \"La semántica predeterminada del elemento se anuló mediante role=\\\"none\\\"\",\n      \"fail\": \"La semántica predeterminada del elemento no se anuló mediante role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"La semántica predeterminada del elemento se anuló mediante role=\\\"presentation\\\"\",\n      \"fail\": \"La semántica predeterminada del elemento no se anuló mediante role=\\\"presentation\\\"\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"La primera fila de una tabla no se usa como título ('caption')\",\n      \"fail\": \"La primera fila de la tabla debería ser un título ('caption') en vez de una celda de tabla\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"El atributo scope solo se usa en elementos de encabezados de tabla (<th>)\",\n      \"fail\": \"En HTML 5, los atributos scope solo se pueden usar en elementos de encabezados de tabla (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"El contenido del atributo summary y de <caption> no están duplicados\",\n      \"fail\": \"El contenido del atributo summary y del elemento <caption> son idénticos\"\n    },\n    \"scope-value\": {\n      \"pass\": \"El atributo scope se usa correctamente\",\n      \"fail\": \"El valor del atributo scope solo puede ser 'row' o 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Todas las celdas de datos no vacías tienen encabezados de tabla\",\n      \"fail\": \"Algunas celdas de datos no vacías no tienen encabezados de tabla\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"El atributo headers se usa exclusivamente para hacer referencia a otras celdas de la tabla\",\n      \"fail\": \"El atributo headers no se usa exclusivamente para hacer referencia a otras celdas de la tabla\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Todas las celdas de encabezados de tabla hacen referencia a celdas de datos\",\n      \"fail\": \"No todas las celdas de encabezados de tabla hacen referencia a celdas de datos\",\n      \"incomplete\": \"Hay celdas de datos de la tabla ausentes o vacías\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Se ha analizado todo el contenido de la página.\",\n      \"fail\": \"Hubo problemas al analizar el contenido de esta página.\",\n      \"incomplete\": \"Hay contenido oculto en la página que no fue analizado. Necesitarás activar la visualización de este contenido a fin de analizarlo.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Corregir cualquiera de las siguientes incidencias:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Corregir (todas) las siguientes incidencias:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"Corregir (todas) las siguientes incidencias:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n}\n"
  },
  {
    "path": "locales/eu.json",
    "content": "{\n  \"lang\": \"eu\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Accesskey atributurako balio bakoitza bakarra dela bermatzen du\",\n      \"help\": \"Accesskey atributuaren balioak bakarra izan behar du\"\n    },\n    \"area-alt\": {\n      \"description\": \"Irudi-mapetako <area> elementuek ordezko testua dutela bermatzen du\",\n      \"help\": \"<area> elementu aktiboek ordezko testua izan behar dute\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"ARIA atributuak elementu baten rolerako baimenduta daudela bermatzen du\",\n      \"help\": \"Elementuek atributu ARIA baimenduak baino ez dituzte erabili behar\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"rol atributuak elementuarentzat balio egokia duela bermatzen du\",\n      \"help\": \"ARIA role egokia izan behar da elementuarentzat\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Bermatzen du aria-hidden = 'true' ez dagoela dokumentuaren 'body'an.\",\n      \"help\": \"aria-hidden = 'true' ez da dokumentuaren 'body'an egon behar\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Bermatzen du 'aria-hidden' elementuek ez dutela fokua onartzen duen elementurik\",\n      \"help\": \"'ARIA hidden' elementuek ez dute fokua onartzen duen elementurik izan behar\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Bermatzen du 'ARIA input field' bakoitzak izen irisgarria duela\",\n      \"help\": \"'ARIA input fields' ek izen erabilerraza dute\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Bermatzen du 'ARIA role' duten elementuek eskatzen diren atributu guztiak dituztela\",\n      \"help\": \"Eskatzen diren ARIA atributuak eman behar dira\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Bermatzen du 'ARIA role' bat duten eta 'child rol' bat behar duten elementuek\",\n      \"help\": \"Zenbait 'ARIA rolak' seme jakin batzuk izan behar dituzte\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Bermatzen du 'ARIA role' bat duten eta 'parent rol'ak eskatzen dituzten elementuak haietan jasota daudela\",\n      \"help\": \"Zenbait 'ARIA rol' aita jakin batzuengan eduki behar dira\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Role atributua duten elementu guztiek balio balioduna erabiltzen dutela bermatzen du\",\n      \"help\": \"Erabilitako 'ARIA rol'ek baliozko balioetarako baldintzak bete behar dituzte\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Bermatzen du 'ARIA toggle field' bakoitzak izen irisgarria duela\",\n      \"help\": \"'ARIA toggle fields'ek izen erabilerraza dute\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Atributu ARIA guztiek balio baliodunak dituztela bermatzen du\",\n      \"help\": \"ARIA Atributuek baliozko balioetarako baldintzak bete behar dituzte\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"aria-z hasten diren atributuak baliozko atributu ARIA direla bermatzen du\",\n      \"help\": \"ARIA atributuek izen baliodunen baldintzak bete behar dituzte\"\n    },\n    \"audio-caption\": {\n      \"description\": \"<audio> elementuek azpitituluak dituztela bermatzen du\",\n      \"help\": \"<audio> elementuek azpititulu-pista bat izan behar dute\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"autocomplete atributua formulario-eremurako zuzena eta egokia dela bermatzea\",\n      \"help\": \"autocomplete atributua behar bezala erabili behar da\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"style atributuen bidez ezarritako testu-tartea estilo-orri pertsonalizatuekin doitu daitekeela bermatzea\",\n      \"help\": \"'inline' testu-tartea doitu egin behar da, estilo-orri pertsonalizatuen bidez.\"\n    },\n    \"blink\": {\n      \"description\": \"Bermatzen du ez direla <blink> elementuak erabiltzen\",\n      \"help\": \"<blink> elementuak zaharkituta daude eta ez dira erabili behar\"\n    },\n    \"button-name\": {\n      \"description\": \"Botoiek testu bereizgarria dutela bermatzen du\",\n      \"help\": \"Botoiek testu bereizgarria izan behar dute\"\n    },\n    \"bypass\": {\n      \"description\": \"Orrialde bakoitzak gutxienez bitarteko bat duela bermatzen du, erabiltzaile batek nabigazioa gainditu eta edukira zuzenean pasatzeko aukera izan dezan\",\n      \"help\": \"Orriek bloke errepikatuak saltatzeko baliabideak izan behar dituzte\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Lehen planoko eta hondoko koloreen arteko kontrasteak WCAG 2 AA kontrasterako ratioaren mugak betetzen dituela bermatzen du.\",\n      \"help\": \"Elementuek kolore-kontraste nahikoa izan behar dute\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Lehen planoko eta hondoko koloreen arteko kontrasteak WCAG 2 AAA kontrasterako ratioaren mugak betetzen dituela bermatzen du.\",\n      \"help\": \"Elementuek kolore-kontraste nahikoa izan behar dute\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Bermatzen du edukia ez dagoela blokeatuta pantailako orientazio espezifiko batean, eta edukia maneiagarria dela pantailako edozein orientabidetan.\",\n      \"help\": \"'CSS Media query'ak ez dira erabiltzen pantailaren orientazioa blokeatzeko \"\n    },\n    \"definition-list\": {\n      \"description\": \"<dl> elementuak behar bezala egituratuta daudela bermatzen du\",\n      \"help\": \"<dl> elementuek zuzenean eduki behar dituzte <dt> eta <dd> multzoak , behar bezala ordenatuta , edo <script> edo <tenplate> elementuak\"\n    },\n    \"dlitem\": {\n      \"description\": \"Bermatzen du <dt> eta <dd> elementuak <dl> batean daudela.\",\n      \"help\": \"<dt> eta <dd> elementuek < dl> batean egon behar dute\"\n    },\n    \"document-title\": {\n      \"description\": \"HTML dokumentu bakoitzak <title> hutsik ez duen elementu bat duela bermatzen du\",\n      \"help\": \"Dokumentuek <title> elementuak izan behar dituzte nabigazioan laguntzeko\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Elementu aktiboen id atributurako balio bakoitza bakarra dela bermatzen du\",\n      \"help\": \"Elementu aktiboen 'ID'ak bakarrak izan behar dira\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"ARIAn eta 'label'etan erabilitako id atributuaren balio bakoitza bakarra dela bermatzen du\",\n      \"help\": \"ARIA eta labelet-an erabiltzen diren 'ID'ak bakarrak izan behar dira\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"id atributurako balio bakoitza bakarra dela bermatzen du\",\n      \"help\": \"id atributuaren balioak bakarra izan behar du\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Goiburuek testu bereizgarria dutela bermatzen du\",\n      \"help\": \"Goiburuak ez dira hutsik egon behar\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Foku ordenako elementuek rol egokia dutela bermatzen du\",\n      \"help\": \"Foku ordenako elementuek rol egokia behar dute eduki interaktiborako\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Formulario-eremuak label elementu ugari ez dituela bermatzen du\",\n      \"help\": \"Formulario-eremuak ez du label elementu ugari izan behar\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Bermatzen du <iframe> eta <frame> elementuek axe-core scripta dutela\",\n      \"help\": \"Markoak axe-core bidez probatu behar dira\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Bermatzen du <iframe> eta <frame> elementuek izenburu bakarreko atributua dutela\",\n      \"help\": \"Markoek title atributu bakarra izan behar dute\"\n    },\n    \"frame-title\": {\n      \"description\": \"Bermatzen du <iframe> eta <frame> elementuek hutsik ez dagoen izenburuaren atributua dutela\",\n      \"help\": \"Markoek title atributua izan behar dute\"\n    },\n    \"heading-order\": {\n      \"description\": \"Goiburuen ordena semantikoki zuzena dela bermatzen du\",\n      \"help\": \"Goiburuen maila soilik 1 gehitu beharko litzateke\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Erabiltzaileei ezkutuko edukiari buruzko informazioa ematen die.\",\n      \"help\": \"Orriaren ezkutuko edukia ezin da aztertu\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"HTML dokumentu bakoitzak lang atributu bat duela bermatzen du\",\n      \"help\": \"<html> elementuak lang atributua izan behar du\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Bermatzen du <html> elementuaren lang atributuak balio balioduna duela\",\n      \"help\": \"<html> elementuak balio balioduna izan behar du lang atributurako\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Lang zein xml:lang atributuak dituzten HTML elementuetan  orrialdearen oinarrizko hizkuntzan komunztadura dagoela bermatzea\",\n      \"help\": \"lang eta xml:lang duten HTML elementuek oinarrizko hizkuntza bera izan behar dute\"\n    },\n    \"image-alt\": {\n      \"description\": \"Bermatzen du <img> elementuek ordezko testua edo none edo presentation rola dutela\",\n      \"help\": \"Irudiek ordezko testua izan behar dute\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Bermatzen du irudiaren alternatiba ez dela testu gisa errepikatuko\",\n      \"help\": \"Irudien testu alternatiboa ez da testu gisa errepikatu behar\"\n    },\n    \"input-button-name\": {\n      \"description\": \"'input button'ek testu bereizgarria dutela bermatzea\",\n      \"help\": \"'input button'ek testu bereizgarria izan behar dute\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Bermatzen du <input type =\\\"image\\\" > elementuek ordezko testua dutela\",\n      \"help\": \"'image buttons'ek testu alternatiboa izan behar dute\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Bere edukiaren bidez etiketatutako elementuetan, bere testu ikusgarriak bere izen irisgarriaren parte izan behar duela bermatzen du.\",\n      \"help\": \"Elementuek beren testua ikusgai izan behar dute, beren izenaren zati irisgarri gisa.\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Bermatzen du inprimakiko elementu bakoitza ez dagoela etiketatuta title edo aria-describedby atributuen bidez soilik.\",\n      \"help\": \"Formularioko elementuek etiketa bat izan behar dute ikusgai\"\n    },\n    \"label\": {\n      \"description\": \"Formularioko elementu bakoitzak etiketa bat duela bermatzen du\",\n      \"help\": \"Formularioko elementuek etiketak izan behar dituzte\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Banner erreferentzia-puntua goiko mailan dagoela bermatzen du\",\n      \"help\": \"Banner erreferentzia-puntuak ez du beste erreferentzia-puntu batean egon behar\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Erreferentzia-puntu osagarria edo asidea goiko mailan dagoela bermatzen du \",\n      \"help\": \"aside-k ez du beste erreferentzia-puntu batean egon behar\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"contentinfo erreferentzia-puntua goiko mailan dagoela bermatzen du\",\n      \"help\": \"contentinfo erreferentzia-puntuak ez du beste erreferentzia-puntu batean egon behar\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Main erreferentzia puntua goi mailan dagoela bermatzen du\",\n      \"help\": \"main erreferentzia-puntuak ez du beste erreferentzia-puntu batean egon behar\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Dokumentuak, gehienez ere, banner erreferentzia-puntu bat duela bermatzen du\",\n      \"help\": \"Dokumentuak ezin du banner erreferentzia-puntu bat baino gehiago izan\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Dokumentuak, gehienez ere, contentinfo erreferentzia-puntu bat duela bermatzen du\",\n      \"help\": \"Dokumentuak ez du izan behar contentinfo erreferentzia-puntu bat baino gehiago\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Bermatzen du dokumentuak main erreferentzia-puntu bakarra duela, eta orrialdean sartutako esparru bakoitzak, gehienez ere, main erreferentzia-puntu bat duela.\",\n      \"help\": \"Dokumentuak main erreferentzia-puntu bat izan behar du\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Erreferentziazko puntuak bakarrak direla bermatzen du\",\n      \"description\": \"Erreferentzia-puntuek role edo role/label/title konbinazio bakarra izan behar dute (hau da, izen irisgarri bakarra)\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Estekak kolorearen menpe egon gabe bereiz daitezke\",\n      \"help\": \"Estekak ondoko testutik bereizi behar dira, kolorearen menpe ez dagoen bitarteko batez.\"\n    },\n    \"link-name\": {\n      \"description\": \"Estekek testu bereizgarria dutela bermatzen du\",\n      \"help\": \"Estekek testu bereizgarria izan behar dute\"\n    },\n    \"list\": {\n      \"description\": \"Zerrendak behar bezala egituratuta daudela bermatzen du\",\n      \"help\": \"<ul> eta <ol> zuzenean <li>, < script> edo < template> elementuak izan behar dituzte\"\n    },\n    \"listitem\": {\n      \"description\": \"<li> elementuak semantikoki erabiltzen direla bermatzen du\",\n      \"help\": \"< li> elementuek < ul> edo < ol> batean egon behar dute\"\n    },\n    \"marquee\": {\n      \"description\": \"Bermatzen du ez dela elementurik erabiltzen <marquee>\",\n      \"help\": \"<marquee> elementuak zaharkituta daude eta ez dira erabili behar\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Bermatzen du ez dela erabiltzen <meta http-equiv =\\\"refresh\\\" >\",\n      \"help\": \"Programatutako freskagarriak ez du egon behar\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Bermatzea <meta name =\\\"viewport\\\" > maila esanguratsuan zabal daitekeela\",\n      \"help\": \"Erabiltzaileek zooma egiteko eta testua %500era arte zabaltzeko aukera izan behar dute\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Bermatzen du <meta name =\\\"viewport\\\" > ez duela eragozten testua zabaltzea eta zooma egitea\",\n      \"help\": \"Zooma eta handitzea ez dira eragotzi behar\"\n    },\n    \"object-alt\": {\n      \"description\": \"Bermatzen du <object> elementuek ordezko testua dutela\",\n      \"help\": \"<object> elementuek testu alternatiboa izan behar dute\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"p elementuak goiburuak diseinatzeko erabiltzen ez direla bermatzea\",\n      \"help\": \"Ez da letra lodiz, letra etzanez edo letra-iturri tamainaz idatzitako testurik erabiltzen p elementuei goiburu-estiloa emateko.\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Orrialdeak, edo gutxienez bere esparruetako batek, 1. mailako goiburu bat duela bermatzea\",\n      \"help\": \"Orrialdeak 1. mailako goiburu bat izan behar du\"\n    },\n    \"region\": {\n      \"description\": \"Orrialdeko eduki guztia erreferentzia-puntuetan sartuta dagoela bermatzen du.\",\n      \"help\": \"Orrialdeko eduki guztiak erreferentzia-puntuetan sartuta egon behar du\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Elementuek [role = 'img'] testu alternatiboa dutela bermatzen du\",\n      \"help\": \"[role = 'img'] elementuek testu alternatibo bat dute\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"scope atributua tauletan behar bezala erabiltzen dela bermatzen du\",\n      \"help\": \"Scope atributua behar bezala erabili beharko litzateke\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Bertikalki mugi daitekeen edukia duten elementuak ('scroll' formatuan) teklatuaren bidez eskuratu ahal izango lirateke.\",\n      \"help\": \"Joan-etorri bertikaleko eremuak ('scroll') teklatu bidezko sarbidea duela ziurtatzea\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Zerbitzariaren aldeko irudi-maparik ez dela erabiltzen bermatzen du.\",\n      \"help\": \"Ez dira erabili behar zerbitzariaren aldeko irudi-mapak\"\n    },\n    \"skip-link\": {\n      \"description\": \"Salto-esteka guztiek ('skip') fokua onartzen duen helmuga dutela bermatzea\",\n      \"help\": \"Jauziko loturaren ('skip') helmugak egon beharko luke eta fokua onartu\"\n    },\n    \"tabindex\": {\n      \"description\": \"tabindex atributuaren balioak 0 baino handiagoak ez direla bermatzen du\",\n      \"help\": \"Elementuek ez lukete 0 baino tabindex handiagoa izan behar\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Taulek summary eta caption bera ez dutela bermatzea\",\n      \"help\": \"<caption> elementuak ez luke summary atributuaren testu bera izan behar\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Titulua duten taulek <caption> elementua erabiltzen dutela bermatzea.\",\n      \"help\": \"Datuen edo goiburuen gelaxkak ez lirateke erabili behar datu-taula bati izenburua emateko.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Taula handi batean hutsik ez dagoen datu-gelaxka bakoitzak taula-goiburu bat edo gehiago dituela bermatzea\",\n      \"help\": \"3 bider 3 baino taula handiagoko td elementu ez huts guztiek lotutako taula-goiburu bat izan behar dute\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Taula bateko goiburuak erabiltzen dituen gelaxka bakoitzak taula horretako beste gelaxka bati erreferentzia egiten diola bermatzea\",\n      \"help\": \"headers atributua erabiltzen duten elementu table baten gelaxka guztiek taula bereko beste gelaxka batzuei bakarrik egin behar diete erreferentzia\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Datu-taula bateko taula-goiburu bakoitzak datu-gelaxkak aipatzen dituela bermatzea\",\n      \"help\": \"th eta 'role = columnheader/rowheader 'duten elementu guztiek deskribatzen duten datu-gelaxkak izan behar dituzte:\"\n    },\n    \"valid-lang\": {\n      \"description\": \"lang atributuek balio baliodunak dituztela bermatzen du\",\n      \"help\": \"lang atributuak balio balioduna izan behar du\"\n    },\n    \"video-caption\": {\n      \"description\": \"Bermatzen du <video> elementuek azpitituluak dituztela\",\n      \"help\": \"<bideo> elementuek azpitituluak izan behar dituzte\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Ez dira 'abstract rolak' erabiltzen\",\n      \"fail\": \"'abstract rolak 'ezin dira zuzenean erabili\"\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA atributuak behar bezala erabiltzen dira zehaztutako rolerako\",\n      \"fail\": {\n        \"singular\": \"ARIAn, atributuak ez daude baimenduta : ${data.values}\",\n        \"plural\": \"ARIAn, atributua ez dago baimenduta : ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA rola baimenduta dago emandako elementurako\",\n      \"fail\": {\n        \"singular\": \"ARIAn, rolak ${data.values} -ak ez daude baimenduta emandako elementurako\",\n        \"plural\": \"ARIAn, rola ${data.values} -a ez dago baimenduta emandako elementurako\"\n      },\n      \"incomplete\": {\n        \"singular\": \"AREAN, rolak kendu behar dira ${data.values} elementua ikusgai egiten denean, zeren elementurako  ez daude baimenduta\",\n        \"plural\": \"AREAN, rola kendu behar da ${data.values} elementua ikusgai egiten denean, zeren elementurako  ez dago baimenduta\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Ez dago dokumentuko 'body'ean agertzen den aria-hidden atributurik\",\n      \"fail\": \"aria-hidden = true ez da dokumentuaren 'body'ean egon behar\"\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Teknika onartu bat erabiltzen du aria-errormessagerako\",\n      \"fail\": {\n        \"singular\": \"aria-errormessagen, bailioaes  ${data.values}`, mezua iragartzeko teknika bat erabili behar da (adibidez: aria-live, aria-describedby, role = alert, etab.).\",\n        \"plural\": \"aria-errormessagen, bailioa  ${data.values}`, mezua iragartzeko teknika bat erabili behar da (adibidez: aria-live, aria-describedby, role = alert, etab.).\"\n      }\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Elementuak widget rola du.\",\n      \"fail\": \"Elementuak ez du widget rolik.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA rola baliozkoa da\",\n      \"fail\": \"Rolak baliozko ARIA rola izan behar du\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Ez dago desadostasunik <label> baten eta izen irisgarriaren artean\",\n      \"incomplete\": \"Egiaztatu < label> zenbakiak ez duela zertan ARIAren ${data} parte izan eremuaren izenerako\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"ARIA atributu guztiak daude\",\n      \"fail\": {\n        \"singular\": \"Eskatutako atributuak ez daude: ${data.values}\",\n        \"plural\": \"Eskatutako atributua ez dago: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Eskatutako ARIA semeak bertan daude\"\n      },\n      \"fail\": {\n        \"singular\": \"Esktatutako ARIARren semeak ez daude : ${data.values}\",\n        \"plural\": \"Esktatutako ARIARren semeaez dago : ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA rola semeentzako: ${data.values}\",\n        \"plural\": \"ARIA rola semearentzako: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"ARIAn eskatzen den aitaren rola bertan dago\",\n      \"fail\": {\n        \"singular\": \"ARIAn eskatutako aitentzako rola  ez dago: ${data.values}\",\n        \"plural\": \"ARIAn eskatutako aitarentzako rola ez dago: ${data.values}\"\n      }\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA atributua onartuta dago\",\n      \"fail\": \"ARIA atributua ez dago oso onartuta pantaila-irakurgailuetan eta laguntza-teknologietan:  ${data.values}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA rola onartuta dago\",\n      \"fail\": \"Erabilitako rola ez dago oso onartuta pantaila-irakurgailuetan eta laguntza-teknologietan:  ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA atributuen balioak baliozkoak dira\",\n      \"fail\": {\n        \"singular\": \"Balioak ez dira baliozkoak ARIA atributorako: ${data.values}\",\n        \"plural\": \"Balioa ez da baliozkoa ARIA atributorako: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Elementuen atributua  ARIA ID ez dago orrian: ${data.values}\",\n        \"plural\": \"Elementuaren atributua  ARIA ID ez dago orrian: ${data.values}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"Baliozko ARIA atributuen izenak\",\n        \"plural\": \"Baliozko ARIA atributuen izena\"\n      },\n      \"fail\": {\n        \"singular\": \"Baliogabeko ARIA atributuen izenak:  ${data.values}\",\n        \"plural\": \"Baliogabeko ARIA atributuen izenak:  ${data.values}\"\n      }\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Elementuak semantika balioduna du foku-ordenan dagoen elementu batentzat.\",\n      \"fail\": \"Elementuak foku-ordenan dagoen elementu batentzat baliozkoa ez den semantika bat du..\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"Elementuak ${data.contrastRatio}-ko kolore-kontraste nahikoa du\",\n      \"fail\": \"Elementuaren ${data.contrastRatio}-ko kolore-kontrastea ez da nahikoa (ehen planoaren kolorea: ${data.fgColor}, hondoaren kolorea: ${data.bgColor}, letra-iturriaren tamaina: ${data.fontSize}, letra-iturriaren lodiera: ${data.fontWeight}). Espero den kontraste-ratioa: ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementuaren hondoko kolorea ezin izan da zehaztu, hondoko irudi batengatik\",\n        \"bgGradient\": \"Elementuaren hondoko kolorea ezin izan da zehaztu hondoko degradatu baten ondorioz\",\n        \"imgNode\": \"Elementuaren hondoaren kolorea ezin izan da zehaztu, elementuak irudi-nodo bat duelako.\",\n        \"bgOverlap\": \"Hondoko kolorea ezin izan da zehaztu, gainjarritako beste elementu bat duelako\",\n        \"fgAlpha\": \"Hondoko kolorea ezin izan da zehaztu alfa gardentasun baten ondorioz\",\n        \"elmPartiallyObscured\": \"Hondoaren kolorea ezin izan da zehaztu, beste elementu batek partzialki ezkutatzen duelako\",\n        \"elmPartiallyObscuring\": \"Elementuaren hondoaren kolorea ezin izan da zehaztu, beste elementu batzuei partzialki gainjartzen baitzaie.\",\n        \"outsideViewport\": \"Elementuaren hondoko kolorea ezin izan da zehaztu, leiho grafikotik kanpo dagoelako ('viewport')\",\n        \"equalRatio\": \"Elementuak 1:1 kontraste-ratioa du hondoarekin\",\n        \"shortTextContent\": \"Elementuaren edukia laburregia da testu-edukia bera den zehazteko\",\n        \"default\": \"Ezinezkoa da kontraste-ratioa zehaztea\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Elementuak ${data.contrastRatio}-ko kolore-kontraste nahikoa du\",\n      \"fail\": \"Elementuaren ${data.contrastRatio}-ko kolore-kontrastea ez da nahikoa (ehen planoaren kolorea: ${data.fgColor}, hondoaren kolorea: ${data.bgColor}, letra-iturriaren tamaina: ${data.fontSize}, letra-iturriaren lodiera: ${data.fontWeight}). Espero den kontraste-ratioa: ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementuaren hondoko kolorea ezin izan da zehaztu, hondoko irudi batengatik\",\n        \"bgGradient\": \"Elementuaren hondoko kolorea ezin izan da zehaztu hondoko degradatu baten ondorioz\",\n        \"imgNode\": \"Elementuaren hondoaren kolorea ezin izan da zehaztu, elementuak irudi-nodo bat duelako.\",\n        \"bgOverlap\": \"Hondoko kolorea ezin izan da zehaztu, gainjarritako beste elementu bat duelako\",\n        \"fgAlpha\": \"Hondoko kolorea ezin izan da zehaztu alfa gardentasun baten ondorioz\",\n        \"elmPartiallyObscured\": \"Hondoaren kolorea ezin izan da zehaztu, beste elementu batek partzialki ezkutatzen duelako\",\n        \"elmPartiallyObscuring\": \"Elementuaren hondoaren kolorea ezin izan da zehaztu, beste elementu batzuei partzialki gainjartzen baitzaie.\",\n        \"outsideViewport\": \"Elementuaren hondoko kolorea ezin izan da zehaztu, leiho grafikotik kanpo dagoelako ('viewport')\",\n        \"equalRatio\": \"Elementuak 1:1 kontraste-ratioa du hondoarekin\",\n        \"shortTextContent\": \"Elementuaren edukia laburregia da testu-edukia bera den zehazteko\",\n        \"default\": \"Ezinezkoa da kontraste-ratioa zehaztea\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Estekak ondoko testuarekiko bereiz daitezke, koloretik kanpo\",\n      \"fail\": \"Alboko testuarekiko loturak koloretik kanpo bereizi behar dira\",\n      \"incomplete\": {\n        \"bgContrast\": \"Ezin izan da elementuaren kontraste-ratioa zehaztu. Hover/focus estilo desberdina dagoen egiaztatzea\",\n        \"bgImage\": \"Elementuaren kontraste-ratioa ezin izan da zehaztu, hondoko irudi batengatik\",\n        \"bgGradient\": \"Elementuaren kontraste-ratioa ezin izan da zehaztu hondoko degradatu baten ondorioz\",\n        \"imgNode\": \"Elementuaren kontraste-ratioa ezin izan da zehaztu, elementuak irudi-nodo bat duelako.\",\n        \"bgOverlap\": \"Elementuaren kontraste-ratioa ezin izan da zehaztu elementuen gainjartzeagatik\",\n        \"default\": \"Ezinezkoa da kontraste-ratioa zehaztea\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"autocompleteren balioa elementu egoki batean dago\",\n      \"fail\": \"autocompleteren balioa ez da egokia input mota horretarako\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"autocompleteren atributua behar bezala formateatuta dago\",\n      \"fail\": \"autocompleteren atributua gaizki formateatuta dago\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Accesskey atributuaren balioa bakarra da\",\n      \"fail\": \"Dokumentuak elementu ugari ditu accesskey berarekin\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Elementuak fokua onartzen duten elementuak ditu\",\n      \"fail\": \"Elementuak fokua onartzen duen edukia izan beharko luke\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Elementuaren barruan ez dago fokua onartzen duen elementurik\",\n      \"fail\": \"Fokuak onartzen duen edukia desaktibatu edo ezabatu egin beharko litzateke\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Elementuak fokua onartzen du\",\n      \"fail\": \"Elementuak fokua onartu beharko luke\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Elementua ez dago tabulazio-ordenan edo testu irisgarria du\",\n      \"fail\": \"Elementua tabulazio-ordenan dago eta ez du testu eskuragarririk\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Elementuaren barruan ez dago fokua onartzen duen elementurik\",\n      \"fail\": \"Fokuak onartzen duen edukiak tabindex = '-1' izan beharko luke edo DOMetik ezabatu beharko litzateke\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} erreferentzia-puntua goiko mailan dago\",\n      \"fail\": \"${data.role} erreferentzia-puntua beste erreferentzia-puntu batean dago.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Orrialdeak 1. mailako goiburu bat du, gutxienez.\",\n      \"fail\": \"Orrialdeak 1. mailako goiburu bat izan behar du\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Dokumentuak gutxienez main erreferentzia-puntu bat du\",\n      \"fail\": \"Dokumentuak ez du main erreferentzia-punturik\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Dokumentuak banner erreferentzia-puntu bat baino ez du\",\n      \"fail\": \"Dokumentuak banner erreferentzia-puntu bat baino gehiago du\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Dokumentuak contentinfo erreferentzia-puntu bat baino ez du\",\n      \"fail\": \"Dokumentuak contentinfo erreferentzia-puntu bat baino gehiago du\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Dokumentuak ez du main erreferentzia-puntu bat baino gehiago\",\n      \"fail\": \"Dokumentuak main erreferentzia-puntu bat baino gehiago du\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Elementuak ez du 0 baino tabindex handiagoa\",\n      \"fail\": \"Elementuak 0 baino tabindex handiagoa du\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Elementuak balio balioduna du alt atributurako\",\n      \"fail\": \"Elementuak espazio-karaktere bat soilik duen alt atributua du, pantaila-irakurgailu guztiek alde batera uzten ez dutena.\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Elementuak ez du bikoizten < img>-ren testu alternatiboan dagoen testua.\",\n      \"fail\": \"Elementuak <img> elementu bat dauka, lehendik dagoen testua bikoizten duen testu alternatiboarekin\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Formularioko elementuak <label> dauka \",\n      \"fail\": \"Formularioko elementuak ez dauka <label> \"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Laguntza-testuak (title o aria-describedby) ez du label testua bikoizten\",\n      \"fail\": \"Laguntza-testuak (title o aria-describedby) label testuaren bera da\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Formularioko elementuak badu <label> ageriko bat\",\n      \"fail\": \"Formularioko elementuak <label> esplizitu ezkutua dauka\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Formularioko elementuak <label> inplizitua du ('bilduta')\",\n      \"fail\": \"Formularioko elementuak ez dauka <label> inplizitu bat ('bilduta')\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Elementuak testu ikusgarri bat dauka, bere izen irisgarriaren zati gisa.\",\n      \"fail\": \"Elementuko testua ez dago sartuta izen irisgarrian\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Formulario-eremuak ez du label elementu askorik\",\n      \"fail\": \"Label elementu asko ez dira asko onartzen laguntza-teknologietan\"\n    },\n    \"title-only\": {\n      \"pass\": \"Formularioko elementuak ez du soilik title atributua erabiltzen bere etiketarako\",\n      \"fail\": \"Formularioko elementu baten etiketa sortzeko baino ez da erabili title\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Erreferentzia-puntuek role edo role/label/title konbinazio bakarra izan behar dute (hau da, izen irisgarri bakarra)\",\n      \"fail\": \"Erreferentzia-puntuak aria-label, aria-labelledby edo title bakarra izan behar du, erreferentzia-puntuak bereizgarriak izan daitezen.\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> elementuak lang atributua du\",\n      \"fail\": \"<html> elementuak ez du lang atributurik\"\n    },\n    \"valid-lang\": {\n      \"pass\": \"lang atributuaren balioa hizkuntza baliodunen zerrendan sartuta dago\",\n      \"fail\": \"lang atributuaren balioa ez dago hizkuntza baliodunen zerrendan\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"lang eta xml: lang atributuek oinarrizko hizkuntza bera dute\",\n      \"fail\": \"lang eta xml: lang atributuek ez dute oinarrizko hizkuntza bera\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Deskribapen-zerrendako elementuak <dl> aita elementua du\",\n      \"fail\": \"Deskribapen-zerrendako elementuak ez dauka <dl> aita elementurik\"\n    },\n    \"listitem\": {\n      \"pass\": \"Zerrendaren elementuak < ul>, < ol> edo role=\\\"list\\\" duen elementu bat du\",\n      \"fail\": \"Zerrendaren elementuak ez du <ul>, <ol> o role=\\\"list\\\" elementu aitarik \"\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Zerrendaren elementuak zuzeneko semeak baino ez ditu, <dt> edo <dd> elementuen barruan baimenduta daudenak.\",\n      \"fail\": \"Zerrendaren elementuak zuzeneko semeak ditu, <dt> edo <dd> elementuen barruan baimenduta ez daudenak\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Zerrendako elementuak zuzeneko seme-alabak baino ez ditu, <li> elementuen barruan baimenduta daudenak.\",\n      \"fail\": \"Zerrendako elementuak zuzeneko seme-alabak ditu, <li> elementuen barruan baimenduta ez daudenak\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Hutsik ez dagoenean, elementuak <dt> eta < dd> ditu.\",\n      \"fail\": \"Hutsik ez dagoenean, elementuak ez du gutxienez <dt> elementurik, eta ondoren, gutxienez <dd> elementurik.\"\n    },\n    \"caption\": {\n      \"pass\": \"Multimedia-elementuak azpititulu-pista bat du\",\n      \"incomplete\": \"Elementurako azpitituluak daudela egiaztatu\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Sartutako markoa axe-core bidez probatu da\",\n      \"fail\": \"Sartutako markoa ezin izan da probatu axe-core bidez\",\n      \"incomplete\": \"Sartutako markoa axe-core bidez probatu behar da oraindik\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Pantaila maneiagarria da eta ez dago orientazio-blokeorik\",\n      \"fail\": \"CSSak orientazioa blokeatzen du, eta pantaila ezin da orientatu.\",\n      \"incomplete\": \"Ezin da zehaztu CSS orientazio-blokeorik dagoen\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta> etiketak ez du zum esanguratsurik eragozten gailu mugikorretan\",\n      \"fail\": \"<meta> etiketak gailu mugikorretan mugatzen du zooma\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta> etiketak ez du zoom eragozten gailu mugikorretan\",\n      \"fail\": \"${data} < meta> etiketan, gailu mugikorretan zoom-a eragozten du\"\n    },\n    \"header-present\": {\n      \"pass\": \"Orriak 'header' bat dauka\",\n      \"fail\": \"Orriak ez dauka 'header' bat\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Goiburuen hurrenkera zuzena da\",\n      \"fail\": \"Goiburuen hurrenkera ez da zuzena\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Aurkitutako jauzi esteka ('skip') baliozkoa da\",\n      \"fail\": \"Ez dira aurkitu jauzi esteka ('skip') baliozkorik\"\n    },\n    \"landmark\": {\n      \"pass\": \"Orrialdeak herrialde bat du erreferentzia-puntu\",\n      \"fail\": \"Orrialdeak ez du herrialde erreferentzia-punturik\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta> etiketak ez du orria freskatzen\",\n      \"fail\": \"<meta> etiketak orria freskatzen du\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> elementuak ez dira goiburu gisa diseinatus\",\n      \"fail\": \"Goiburuko elementuak erabili beharko lirateke <p> estiloko elementuak erabili beharrean\"\n    },\n    \"region\": {\n      \"pass\": \"Orriaren eduki guztia erreferentzia-puntuetan sartuta dago.\",\n      \"fail\": \"Erreferentzia-puntuetan sartu gabeko edukia du orriak\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Badago jauziko loturaren helmuga ('skip')\",\n      \"incomplete\": \"Jauzi-loturaren ('skip') helmuga agerian geratu beharko litzateke aktibazioan \",\n      \"fail\": \"Ez dago helmugarik jauziko estekarako ('skip')\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Elementuaren title atributua bakarra da\",\n      \"fail\": \"Elementuaren title atributua ez da bakarra\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Dokumentuak ez dauka id atributu bera partekatzen duen elementu aktiborik\",\n      \"fail\": \"Dokumentuak elementu aktiboak ditu, id atributu berarekin: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Dokumentuak ez dauka id atributu bera partekatzen duen Aari edo etiketekin lotutako elementurik\",\n      \"fail\": \"Dokumentuak hainbat elementu ditu, id atributu bereko aria batekin lotuta: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Dokumentuak ez dauka id atributu bera partekatzen duen elementu estatikorik\",\n      \"fail\": \"Dokumentuak hainbat elementu estatiko ditu, id atributu berarekin\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label atributua existitzen da eta ez dago hutsik\",\n      \"fail\": \"aria-label atributua ez da existitzen edo hutsik dago\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby atributua existitzen da eta pantaila-irakurgailuetarako ikus daitezkeen elementuei egiten die erreferentzia\",\n      \"fail\": \"aria-labelledby atributua ez da existitzen, existitzen ez diren elementuei egiten die erreferentzia edo elementu hutsei egiten die erreferentzia\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Ez da testu-tarteari eragiten dion '!important' estilorik zehaztu.\",\n      \"fail\": {\n        \"singular\": \"Ezabatu '!important' inline styleetatik ${data.values}, nabigatzaile gehienetan ez delako onartzen baliogabetzea.\",\n        \"plural\": \"Ezabatu '!important' inline styletik ${data.values}, nabigatzaile gehienetan ez delako onartzen baliogabetzea.\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Elementuak pantaila-irakurgailuek ikusteko moduko barne-testua du.\",\n      \"fail\": \"Elementuak ez du pantaila-irakurgailuek ikusteko moduko barne-testurik\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Dokumentuak badu elementu bat <title> ez hutsik\",\n      \"fail\": \"Dokumentuak ez dauka <title> elementurik hutsik\"\n    },\n    \"exists\": {\n      \"pass\": \"Elementua ez da existitzen\",\n      \"fail\": \"Elementua existitzen da\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Elementuak alt atributua du\",\n      \"fail\": \"Elementuak ez dauka alt atributurik\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Elementuak pantaila-irakurgailuetarako testua du ikusgai\",\n      \"fail\": \"Elementuak ez du pantaila-irakurgailuetarako testu ikusgarririk\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Elementua ez da ikusten\",\n      \"fail\": \"Elementua ikusten da\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Elementuak alt atributua du, eta ez dago hutsik\",\n      \"fail\": \"Elementuak ez du alt atributurik edo alt atributua hutsik dago\"\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \", eta ez du  balio-atributurik\",\n        \"has-label\": \"elementuak hutsik ez dagoen balio-atributua du  balio-atributurik\"\n      },\n      \"fail\": \"Elementuak balio-atributua du, eta balio-atributua hutsik dago\"\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Elementuak title atributua du\",\n      \"fail\": \"Elementuak ez du title atributurik edo title atributua hutsik dago\"\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Elementuak hutsik ez dagoen balio-atributua du\",\n      \"fail\": \"Elementuak ez dauka balio-atributurik edo balio-atributua hutsik dago\"\n    },\n    \"role-none\": {\n      \"pass\": \"Elementuaren semantika lehenetsia role =\\\"none\\\" bidez deuseztatu da.\",\n      \"fail\": \"Elementuaren semantika lehenetsia ez da baliogabetu role bidez =\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Elementuaren semantika lehenetsia role =\\\"presentation\\\" bidez deuseztatu da.\",\n      \"fail\": \"Elementuaren semantika lehenetsia ez da ezeztatu role =\\\"presentation\\\" bidez\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Taula baten lehen lerroa ez da izenburu gisa erabiltzen ('caption')\",\n      \"fail\": \"Taularen lehen ilarak izenburu bat izan beharko luke ('caption'), taula-gelaxka baten ordez\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Scope atributua taulako goiburuen elementuetan bakarrik erabiltzen da (<th>)\",\n      \"fail\": \"HTML5en, scope atributuak taula-goiburuen elementuetan bakarrik erabil daitezke (<th>) \"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Summary eta <caption> atributuaren edukia ez dago bikoiztuta\",\n      \"fail\": \"Summary atributuaren eta <caption> elementuaren edukia berdinak dira\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Scope atributua ondo erabiltzen da\",\n      \"fail\": \"Scope atributuaren balioa 'row' edo 'col' bakarrik izan daiteke\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Hutsik ez dauden datu-gelaxka guztiek taula-goiburuak dituzte\",\n      \"fail\": \"Hutsik ez dauden datu-gelaxka batzuek ez dute taula-goibururik\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Headers atributua taulako beste gelaxka batzuei erreferentzia egiteko baino ez da erabiltzen\",\n      \"fail\": \"Headers atributua ez da soilik erabiltzen taulako beste gelaxka batzuei erreferentzia egiteko\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Taulako goiburuen gelaxka guztiak datu-gelaxkak dira.\",\n      \"fail\": \"Taulako goiburuen gelaxka guztiak ez dira datu-gelaxkak.\",\n      \"incomplete\": \"Taulako datu-gelaxkak ez daude edo hutsik daude\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Orriaren eduki osoa aztertu da.\",\n      \"fail\": \"Arazoak egon ziren orrialde honen edukia aztertzean.\",\n      \"incomplete\": \"Orrian ezkutuko edukia dago, baina ez da aztertu. Eduki horren bistaratzea aktibatu beharko duzu, aztertzeko.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Gorabehera hauetako edozein zuzentzea::{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Gorabehera hauek zuzentzea (guztiak):{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  }\n}\n"
  },
  {
    "path": "locales/fr.json",
    "content": "{\n  \"lang\": \"fr\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Vérifier que chaque valeur de l’attribut accesskey est unique\",\n      \"help\": \"La valeur de l’attribut accesskey doit être unique\"\n    },\n    \"area-alt\": {\n      \"description\": \"Vérifier que les éléments <area> d’une image réactive ont une alternative textuelle\",\n      \"help\": \"Les éléments <area> actifs doivent avoir une alternative textuelle\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Vérifier que les attributs ARIA sont autorisés pour le rôle d’un élément\",\n      \"help\": \"Les éléments doivent seulement utiliser les attributs ARIA autorisés\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Vérifier que l’attribut role a une valeur valide pour cet élément\",\n      \"help\": \"Le rôle ARIA doit être valide pour cet élément\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Vérifier que chaque \\\"button\\\", \\\"link\\\" et \\\"menuitem\\\" ARIA a un nom accessible\",\n      \"help\": \"Les commandes ARIA doivent avoir un nom accessible\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Vérifier que chaque nœud ARIA \\\"dialog\\\" et \\\"alertdialog\\\" a un nom accessible\",\n      \"help\": \"Les nœuds ARIA \\\"dialog\\\" and \\\"alertdialog\\\" doivent avoir un nom accessible\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Vérifier qu’aria-hidden='true' n’est pas présent sur le corps du document (élément body)\",\n      \"help\": \"aria-hidden='true' ne doit pas être présent sur <body>\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Vérifier qu’aria-hidden n’est pas assigné aux éléments qui reçoivent le focus au clavier\",\n      \"help\": \"aria-hidden n’est pas assigné aux éléments qui reçoivent le focus au clavier\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Vérifier que chaque champ de formulaire avec ARIA est doté d’un intitulé accessible\",\n      \"help\": \"Les champs de formulaire ARIA ont un intitulé accessible\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Vérifier que chaque nœud ARIA \\\"meter\\\" a un nom accessible\",\n      \"help\": \"Les nœuds ARIA \\\"meter\\\" doivent avoir un nom accessible\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Vérifier que chaque nœud ARIA \\\"progressbar\\\" a un nom accessible\",\n      \"help\": \"Les nœuds ARIA \\\"progressbar\\\" doivent avoir un nom accessible\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Vérifier que les éléments avec des rôles ARIA ont les attributs ARIA requis\",\n      \"help\": \"Les attributs ARIA requis doivent être présents\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Vérifier que les éléments avec un rôle ARIA comportent aussi des rôles pour les descendants directs\",\n      \"help\": \"Certains rôles ARIA doivent comporter des descendants directs spécifiques\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Vérifier que les éléments avec un rôle ARIA requérant des rôles parents y sont contenus\",\n      \"help\": \"Certains rôles ARIA doivent être contenus par des parents spécifiques\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Vérifier qu’aria-roledescription n’est utilisé que sur des éléments qui ont un rôle implicite ou explicite\",\n      \"help\": \"Utiliser aria-roledescription sur les éléments dont le rôle a une valeur sémantique\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Vérifier que les éléments avec un attribut role utilisent une valeur valide\",\n      \"help\": \"Les rôles ARIA doivent se conformer aux valeurs valides\"\n    },\n    \"aria-text\": {\n      \"description\": \"Vérifier que \\\"role=text\\\" est uniquement utilisé sur des éléments sans descendants focalisables\",\n      \"help\": \"\\\"role=text\\\" ne doit pas avoir de descendant focalisable\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Vérifier que chaque champ de basculement ARIA a un libellé accessible\",\n      \"help\": \"Les champs de basculement ARIA ont un libellé accessible\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Vérifier que chaque nœud ARIA \\\"tooltip\\\" a un nom accessible\",\n      \"help\": \"Les nœuds ARIA \\\"tooltip\\\" doivent avoir un nom accessible\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Vérifier que chaque nœud ARIA \\\"treeitem\\\" a un nom accessible\",\n      \"help\": \"Les nœuds ARIA \\\"treeitem\\\" doivent avoir un nom accessible\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Vérifier que tous les attributs ARIA comportent des valeurs valides\",\n      \"help\": \"Les attributs ARIA doivent comporter des valeurs valides\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Vérifier que les attributs commençant par aria- sont des attributs ARIA valides\",\n      \"help\": \"Les attributs ARIA doivent se conformer aux noms valides\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Vérifier que les éléments <audio> ont des sous-titres\",\n      \"help\": \"Les éléments <audio> doivent avoir une piste de sous-titres\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Vérifier que l’attribut autocomplete est correctement adapté au champ de formulaire\",\n      \"help\": \"L’attribut autocomplete doit être utilisé correctement\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Vérifier que l’espacement du texte défini à travers une attribution de styles peut être ajusté via une feuille de style personnalisée\",\n      \"help\": \"L’espacement du texte inline peut être ajusté avec des feuilles de style personnalisées\"\n    },\n    \"blink\": {\n      \"description\": \"Vérifier que l’élément <blink> n’est pas utilisé\",\n      \"help\": \"L’élément <blink> est déprécié et ne doit pas être utilisé\"\n    },\n    \"button-name\": {\n      \"description\": \"Vérifier que les boutons ont un texte perceptible\",\n      \"help\": \"Les boutons doivent avoir un texte perceptible\"\n    },\n    \"bypass\": {\n      \"description\": \"Vérifier que chaque page dispose au minimum d’un mécanisme de contournement de la navigation pour accéder directement au contenu\",\n      \"help\": \"Chaque page doit fournir des moyens de contourner les contenus répétés\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Vérifier que les contrastes entre le premier plan et l’arrière-plan rencontrent les seuils de contrastes exigés par les WCAG 2 AAA\",\n      \"help\": \"Les éléments doivent avoir un contraste de couleurs suffisant\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Vérifier que les contrastes entre le premier plan et l’arrière-plan rencontrent les seuils de contrastes exigés par les WCAG 2 AA\",\n      \"help\": \"Les éléments doivent avoir un contraste de couleurs suffisant\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Vérifier que les contenus ne sont pas limités à une orientation spécifique de l’écran, et que le contenu est utilisable sous toutes les orientations de l’écran\",\n      \"help\": \"Les CSS Media queries ne sont pas utilisées pour verrouiller l’orientation de l’écran\"\n    },\n    \"definition-list\": {\n      \"description\": \"Vérifier que les éléments <dl> sont correctement structurés\",\n      \"help\": \"Les éléments <dl> ne doivent contenir directement que des groupes d’éléments <dt> et <dd> correctement ordonnés, ou des éléments <script> ou <template>\"\n    },\n    \"dlitem\": {\n      \"description\": \"Vérifier que les éléments <dt> et <dd> sont contenus dans un élément <dl>\",\n      \"help\": \"Les éléments <dt> et <dd> doivent être contenus dans un élément <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Vérifier que chaque document HTML contient un élément <title> non vide\",\n      \"help\": \"Chaque document doit avoir un élément <title> pour aider à la navigation\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Vérifier que la valeur d’attribut id de chaque élément actif est unique\",\n      \"help\": \"Les IDs des éléments actifs doivent être uniques\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Vérifier que chaque valeur d’attribut id utilisée avec ARIA et dans les étiquettes est unique\",\n      \"help\": \"Les IDs utilisés avec ARIA et dans les étiquettes doivent être uniques\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Vérifier que la valeur de chaque attribut id est unique\",\n      \"help\": \"La valeur de l’attribut id doit être unique\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Vérifier que les niveaux de titre ont un texte perceptible\",\n      \"help\": \"Les niveaux de titre ne doivent pas être vides\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Vérifier que les entêtes de tableaux ont un texte perceptible\",\n      \"help\": \"Les textes d’entêtes de tableaux ne doivent pas être vides\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Vérifier que les éléments dans le parcours du focus ont un rôle approprié\",\n      \"help\": \"Les éléments dans le parcours du focus doivent avoir un rôle approprié pour le contenu interactif\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Vérifier que le champ de formulaire n’a pas plusieurs éléments d’étiquettes\",\n      \"help\": \"Le champ de formulaire ne devrait pas comporter plusieurs éléments d’étiquettes\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Vérifier que les éléments <frame> et <iframe> avec du contenu focalisable n’ont pas de tabindex=-1\",\n      \"help\": \"Les cadres avec du contenu focalisable ne doivent pas avoir tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Vérifier que les éléments <iframe> et <frame> contiennent le script axe-core\",\n      \"help\": \"Les cadres doivent être testés avec axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Vérifier que les éléments <iframe> et <frame> ont un attribut title unique\",\n      \"help\": \"Chaque cadre doit avoir un attribut title unique\"\n    },\n    \"frame-title\": {\n      \"description\": \"Vérifier que les éléments <iframe> et <frame> ont un attribut title non vide\",\n      \"help\": \"Chaque cadre doit avoir un attribut title\"\n    },\n    \"heading-order\": {\n      \"description\": \"Vérifier que la hiérarchie des niveaux de titre est sémantiquement correcte\",\n      \"help\": \"Les niveaux de titre doivent s’incrémenter d’un seul niveau à la fois\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informer les utilisateurs sur les contenus cachés\",\n      \"help\": \"Le contenu caché sur la page ne peut pas être analysé\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Vérifier que chaque document HTML a un attribut lang\",\n      \"help\": \"L’élément <html> doit avoir un attribut lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Vérifier que l’attribut lang sur l’élément <html> a une valeur valide\",\n      \"help\": \"L’élément <html> doit avoir une valeur valide pour l’attribut lang\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Vérifier que les éléments HTML avec les attributs lang et xml:lang valides indiquent la même langue de base pour la page\",\n      \"help\": \"Les éléments HTML avec les attributs lang et xml:lang doivent avoir la même langue de base\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Vérifier que les liens qui ont le même nom accessible ont la même finalité\",\n      \"help\": \"Les liens avec le même nom ont la même finalité\"\n    },\n    \"image-alt\": {\n      \"description\": \"Vérifier que les éléments <img> ont une alternative textuelle, ou un rôle de type 'none' ou 'presentation'\",\n      \"help\": \"Les images doivent avoir une alternative textuelle\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Vérifier que l’intitulé des liens et boutons n’est pas répété dans l’alternative de l’image\",\n      \"help\": \"L’intitulé des liens et boutons ne doit pas être répété dans l’alternative de l’image\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Vérifier que la valeur textuelle des contrôles de boutons est perceptible\",\n      \"help\": \"La valeur textuelle des contrôles de boutons doit être perceptible\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Vérifier que les éléments <input type=\\\"image\\\"> ont une alternative textuelle\",\n      \"help\": \"Les boutons images doivent avoir une alternative textuelle\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Vérifier que dans le cas d’éléments identifiés par leur contenu textuel, le texte visible fait partie de l’intitulé accessible\",\n      \"help\": \"Le contenu textuel des éléments doit aussi se retrouver dans leur intitulé accessible\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Vérifier que chaque élément de formulaire n’est pas labellisé uniquement par les attributs title ou aria-describedby\",\n      \"help\": \"Chaque élément de formulaire doit avoir un label visible\"\n    },\n    \"label\": {\n      \"description\": \"Vérifier que chaque élément de formulaire a une étiquette\",\n      \"help\": \"Chaque élément de formulaire doit avoir une étiquette\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"La région banner ne devrait pas être contenue dans une autre région\",\n      \"help\": \"La région banner doit être au niveau le plus haut\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Vérifier que les landmarks complementary ou aside se retrouvent au plus haut niveau\",\n      \"help\": \"Aside ne doit pas être contenu dans un autre landmark\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"La région contentinfo ne devrait pas être contenue dans une autre région\",\n      \"help\": \"La région contentinfo doit être au niveau le plus haut\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"La région main ne devrait pas être contenue dans une autre région\",\n      \"help\": \"La région main doit être au niveau le plus haut\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Vérifier que le document n’a pas plus d’une région banner\",\n      \"help\": \"Le document contient au plus une région banner\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Vérifier que le document n’a pas plus d’une région contentinfo\",\n      \"help\": \"Le document contient au plus une région contentinfo\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Vérifier que le document a tout au plus, un seul landmark main\",\n      \"help\": \"Le document ne doit pas contenir plus d’un landmark main\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Vérifier qu’une navigation pointe vers le contenu principal de la page. Si la page contient des iframes, chaque iframe ne doit contenir au plus qu’une région main\",\n      \"help\": \"La page doit contenir une région main\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Vérifier que chaque landmark est unique\",\n      \"description\": \"Les landmarks doivent comporter un rôle unique, ou une étiquette accessible par la combinaison de role/label/title\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Les liens doivent pouvoir être distingués autrement que par la couleur\",\n      \"help\": \"Les liens doivent pouvoir être distingués du texte environnant d’une façon qui ne repose pas sur la couleur\"\n    },\n    \"link-name\": {\n      \"description\": \"Vérifier que les liens ont un texte perceptible\",\n      \"help\": \"Les liens doivent avoir un texte perceptible\"\n    },\n    \"list\": {\n      \"description\": \"Vérifier que les listes sont structurées correctement\",\n      \"help\": \"<ul> et <ol> ne doivent contenir directement que des éléments <li>, <script> ou <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Vérifier que les éléments <li> sont utilisés sémantiquement\",\n      \"help\": \"Les éléments <li> doivent être contenus dans un élément <ul> ou <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Vérifier que l’élément <marquee> n’est pas utilisé\",\n      \"help\": \"L’élément <marquee> est déprécié et ne doit pas être utilisé\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Vérifier que <meta http-equiv=\\\"refresh\\\"> n’est pas utilisé pour une actualisation différée\",\n      \"help\": \"L'actualisation différée ne doit pas être utilisée\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Vérifier que <meta http-equiv=\\\"refresh\\\"> n’est pas utilisé pour une actualisation différée\",\n      \"help\": \"L'actualisation différée en dessous de 20 heures ne doit pas être utilisée\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Vérifier que <meta name=\\\"viewport\\\"> permet un agrandissement significatif\",\n      \"help\": \"Les utilisateurs devraient pouvoir zoomer et agrandir le texte jusqu’à 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Vérifier que <meta name=\\\"viewport\\\"> ne désactive pas le zoom ni l’agrandissement\",\n      \"help\": \"Le zoom et l’agrandissement ne doivent pas être désactivés\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Les éléments interactifs imbriqués ne sont pas annoncés par les lecteurs d’écrans\",\n      \"help\": \"Verifier que les éléments interactifs ne sont pas imbriqués\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Vérifier que les éléments <video> ou <audio> ne jouent pas de son automatiquement pendant plus de 3 secondes sans mécanisme de contrôle pour stopper la lecture ou couper le son.\",\n      \"help\": \"Les éléments <video> ou <audio> ne jouent pas de son automatiquement\"\n    },\n    \"object-alt\": {\n      \"description\": \"Vérifier que les éléments <object> ont une alternative textuelle\",\n      \"help\": \"Les éléments <object> doivent avoir une alternative textuelle\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Vérifier que les éléments p ne sont pas utilisés pour styler des niveaux de titres\",\n      \"help\": \"La graisse, le style et le corps du texte ne doivent pas être utilisés pour styler les éléments p comme des niveaux de titres\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Vérifier que la page, ou au moins une de ses iframes, contient un titre de niveau 1\",\n      \"help\": \"La page doit contenir un titre de niveau 1\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Signaler les éléments dont le rôle est 'none' ou 'presentation' et qui déclenchent la résolution de conflits de rôles.\",\n      \"help\": \"Les éléments avec un rôle 'none' ou 'presentation' doivent être signalés\"\n    },\n    \"region\": {\n      \"description\": \"Vérifier que tout le contenu est localisé dans une région\",\n      \"help\": \"Le contenu doit être localisé dans une région\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Vérifier que les éléments avec [role='img'] ont une équivalence textuelle\",\n      \"help\": \"Les éléments avec [role='img'] ont une équivalence textuelle\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Vérifier que l’attribut scope est utilisé correctement dans les tableaux\",\n      \"help\": \"L’attribut scope doit être utilisé correctement\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Les éléments dont le contenu défile devraient être accessibles au clavier\",\n      \"help\": \"Vérifier que les régions défilantes sont accessibles au clavier\"\n    },\n    \"select-name\": {\n      \"description\": \"Vérifier que l’élément 'select' a un nom accessible\",\n      \"help\": \"L’élément 'select' doit avoir un nom accessible\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Vérifier que les images réactives côté serveur ne sont pas utilisées\",\n      \"help\": \"Les images réactives côté serveur ne devraient pas être utilisées\"\n    },\n    \"skip-link\": {\n      \"description\": \"Vérifier que tous les liens d’évitement ont une cible pouvant recevoir le focus\",\n      \"help\": \"La cible d’un lien d’évitement doit exister et pouvoir recevoir le focus\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Vérifier que les éléments svg avec un rôle 'img', 'graphics-document', ou 'graphics-symbol' ont un texte accessible\",\n      \"help\": \"Les éléments svg avec un rôle 'img' ont un texte alternatif\"\n    },\n    \"tabindex\": {\n      \"description\": \"Vérifier que les valeurs de l’attribut tabindex ne sont pas supérieures à 0\",\n      \"help\": \"Aucun élément ne devrait avoir un tabindex avec une valeur supérieure à zéro\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Vérifier que chaque tableau n’ait pas un summary et un caption identiques\",\n      \"help\": \"L’élément <caption> ne devrait pas contenir le même texte que l’attribut summary\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Vérifier que les tableaux avec une légende utilisent l’élément <caption>\",\n      \"help\": \"Les données ou les cellules d’entête ne devraient pas être utilisées pour légender un tableau de données\"\n    },\n    \"target-size\": {\n      \"description\": \"Vérifier que la cible tactile a une taille et un espace suffisants\",\n      \"help\": \"Toutes les cibles tactiles doivent faire 24px de large, ou être suffisamment grandes\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Vérifier que chaque cellule de données non vide dans un tableau de données a une ou plusieurs cellules d’entête\",\n      \"help\": \"Chaque élément td non vide dans un tableau plus grand que 3 × 3 doit avoir une cellule d’entête associée\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Vérifier que chaque cellule utilisant l’attribut headers fait référence à une autre cellule du même tableau\",\n      \"help\": \"Les cellules utilisant l’attribut headers ne doivent faire référence qu’à d’autres cellules du même tableau\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Vérifier que chaque cellule d’entête dans un tableau de données fait référence à des cellules de données\",\n      \"help\": \"Tous les éléments th et ceux avec role=columnheader/rowheader doivent décrire des cellules de données\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Vérifier que les attributs lang ont des valeurs valides\",\n      \"help\": \"L’attribut lang doit avoir une valeur valide\"\n    },\n    \"video-caption\": {\n      \"description\": \"Vérifier que les éléments <video> ont des sous-titres\",\n      \"help\": \"Les éléments <video> doivent avoir des sous-titres\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Les rôles abstraits ne sont pas utilisés\",\n      \"fail\": {\n        \"singular\": \"Le rôle abstrait ne peut pas être utilisé directement : ${data.values}\",\n        \"plural\": \"Les rôles abstraits ne peuvent pas être utilisés directement : ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Les attributs ARIA sont utilisés correctement pour le rôle défini\",\n      \"fail\": {\n        \"singular\": \"L’attribut ARIA n’est pas autorisé : ${data.values}\",\n        \"plural\": \"Les attributs ARIA ne sont pas autorisés : ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"Le rôle ARIA est autorisé pour l’élément donné\",\n      \"fail\": {\n        \"singular\": \"Le rôle ARIA ${data.values} n’est pas autorisé pour l’élément donné\",\n        \"plural\": \"Les rôles ARIA ${data.values} ne sont pas autorisés pour l’élément donné\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Le rôle ARIA ${data.values} doit être retiré lorsque l’élément est rendu visible, car il n’est pas autorisé pour cet élément\",\n        \"plural\": \"Les rôles ARIA ${data.values} doivent être retirés lorsque l’élément est rendu visible, car ils ne sont pas autorisés pour cet élément\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Utiliser une technique prise en charge pour aria-errormessage\",\n      \"fail\": {\n        \"singular\": \"La valeur d’aria-errormessage `${data.values}` doit recourir à une technique pour annoncer le message (aria-live, aria-describedby, role=alert, etc.)\",\n        \"plural\": \"Les valeurs aria-errormessage `${data.values}` doivent recourir à une technique pour annoncer le message (aria-live, aria-describedby, role=alert, etc.)\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Vérifier que la valeur de l’attribut 'aria-errormessage' `${data.values}` se réfère à un élément existant\",\n        \"plural\": \"Vérifier que les valeurs de l’attribut 'aria-errormessage' `${data.values}` se réfèrent à des éléments existants\",\n        \"idrefs\": \"Impossible de déterminer si l’élément référencé par 'aria-errormessage' existe dans la page : ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Aucun attribut aria-hidden n’est présent sur body\",\n      \"fail\": \"aria-hidden=true ne devrait pas être présent sur body\"\n    },\n    \"aria-level\": {\n      \"pass\": \"Les valeurs d’aria-level sont valides\",\n      \"incomplete\": \"Les valeurs d’aria-level supérieures à 6 ne sont pas supportées par toutes les combinaisons de navigateurs et de lecteurs d’écrans\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"L’attribut ARIA est autorisé\",\n      \"fail\": \"L’attribut ARIA ne peut pas être utilisé, ajoutez un attribut role ou utilisez un élément différent : ${data.values}\",\n      \"incomplete\": \"L’attribut ARIA n’est pas bien supporté sur l’élément et le contenu texte sera utilisé à la place : ${data.values}\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Tous les attributs ARIA requis sont présents\",\n      \"fail\": {\n        \"singular\": \"L’attribut ARIA requis est manquant : ${data.values}\",\n        \"plural\": \"Les attributs ARIA requis sont manquants : ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Les descendants ARIA requis sont présents\"\n      },\n      \"fail\": {\n        \"singular\": \"Le descendant ARIA requis est manquant : ${data.values}\",\n        \"plural\": \"Les descendants ARIA requis sont manquants : ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Le rôle du descendant ARIA attendu doit être ajouté : ${data.values}\",\n        \"plural\": \"Les rôles des descendants ARIA attendus doivent être ajoutés : ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Les rôles parents ARIA requis sont présents\",\n      \"fail\": {\n        \"singular\": \"Le rôle parent ARIA requis est manquant : ${data.values}\",\n        \"plural\": \"Les rôles parents ARIA requis sont manquants : ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription utilisé sur un élément sémantique supporté\",\n      \"incomplete\": \"Vérifier que la valeur d’aria-roledescription est annoncée par les lecteurs d’écran supportés\",\n      \"fail\": \"Attribuer à l’élément un rôle qui supporte aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"L’attribut ARIA est supporté\",\n      \"fail\": \"L’attribut ARIA n’est pas suffisamment supporté par les lecteurs d’écran et autres technologies d’assistance : ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Les valeurs d’attribut ARIA sont valides\",\n      \"fail\": {\n        \"singular\": \"La valeur d’attribut ARIA est invalide : ${data.values}\",\n        \"plural\": \"Les valeurs d’attribut ARIA sont invalides : ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"L’ID d’élément référencé par l’attribut ARIA n’existe pas dans la page : ${data.needsReview}\",\n        \"ariaCurrent\": \"La valeur de l’attribut ARIA est invalide et sera traitée comme \\\"aria-current=true\\\" : ${data.needsReview}\",\n        \"idrefs\": \"Impossible de vérifier si l’ID d’élément référencé par l’attribut ARIA existe dans la page : ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"Les noms d’attributs ARIA sont valides\",\n        \"plural\": \"Le nom d’attribut ARIA est valide\"\n      },\n      \"fail\": {\n        \"singular\": \"Le nom d’attribut ARIA est invalide : ${data.values}\",\n        \"plural\": \"Les noms d’attributs ARIA sont invalides : ${data.values}\"\n      }\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Une seule valeur de rôle utilisée\",\n      \"fail\": \"Utiliser une seule valeur de rôle, dans la mesure où les rôles de secours ne sont pas supportés par les navigateurs anciens\",\n      \"incomplete\": \"Utiliser seulement les rôles 'presentation' ou 'none' puisqu’ils sont synonymes.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"L’élément a un attribut ARIA global : ${data.values}\",\n        \"plural\": \"L’élément a des attributs ARIA globaux : ${data.values}\"\n      },\n      \"fail\": \"L’élément n’a pas d’attribut ARIA global\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"L’élément a un rôle widget.\",\n      \"fail\": \"L’élément n’a pas de rôle widget.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"Le rôle ARIA est valide\",\n      \"fail\": {\n        \"singular\": \"Le rôle doit être un rôle ARIA valide : ${data.values}\",\n        \"plural\": \"Les rôles doivent être des rôles ARIA valides : ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"L’élément est focalisable.\",\n      \"fail\": \"L’élément n’est pas focalisable.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Il n’y a pas de décalage entre le <label> et l’intitulé accessible\",\n      \"incomplete\": \"Vérifier que le <label> n’a pas à faire partie du nom du champ de formulaire ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"Le rôle ARIA est supporté\",\n      \"fail\": \"Le rôle utilisé n’est pas suffisamment supporté par les technologies d’assistance\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"L’élément a une sémantique valide pour un élément dans l’ordre de tabulation.\",\n      \"fail\": \"L’élément n’a pas une sémantique valide pour un élément dans l’ordre de tabulation.\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"L’élément a un contraste de couleurs suffisant de ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} (couleur d’avant plan : ${data.fgColor}, couleur d’arrière plan : ${data.bgColor}, taille de police : ${data.fontSize}, graisse : ${data.fontWeight}). Contraste de couleur attendu : ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} entre l’avant plan et la couleur de l’ombre de texte (couleur d’avant plan : ${data.fgColor}, couleur de l’ombre de texte : ${data.shadowColor}, taille de police : ${data.fontSize}, graisse: ${data.fontWeight}). Contraste de couleurs attendu : ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} entre la couleur de l’ombre de texte et l’arrière plan (couleur de l’ombre de texte : ${data.shadowColor}, couleur d’arrière plan : ${data.bgColor}, taille de police : ${data.fontSize}, graisse: ${data.fontWeight}). Contraste de couleurs attendu : ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Impossible de déterminer le rapport de contraste\",\n        \"bgImage\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée à cause d’une image d’arrière-plan\",\n        \"bgGradient\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée à cause d’un dégradé d’arrière-plan\",\n        \"imgNode\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car l’élément contient une balise image\",\n        \"bgOverlap\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car un autre élément le chevauche\",\n        \"fgAlpha\": \"La couleur du texte de l’élément n’a pu être déterminée à cause d’une opacité réduite\",\n        \"elmPartiallyObscured\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car l’élément est partiellement masqué par un autre élément\",\n        \"elmPartiallyObscuring\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car il chevauche partiellement un autre élément\",\n        \"outsideViewport\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car il est à l’extérieur du viewport\",\n        \"equalRatio\": \"L’élément a un rapport de contraste de 1:1 avec son arrière-plan\",\n        \"shortTextContent\": \"Le contenu de l’élément est trop court pour déterminer s’il s’agit réellement d’un contenu textuel\",\n        \"nonBmp\": \"Le contenu de l’élément contient seulement des caractères non textuels\",\n        \"pseudoContent\": \"La couleur d’arrière plan de l’élément n’a pu être déterminée à cause d’un pseudo-élément\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"L’élément a un contraste de couleurs suffisant de ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} (couleur d’avant plan : ${data.fgColor}, couleur d’arrière plan : ${data.bgColor}, taille de police : ${data.fontSize}, graisse : ${data.fontWeight}). Contraste de couleur attendu : ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} entre l’avant plan et la couleur de l’ombre de texte (couleur d’avant plan : ${data.fgColor}, couleur de l’ombre de texte : ${data.shadowColor}, taille de police : ${data.fontSize}, graisse: ${data.fontWeight}). Contraste de couleurs attendu : ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"L’élément a un contraste de couleurs insuffisant de ${data.contrastRatio} entre la couleur de l’ombre de texte et l’arrière plan (couleur de l’ombre de texte : ${data.shadowColor}, couleur d’arrière plan : ${data.bgColor}, taille de police : ${data.fontSize}, graisse: ${data.fontWeight}). Contraste de couleurs attendu : ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Impossible de déterminer le rapport de contraste\",\n        \"bgImage\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée à cause d’une image d’arrière-plan\",\n        \"bgGradient\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée à cause d’un dégradé d’arrière-plan\",\n        \"imgNode\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car l’élément contient une balise image\",\n        \"bgOverlap\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car un autre élément le chevauche\",\n        \"fgAlpha\": \"La couleur du texte de l’élément n’a pu être déterminée à cause d’une opacité réduite\",\n        \"elmPartiallyObscured\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car l’élément est partiellement masqué par un autre élément\",\n        \"elmPartiallyObscuring\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car il chevauche partiellement un autre élément\",\n        \"outsideViewport\": \"La couleur d’arrière-plan de l’élément n’a pu être déterminée, car il est à l’extérieur du viewport\",\n        \"equalRatio\": \"L’élément a un rapport de contraste de 1:1 avec son arrière-plan\",\n        \"shortTextContent\": \"Le contenu de l’élément est trop court pour déterminer s’il s’agit réellement d’un contenu textuel\",\n        \"nonBmp\": \"Le contenu de l’élément contient seulement des caractères non textuels\",\n        \"pseudoContent\": \"La couleur d’arrière plan de l’élément n’a pu être déterminée à cause d’un pseudo-élément\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Les liens peuvent être distingués du texte environnant par un autre moyen que la couleur\",\n      \"fail\": \"Les liens doivent se distinguer du texte environnant par un autre moyen que la couleur\",\n      \"incomplete\": {\n        \"default\": \"Impossible de déterminer le rapport de contraste\",\n        \"bgContrast\": \"Le rapport de contraste de l’élément n’a pu être déterminé. Recherchez un style différent pour le hover/focus.\",\n        \"bgImage\": \"Le rapport de contraste de l’élément n’a pu être déterminé à cause d’une image d’arrière-plan\",\n        \"bgGradient\": \"Le rapport de contraste de l’élément n’a pu être déterminé à cause d’un dégradé d’arrière-plan\",\n        \"imgNode\": \"Le rapport de contraste de l’élément n’a pu être déterminé, car l’élément contient une balise image\",\n        \"bgOverlap\": \"Le rapport de contraste de l’élément n’a pu être déterminé à cause d’un chevauchement\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"La valeur autocomplete est sur un élément approprié\",\n      \"fail\": \"La valeur autocomplete est inappropriée sur ce type de champ de formulaire\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"L’attribut autocomplete est formaté correctement\",\n      \"fail\": \"L’attribut autocomplete n’est pas formaté correctement\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"La valeur de l’attribut accesskey est unique\",\n      \"fail\": \"Plusieurs éléments ont le même accesskey au sein du document\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"L’élément contient des éléments focalisables\",\n      \"fail\": \"L’élément devrait avoir du contenu focalisable\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Aucun élément focalisable contenu dans l’élément\",\n      \"fail\": \"Le contenu focalisable devrait être désactivé ou retiré du DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"L’élément est focalisable\",\n      \"fail\": \"L’élément devrait être focalisable\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Pas d’élément focalisable quand une modale est ouverte\",\n      \"incomplete\": \"Vérifiez que les éléments focalisables ne sont pas atteignables via la tabulation dans l’état actuel\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"L’élément n’est pas dans l’ordre de tabulation ou a un intitulé accessible\",\n      \"fail\": \"L’élément est dans l’ordre de tabulation et n’a pas d’intitulé accessible\",\n      \"incomplete\": \"Impossible de déterminer si l’élément a un nom accessible\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Aucun élément focalisable contenu dans l’élément\",\n      \"fail\": \"Le contenu focalisable devrait se voir assigné un tabindex='-1' ou être retiré du DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"L’élément n’a pas de descendants focalisables\",\n      \"fail\": \"L’élément a des descendants focalisables\",\n      \"incomplete\": \"Impossible de déterminer si l’élément a des descendants\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"La région ${data.role} est au niveau le plus haut.\",\n      \"fail\": \"La région ${data.role} est contenue dans une autre région.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"L’élément n’a pas de descendants focalisables\",\n      \"fail\": \"L’élément a des descendants focalisables\",\n      \"incomplete\": \"Impossible de déterminer si l’élément a des descendants\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"La page a au moins un titre de niveau un\",\n      \"fail\": \"La page doit avoir un titre de niveau un\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"La page a au moins une région main\",\n      \"fail\": \"La page doit avoir une région main\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Le document n’a pas plus d’une région banner\",\n      \"fail\": \"Le document a plus d’une région banner\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Le document n’a pas plus d’une région contentinfo\",\n      \"fail\": \"Le document a plus d’une région contentinfo\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Le document n’a pas plus d’une région main\",\n      \"fail\": \"Le document a plus d’une région banner\"\n    },\n    \"tabindex\": {\n      \"pass\": \"L’élément n’a pas de tabindex supérieur à 0\",\n      \"fail\": \"L’élément a un tabindex supérieur à 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"L’élément a une valeur d’attribut alt valide\",\n      \"fail\": \"L’élément a un attribut alt qui contient un caractère d’espacement qui n’est pas ignoré par les lecteurs d’écran\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"L’élément ne duplique pas un texte existant dans l’alternative textuelle de l’élément <img>\",\n      \"fail\": \"L’élément contient un élément <img> dont l’alternative textuelle duplique un texte existant\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"L’élément de formulaire a un <label> explicite\",\n      \"fail\": \"L’élément de formulaire n’a pas de <label> explicite\",\n      \"incomplete\": \"Impossible de déterminer si l’élément de formulaire a un <label> explicite\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"L’aide à la saisie (title ou aria-describedby) ne duplique pas le contenu du label\",\n      \"fail\": \"L’aide à la saisie (title ou aria-describedby) est identique au contenu du label\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"L’élément de formulaire a un <label> visible et explicite\",\n      \"fail\": \"L’élément de formulaire a un <label> explicite qui est masqué\",\n      \"incomplete\": \"Impossible de déterminer si l’élément de formulaire a un <label> explicite qui est masqué\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"L’élément de formulaire a un <label> implicite (imbriqué)\",\n      \"fail\": \"L’élément de formulaire n’a pas de <label> implicite (imbriqué)\",\n      \"incomplete\": \"Impossible de déterminer si l’élément de formulaire a un <label> implicite (imbriqué)\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"L’élément contient du texte visible qui n’est pas inclus dans l’intitulé accessible\",\n      \"fail\": \"Le texte contenu dans l’élément n’est pas inclus dans l’intitulé accessible\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"L’élément de formulaire n’a pas plusieurs éléments <label>\",\n      \"incomplete\": \"Des éléments associés à plusieurs étiquettes ne sont pas suffisamment supportés par les technologies d’assistance. Vérifier que la première étiquette contient toute l’information nécessaire.\"\n    },\n    \"title-only\": {\n      \"pass\": \"L’élément de formulaire n’a pas uniquement l’attribut title comme étiquette\",\n      \"fail\": \"Seul l’attribut title est utilisé comme étiquette pour l’élément de formulaire\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Les landmarks doivent comporter un rôle unique, ou une étiquette accessible par la combinaison de role/label/title\",\n      \"fail\": \"L’attribut landmark doit comporter une valeur d’attribut aria-label, aria-labelledby, ou title unique pour rendre le landmark distinct\"\n    },\n    \"has-lang\": {\n      \"pass\": \"L’élément <html> a un attribut lang\",\n      \"fail\": {\n        \"noXHTML\": \"L’attribut xml:lang n’est pas valide sur les pages HTML, utiliser l’attribut lang.\",\n        \"noLang\": \"L’élément <html> n’a pas d’attribut lang\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"La valeur de l’attribut lang fait partie des codes de langues valides\",\n      \"fail\": \"La valeur de l’attribut lang ne fait pas partie des codes de langues valides\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Les attributs lang et xml:lang indiquent la même langue de base\",\n      \"fail\": \"Les attributs lang et xml:lang indiquent des langues de base différentes\"\n    },\n    \"dlitem\": {\n      \"pass\": \"L’item de liste de description a un élément <dl> parent\",\n      \"fail\": \"L’item de liste de description n’a pas d’élément <dl> parent\"\n    },\n    \"listitem\": {\n      \"pass\": \"L’item de liste a un élément <ul>, <ol> ou role=\\\"list\\\" parent\",\n      \"fail\": {\n        \"default\": \"L’item de liste n’a pas d’élément <ul> ou <ol> parent\",\n        \"roleNotValid\": \"L’item de liste n’a pas d’élément <ul> ou <ol> parent sans un role ou un role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"L’élément de liste n’a que des descendants directs qui sont autorisés dans les éléments <dt> ou <dd>\",\n      \"fail\": \"L’élément de liste a des descendants directs qui ne sont pas autorisés dans les éléments <dt> ou <dd>\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"L’élément de liste n’a que des descendants directs qui sont autorisés dans les éléments <li>\",\n      \"fail\": {\n        \"default\": \"L’élément de liste comporte des descendants directs qui ne sont pas autorisés à l’intérieur de l’élément <li>\",\n        \"roleNotValid\": \"L’élément de liste comporte des descendants directs avec un rôle qui n’est pas autorisé : ${data.roles}\"\n      }\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"S’il n’est pas vide, l’élément contient au moins un élément <dt> et un élément <dd>\",\n      \"fail\": \"S’il n’est pas vide, l’élément doit contenir au moins un élément <dt> et un élément <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"L’élément multimédia a une piste de sous-titres\",\n      \"incomplete\": \"Aucune piste de sous-titres n’a pu être trouvée pour cet élément\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"L’iframe a été testée avec axe-core\",\n      \"fail\": \"L’iframe n’a pu être testée avec axe-core\",\n      \"incomplete\": \"L’iframe doit encore être testée avec axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> ou <audio> ne produit pas de son pour une durée plus grande que celle permise ou a un mécanisme de contrôle\",\n      \"fail\": \"<video> ou <audio> produisent du son pour une durée plus grande que celle permise et n’ont pas de mécanisme de contrôle\",\n      \"incomplete\": \"Verifier que l’élément <video> ou <audio> ne produit pas de son pour une durée plus grande que celle permise ou a un mécanisme de contrôle\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"L’utilisation de l’écran est indépendante de l’orientation et n’est pas limitée à un mode d’affichage donné\",\n      \"fail\": \"L’utilisation de l’écran est limitée à une orientation donnée par CSS, rendant l’affichage inutilisable\",\n      \"incomplete\": \"Le verrouillage de l’orientation d’affichage par CSS ne peut être déterminé\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"La balise <meta> ne limite pas l’agrandissement sur les appareils mobiles\",\n      \"fail\": \"La balise <meta> limite l’agrandissement sur les appareils mobiles\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"La balise <meta> n’empêche pas l’agrandissement sur les appareils mobiles\",\n      \"fail\": \"La balise <meta> empêche l’agrandissement sur les appareils mobiles\"\n    },\n    \"header-present\": {\n      \"pass\": \"La page a un entête\",\n      \"fail\": \"La page n’a pas d’entête\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Hiérarchie entre les titres valide\",\n      \"fail\": \"Hiérarchie entre les titres invalide\",\n      \"incomplete\": \"Impossible de déterminer le titre précédent\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Il n’y a pas d’autre lien avec le même nom qui a pour destination une URL différente\",\n      \"incomplete\": \"Vérifier que les liens ont la même finalité ou sont volontairement ambigus.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Lien d’évitement valide trouvé\",\n      \"fail\": \"Aucun lien d’évitement valide trouvé\"\n    },\n    \"landmark\": {\n      \"pass\": \"La page a une région\",\n      \"fail\": \"La page n’a pas de région\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Aucune balise <meta> ne rafraîchit immédiatement la page\",\n      \"fail\": \"La balise <meta> force le rafraîchissement minuté de la page\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Les éléments <p> ne sont pas stylés comme des titres\",\n      \"fail\": \"Des titres doivent être utilisés au lieu de styler des éléments <p>\"\n    },\n    \"region\": {\n      \"pass\": \"Contenu imbriqué dans une région ARIA\",\n      \"fail\": \"Contenu non imbriqué dans une région ARIA\"\n    },\n    \"skip-link\": {\n      \"pass\": \"La cible du lien d’évitement existe\",\n      \"incomplete\": \"La cible du lien d’évitement devrait devenir visible lors de l’activation\",\n      \"fail\": \"Lien d’évitement sans cible\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"L’attribut title de l’élément est unique\",\n      \"fail\": \"L’attribut title de l’élément n’est pas unique\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Le document ne comporte aucun élément actif partageant la même valeur d’attribut id\",\n      \"fail\": \"Le document comporte ou un plusieurs éléments actifs partageant la même valeur d’attribut id : ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Le document ne comporte aucun élément référencé par ARIA ou étiquettes partageant la même valeur d’attribut id\",\n      \"fail\": \"Le document comporte un ou plusieurs éléments référencés par ARIA partageant la même valeur d’attribut id : ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Le document n’a pas d’éléments qui partagent le même attribut id\",\n      \"fail\": \"Le document a plusieurs éléments avec le même attribut id : ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"L’attribut aria-label existe et n’est pas vide\",\n      \"fail\": \"L’attribut aria-label n’existe pas ou est vide\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"L’attribut aria-labelledby existe et fait référence à des éléments visibles par les lecteurs d’écran\",\n      \"fail\": \"L’attribut aria-labelledby n’existe pas, fait référence à des éléments qui n’existent pas ou à des éléments vides ou non visibles\",\n      \"incomplete\": \"S’assurer que l’attribut aria-labelledby fait référence à un élément existant\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Aucun style inline affectant l’espacement du texte avec '!important' n’a été spécifié\",\n      \"fail\": {\n        \"singular\": \"Retirer '!important' du style inline ${data.values}, car le remplacement n’est pas pris en charge par la plupart des navigateurs\",\n        \"plural\": \"Retirer '!important' des styles inline ${data.values}, car le remplacement n’est pas pris en charge par la plupart des navigateurs\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"L’élément a un contenu textuel visible par les lecteurs d’écran\",\n      \"fail\": \"L’élément n’a aucun contenu textuel visible par les lecteurs d’écran\",\n      \"incomplete\": \"Impossible de vérifier si l’élément a des enfants\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Le document a un élément <title> non vide\",\n      \"fail\": \"Le document n’a pas d’élément <title> non vide\"\n    },\n    \"exists\": {\n      \"pass\": \"L’élément n’existe pas\",\n      \"incomplete\": \"L’élément existe\"\n    },\n    \"has-alt\": {\n      \"pass\": \"L’élément a un attribut alt\",\n      \"fail\": \"L’élément n’a pas d’attribut alt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"L’élément a un contenu textuel visible par les lecteurs d’écran\",\n      \"fail\": \"L’élément n’a aucun contenu textuel visible par les lecteurs d’écran\",\n      \"incomplete\": \"Impossible de vérifier si l’élément a des enfants\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"L’élément n’est pas visible\",\n      \"fail\": \"L’élément est visible\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"L’élément a un attribut alt non vide\",\n      \"fail\": {\n        \"noAttr\": \"L’élément n’a pas d’attribut alt\",\n        \"emptyAttr\": \"L’élément a un attribut alt vide\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"L’élément n’a pas d’attribut value\",\n        \"has-label\": \"L’élément a un attribut value non-vide\"\n      },\n      \"fail\": \"L’élément a un attribut value, et cet attribut est vide\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"L’élément a un attribut placeholder\",\n      \"fail\": {\n        \"noAttr\": \"L’élément n’a pas d’attribut placeholder\",\n        \"emptyAttr\": \"L’élément a un attribut placeholder vide\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"L’élément a un attribut title\",\n      \"fail\": {\n        \"noAttr\": \"L’élément n’a pas d’attribut title\",\n        \"emptyAttr\": \"L’élément a un attribut title vide\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"L’élément a un attribut value non vide\",\n      \"fail\": {\n        \"noAttr\": \"L’élément n’a pas d’attribut value\",\n        \"emptyAttr\": \"L’élément a un attribut value vide\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"La sémantique par défaut de l’élément a été remplacée par role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"La sémantique par défaut de l’élément n’a pas été remplacée par role=\\\"none\\\" ou role=\\\"presentation\\\"\",\n        \"globalAria\": \"Le rôle de l’élément n’est pas un rôle de présentation car il a un attribut ARIA global\",\n        \"focusable\": \"Le rôle de l’élément n’est pas un rôle de présentation car il est focalisable\",\n        \"both\": \"Le rôle de l’élément n’est pas un rôle de présentation car il a un attribut ARIA global et est focalisable\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"La sémantique par défaut de l’élément est annulée avec role=\\\"none\\\"\",\n      \"fail\": \"La sémantique par défaut de l’élément n’est pas annulée avec role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"La sémantique par défaut de l’élément est annulée avec role=\\\"presentation\\\"\",\n      \"fail\": \"La sémantique par défaut de l’élément n’est pas annulée avec role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"L’élément a un enfant qui est un titre\",\n      \"fail\": {\n        \"noTitle\": \"L’élément n’a pas d’enfant qui est un titre\",\n        \"emptyTitle\": \"Le titre qui est enfant de cet élément est vide\"\n      },\n      \"incomplete\": \"Impossible de déterminer si l’élément a un enfant qui est un titre\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"La première ligne d’un tableau n’est pas utilisée en guise de légende\",\n      \"fail\": \"La première ligne d’un tableau devrait être un caption et non des cellules de tableau\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"L’attribut scope est utilisé uniquement sur des cellules d’entête de tableau\",\n      \"fail\": \"En HTML 5, l’attribut scope ne peut être utilisé que sur des cellules d’entête de tableau\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Les contenus de l’attribut summary et de <caption> ne sont pas identiques\",\n      \"fail\": \"Les contenus de l’attribut summary et de <caption> sont identiques\"\n    },\n    \"scope-value\": {\n      \"pass\": \"L’attribut scope est utilisé correctement\",\n      \"fail\": \"La valeur de l’attribut scope ne peut être que 'row' ou 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Toutes les cellules de données non vides ont un entête de tableau\",\n      \"fail\": \"Certaines cellules de données non vides n’ont pas d’entête de tableau\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"L’attribut headers est utilisé exclusivement pour faire référence à d’autres cellules dans le tableau\",\n      \"incomplete\": \"L’attribut headers est vide\",\n      \"fail\": \"L’attribut headers n’est pas utilisé exclusivement pour faire référence à d’autres cellules dans le tableau\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Toutes les cellules d’entête de tableau font référence à des cellules de données\",\n      \"fail\": \"Toutes les cellules d’entête de tableau ne font pas référence à des cellules de données\",\n      \"incomplete\": \"Les cellules de données sont absentes ou vides\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Tout le contenu de la page a été analysé.\",\n      \"fail\": \"Il y a eu des problèmes pour analyser le contenu de cette page.\",\n      \"incomplete\": \"Il y a du contenu caché sur la page qui n’a pas été analysé. Vous allez devoir modifier l’affichage de ce contenu afin de l’analyser.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Corriger l’un des éléments suivants : {{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Corriger tous les éléments suivants : {{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe n’a pu en déterminer la raison. Il est temps de sortir l’inspecteur d’éléments !\"\n}\n"
  },
  {
    "path": "locales/he.json",
    "content": "{\n  \"lang\": \"he\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"מוודא שכל ערך תכונה של accesskey יהיה ייחודי\",\n      \"help\": \"ערך התכונה accesskey צריך להיות ייחודי\"\n    },\n    \"area-alt\": {\n      \"description\": \"מוודא שלאלמנטים של <area> של מפות תמונה יש טקסט חלופי\",\n      \"help\": \"אלמנטים פעילים של <area> מוכרחים להיות עם טקסט חלופי\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"מוודא שתכונות ARIA מורשות לתפקיד אלמנט\",\n      \"help\": \"אלמנטים מוכרחים להרשות רק תכונות ARIA\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"מוודא שלתכונת תפקיד יש ערך מתאים לאלמנט\",\n      \"help\": \"תפקיד ARIA צריך להתאים לאלמנט\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"מוודא שלכל כפתור, קישור ופריט תפריט של ARIA יש שם נגיש\",\n      \"help\": \"על פקודות ARIA להיות עם שם נגיש\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"מוודא שלכל רכיב תיבת דיאלוג או תיבת התראה של ARIA יש שם נגיש\",\n      \"help\": \"על רכיבי תיבת דיאלוג או תיבת התראה של ARIA להיות עם שם נגיש\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"מוודא ש-aria-hidden='true' לא מוצג בגוף המסמך.\",\n      \"help\": \"אסור ש-aria-hidden='true' יוצג בגוף המסמך\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"מוודא שאלמנטים של aria-hidden לא מכילים אלמנטים בני מיקוד\",\n      \"help\": \"אסור שאלמנט סמוי של ARIA יכיל אלמנטים בני מיקוד\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"מוודא שלכל שדה הזנה של ARIA יש שם נגיש\",\n      \"help\": \"שדות הזנה של ARIA מוכרחים להיות עם שם נגיש\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"מוודא שלכל רכיב meter של ARIA יש שם נגיש\",\n      \"help\": \"על רכיבי meter של ARIA להיות עם שם נגיש\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"מוודא שלכל רכיב progressbar של ARIA יש שם נגיש\",\n      \"help\": \"על רכיבי progressbar של ARIA להיות עם שם נגיש\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"מוודא שלאלמנטים עם תפקידי ARIA יש את כל תכונות ARIA הדרושות\",\n      \"help\": \"יש לספק את תכונות ARIA הדרושות\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"מוודא שאלמנטים עם תפקיד ARIA שדורשים תפקידי ילדים מכילים אותם\",\n      \"help\": \"תפקידי ARIA מסוימים מוכרחים להכיל ילדים מסוימים\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"מוודא שאלמנטים עם תפקיד ARIA שדורשים תפקידי הורים מכילים אותם\",\n      \"help\": \"תפקידי ARIA מסוימים מוכרחים להכיל הורים מסוימים\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"מוודא ש-aria-roledescription הוא בשימוש רק על אלמנטים עם תפקיד מרומז או מפורש\",\n      \"help\": \"aria-roledescription מוכרח להיות על אלמנטים עם תפקיד סמנטי\"\n    },\n    \"aria-roles\": {\n      \"description\": \"מוודא שכל האלמנטים עם תכונת role משתמשים בערך קביל\",\n      \"help\": \"תפקידי ARIA שבשימוש מוכרחים להיות עם ערכים קבילים\"\n    },\n    \"aria-text\": {\n      \"description\": \"מוודא ש-\\\"role=text\\\" הוא בשימוש על אלמנטים ללא צאצאים בני מיקוד\",\n      \"help\": \"על \\\"role=text\\\" להיות ללא צאצאים בני מיקוד\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"מוודא שלכל שדה toggle של ARIA יש שם נגיש\",\n      \"help\": \"על שדות toggle של ARIA להיות עם שם נגיש\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"מוודא שלכל רכיב tooltip של ARIA יש שם נגיש\",\n      \"help\": \"על רכיבי toolrip של ARIA להיות עם שם נגיש\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"מוודא שלכל רכיב treeitem של ARIA יש שם נגיש\",\n      \"help\": \"רכיבי treeitem של ARIA צריכים להיות עם שם נגיש\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"מוודא שלכל תכונות ARIA יש ערכים קבילים\",\n      \"help\": \"תכונות ARIA מוכרחות להתאים לערכים קבילים\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"מוודא שתכונות שמתחילות עם aria- הן תכונות ARIA קבילות\",\n      \"help\": \"תכונות ARIA מוכרחות להתאים לשמות קבילים\"\n    },\n    \"audio-caption\": {\n      \"description\": \"מוודא שלאלמנטים של <audio> יש כתוביות\",\n      \"help\": \"אלמנטים של <audio> מוכרחים להיות עם רצועת כתוביות\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"מוודא שתכונת autocomplete נכונה ומתאימה לשדה הטופס\",\n      \"help\": \"יש להשתמש בתכונה autocomplete באופן נכון\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"מוודא שאפשר לשנות את ריווח הטקסט שהוגדר בתכונות העיצוב לפי stylesheets מותאמים אישית\",\n      \"help\": \"ריווח טקסט בשורה מוכרח להיות ניתן להתאמה עם stylesheets מותאמים אישית\"\n    },\n    \"blink\": {\n      \"description\": \"מוודא שאלמנטים של <blink> אינם בשימוש\",\n      \"help\": \"אלמנטים של <blink> אסורים ואין להשתמש בהם\"\n    },\n    \"button-name\": {\n      \"description\": \"מוודא שללחצנים יש טקסט מובן\",\n      \"help\": \"לחצנים מוכרחים להיות עם טקסט מובן\"\n    },\n    \"bypass\": {\n      \"description\": \"מוודא שלכל עמוד יש לפחות מנגנון אחד למשתמש לעקוף ניווט ולקפוץ ישירות לתוכן\",\n      \"help\": \"לעמוד מוכרחים להיות אמצעים לעקיפת חסימות חוזרות\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"מוודא שהניגוד בין צבעי הרקע והחזית עונה על דרישות הסף עבור יחסי הניגוד של WCAG 2 AAA\",\n      \"help\": \"לאלמנטים צריך להיות ניגוד צבעים מספק\"\n    },\n    \"color-contrast\": {\n      \"description\": \"מוודא שהניגוד בין צבעי הרקע והחזית עונה על דרישות הסף עבור יחסי הניגוד של WCAG 2 AA\",\n      \"help\": \"לאלמנטים צריך להיות ניגוד צבעים מספק\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"מוודא שהתוכן אינו נעול לכיוון תצוגה מסוים, והתוכן הוא בר ביצוע בכל כיווני התצוגה\",\n      \"help\": \"אסור ששאילתות CSS מדיה ינעלו את כיוון התצוגה\"\n    },\n    \"definition-list\": {\n      \"description\": \"מוודא שאלמנטים של <dl> בנויים נכונה\",\n      \"help\": \"אלמנטים של <dl> חייבים להכיל רק באופן ישיר קבוצות <dt> וכן <dd> שהוזמנו כראוי, <script>, <template> או אלמנטים של <div>\"\n    },\n    \"dlitem\": {\n      \"description\": \"מוודא שאלמנטים של <dt> וכן <dd> מוכלים על ידי <dl>\",\n      \"help\": \"אלמנטים של <dt> וכן <dd> מוכלים על ידי <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"מוודא שכל מסמך HTML מכיל אלמנט לא-ריק של <title>\",\n      \"help\": \"מסמכים מוכרחים להיות עם אלמנט <title> כדי לסייע בניווט\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"מוודא שכל ערך של התכונה id של אלמנטים פעילים הוא ייחודי\",\n      \"help\": \"ID של אלמנטים פעילים מוכרח להיות ייחודי\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"מוודא שכל ערך תכונת id שבשימוש ב-ARIA ובתוויות הוא ייחודי\",\n      \"help\": \"ID בשימוש ב-ARIA ובתוויות מוכרח להיות ייחודי\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"מוודא שכל ערך של התכונה id הוא ייחודי\",\n      \"help\": \"ערכי התכונה id מוכרחים להיות ייחודיים\"\n    },\n    \"empty-heading\": {\n      \"description\": \"מוודא שלכותרות יש טקסט מובן\",\n      \"help\": \"אסור שכותרות יהיו ריקות\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"מוודא שלכותרות של טבלה יש טקסט מובן\",\n      \"help\": \"אסור שכותרות של טבלה יהיו ריקות\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"מוודא שלאלמנטים בסדר המיקוד יש תפקיד מתאים לתוכן אינטראקטיבי\",\n      \"help\": \"אלמנטים בסדר המיקוד צריכים להיות עם תפקיד מתאים\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"מוודא ששדות הזנה הם בלי מספר אלמנטים של תווית\",\n      \"help\": \"אסור ששדות הזנה יהיו עם מספר אלמנטים של תווית\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"מוודא שלאלמנטים של <frame> ושל <iframe> עם תוכן בר מיקוד אין tabindex=-1\",\n      \"help\": \"אסור שלמסגרות עם תוכן בר מיקוד יהיה tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"מוודא שאלמנטים של <iframe> ושל <frame> מכילים את סקריפט ה-axe-core\",\n      \"help\": \"על מסגרות להיבחן עם axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"מוודא שאלמנטים של <iframe> ושל <frame> מכילים תכונת כותרת ייחודית\",\n      \"help\": \"מסגרות מוכרחות להיות עם תכונת כותרת ייחודית\"\n    },\n    \"frame-title\": {\n      \"description\": \"מוודא שלאלמנטים של <iframe> ושל <frame> יש שמות נגישים\",\n      \"help\": \"מסגרות מוכרחות להיות עם שמות נגישים\"\n    },\n    \"heading-order\": {\n      \"description\": \"מוודא שסדר הכותרות נכון סמנטית\",\n      \"help\": \"הדרגות של הכותרות צריכות לגדול רק באחת\"\n    },\n    \"hidden-content\": {\n      \"description\": \"מיידע את המשתמשים על תוכן נסתר.\",\n      \"help\": \"יש לנתח תוכן נסתר בעמוד\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"מוודא שלכל מסמך HTML יש תכונת lang\",\n      \"help\": \"אלמנט <html> מוכרח להיות עם תכונת lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"מוודא שלתכונת lang של האלמנט <html> יש ערך תקין\",\n      \"help\": \"אלמנט <html> מוכרח להיות עם ערך תקין לתכונה lang\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"מוודא שאלמנטים של HTML עם תכונות lang ו-xml:lang תקינות מסכימים על שפת הבסיס של העמוד\",\n      \"help\": \"אלמנטים של HTML עם lang ו-xml:lang מוכרחים להיות עם אותה שפת בסיס\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"מוודא שקישורים עם אותו שם נגיש משרתים מטרה דומה\",\n      \"help\": \"קישורים עם אותו השם מוכרחים לשרת מטרה דומה\"\n    },\n    \"image-alt\": {\n      \"description\": \"מוודא שלאלמנטים של <img> יש טקסט חלופי או תפקיד של none או תצוגה\",\n      \"help\": \"תמונות מוכרחות להיות עם טקסט חלופי\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"מוודא שהחלופה של התמונה לא חוזרת על עצמה בטקסט\",\n      \"help\": \"טקסט חלופי של תמונות לא אמור לחזור על עצמו בטקסט\"\n    },\n    \"input-button-name\": {\n      \"description\": \"מוודא שללחצני קלט יש טקסט מובן\",\n      \"help\": \"לחצני קלט מוכרחים להיות עם טקסט מובן\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"מוודא שלאלמנטים של <input type=\\\"image\\\"> יש טקסט חלופי\",\n      \"help\": \"לחצני תמונה מוכרחים להיות עם טקסט חלופי\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"מוודא שאלמנטים שמקבלים תווית דרך התוכן שלהם עם טקסט גלוי כחלק משמם הנגיש\",\n      \"help\": \"אלמנטים מוכרחים להיות עם טקסט גלוי כחלק משמם הנגיש\"\n    },\n    \"label-title-only\": {\n      \"description\": \"מוודא שלכל אלמנט של טופס יש תווית נראית ולא מתויג רק בשימוש בתוויות נסתרות, או בשימוש בתכונות של כותרת או aria-describedby\",\n      \"help\": \"אלמנטים של טופס צריכים תווית ברורה וגלויה\"\n    },\n    \"label\": {\n      \"description\": \"מוודא שלכל אלמנט של טופס יש תווית\",\n      \"help\": \"אלמנטים של טופס מוכרחים להיות עם תוויות\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"מוודא שבאנר ציון הדרך הוא ברמה העליונה בהיררכיה\",\n      \"help\": \"באנר ציון הדרך לא צריך להיות מוכל בתוך ציון דרך אחר\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"מוודא שציון הדרך המשלים או aside הוא ברמה העליונה\",\n      \"help\": \"אין להכיל aside בתוך ציון דרך אחר\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"מוודא שציון דרך מידע-תוכן הוא ברמה העליונה בהיררכיה\",\n      \"help\": \"ציון הדרך מידע-תוכן לא צריך להיות מוכל בתוך ציון דרך אחר\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"מוודא שציון הדרך הראשי הוא ברמה העליונה בהיררכיה\",\n      \"help\": \"ציון הדרך הראשי לא צריך להיות מוכל בתוך ציון דרך אחר\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"מוודא שלמסמך יש לכל היותר באנר ציון דרך אחד\",\n      \"help\": \"המסמך לא צריך להיות עם יותר מבאנר ציון דרך אחד\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"מוודא שלמסמך יש לכל היותר ציון דרך מידע-תוכן אחד\",\n      \"help\": \"מסמך לא צריך להיות עם יותר מציון דרך מידע-תוכן אחד\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"מוודא שלמסמך יש ציון דרך אחד מרכזי לכל היותר\",\n      \"help\": \"במסמך לא נכון שיהיה יותר מציון דרך ראשי אחד\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"מוודא שלמסמך יש ציון דרך מרכזי\",\n      \"help\": \"במסמך צריך להיות ציון דרך ראשי אחד\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"מוודא שציוני דרך הם ייחודיים\",\n      \"description\": \"לציוני דרך צריך להיות תפקיד או שילוב של תפקיד/תווית/כותרת (לדוג' שם נגיש) ייחודיים\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"מוודא שקישורים נבדלים מהטקסט מסביב באופן שאינו נסמך על צבע\",\n      \"help\": \"על קישורים להיות נבדלים מבלי להסתמך על צבע\"\n    },\n    \"link-name\": {\n      \"description\": \"מוודא שלקישורים יש טקסט מובן\",\n      \"help\": \"קישורים מוכרחים להיות עם טקסט מובן\"\n    },\n    \"list\": {\n      \"description\": \"מוודא שרשימות בנויות נכונה\",\n      \"help\": \"<ul> וכן <ol> חייבים להכיל רק באופן ישיר אלמנטים של <li>, <script> וכן <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"מוודא שאלמנטים של <li> הם בשימוש סמנטי\",\n      \"help\": \"אלמנטים של <li> חייבים להיות מוכלים על ידי <ul> או <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"מוודא שאלמנטים של <marquee> אינם בשימוש\",\n      \"help\": \"אלמנטים של <marquee> אסורים ואין להשתמש בהם\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"מוודא ש-<meta http-equiv=\\\"refresh\\\"> אינו בשימוש עבור ריענון מושהה\",\n      \"help\": \"אסור שריענון מושהה יתקיים\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"מוודא ש-<meta http-equiv=\\\"refresh\\\"> אינו בשימוש עבור ריענון מושהה\",\n      \"help\": \"אסור להשתמש בריענון מושהה של פחות מ-20 שעות\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"מוודא ש-<meta name=\\\"viewport\\\"> יכול להגדיל בכמות משמעותית\",\n      \"help\": \"משתמשים צריכים להצליח להגדיל את הטקסט עד 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"מוודא ש-<meta name=\\\"viewport\\\"> לא מנטרל הגדלת טקסט ומסך\",\n      \"help\": \"אין לבטל את הפונקציות של הגדלת המסך וטקסט\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"מוודא שמנגנוני בקרה לא מקוננים משום שקוראי מסך לא תמיד מתריעים עליהם או שהם יכולים לגרום לבעיות מיקוד עבור טכנולוגיות מסייעות\",\n      \"help\": \"אסור שמנגנוני בקרה לא פעילים יהיו מקוננים\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"מוודא שאלמנטים של <video> או <audio> אינם מפעילים שמע באופן אוטומטי למשך יותר מ-3 שניות ללא מנגנון בקרה לעצירה או להנמכת עוצמת השמע\",\n      \"help\": \"אלמנטים של <video> או <audio> אינם מופעלים באופן אוטומטי\"\n    },\n    \"object-alt\": {\n      \"description\": \"מוודא שלאלמנטים של <object> יש טקסט חלופי\",\n      \"help\": \"אלמנטים של <object> מוכרחים להיות עם טקסט חלופי\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"מוודא שטקסט דגוש, נטוי וגודל פונט לא בשימוש בעיצוב אלמנטים של <p> ככותרת\",\n      \"help\": \"אסור שאלמנטים מעוצבים של <p> ישמשו ככותרות\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"מוודא שהעמוד, או לפחות אחת המסגרות שלו, מכילים כותרת רמה אחת\",\n      \"help\": \"העמוד אמור להכיל כותרת רמה אחת\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"מסמן אלמנטים שהתפקיד שלהם הוא none או presentation ושמפעיל את הטריגר של פתרון תפקידים מתנגשים.\",\n      \"help\": \"על אלמנטים של תפקיד none או presentation להיות מסומנים\"\n    },\n    \"region\": {\n      \"description\": \"מוודא שכל תוכן העמוד בתוך ציוני דרך\",\n      \"help\": \"כל תוכן העמוד צריך להיות בתוך ציוני דרך\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"מוודא שלאלמנטים של [role='img'] יש טקסט חלופי\",\n      \"help\": \"אלמנטים של [role='img'] עם תפקיד של תמונה חייבים להיות עם טקסט חלופי\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"מוודא שמשתמשים בתכונה תחום נכונה על טבלאות\",\n      \"help\": \"יש להשתמש בתכונה תחום נכונה\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"מוודא שאלמנטים שיש להם תוכן בר גלילה נגישים על ידי מקלדת\",\n      \"help\": \"אזורי גלילה מוכרחים להיות עם נגישות של מקלדת\"\n    },\n    \"select-name\": {\n      \"description\": \"מוודא שלאלמנט הנבחר יש שם נגיש\",\n      \"help\": \"לאלמנט הנבחר צריך להיות שם נגיש\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"מוודא שמפות תמונה צד-שרת לא יהיו בשימוש\",\n      \"help\": \"אסור שמפות תמונה צד-שרת יהיו בשימוש\"\n    },\n    \"skip-link\": {\n      \"description\": \"מוודא שלכל קישורי דילוג לתוכן יש מטרה ברת מיקוד\",\n      \"help\": \"המטרה של קישור דילוג לתוכן צריכה להתקיים ולהיות ברת מיקוד\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"מוודא שלאלמנטים של <svg> עם תפקיד תמונה, מסמך גרפי או סמל גרפי יש טקסט נגיש\",\n      \"help\": \"אלמנטים של <svg> עם תפקיד של תמונה חייבים להיות עם טקסט חלופי\"\n    },\n    \"tabindex\": {\n      \"description\": \"מוודא שערכי התכונה tabindex אינם גדולים מ-0\",\n      \"help\": \"אלמנטים לא צריכים להיות עם tabindex גדול מאפס\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"מוודא שהאלמנט <caption> לא מכיל אותו טקסט כמו התכונה תקציר\",\n      \"help\": \"לטבלאות לא צריכים להיות אותם תקציר טבלה וכיתוב\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"מוודא שטבלאות עם כיתוב משתמשות באלמנט <caption>.\",\n      \"help\": \"תאי מידע או כותרת לא אמורים לשמש כדי לתת כיתוב לטבלת מידע.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"מוודא שלכל תאי מידע לא-ריק ב-<table> גדולה מ-3X3 יש כותרות טבלה אחת או יותר\",\n      \"help\": \"אלמנטים לא ריקים של <td> ב-<table> גדולה יותר חייבים להיות מקושרים עם כותרת טבלה \"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"מוודא שכל תא בטבלה שמשתמש בתכונת הכותרת מתייחס רק לתאים אחרים באותה טבלה\",\n      \"help\": \"תאי טבלה שמשתמשים בתכונת כותרות חייבים להתייחס לתאים באותה הטבלה\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"מוודא שלאלמנטים של <th> ולאלמנטים עם role=columnheader/rowheader יש תאי מידע שהם מתארים\",\n      \"help\": \"כותרות טבלה בטבלת מידע חייבים להתייחס לתאי מידע\"\n    },\n    \"valid-lang\": {\n      \"description\": \"מוודא שלתכונות lang יש ערכים קבילים\",\n      \"help\": \"לתכונה lang חייב להיות ערך קביל\"\n    },\n    \"video-caption\": {\n      \"description\": \"מוודא שלאלמנטים של <video> יש כתוביות\",\n      \"help\": \"אלמנטים של <video> מוכרחים להיות עם כתוביות\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"תפקידים מופשטים אינם בשימוש\",\n      \"fail\": {\n        \"singular\": \"תפקיד מופשט לא יכול להיות בשימוש ישיר: ${data.values}\",\n        \"plural\": \"תפקידים מופשטים לא יכולים להיות בשימוש ישיר: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"תכונות ARIA בשימוש נכון עבור התפקיד המוגדר\",\n      \"fail\": {\n        \"singular\": \"תכונת ARIA לא מורשית: ${data.values}\",\n        \"plural\": \"תכונות ARIA לא מורשות: ${data.values}\"\n      },\n      \"incomplete\": \"בדקו שאין בעיה אם מתעלמים מתכונת ה-ARIA באלמנט הזה: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"תפקיד ARIA מורשה עבור האלמנט הנתון\",\n      \"fail\": {\n        \"singular\": \"תפקיד ARIA ${data.values} לא מורשה עבור האלמנט הנתון\",\n        \"plural\": \"תפקידי ARIA ${data.values} לא מורשים עבור האלמנט הנתון\"\n      },\n      \"incomplete\": {\n        \"singular\": \"מוכרחים להסיר תפקיד ARIA ${data.values} כאשר האלמנט נעשה גלוי, שכן הוא לא מורשה עבור האלמנט\",\n        \"plural\": \"מוכרחים להסיר תפקידי ARIA ${data.values} כאשר האלמנט נעשה גלוי, שכן הם לא מורשים עבור האלמנט\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage קיים ומפנה לאלמנטים הגלויים לקוראי מסך שמשתמשים בטכניקת aria-errormessage נתמכת\",\n      \"fail\": {\n        \"singular\": \"ערך aria-errormessage `${data.values}` מוכרח להשתמש בטכניקה להקראת ההודעה (לדוג', aria-live, aria-describedby, role=alert, וכו')\",\n        \"plural\": \"ערכי aria-errormessage `${data.values}` מוכרחים להשתמש בטכניקה להקראת ההודעה (לדוג', aria-live, aria-describedby, role=alert, וכו')\",\n        \"hidden\": \"ערך aria-errormessage `${data.values}` לא יכול להפנות לאלמנט סמוי\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ודאו שערך `aria-errormessage `${data.values} מפנה לאלמנט קיים\",\n        \"plural\": \"ודאו שערכי `aria-errormessage `${data.values} מפנים לאלמנטים קיימים\",\n        \"idrefs\": \"לא ניתן לקבוע אם אלמנט aria-errormessage קיים בעמוד: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"אף תכונת aria-hidden לא נמצאת בגוף המסמך\",\n      \"fail\": \"אסור ש-aria-hidden=true יימצא בגוף המסמך\"\n    },\n    \"aria-level\": {\n      \"pass\": \"ערכי aria-level קבילים\",\n      \"incomplete\": \"ערכי aria-level אשר גדולים מ-6 לא נתמכים בכל השילובים של קוראי מסך ודפדפנים\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"תכונת ARIA מורשית\",\n      \"fail\": {\n        \"hasRolePlural\": \"לא ניתן להשתמש בתכונת ${data.prohibited} עם תפקיד \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"לא ניתן להשתמש בתכונות ${data.prohibited} עם תפקיד \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"לא ניתן להשתמש בתכונות ${data.prohibited} עבור ${data.nodeName} ללא תכונת תפקיד תקפה.\",\n        \"noRoleSingular\": \"לא ניתן להשתמש בתכונת ${data.prohibited} עבור ${data.nodeName} ללא תכונת תפקיד תקפה.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"תכונת ${data.prohibited} אינה נתמכת היטב עבור תפקיד \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"תכונות ${data.prohibited} אינן נתמכות היטב עבור תפקיד \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"תכונת ${data.prohibited} אינה נתמכת היטב עבור ${data.nodeName} ללא תכונת תפקיד תקפה.\",\n        \"noRolePlural\": \"תכונות ${data.prohibited} אינן נתמכות היטב עבור ${data.nodeName} ללא תכונת תפקיד תקפה.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"כל תכונות ARIA הדרושות נמצאות\",\n      \"fail\": {\n        \"singular\": \"תכונת ARIA הדרושה לא נמצאת: ${data.values}\",\n        \"plural\": \"תכונות ARIA הדרושות לא נמצאות: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"ילדי ARIA הדרושים נמצאים\"\n      },\n      \"fail\": {\n        \"singular\": \"תפקיד ילד ARIA הדרוש אינו נמצא: ${data.values}\",\n        \"plural\": \"תפקיד ילדי ARIA הדרושים אינם נמצאים: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"מצפים שתפקיד ילד ARIA יתווסף: ${data.values}\",\n        \"plural\": \"מצפים שתפקידי ילדי ARIA יתווספו: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"תפקיד הורה ARIA דרוש נמצא\",\n      \"fail\": {\n        \"singular\": \"תפקיד הורה ARIA דרוש לא נמצא: ${data.values}\",\n        \"plural\": \"תפקידי הורים ARIA דרושים לא נמצאים: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription בשימוש על תפקיד סמנטי נתמך\",\n      \"incomplete\": \"בדקו שה-aria-roledescription מוצהר על ידי קוראי מסך נתמכים\",\n      \"fail\": \"תנו לאלמנט תפקיד שתומך ב-aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"תכונת ARIA נתמכת\",\n      \"fail\": \"תכונת ARIA לא נתמכת בהרחבה בקוראי מסך וטכנולוגיות מסייעות: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"כל ערכי תכונות ARIA תקינים\",\n      \"fail\": {\n        \"singular\": \"ערך לא תקין של תכונת ARIA: ${data.values}\",\n        \"plural\": \"ערכים לא תקינים של תכונת ARIA: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"תכונת ARIA האלמנט ID לא קיים בעמוד: ${data.needsReview}\",\n        \"noIdShadow\": \"אלמנט ID בתכונת ARIA אינו קיים בעמוד או שהוא צאצא של צל עץ DOM אחר: ${data.needsReview}\",\n        \"ariaCurrent\": \"ערך תכונת ARIA לא תקין ויתייחסו אליו כאל \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"לא ניתן לקבוע אם בתכונת ARIA האלמנט ID קיים בעמוד: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"שם תכונת ARIA קביל\",\n      \"fail\": {\n        \"singular\": \"שם לא קביל לתכונת ARIA: ${data.values}\",\n        \"plural\": \"שמות לא קבילים לתכונות ARIA: ${data.values}\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"תפקיד ARIA אינו נגנז\",\n      \"fail\": \"תפקיד זה אינו נגנז: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"ערך תפקיד אחד בלבד בשימוש\",\n      \"fail\": \"השתמשו רק בערך תפקיד אחד, מאחר ותפקידי fallback לא נתמכים בדפדפנים ישנים\",\n      \"incomplete\": \"השתמשו רק בתפקיד 'presentation' או 'none' מאחר והם נרדפים.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"לאלמנט יש תכונת ARIA גלובלית: ${data.values}\",\n        \"plural\": \"לאלמנט יש תכונות ARIA גלובליות: ${data.values}\"\n      },\n      \"fail\": \"לאלמנט אין תכונת ARIA גלובלית: \"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"לאלמנט יש תפקיד של וגדג'ט.\",\n      \"fail\": \"לאלמנט אין תפקיד של וגדג'ט.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"תפקיד ARIA קביל\",\n      \"fail\": {\n        \"singular\": \"התפקיד צריך להיות אחד מתפקידי ARIA הקבילים: ${data.values}\",\n        \"plural\": \"התפקידים צריכים להיות מתפקידי ARIA הקבילים: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"האלמנט הוא בר מיקוד.\",\n      \"fail\": \"האלמנט אינו בר מיקוד.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"אין חוסר תיאום בין <label> ושם נגיש\",\n      \"incomplete\": \"בדקו שה-<label> לא צריך להיות חלק משם השדה של ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"תפקיד ARIA נתמך\",\n      \"fail\": \"התפקיד בשימוש לא נתמך בהרחבה בקוראי מסך וטכנולוגיות מסייעות: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"לאלמנט יש שדות סמנטיים קבילים לאלמנט בסדר המיקוד.\",\n      \"fail\": \"לאלמנט אין שדות סמנטיים קבילים לאלמנט בסדר המיקוד.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"לאלמנט יש ניגוד צבעים מספק של ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} (צבע חזית: ${data.fgColor}, צבע רקע: ${data.bgColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}).  יחס ניגוד מצופה של ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} בין צבע החזית וצבע הצל (צבע חזית: ${data.fgColor}, צבע צל טקסט: ${data.shadowColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}). יחס ניגוד מצופה של ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} בין צבע הצל וצבע הרקע (צבע צל טקסט: ${data.shadowColor}, צבע רקע: ${data.bgColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}). יחס ניגוד מצופה של ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"לא ניתן לקבוע את יחס הניגוד\",\n        \"bgImage\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל תמונת רקע\",\n        \"bgGradient\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל גרדה נאט רקע\",\n        \"imgNode\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהאלמנט מכיל רכיב תמונה\",\n        \"bgOverlap\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא חופף עם אלמנט אחר\",\n        \"fgAlpha\": \"לא ניתן לקבוע את צבע החזית של האלמנט בגלל שקיפות אלפא\",\n        \"elmPartiallyObscured\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא מוסתר חלקית על ידי אלמנט אחר\",\n        \"elmPartiallyObscuring\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא חופף חלקית עם אלמנטים אחרים\",\n        \"outsideViewport\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא מחוץ ל-viewpoint\",\n        \"equalRatio\": \"לאלמנט יש יחס ניגוד 1:1 עם הרקע\",\n        \"shortTextContent\": \"תוכן האלמנט קצר מכדי לקבוע אם מדובר בתוכן טקסט אמיתי\",\n        \"nonBmp\": \"תוכן האלמנט מכיל רק תווים שאינם טקסט\",\n        \"pseudoContent\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל פסאודו-אלמנט\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"לאלמנט יש ניגוד צבעים מספק של ${data.contrastRatio}\",\n        \"hidden\": \"האלמנט מוסתר\"\n      },\n      \"fail\": {\n        \"default\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} (צבע חזית: ${data.fgColor}, צבע רקע: ${data.bgColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}).  יחס ניגוד מצופה של ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} בין צבע החזית וצבע הצל (צבע חזית: ${data.fgColor}, צבע צל טקסט: ${data.shadowColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}). יחס ניגוד מצופה של ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"לאלמנט יש ניגוד צבעים לא מספק של ${data.contrastRatio} בין צבע הצל וצבע הרקע (צבע צל טקסט: ${data.shadowColor}, צבע רקע: ${data.bgColor}, גודל פונט: ${data.fontSize}, משקל פונט: ${data.fontWeight}). יחס ניגוד מצופה של ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"לא ניתן לקבוע את יחס הניגוד\",\n        \"bgImage\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל תמונת רקע\",\n        \"bgGradient\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל גרדה נאט רקע\",\n        \"imgNode\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהאלמנט מכיל רכיב תמונה\",\n        \"bgOverlap\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא חופף עם אלמנט אחר\",\n        \"fgAlpha\": \"לא ניתן לקבוע את צבע החזית של האלמנט בגלל שקיפות אלפא\",\n        \"elmPartiallyObscured\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא מוסתר חלקית על ידי אלמנט אחר\",\n        \"elmPartiallyObscuring\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא חופף חלקית עם אלמנטים אחרים\",\n        \"outsideViewport\": \"לא ניתן לקבוע את צבע הרקע של האלמנט כיוון שהוא מחוץ ל-viewpoint\",\n        \"equalRatio\": \"לאלמנט יש יחס ניגוד 1:1 עם הרקע\",\n        \"shortTextContent\": \"תוכן האלמנט קצר מכדי לקבוע אם מדובר בתוכן טקסט אמיתי\",\n        \"nonBmp\": \"תוכן האלמנט מכיל רק תווים שאינם טקסט\",\n        \"pseudoContent\": \"לא ניתן לקבוע את צבע הרקע של האלמנט בגלל פסאודו-אלמנט\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"קישורים יכולים להיבדל מהטקסט סביב בדרך אחרת שהיא לא צבע\",\n      \"fail\": \"קישורים צריכים להיבדל מהטקסט סביב בדרך אחרת שהיא לא צבע\",\n      \"incomplete\": {\n        \"default\": \"לא ניתן לקבוע את יחס הניגוד\",\n        \"bgContrast\": \"יחס הניגוד של האלמנט לא יכול היה להיקבע.  בדקו עבור עיצוב מבדיל של מעבר עכבר/מיקוד\",\n        \"bgImage\": \"יחס הניגוד של האלמנט לא יכול היה להיקבע בגלל תמונת רקע\",\n        \"bgGradient\": \"יחס הניגוד של האלמנט לא יכול היה להיקבע בגלל גרדיאנט רקע\",\n        \"imgNode\": \"יחס הניגוד של האלמנט לא יכול היה להיקבע כיוון שהאלמנט מכיל \",\n        \"bgOverlap\": \"יחס הניגוד של האלמנט לא יכול היה להיקבע בגלל חפיפת אלמנטים\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"ערך ההשלמה האוטומטית הוא על האלמנט הנכון\",\n      \"fail\": \"ערך ההשלמה האוטומטית אינו נכון לסוג זה של קלט\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"תכונת ההשלמה האוטומטית מוגדרת נכונה\",\n      \"fail\": \"תכונת ההשלמה האוטומטית אינה מוגדרת נכונה\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"ערך תכונת accesskey הוא ייחודי\",\n      \"fail\": \"למסמך יש מספר אלמנטים עם אותו accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"אלמנט מכיל אלמנטים בני מיקוד\",\n      \"fail\": \"לאלמנט צריך להיות תוכן בר מיקוד\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"אין אלמנטים בני מיקוד שמוכלים בתוך אלמנט\",\n      \"incomplete\": \"בדקו אם האלמנטים בני המיקוד מניעים באופן מיידי את סמן המיקוד\",\n      \"fail\": \"צריך לנטרל תוכן בר מיקוד או להסירו מה-DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"אלמנט הוא בר מיקוד\",\n      \"fail\": \"אלמנט צריך להיות בר מיקוד\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"אין אלמנטים בני מיקוד בזמן שחלון פופ-אפ פתוח\",\n      \"incomplete\": \"בדקו שאי אפשר לפתוח בכרטיסיות אלמנטים בני מיקוד במצב הנוכחי \"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"אלמנט אינו בסדר הכרטיסיות או שיש לו טקסט נגיש\",\n      \"fail\": \"אלמנט הוא בסדר הכרטיסיות ואין לו טקסט נגיש\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט שם נגיש\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"אין אלמנטים בני מיקוד שמוכלים בתוך אלמנט\",\n      \"incomplete\": \"בדקו אם האלמנטים בני המיקוד מניעים באופן מיידי את סמן המיקוד\",\n      \"fail\": \"לתוכן בר מיקוד צריך שיהיה tabindex='-1' או שיוסר מה-DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"לאלמנט אין צאצאים בני מיקוד\",\n      \"fail\": \"לאלמנט יש צאצאים בני מיקוד\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט יש צאצאים\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"ציון הדרך ${data.role} הוא ברמה הגבוהה ביותר.\",\n      \"fail\": \"ציון הדרך ${data.role} מוכל בציון דרך אחר.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"לאלמנט אין צאצאים בני מיקוד\",\n      \"fail\": {\n        \"default\": \"לאלמנט יש צאצאים בני מיקוד\",\n        \"notHidden\": \"שימוש ב- tabindexשלילי על אלמנט בתוך בקרה אינטראקטיבית אינו מונע מטכנולוגיות סיוע למקד את האלמנט (אפילו עם 'aria-hidden=true')\"\n      },\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט יש צאצאים\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"לעמוד יש לפחות כותרת אחת ברמה 1\",\n      \"fail\": \"לעמוד מוכרחה להיות כותרת רמה 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"לעמוד יש לפחות ציון דרך ראשי אחד\",\n      \"fail\": \"לעמוד אין ציון דרך ראשי\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"למסמך אין יותר מבאנר ציון דרך אחד\",\n      \"fail\": \"למסמך יש יותר מבאנר ציון דרך אחד\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"למסמך אין יותר מציון דרך מידע-תוכן אחד\",\n      \"fail\": \"למסמך יש יותר מציון דרך מידע-תוכן אחד\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"למסמך אין יותר מציון דרך ראשי אחד\",\n      \"fail\": \"למסמך יש יותר מציון דרך ראשי אחד\"\n    },\n    \"tabindex\": {\n      \"pass\": \"לאלמנט אין tabindex גדול מ-0\",\n      \"fail\": \"לאלמנט יש tabindex גדול מ-0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"לאלמנט יש ערך תכונה חלופי קביל\",\n      \"fail\": \"לאלמנט יש תכונה חלופית שמכילה רק תו רווח, שלא כל קוראי המסך מתעלמים ממנו\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"אלמנט לא משכפל טקסט קיים בטקסט חלופי של <img>\",\n      \"fail\": \"אלמנט מכיל אלמנט <img> עם טקסט חלופי שמשכפל טקסט קיים\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"לאלמנט טופס יש <label> מפורש\",\n      \"fail\": \"לאלמנט טופס אין <label> מפורש\",\n      \"incomplete\": \"אי אפשר לקבוע אם לאלמנט טופס יש <label> מפורש\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"טקסט עזרה (כותרת או aria-describedby) לא משכפל טקסט תווית\",\n      \"fail\": \"טקסט עזרה (כותרת או aria-describedby) זהה לטקסט התווית\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"לאלמנט טופס יש <label> ברור וגלוי\",\n      \"fail\": \"לאלמנט טופס יש <label> ברור שהוא חבוי\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט טופס יש <label> ברור שהוא גלוי\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"לאלמנט טופס יש <label> מרומז (גולש)\",\n      \"fail\": \"לאלמנט טופס אין <label> מרומז (גולש)\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט טופס יש <label> מרומז (גולש)\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"אלמנט מכיל טקסט גלוי כחלק משמו הנגיש\",\n      \"fail\": \"הטקסט בתוך באלמנט לא כלול בשם הנגיש\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"לשדה טופס אין הרבה אלמנטים של תווית\",\n      \"incomplete\": \"אלמנטים מרובים של תווית אינם נתמכים באופן נרחב בטכנולוגיות מסייעות. ודאו שהתווית הראשונה מכילה את כל המידע הנחוץ.\"\n    },\n    \"title-only\": {\n      \"pass\": \"אלמנט טופס לא משתמש רק בתכונה כותרת בתור תווית\",\n      \"fail\": \"רק כותרת משמשת לייצור תווית לאלמנט טופס\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"ציוני דרך מוכרחים להיות עם תפקיד ייחודי או שילוב תפקיד/תווית/כותרת (לדוג' שם נגיש)\",\n      \"fail\": \"לציון הדרך מוכרח להיות aria-label, aria-labelledby, או כותרת כדי להפוך ציוני דרך לייחודיים\"\n    },\n    \"has-lang\": {\n      \"pass\": \"לאלמנט <html> יש תכונת lang\",\n      \"fail\": {\n        \"noXHTML\": \"התכונה xml:lang אינה תקינה בעמודי HTML, השתמשו בתכונה lang.\",\n        \"noLang\": \"לאלמנט <html> אין תכונת lang\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"ערך התכונה lang כלול ברשימת השפות הקבילות\",\n      \"fail\": \"ערך התכונה lang אינו כלול ברשימת השפות הקבילות\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"לתכונות lang ו-xml:lang יש את אותה שפת בסיס\",\n      \"fail\": \"לתכונות lang ו-xml:lang אין את אותה שפת בסיס\"\n    },\n    \"dlitem\": {\n      \"pass\": \"לפריט רשימה תיאורית יש אלמנט הורה <dl>\",\n      \"fail\": \"לפריט רשימה תיאורית אין אלמנט הורה <dl>\"\n    },\n    \"listitem\": {\n      \"pass\": \"לפריט רשימה יש <ul>, <ol> או שאלמנט הורה הוא role=\\\"list\\\"\",\n      \"fail\": {\n        \"default\": \"לפריט רשימה אין <ul>, <ol> אלמנט הורה\",\n        \"roleNotValid\": \"לפריט רשימה אין <ul>, <ol> אלמנט הורה ללא תפקיד, או שמתקיים role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"לאלמנט רשימה יש רק ילדים ישירים שמורשים בתוך אלמנטים של <dt> או <dd>\",\n      \"fail\": \"לאלמנט רשימה יש ילידים ישירים שאינם מורשים בתוך אלמנטים של <dt> או <dd>\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"לאלמנט רשימה יש רק ילדים ישירים שמורשים בתוך אלמנטים של <li>\",\n      \"fail\": {\n        \"default\": \"לאלמנט רשימה יש ילידים ישירים שאינם מורשים בתוך אלמנטים של <li>\",\n        \"roleNotValid\": \"לאלמנט רשימה יש ילדים ישירים עם תפקיד שלא מורשה: ${data.roles}\"\n      }\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"כאשר אינו ריק, לאלמנט יש הן אלמנטים של <dt> והן אלמנטים של <dd>\",\n      \"fail\": \"כאשר אינו ריק, לאלמנט אין הן לפחות אלמנט אחד של <dt> ולאחר מכן לפחות אלמנט אחד של <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"לאלמנט מולטימדיה יש רצועת כתוביות\",\n      \"incomplete\": \"בדקו שהכתוביות זמינות עבור האלמנט\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"ה-iframe נבחנה עם axe-core\",\n      \"fail\": \"אי אפשר היה לבחון את ה-iframe עם axe-core\",\n      \"incomplete\": \"עדיין צריך לבחון את ה-iframe עם axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> או <audio> אינם מפיקים שמע עבור יותר ממשך הזמן המותר או שקיימים מנגנוני בקרה\",\n      \"fail\": \"<video> או <audio> מפיקים שמע עבור יותר ממשך הזמן המותר או שלא קיימים מנגנוני בקרה\",\n      \"incomplete\": \"בדקו שה-<video> או ה-<audio> אינם מפיקים שמע עבור יותר ממשך הזמן המותר או מספקים מנגנוני בקרה\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"התצוגה ניתנת להפעלה, ונעילות כיוון מסך לא קיימת\",\n      \"fail\": \"נעילת מסך ב-CSS מופעלת, והופכת את התצוגה לבלתי ניתנת להפעלה\",\n      \"incomplete\": \"לא ניתן לקבוע נעילת מסך ב-CSS\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"תגית <meta> לא מונעת הגדלה משמעותית על מכשירים ניידים\",\n      \"fail\": \"תגית <meta> מגבילה את ההגדלה על מכשירים ניידים\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"תגית <meta> לא מבטלת הגדלה על מכשירים ניידים\",\n      \"fail\": \"${data} על תגית <meta> מבטלת הגדלה על מכשירים ניידים\"\n    },\n    \"header-present\": {\n      \"pass\": \"יש לעמוד כותרת\",\n      \"fail\": \"אין לעמוד כותרת\"\n    },\n    \"heading-order\": {\n      \"pass\": \"סדר הכותרות תקין\",\n      \"fail\": \"סדר הכותרות לא תקין\",\n      \"incomplete\": \"אי אפשר לקבוע מה הכותרת הקודמת\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"אין קישורים עם אותו השם שמובילים ל-URL שונים\",\n      \"incomplete\": \"בדקו שלקישורים יש אותה מטרה, או שהם דו-משמעיים בכוונה.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"נמצא קישור דילוג תקין\",\n      \"fail\": \"לא נמצא קישור דילוג תקין\"\n    },\n    \"landmark\": {\n      \"pass\": \"לעמוד יש אזור ציון דרך\",\n      \"fail\": \"לעמוד אין אזור ציון דרך\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"תגית <meta> לא מרעננת מייד את העמוד\",\n      \"fail\": \"תגית <meta> כופה ריענון מתוזמן של העמוד\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"תגית <meta> לא מרעננת מייד את העמוד\",\n      \"fail\": \"תגית <meta> כופה ריענון מתוזמן של העמוד (פחות מ-20 שעות)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"אלמנטים של <p> לא מעוצבים ככותרות\",\n      \"fail\": \"יש להשתמש באלמנטים של כותרת במקום באלמנטים מעוצבים של <p>\",\n      \"incomplete\": \"לא ניתן לקבוע אם אלמנטים של <p> מעוצבים ככותרות\"\n    },\n    \"region\": {\n      \"pass\": \"כל התוכן בעמוד מוכל בציוני דרך\",\n      \"fail\": \"חלק מתוכן העמוד לא מוכל בציוני דרך\"\n    },\n    \"skip-link\": {\n      \"pass\": \"קיימת מטרה לקישור דילוג לתוכן\",\n      \"incomplete\": \"מטרה לקישור דילוג לתוכן צריכה להפוך לגלויה עם ההפעלה\",\n      \"fail\": \"אין מטרה לקישור דילוג לתוכן\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"תכונת הכותרת של האלמנט ייחודית\",\n      \"fail\": \"תכונת הכותרת של האלמנט אינה ייחודית\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"למסמך אין אלמנטים פעילים שחולקים את אותה תכונת id\",\n      \"fail\": \"למסמך יש אלמנטים פעילים עם אותה תכונת id: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"למסמך אין אלמנטים עם הפניה על ידי ARIA או תוויות שחולקות את אותה תכונת id\",\n      \"fail\": \"למסמך יש מספר אלמנטים המופנים על ידי ARIA עם אותה תכונת id: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"למסמך אין אלמנטים סטטיים שחולקים אותה תכונת id\",\n      \"fail\": \"למסמך יש מספר אלמנטים סטטיים עם אותה תכונת id: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"תכונת aria-label קיימת ואינה ריקה\",\n      \"fail\": \"תכונת aria-label אינה קיימת או שהיא ריקה\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"תכונת aria-labeledby קיימת ומפנה לאלמנטים שגלויים לקוראי מסך\",\n      \"fail\": \"תכונת aria-labeledby לא קיימת, מפנה לאלמנטים שלא קיימים או מפנה לאלמנטים ריקים\",\n      \"incomplete\": \"ודאו ש- aria-labeledby מפנה לאלמנט קיים\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"אין עיצובים בתוך השורה עם '!important' שמשפיעים על ריווח הטקסט שהוגדר\",\n      \"fail\": {\n        \"singular\": \"הסירו את '!important' מעיצוב בתוך השורה ${data.values}, כיוון שדריסתו לא נתמכת על ידי רוב הדפדפנים\",\n        \"plural\": \"הסירו את '!important' מעיצובי בתוך השורה ${data.values}, כיוון שדריסתו לא נתמכת על ידי רוב הדפדפנים\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"לאלמנט יש טקסט פנימי שגלוי לקוראי מסך\",\n      \"fail\": \"לאלמנט אין טקסט פנימי שגלוי לקוראי מסך\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט יש ילדים\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"למסמך יש אלמנט לא-ריק של <title>\",\n      \"fail\": \"למסמך אין אלמנט לא-ריק של <title>\"\n    },\n    \"exists\": {\n      \"pass\": \"האלמנט לא קיים\",\n      \"incomplete\": \"האלמנט קיים\"\n    },\n    \"has-alt\": {\n      \"pass\": \"לאלמנט יש תכונת טקסט חלופי\",\n      \"fail\": \"לאלמנט אין תכונת טקסט חלופי\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"לאלמנט יש טקסט שגלוי לקוראי מסך\",\n      \"fail\": \"לאלמנט אין טקסט שגלוי לקוראי מסך\",\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט יש ילדים\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"האלמנט אינו גלוי\",\n      \"fail\": \"האלמנט גלוי\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"לאלמנט יש תכונת טקסט חלופי לא ריקה\",\n      \"fail\": {\n        \"noAttr\": \"לאלמנט אין תכונת טקסט חלופי\",\n        \"emptyAttr\": \"לאלמנט יש תכונת טקסט חלופי ריקה\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"לאלמנט אין תכונת ערך\",\n        \"has-label\": \"לאלמנט יש תכונת ערך לא ריקה\"\n      },\n      \"fail\": \"לאלמנט יש תכונת ערך ותכונת הערך ריקה\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"לאלמנט יש תכונה של ממלא מקום\",\n      \"fail\": {\n        \"noAttr\": \"לאלמנט אין תכונה של ממלא מקום\",\n        \"emptyAttr\": \"לאלמנט יש תכונה של ממלא מקום ריק\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"לאלמנט יש תכונת כותרת\",\n      \"fail\": {\n        \"noAttr\": \"לאלמנט אין תכונת כותרת\",\n        \"emptyAttr\": \"לאלמנט יש תכונת כותרת ריקה\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"לאלמנט יש תכונת ערך לא ריקה\",\n      \"fail\": {\n        \"noAttr\": \"לאלמנט אין תכונת ערך\",\n        \"emptyAttr\": \"לאלמנט יש תכונת ערך ריקה\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"הסמנטיקה ברירת המחדל של האלמנט נדרסה עם role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"הסמנטיקה ברירת המחדל של האלמנט לא נדרסה עם role=\\\"none\\\" או role=\\\"presentation\\\"\",\n        \"globalAria\": \"תפקיד האלמנט לא אפשרי לתצוגה כי יש לו תכונת ARIA גלובלית\",\n        \"focusable\": \"תפקיד האלמנט לא אפשרי לתצוגה כי הוא בר מיקוד\",\n        \"both\": \"תפקיד האלמנט לא אפשרי לתצוגה כי יש לו תכונת ARIA גלובלית והוא בר מיקוד\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"השדות ברירת המחדל של האלמנט נדרסו עם role=\\\"none\\\"\",\n      \"fail\": \"השדות ברירת המחדל של האלמנט לא נדרסו עם role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"השדות ברירת המחדל של האלמנט נדרסו עם role=\\\"presentation\\\"\",\n      \"fail\": \"השדות ברירת המחדל של האלמנט לא נדרסו עם role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"לאלמנט יש ילד שהוא כותרת\",\n      \"fail\": {\n        \"noTitle\": \"לאלמנט אין ילד שהוא כותרת\",\n        \"emptyTitle\": \"כותרת ילד אלמנט ריקה\"\n      },\n      \"incomplete\": \"לא ניתן לקבוע אם לאלמנט יש ילד שהוא כותרת\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"השורה הראשונה בטבלה לא משמשת ככתובית\",\n      \"fail\": \"הילד הראשון של הטבלה צריך להיות כתובית במקום תא בטבלה\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"התכונה scope בשימוש רק על אלמנטים של כותרת טבלה (<th>)\",\n      \"fail\": \"ב-HTML5, תכונות scope יכולות להיות בשימוש רק על אלמנטים של כותרת טבלה (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"תוכן תכונת התקציר ו-<caption> אינם משוכפלים\",\n      \"fail\": \"תוכן תכונת התקציר ואלמנט <caption> זהים\"\n    },\n    \"scope-value\": {\n      \"pass\": \"התכונה scope בשימוש נכון\",\n      \"fail\": \"ערך התכונה scope יכול להיות רק על 'row' או 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"לכל תאי המידע שאינם ריקים יש כותרות טבלה\",\n      \"fail\": \"לחלק מתאי המידע שאינם ריקים אין כותרות טבלה\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"תכונת הכותרות משמשת רק בהתייחסות לתאים אחרים בטבלה\",\n      \"incomplete\": \"תכונת הכותרות ריקה\",\n      \"fail\": \"תכונת הכותרות לא משמשת רק בהתייחסות לתאים אחרים בטבלה\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"כל תאי כותרת בטבלה מתייחסים לתאי מידע\",\n      \"fail\": \"לא כל תאי הכותרת בטבלה מתייחסים לתאי מידע\",\n      \"incomplete\": \"תאי מידע בטבלה חסרים או ריקים\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"כל התוכן בעמוד נותח.\",\n      \"fail\": \"היו בעיות בניתוח התוכן בעמוד זה.\",\n      \"incomplete\": \"יש תוכן נסתר בעמוד שלא עבר ניתוח.  יש להפעיל את תצוגת תוכן זה כדי לנתח אותו.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"תקנו את אחד מהבאים:{{~it:value}}{{=value.split('\\n').join('\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"תקנו את כל הבאים:{{~it:value}}{{=value.split('\\n').join('\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe לא הצליח למצוא את הסיבה. הגיע הזמן להיפרד מבודק האלמנטים!\"\n}\n"
  },
  {
    "path": "locales/it.json",
    "content": "{\n  \"lang\": \"it\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Assicurati che ogni valore dell'attributo accesskey sia univoco\",\n      \"help\": \"Il valore dell'attributo accesskey deve essere univoco\"\n    },\n    \"area-alt\": {\n      \"description\": \"Assicurati che gli elementi <area> delle mappe immagine abbiano un testo alternativo\",\n      \"help\": \"Gli elementi <area> attivi devono avere un testo alternativo\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Assicurati che il ruolo di un elemento supporti i suoi attributi ARIA\",\n      \"help\": \"Gli elementi devono utilizzare solo gli attributi ARIA supportati\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Assicurati che il valore dell'attributo role sia appropriato per l'elemento\",\n      \"help\": \"Il ruolo ARIA deve essere appropriato per l'elemento\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Assicurati che aria-braillelabel e aria-brailleroledescription abbiano un equivalente non braille\",\n      \"help\": \"Gli attributi aria-braille devono avere un equivalente non braille\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Assicurati che ogni bottone, link e menuitem gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"I controlli gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Assicurati che gli attributi ARIA vengano utilizzati come descritto nella specifica del ruolo dell'elemento\",\n      \"help\": \"Gli attributi ARIA devono essere utilizzati come specificato per il ruolo dell'elemento\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Assicurati che gli elementi non utilizzino ruoli obsoleti\",\n      \"help\": \"I ruoli ARIA obsoleti non devono essere utilizzati\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Assicurati che ogni nodo dialog e alertdialog ARIA abbia un nome accessibile\",\n      \"help\": \"I nodi dialog e alertdialog ARIA devono avere un nome accessibile\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Assicurati che aria-hidden=\\\"true\\\" non sia presente sull'elemento body del documento\",\n      \"help\": \"aria-hidden=\\\"true\\\" non deve essere presente sull'elemento body del documento\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Assicurati che gli elementi nascosti con aria-hidden non possano nè ricevere focus, nè contengano elementi che possono ricevere focus\",\n      \"help\": \"L'elemento nascosto con aria-hidden non deve ricevere focus, nè può contenere elementi che ricevono focus\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Assicurati che ogni input gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"Gli input gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Assicurati che ogni nodo meter gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"I nodi meter gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Assicurati che ogni nodo progessbar gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"I nodi progressbar gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Assicurati che gli attributi ARIA non siano proibiti per il ruolo di un elemento\",\n      \"help\": \"Gli elementi devono utilizzare solo gli attributi ARIA consentiti\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Assicurati che gli elementi con ruoli ARIA abbiano tutti gli attributi ARIA obbligatori\",\n      \"help\": \"Gli attributi ARIA obbligatori devono essere forniti\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Assicurati che gli elementi con un ruolo ARIA che richiede ruoli figlio contengano questi ultimi\",\n      \"help\": \"Alcuni ruoli ARIA devono contenere ruoli figlio specifici\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Assicurati che gli elementi con un ruolo ARIA che richiede ruoli genitore siano contenuti da questi ultimi\",\n      \"help\": \"Alcuni ruoli ARIA devono essere contenuti da ruoli genitore specifici\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Assicurati che aria-roledescription venga utilizzato solo su elementi con un ruolo implicito o esplicito\",\n      \"help\": \"aria-roledescription deve essere presente solo su elementi con un ruolo semantico\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Assicurati che tutti gli elementi con un attributo role utilizzino un valore valido\",\n      \"help\": \"I ruoli ARIA utilizzati devono essere conformi ai valori validi\"\n    },\n    \"aria-text\": {\n      \"description\": \"Assicurati che role=\\\"text\\\" venga utilizzato su elementi i cui discendenti non ricevono focus\",\n      \"help\": \"\\\"role=text\\\" non deve avere discendenti che possono ricevere focus\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Assicurati che ogni bottone toggle gestito con aria ARIA abbia un nome accessibile\",\n      \"help\": \"I bottoni toggle gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Assicurati che ogni nodo tooltip gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"I nodi tooltip gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Assicurati che ogni nodo treeitem gestito con ARIA abbia un nome accessibile\",\n      \"help\": \"I nodi treeitem gestiti con ARIA devono avere un nome accessibile\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Assicurati che tutti gli attributi ARIA abbiano valori validi\",\n      \"help\": \"Gli attributi ARIA devono essere conformi ai valori validi\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Assicurati che gli attributi che iniziano con aria- siano attributi ARIA validi\",\n      \"help\": \"Gli attributi ARIA devono essere conformi ai nomi validi\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Assicurati che gli elementi <audio> abbiano didascalie\",\n      \"help\": \"Gli elementi <audio> devono avere una didascalia\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Assicurati che l'attributo autocomplete sia corretto e adatto al campo specifico\",\n      \"help\": \"L'attributo autocomplete deve essere utilizzato correttamente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Assicurati che la spaziatura del testo impostata tramite attributi di stile possa essere regolata con fogli di stile personalizzati\",\n      \"help\": \"La spaziatura del testo inline deve poter essere regolata con fogli di stile personalizzati\"\n    },\n    \"blink\": {\n      \"description\": \"Assicurati che non vengano utilizzati elementi <blink>\",\n      \"help\": \"Gli elementi <blink> sono deprecati e non devono essere utilizzati\"\n    },\n    \"button-name\": {\n      \"description\": \"Assicurati che i pulsanti abbiano un testo distinguibile\",\n      \"help\": \"I pulsanti devono avere un testo distinguibile\"\n    },\n    \"bypass\": {\n      \"description\": \"Assicurati che ogni pagina abbia almeno un meccanismo per consentire all'utente di ignorare la navigazione e passare direttamente al contenuto\",\n      \"help\": \"La pagina deve avere un modo per ignorare i blocchi ripetuti\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Assicurati che il contrasto tra i colori in primo piano e di sfondo soddisfi le soglie di rapporto di contrasto avanzato WCAG 2 AAA\",\n      \"help\": \"Gli elementi devono soddisfare le soglie di rapporto di contrasto di colore avanzato\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Assicurati che il contrasto tra i colori in primo piano e di sfondo soddisfi le soglie minime del rapporto di contrasto WCAG 2 AA\",\n      \"help\": \"Gli elementi devono soddisfare le soglie minime del rapporto di contrasto di colore\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Assicurati che il contenuto non sia bloccato in un'orientamento di visualizzazione specifico e che sia operabile in tutti gli orientamenti di visualizzazione\",\n      \"help\": \"Le Media queries CSS non devono bloccare l'orientamento di visualizzazione\"\n    },\n    \"definition-list\": {\n      \"description\": \"Assicurati che gli elementi <dl> siano strutturati correttamente\",\n      \"help\": \"Gli elementi <dl> devono contenere direttamente solo gruppi <dt> e <dd> ordinati correttamente, <script>, <template> o elementi <div>\"\n    },\n    \"dlitem\": {\n      \"description\": \"Assicurati che gli elementi <dt> e <dd> siano contenuti in un <dl>\",\n      \"help\": \"Gli elementi <dt> e <dd> devono essere contenuti in un <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Assicurati che ogni documento HTML contenga un elemento <title> non vuoto\",\n      \"help\": \"I documenti devono avere un elemento <title> per aiutare nella navigazione\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Assicurati che ogni valore dell'attributo id degli elementi attivi sia univoco\",\n      \"help\": \"Gli ID degli elementi attivi devono essere univoci\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Assicurati che ogni valore dell'attributo id utilizzato in ARIA e nelle etichette sia univoco\",\n      \"help\": \"Gli ID utilizzati in ARIA e nelle etichette devono essere univoci\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Assicurati che ogni valore dell'attributo id sia univoco\",\n      \"help\": \"Il valore dell'attributo id deve essere univoco\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Assicurati che le intestazioni abbiano un testo distinguibile\",\n      \"help\": \"Le intestazioni non devono essere vuote\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Assicurati che le intestazioni di tabella abbiano un testo distinguibile\",\n      \"help\": \"Il testo dell'intestazione di tabella non deve essere vuoto\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Assicurati che gli elementi presenti nell'ordine di focus abbiano un ruolo appropriato per il contenuto interattivo\",\n      \"help\": \"Gli elementi presenti nell'ordine di focus devono avere un ruolo appropriato\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Assicurati che il campo non abbia elementi label multipli associati\",\n      \"help\": \"Il campo deve avere un solo elemento label associato\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Assicurati che gli elementi <frame> e <iframe> con contenuto che può ricevere focus non abbiano tabindex=-1\",\n      \"help\": \"I frames con contenuto che può ricecere focus non devono avere tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Assicurati che gli elementi <iframe> e <frame> contengano lo script axe-core\",\n      \"help\": \"I frames devono essere testate con axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Assicurati che gli elementi <iframe> e <frame> contengano un attributo title univoco\",\n      \"help\": \"I frames devono avere un attributo title univoco\"\n    },\n    \"frame-title\": {\n      \"description\": \"Assicurati che gli elementi <iframe> e <frame> abbiano un nome accessibile\",\n      \"help\": \"I frames devono avere un nome accessibile\"\n    },\n    \"heading-order\": {\n      \"description\": \"Assicurati che l'ordine delle intestazioni sia semanticamente corretto\",\n      \"help\": \"I livelli delle intestazioni possono aumentare solo di uno\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informa gli utenti sul contenuto nascosto\",\n      \"help\": \"Il contenuto nascosto nella pagina deve essere analizzato\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Assicurati che ogni documento HTML abbia un attributo lang\",\n      \"help\": \"L'elemento <html> deve avere un attributo lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Assicurati che l'attributo lang dell'elemento <html> abbia un valore valido\",\n      \"help\": \"L'attributo lang dell'elemento <html> deve avere un valore valido\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Assicurati che gli elementi HTML con entrambi gli attributi lang e xml:lang siano coerenti con la lingua di base della pagina\",\n      \"help\": \"Gli elementi con attributi lang e xml:lang devono avere la stessa lingua di base\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Assicurati che i collegamenti con lo stesso nome accessibile abbiano uno scopo simile\",\n      \"help\": \"I collegamenti con lo stesso nome devono avere uno scopo simile\"\n    },\n    \"image-alt\": {\n      \"description\": \"Assicurati che gli elementi <img> abbiano un testo alternativo o un ruolo none o presentation\",\n      \"help\": \"Le immagini devono avere un testo alternativo\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Assicurati che l'alternativa testuale delle immagini non venga ripetuta come testo\",\n      \"help\": \"Il testo alternativo delle immagini non deve essere ripetuto come testo\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Assicurati che i pulsanti di input abbiano un testo distinguibile\",\n      \"help\": \"I pulsanti di input devono avere un testo distinguibile\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Assicurati che gli elementi <input type=\\\"image\\\"> abbiano un testo alternativo\",\n      \"help\": \"I pulsanti immagine devono avere un testo alternativo\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Assicurati che gli elementi etichettati tramite il loro contenuto abbiano il loro testo visibile come parte del loro nome accessibile\",\n      \"help\": \"Gli elementi devono avere il loro testo visibile come parte del loro nome accessibile\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Assicurati che ogni elemento del modulo abbia un'etichetta visibile e non sia etichettato esclusivamente utilizzando etichette nascoste, o gli attributi title o aria-describedby\",\n      \"help\": \"Gli elementi del modulo devono avere un'etichetta visibile\"\n    },\n    \"label\": {\n      \"description\": \"Assicurati che ogni elemento del modulo abbia un'etichetta\",\n      \"help\": \"Gli elementi del modulo devono avere etichette\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Assicurati che il landmark banner sia a livello superiore\",\n      \"help\": \"Il landmark banner non deve essere contenuto in un altro landmark\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Assicurati che il landmark complementary o l'elemento aside sia a livello superiore\",\n      \"help\": \"L'elemento aside non deve essere contenuto in un altro landmark\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Assicurati che il landmark contentinfo sia a livello superiore\",\n      \"help\": \"Il landmark contentinfo non deve essere contenuto in un altro landmark\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Assicurati che il landmark main sia a livello superiore\",\n      \"help\": \"Il landmark main non deve essere contenuto in un altro landmark\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Assicurati che il documento abbia al massimo un landmark banner\",\n      \"help\": \"Il documento non deve avere più di un landmark banner\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Assicurati che il documento abbia al massimo un landmark contentinfo\",\n      \"help\": \"Il documento non deve avere più di un landmark contentinfo\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Assicurati che il documento abbia al massimo un landmark main\",\n      \"help\": \"Il documento non deve avere più di un landmark main\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Assicurati che il documento abbia un landmark main\",\n      \"help\": \"Il documento deve avere un landmark main\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Assicurati che i landmark siano univoci\",\n      \"description\": \"I landmark devono avere un ruolo univoco o una combinazione univoca di ruolo/etichetta/titolo (come ad esempio un nome accessibile univoco)\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Assicurati che i collegamenti siano distinguibili dal testo circostante in un modo che non si basa sul colore\",\n      \"help\": \"I collegamenti devono essere distinguibili senza fare affidamento sul colore\"\n    },\n    \"link-name\": {\n      \"description\": \"Assicurati che i collegamenti abbiano un testo distinguibile\",\n      \"help\": \"I collegamenti devono avere un testo distinguibile\"\n    },\n    \"list\": {\n      \"description\": \"Assicurati che le liste siano strutturate correttamente\",\n      \"help\": \"<ul> e <ol> devono contenere direttamente solo elementi <li>, <script> o <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Assicurati che gli elementi <li> siano utilizzati semanticamente\",\n      \"help\": \"Gli elementi <li> devono essere contenuti in un <ul> o <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Assicurati che gli elementi <marquee> non siano utilizzati\",\n      \"help\": \"Gli elementi <marquee> sono deprecati e non devono essere utilizzati\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Assicurati che <meta http-equiv=\\\"refresh\\\"> non venga utilizzato per il refresh ritardato\",\n      \"help\": \"Il refresh ritardato non deve essere utilizzato\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Assicurati che <meta http-equiv=\\\"refresh\\\"> non venga utilizzato per il refresh ritardato\",\n      \"help\": \"Il refresh ritardato al di sotto delle 20 ore non deve essere utilizzato\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Assicurati che <meta name=\\\"viewport\\\"> possa scalare una quantità significativa\",\n      \"help\": \"Gli utenti devono essere in grado di ingrandire e scalare il testo fino al 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Assicurati che <meta name=\\\"viewport\\\"> non disabiliti il ridimensionamento e lo zoom del testo\",\n      \"help\": \"Lo zoom e il ridimensionamento non devono essere disabilitati\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Assicurati che i controlli interattivi non siano annidati, poiché non vengono sempre annunciati dagli screen reader o possono causare problemi di focus per le tecnologie assistive\",\n      \"help\": \"I controlli interattivi non devono essere annidati\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Assicurati che gli elementi <video> o <audio> non riproducano automaticamente l'audio per più di 3 secondi senza un meccanismo di controllo per arrestare o disattivare l'audio\",\n      \"help\": \"Gli elementi <video> o <audio> non devono essere riprodotti automaticamente\"\n    },\n    \"object-alt\": {\n      \"description\": \"Assicurati che gli elementi <object> abbiano un testo alternativo\",\n      \"help\": \"Gli elementi <object> devono avere un testo alternativo\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Assicurati che il grassetto, il corsivo e le dimensioni del font non vengano utilizzati per rappresentare elementi <p> come intestazione\",\n      \"help\": \"Gli elementi <p> personalizzati non devono essere utilizzati come intestazioni\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Assicurati che la pagina, o almeno uno dei suoi frame, contenga un'intestazione di livello uno\",\n      \"help\": \"La pagina dovrebbe contenere un'intestazione di livello uno\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Gli elementi contrassegnati come presentazionali non devono avere attributi ARIA globali o tabindex per assicurare che tutti gli screen reader li ignorino\",\n      \"help\": \"Assicurati che gli elementi contrassegnati come presentazionali vengano ignorati in modo coerente\"\n    },\n    \"region\": {\n      \"description\": \"Assicurati che tutti i contenuti della pagina siano contenuti nei landmark\",\n      \"help\": \"Tutti i contenuti della pagina dovrebbero essere contenuti nei landmark\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Assicurati che gli elementi [role=\\\"img\\\"] abbiano un testo alternativo\",\n      \"help\": \"Gli elementi [role=\\\"img\\\"] devono avere un testo alternativo\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Assicurati che l'attributo scope venga utilizzato correttamente nelle tabelle\",\n      \"help\": \"L'attributo scope deve essere utilizzato correttamente\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Assicurati che gli elementi che hanno contenuti scorrevoli siano accessibili da tastiera\",\n      \"help\": \"La regione scrollabile deve avere accesso da tastiera\"\n    },\n    \"select-name\": {\n      \"description\": \"Assicurati che l'elemento select abbia un nome accessibile\",\n      \"help\": \"L'elemento select deve avere un nome accessibile\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Assicurati che non vengano utilizzate mappe immagini lato server\",\n      \"help\": \"Le mappe immagini lato server non devono essere utilizzate\"\n    },\n    \"skip-link\": {\n      \"description\": \"Assicurati che tutti gli skip link puntino ad un elemento che può ricevere focus\",\n      \"help\": \"L'elemento a cui lo skip link punta deve esistere e deve poter ricevere focus\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Assicurati che gli elementi <svg> con un ruolo img, graphics-document o graphics-symbol abbiano un testo accessibile\",\n      \"help\": \"Gli elementi <svg> con un ruolo img devono avere un testo alternativo\"\n    },\n    \"tabindex\": {\n      \"description\": \"Assicurati che i valori degli attributi tabindex non siano maggiori di 0\",\n      \"help\": \"Gli elementi non devono avere un tabindex maggiore di zero\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Assicurati che l'elemento <caption> non contenga lo stesso testo dell'attributo summary\",\n      \"help\": \"Il riepilogo e la didascalia di una tabella non devono essere uguali\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Assicurati che le tabelle con una didascalia utilizzino l'elemento <caption>.\",\n      \"help\": \"Le celle di dati o di intestazione non devono essere utilizzate per fornire una didascalia a una tabella di dati.\"\n    },\n    \"target-size\": {\n      \"description\": \"Assicurati che gli elementi che possono essere attivati tramite puntatore abbiano dimensioni e spaziature sufficienti\",\n      \"help\": \"Tutti gli elementi che possono essere attivati tramite puntatore devono avere una largezza di 24px, o lasciare spazio sufficiente\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Assicurati che ogni cella di dati non vuota in una tabella più grande di 3x3 abbia una o più intestazioni di tabella\",\n      \"help\": \"Le celle <td> non vuote in una tabella di dimensioni considerevoli devono avere un'intestazione di tabella associata\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Assicurati che ogni cella in una tabella che utilizza l'attributo headers si riferisca solo ad altre celle nella stessa tabella\",\n      \"help\": \"Le celle di tabella che utilizzano l'attributo headers devono riferirsi solo a celle nella stessa tabella\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Assicurati che gli elementi <th> e gli elementi con role=columnheader/rowheader descrivano le celle di dati corrispondenti\",\n      \"help\": \"Le intestazioni di tabella in una tabella di dati devono fare riferimento a celle di dati\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Assicurati che gli attributi lang abbiano valori validi\",\n      \"help\": \"L'attributo lang deve avere un valore valido\"\n    },\n    \"video-caption\": {\n      \"description\": \"Assicurati che gli elementi <video> abbiano didascalie\",\n      \"help\": \"Gli elementi <video> devono avere didascalie\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"I ruoli astratti non vengono utilizzati\",\n      \"fail\": {\n        \"singular\": \"Il ruolo astratto non può essere utilizzato direttamente: ${data.values}\",\n        \"plural\": \"I ruoli astratti non possono essere utilizzati direttamente: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Gli attributi ARIA sono utilizzati correttamente per il ruolo definito\",\n      \"fail\": {\n        \"singular\": \"L'attributo ARIA non è consentito: ${data.values}\",\n        \"plural\": \"Gli attributi ARIA non sono consentiti: ${data.values}\"\n      },\n      \"incomplete\": \"Verifica che non ci siano problemi se l'attributo ARIA viene ignorato su questo elemento: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"Il ruolo ARIA è consentito per l'elemento dato\",\n      \"fail\": {\n        \"singular\": \"Il ruolo ARIA ${data.values} non è consentito per l'elemento dato\",\n        \"plural\": \"I ruoli ARIA ${data.values} non sono consentiti per l'elemento dato\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Il ruolo ARIA ${data.values} deve essere rimosso quando l'elemento viene reso visibile, poiché non è consentito per l'elemento\",\n        \"plural\": \"I ruoli ARIA ${data.values} devono essere rimossi quando l'elemento viene reso visibile, poiché non sono consentiti per l'elemento\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"L'elemento ha un attributo aria-busy\",\n      \"fail\": \"L'elemento utilizza aria-busy=\\\"true\\\" mentre viene mostrato un caricamento\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"L'attributo ARIA è consentito\",\n      \"fail\": {\n        \"checkbox\": \"Rimuovi aria-checked, o impostalo su \\\"${data.checkState}\\\" per farlo combaciare allo stato reale della casella di controllo\",\n        \"rowSingular\": \"Questo attributo è supportato con le righe treegrid, ma non ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"Questi attributi sono supportati con le righe treegrid, ma non ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage esiste e fa riferimento ad elementi visibili agli screen reader che utilizzano una tecnica aria-errormessage supportata\",\n      \"fail\": {\n        \"singular\": \"Il valore aria-errormessage `${data.values}` deve utilizzare una tecnica per annunciare il messaggio (ad esempio, aria-live, aria-describedby, role=alert, ecc.)\",\n        \"plural\": \"I valori aria-errormessage `${data.values}` devono utilizzare una tecnica per annunciare il messaggio (ad esempio, aria-live, aria-describedby, role=alert, ecc.)\",\n        \"hidden\": \"Il valore aria-errormessage `${data.values}` non può fare riferimento a un elemento nascosto\"\n      },\n      \"incomplete\": {\n        \"singular\": \"assicurati che il valore aria-errormessage `${data.values}` faccia riferimento a un elemento esistente\",\n        \"plural\": \"assicurati che i valori aria-errormessage `${data.values}` facciano riferimento a elementi esistenti\",\n        \"idrefs\": \"non è possibile determinare se l'elemento aria-errormessage esiste nella pagina: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Non è presente l'attributo aria-hidden sull'elemento body del documento\",\n      \"fail\": \"aria-hidden=true non deve essere presente sull'elemento body del documento\"\n    },\n    \"aria-level\": {\n      \"pass\": \"I valori aria-level sono validi\",\n      \"incomplete\": \"I valori aria-level superiori a 6 non sono supportati in tutte le combinazioni di screenreader e browser\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"L'attributo ARIA è consentito\",\n      \"fail\": {\n        \"hasRolePlural\": \"Gli attributi ${data.prohibited} non possono essere utilizzati con il ruolo \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"L'attributo ${data.prohibited} non può essere utilizzato con il ruolo \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"Gli attributi ${data.prohibited} non possono essere utilizzati su un ${data.nodeName} senza un ruolo valido.\",\n        \"noRoleSingular\": \"L'attributo ${data.prohibited} non può essere utilizzato su un ${data.nodeName} senza un ruolo valido.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"L'attributo ${data.prohibited} non è ben supportato con il ruolo \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"Gli attributi ${data.prohibited} non sono ben supportati con il ruolo \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"L'attributo ${data.prohibited} non è ben supportato su un ${data.nodeName} senza un ruolo valido.\",\n        \"noRolePlural\": \"Gli attributi ${data.prohibited} non sono ben supportati su un ${data.nodeName} senza un ruolo valido.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Tutti gli attributi ARIA obbligatori sono presenti\",\n      \"fail\": {\n        \"singular\": \"L'attributo ARIA obbligatorio non è presente: ${data.values}\",\n        \"plural\": \"Gli attributi ARIA obbligatori non sono presenti: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": \"I figli ARIA obbligatori sono presenti\",\n      \"fail\": {\n        \"singular\": \"Il ruolo ARIA obbligatorio per l'elemento figlio non è presente: ${data.values}\",\n        \"plural\": \"Il ruolo ARIA obbligatorio per gli elementi figli non è presente: ${data.values}\",\n        \"unallowed\": \"L'elemento ha figli che non sono consentiti: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Ci si aspetta che venga aggiunto un figlio con un ruolo ARIA: ${data.values}\",\n        \"plural\": \"Ci si aspetta che vengano aggiunti i figli con un ruolo ARIA: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Il ruolo ARIA obbligatorio per l'elemento genitore è presente\",\n      \"fail\": {\n        \"singular\": \"Il ruolo ARIA obbligatorio per l'elemento genitore non è presente: ${data.values}\",\n        \"plural\": \"I ruoli ARIA obbligatori per gli elementi genitori non sono presenti: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription utilizzato su un ruolo semantico supportato\",\n      \"incomplete\": \"Verifica che aria-roledescription venga annunciato dagli screen reader supportati\",\n      \"fail\": \"Assegna all'elemento un ruolo che supporta aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"L'attributo ARIA è supportato\",\n      \"fail\": \"L'attributo ARIA non è ampiamente supportato dagli screen reader e dalle tecnologie assistive: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"I valori degli attributi ARIA sono validi\",\n      \"fail\": {\n        \"singular\": \"Il valore dell'attributo ARIA non è valido: ${data.values}\",\n        \"plural\": \"I valori degli attributi ARIA non sono validi: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"Nella pagina non esiste un elemento con ID corrispondente all'attributo ARIA: ${data.needsReview}\",\n        \"noIdShadow\": \"Nella pagina non esiste un elemento con ID corrispondente all'attributo ARIA o è un discendente di un diverso albero shadow DOM: ${data.needsReview}\",\n        \"ariaCurrent\": \"Il valore dell'attributo ARIA non è valido e verrà trattato come \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Non è possibile determinare se esiste un elemento con ID corrispondente all'attributo ARIA nella pagina: ${data.needsReview}\",\n        \"empty\": \"Il valore dell'attributo ARIA viene ignorato mentre è vuoto: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"Il nome dell'attributo ARIA è valido\",\n      \"fail\": {\n        \"singular\": \"Il nome dell'attributo ARIA non è valido: ${data.values}\",\n        \"plural\": \"I nomi degli attributi ARIA non sono validi: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabel viene utilizzato su un elemento con testo accessibile\",\n      \"fail\": \"aria-braillelabel viene utilizzato su un elemento senza testo accessibile\",\n      \"incomplete\": \"Non è possibile calcolare il testo accessibile\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescription viene utilizzato su un elemento con aria-roledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription viene utilizzato su un elemento senza aria-roledescription\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription viene utilizzato su un elemento con aria-roledescription vuoto\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"Il ruolo ARIA non è deprecato\",\n      \"fail\": \"Il ruolo utilizzato è deprecato: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Viene utilizzato un solo valore di ruolo\",\n      \"fail\": \"Utilizza un solo valore di ruolo, poiché i ruoli di fallback non sono supportati nei browser più vecchi\",\n      \"incomplete\": \"Utilizza solo il ruolo 'presentation' o 'none' poiché sono sinonimi.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"L'elemento ha un attributo ARIA globale: ${data.values}\",\n        \"plural\": \"L'elemento ha attributi ARIA globali: ${data.values}\"\n      },\n      \"fail\": \"L'elemento non ha un attributo ARIA globale\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"L'elemento ha un ruolo widget.\",\n      \"fail\": \"L'elemento non ha un ruolo widget.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"Il ruolo ARIA è valido\",\n      \"fail\": {\n        \"singular\": \"Il ruolo deve essere uno dei ruoli ARIA validi: ${data.values}\",\n        \"plural\": \"I ruoli devono essere uno dei ruoli ARIA validi: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"L'elemento può ricevere focus.\",\n      \"fail\": \"L'elemento non può ricevere focus.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Non c'è incongruenza tra etichetta <label> e nome accessibile\",\n      \"incomplete\": \"Verifica che l'etichetta <label> non debba far parte del nome del campo ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"Il ruolo ARIA è supportato\",\n      \"fail\": \"Il ruolo utilizzato non è ampiamente supportato dagli screen reader e dalle tecnologie assistive: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"L'elemento ha una semantica valida per un elemento presente nell'ordine di focus.\",\n      \"fail\": \"L'elemento ha una semantica non valida per un elemento presente nell'ordine di focus.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"L'elemento ha un contrasto di colore sufficiente di ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} (colore in primo piano: ${data.fgColor}, colore di sfondo: ${data.bgColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} tra il primo piano e il colore dell'ombra (colore in primo piano: ${data.fgColor}, colore dell'ombra del testo: ${data.shadowColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} tra il colore dell'ombra e il colore di sfondo (colore dell'ombra del testo: ${data.shadowColor}, colore di sfondo: ${data.bgColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Impossibile determinare il rapporto di contrasto\",\n        \"bgImage\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di un'immagine di sfondo\",\n        \"bgGradient\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di un gradiente di sfondo\",\n        \"imgNode\": \"Il colore di sfondo dell'elemento non può essere determinato perché l'elemento contiene un nodo immagine\",\n        \"bgOverlap\": \"Il colore di sfondo dell'elemento non può essere determinato perché c'è un altro elemento sovrapposto\",\n        \"fgAlpha\": \"Il colore in primo piano dell'elemento non può essere determinato a causa della trasparenza alfa\",\n        \"elmPartiallyObscured\": \"Il colore di sfondo dell'elemento non può essere determinato perché è parzialmente oscurato da un altro elemento\",\n        \"elmPartiallyObscuring\": \"Il colore di sfondo dell'elemento non può essere determinato perché si sovrappone parzialmente ad altri elementi\",\n        \"outsideViewport\": \"Il colore di sfondo dell'elemento non può essere determinato perché è al di fuori della finestra di visualizzazione\",\n        \"equalRatio\": \"L'elemento ha un rapporto di contrasto di 1:1 con lo sfondo\",\n        \"shortTextContent\": \"Il contenuto dell'elemento è troppo breve per determinare se si tratta di un contenuto di testo effettivo\",\n        \"nonBmp\": \"Il contenuto dell'elemento contiene solo caratteri non di testo\",\n        \"pseudoContent\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di uno pseudo-elemento\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"L'elemento ha un contrasto di colore sufficiente di ${data.contrastRatio}\",\n        \"hidden\": \"L'elemento è nascosto\"\n      },\n      \"fail\": {\n        \"default\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} (colore in primo piano: ${data.fgColor}, colore di sfondo: ${data.bgColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} tra il primo piano e il colore dell'ombra (colore in primo piano: ${data.fgColor}, colore dell'ombra del testo: ${data.shadowColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"L'elemento ha un contrasto di colore insufficiente di ${data.contrastRatio} tra il colore dell'ombra e il colore di sfondo (colore dell'ombra del testo: ${data.shadowColor}, colore di sfondo: ${data.bgColor}, dimensione del font: ${data.fontSize}, peso del font: ${data.fontWeight}). Contrasto atteso di ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Impossibile determinare il rapporto di contrasto\",\n        \"bgImage\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di un'immagine di sfondo\",\n        \"bgGradient\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di un gradiente di sfondo\",\n        \"imgNode\": \"Il colore di sfondo dell'elemento non può essere determinato perché l'elemento contiene un nodo immagine\",\n        \"bgOverlap\": \"Il colore di sfondo dell'elemento non può essere determinato perché c'è un altro elemento sovrapposto\",\n        \"complexTextShadows\": \"Il contrasto dell'elemento non può essere determinato perché utilizza ombre di testo complesse\",\n        \"fgAlpha\": \"Il colore in primo piano dell'elemento non può essere determinato a causa della trasparenza alfa\",\n        \"elmPartiallyObscured\": \"Il colore di sfondo dell'elemento non può essere determinato perché è parzialmente oscurato da un altro elemento\",\n        \"elmPartiallyObscuring\": \"Il colore di sfondo dell'elemento non può essere determinato perché si sovrappone parzialmente ad altri elementi\",\n        \"outsideViewport\": \"Il colore di sfondo dell'elemento non può essere determinato perché è al di fuori della finestra di visualizzazione\",\n        \"equalRatio\": \"L'elemento ha un rapporto di contrasto di 1:1 con lo sfondo\",\n        \"shortTextContent\": \"Il contenuto dell'elemento è troppo breve per determinare se si tratta di un contenuto di testo effettivo\",\n        \"nonBmp\": \"Il contenuto dell'elemento contiene solo caratteri non di testo\",\n        \"pseudoContent\": \"Il colore di sfondo dell'elemento non può essere determinato a causa di uno pseudo-elemento\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"I collegamenti possono essere distinti dal testo circostante grazie ad uno stile visivo\",\n      \"incomplete\": {\n        \"default\": \"Verifica se il collegamento necessita di uno stile per distinguerlo dal testo circostante\",\n        \"pseudoContent\": \"Verifica se lo pseudo-stile del collegamento è sufficiente per distinguerlo dal testo circostante\"\n      },\n      \"fail\": \"Il collegamento non ha uno stile (come la sottolineatura) per distinguerlo dal testo circostante\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"I collegamenti possono essere distinti dal testo circostante in qualche modo che non sia solo attraverso il colore\",\n      \"fail\": {\n        \"fgContrast\": \"Il collegamento ha un contrasto di colore insufficiente di ${data.contrastRatio}:1 con il testo circostante. (Contrasto minimo è ${data.requiredContrastRatio}:1, colore del testo del collegamento: ${data.nodeColor}, colore del testo circostante: ${data.parentColor})\",\n        \"bgContrast\": \"Lo sfondo del collegamento ha un contrasto di colore insufficiente di ${data.contrastRatio} (Contrasto minimo è ${data.requiredContrastRatio}:1, colore dello sfondo del collegamento: ${data.nodeBackgroundColor}, colore dello sfondo circostante: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Il rapporto di contrasto dei colori in primo piano dell'elemento non può essere determinato\",\n        \"bgContrast\": \"Il rapporto di contrasto dello sfondo dell'elemento non può essere determinato\",\n        \"bgImage\": \"Il rapporto di contrasto dell'elemento non può essere determinato a causa di un'immagine di sfondo\",\n        \"bgGradient\": \"Il rapporto di contrasto dell'elemento non può essere determinato a causa di un gradiente di sfondo\",\n        \"imgNode\": \"Il rapporto di contrasto dell'elemento non può essere determinato perché l'elemento contiene un nodo immagine\",\n        \"bgOverlap\": \"Il rapporto di contrasto dell'elemento non può essere determinato a causa della sovrapposizione dell'elemento\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"il valore di autocomplete è appropriato per questo elemento\",\n      \"fail\": \"il valore di autocomplete non è appropriato per questo tipo di input\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"l'attributo autocomplete è formattato correttamente\",\n      \"fail\": \"l'attributo autocomplete non è formattato correttamente\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Il valore dell'attributo accesskey è univoco\",\n      \"fail\": \"Il documento ha più elementi con lo stesso accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"L'elemento contiene elementi che possono ricevere focus\",\n      \"fail\": \"L'elemento dovrebbe avere contenuto che può ricevere focus\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Non ci sono elementi che possono ricevere focus all'interno dell'elemento\",\n      \"incomplete\": \"Controlla se gli elementi che possono ricevere focus spostano immediatamente l'indicatore di focus\",\n      \"fail\": \"Il contenuto che può ricevere focus dovrebbe essere disabilitato o rimosso dal DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"L'elemento può ricevere focus\",\n      \"fail\": \"L'elemento dovrebbe poter ricevere focus\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Non ci sono elementi che possono ricevere focus mentre è aperta una modale\",\n      \"incomplete\": \"Controlla che gli elementi che possono ricevere focus non siano tabbabili nello stato attuale\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"L'elemento non è nell'ordine di tab o ha un testo accessibile\",\n      \"fail\": \"L'elemento è nell'ordine di tab e non ha un testo accessibile\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento ha un nome accessibile\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Non ci sono elementi che possono ricevere focus all'interno dell'elemento\",\n      \"incomplete\": \"Controlla se gli elementi che possono ricevere focus spostano immediatamente l'indicatore di focus\",\n      \"fail\": \"Il contenuto che può ricevere focus dovrebbe avere tabindex=\\\"-1\\\" o essere rimosso dal DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"L'elemento non ha discendenti che possono ricevere focus\",\n      \"fail\": \"L'elemento ha discendenti che possono ricevere focus\",\n      \"incomplete\": \"Non è stato possibile determinare se l'elemento ha discendenti\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"Il landmark ${data.role} è al livello superiore.\",\n      \"fail\": \"Il landmark ${data.role} è contenuto in un altro landmark.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"L'elemento non ha discendenti che possono ricevere focus\",\n      \"fail\": {\n        \"default\": \"L'elemento ha discendenti che possono ricevere focus\",\n        \"notHidden\": \"L'utilizzo di un tabindex negativo su un elemento all'interno di un controllo interattivo non impedisce alle tecnologie assistive di spostare il focus sull'elemento (anche con aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Non è stato possibile determinare se l'elemento ha discendenti\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"La pagina ha almeno un'intestazione di livello uno\",\n      \"fail\": \"La pagina deve avere un'intestazione di livello uno\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Il documento ha almeno un landmark main\",\n      \"fail\": \"Il documento non ha un landmark main\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Il documento non ha più di un landmark banner\",\n      \"fail\": \"Il documento ha più di un landmark banner\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Il documento non ha più di un landmark contentinfo\",\n      \"fail\": \"Il documento ha più di un landmark contentinfo\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Il documento non ha più di un landmark main\",\n      \"fail\": \"Il documento ha più di un landmark main\"\n    },\n    \"tabindex\": {\n      \"pass\": \"L'elemento non ha un tabindex maggiore di 0\",\n      \"fail\": \"L'elemento ha un tabindex maggiore di 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Il valore dell'attributo alt dell'elemento è valido\",\n      \"fail\": \"L'elemento ha un attributo alt che contiene solo uno spazio, che non viene ignorato da tutti gli screen reader\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"L'elemento non duplica il testo esistente nel testo alternativo dell'immagine\",\n      \"fail\": \"L'elemento contiene un elemento immagine con testo alternativo che duplica il testo esistente\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"L'elemento del modulo ha un'etichetta esplicita <label>\",\n      \"fail\": \"L'elemento del modulo non ha un'etichetta esplicita <label>\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento del modulo ha un'etichetta esplicita <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Il testo di aiuto (title o aria-describedby) non duplica il testo dell'etichetta\",\n      \"fail\": \"Il testo di aiuto (title o aria-describedby) è lo stesso del testo dell'etichetta\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"L'elemento del modulo ha un'etichetta esplicita <label> visibile\",\n      \"fail\": \"L'elemento del modulo ha un'etichetta esplicita <label> nascosta\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento del modulo ha un'etichetta esplicita <label> nascosta\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"L'elemento del modulo ha un'etichetta implicita (racchiusa) <label>\",\n      \"fail\": \"L'elemento del modulo non ha un'etichetta implicita (racchiusa) <label>\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento del modulo ha un'etichetta implicita (racchiusa) <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"L'elemento contiene del testo visibile come parte del suo nome accessibile\",\n      \"fail\": \"Il testo all'interno dell'elemento non è incluso nel nome accessibile\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Il campo del modulo non ha più elementi etichetta\",\n      \"incomplete\": \"L'utilizzo di più elementi etichetta non è ampiamente supportato dalle tecnologie assistive. Assicurati che la prima etichetta contenga tutte le informazioni necessarie.\"\n    },\n    \"title-only\": {\n      \"pass\": \"L'elemento del modulo non utilizza solo l'attributo title per generare la sua etichetta\",\n      \"fail\": \"Solo il titolo viene utilizzato per generare l'etichetta dell'elemento del modulo\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"I landmark devono avere un ruolo univoco o una combinazione univoca di ruolo/etichetta/titolo (come ad esempio un nome accessibile univico)\",\n      \"fail\": \"Il landmark deve avere un aria-label, aria-labelledby o titolo univoco per rendere i landmark distinguibili\"\n    },\n    \"has-lang\": {\n      \"pass\": \"L'elemento <html> ha un attributo lang\",\n      \"fail\": {\n        \"noXHTML\": \"L'attributo xml:lang non è valido nelle pagine HTML, utilizza l'attributo lang.\",\n        \"noLang\": \"L'elemento <html> non ha un attributo lang\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Il valore dell'attributo lang è incluso nell'elenco delle lingue valide\",\n      \"fail\": \"Il valore dell'attributo lang non è incluso nell'elenco delle lingue valide\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Gli attributi lang e xml:lang hanno la stessa lingua di base\",\n      \"fail\": \"Gli attributi lang e xml:lang non hanno la stessa lingua di base\"\n    },\n    \"dlitem\": {\n      \"pass\": \"L'elemento di descrizione della lista ha un elemento <dl> come genitore\",\n      \"fail\": \"L'elemento di descrizione della lista non ha un elemento <dl> come genitore\"\n    },\n    \"listitem\": {\n      \"pass\": \"L'elemento della lista ha un elemento <ul>, <ol> o role=\\\"list\\\" come genitore\",\n      \"fail\": {\n        \"default\": \"L'elemento della lista non ha come genitore un elemento <ul> o <ol>\",\n        \"roleNotValid\": \"L'elemento della lista non ha come genitore un elemento <ul>, <ol> o un elemento con role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"L'elemento dl contiene al suo interno solo figli diretti consentiti; <dt>, <dd>, o elementi <div>\",\n      \"fail\": \"L'elemento dl ha figli diretti che non sono consentiti: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"L'elemento della lista contiene al suo interno solo figli diretti consentiti\",\n      \"fail\": \"L'elemento della lista ha figli diretti che non sono consentiti: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Quando non è vuoto, l'elemento ha sia elementi <dt> che <dd>\",\n      \"fail\": \"Quando non è vuoto, l'elemento non ha nemmeno un elemento <dt> seguito da almeno un elemento <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"L'elemento multimediale ha una didascalia\",\n      \"incomplete\": \"Controlla che le didascalie siano disponibili per l'elemento\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"L'iframe è stato testato con axe-core\",\n      \"fail\": \"L'iframe non può essere testato con axe-core\",\n      \"incomplete\": \"L'iframe deve ancora essere testato con axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> o <audio> non riproduce audio per più della durata consentita o ha un meccanismo di controllo\",\n      \"fail\": \"<video> o <audio> riproduce audio per più della durata consentita e non ha un meccanismo di controllo\",\n      \"incomplete\": \"Controlla che il <video> o <audio> non riproduca audio per più della durata consentita o fornisca un meccanismo di controllo\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Il display è utilizzabile e l'orientamento del dispositivo non viene bloccato\",\n      \"fail\": \"Il blocco dell'orientamento tramite CSS viene applicato e rende il display non utilizzabile\",\n      \"incomplete\": \"Il blocco dell'orientamento tramite CSS non può essere determinato\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"Il tag <meta> non impedisce lo zoom in modo significativo sui dispositivi mobili\",\n      \"fail\": \"Il tag <meta> limita lo zoom sui dispositivi mobili\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"Il tag <meta> non disabilita lo zoom sui dispositivi mobili\",\n      \"fail\": \"${data} sul tag <meta> disabilita lo zoom sui dispositivi mobili\"\n    },\n    \"target-offset\": {\n      \"pass\": \"L'elemento ha spazio sufficiente dai suoi vicini più prossimi. Lo spazio sicuro e cliccabile ha un diametro di ${data.closestOffset}px, che è superiore al requisito di ${data.minOffset}px.\",\n      \"fail\": \"L'elemento non ha spazio sufficiente rispetto ai suoi vicini più prossimi. Lo spazio sicuro e cliccabile ha un diametro di ${data.closestOffset}px, che è inferiore rispetto al requisito di ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"L'elemento con tabindex negativo non ha spazio sufficiente rispetto ai suoi vicini più prossimi. Lo spazio sicuro e cliccabile ha un diametro di ${data.closestOffset}px, che è inferiore rispetto al requisito di ${data.minOffset}px. Si tratta di un elemento che può essere attivato?\",\n        \"nonTabbableNeighbor\": \"L'elemento non ha spazio sufficiente rispetto ai suoi vicini più prossimi. Lo spazio sicuro e cliccabile ha un diametro di ${data.closestOffset}px, che è inferiore rispetto al requisito di ${data.minOffset}px. Il vicino più prossimo è un elemento che può essere attivato?\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Il controllo ha dimensioni sufficienti (${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px)\",\n        \"obscured\": \"Il controllo viene ignorato perché è completamente oscurato e quindi non cliccabile\"\n      },\n      \"fail\": {\n        \"default\": \"L'elemento ha dimensioni insufficienti (${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px)\",\n        \"partiallyObscured\": \"L'elemento ha dimensioni insufficienti perché è parzialmente oscurato (lo spazio più piccolo è ${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"L'elemento con tabindex negativo ha dimensioni insufficienti (${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px). Si tratta di un elemento che può essere attivato?\",\n        \"contentOverflow\": \"Le dimensioni dell'elemento non possono essere determinate con precisione a causa del contenuto in overflow\",\n        \"partiallyObscured\": \"L'elemento con tabindex negativo ha dimensioni insufficienti perché è parzialmente oscurato (lo spazio più piccolo è ${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px). Si tratta di un elemento che può essere attivato?\",\n        \"partiallyObscuredNonTabbable\": \"L'elemento ha dimensioni insufficienti perché è parzialmente oscurato da un vicino con tabindex negativo (lo spazio più piccolo è ${data.width}px per ${data.height}px, dovrebbe essere almeno ${data.minSize}px per ${data.minSize}px). Il vicino più prossimo è un elemento che può essere attivato?\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"La pagina ha un'intestazione\",\n      \"fail\": \"La pagina non ha nemmeno un'intestazione\"\n    },\n    \"heading-order\": {\n      \"pass\": \"L'ordine delle intestazioni è valido\",\n      \"fail\": \"L'ordine delle intestazioni non è valido\",\n      \"incomplete\": \"Non è possibile determinare l'intestazione precedente\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Non ci sono altri collegamenti con lo stesso nome, che puntano ad un URL diverso\",\n      \"incomplete\": \"Controlla che i collegamenti abbiano lo stesso scopo, o siano intenzionalmente ambigui.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Trovato skip link valido\",\n      \"fail\": \"Non è stato trovato alcuno skip link valido\"\n    },\n    \"landmark\": {\n      \"pass\": \"La pagina ha una regione landmark\",\n      \"fail\": \"La pagina non ha una regione landmark\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"Il tag <meta> non aggiorna immediatamente la pagina\",\n      \"fail\": \"Il tag <meta> forza il refresh temporizzato della pagina\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Il tag <meta> non aggiorna immediatamente la pagina\",\n      \"fail\": \"Il tag <meta> forza il refresh temporizzato della pagina (al di sotto delle 20 ore)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Gli elementi <p> non sono disegnati come intestazioni\",\n      \"fail\": \"Al posto di elementi <p> disegnati come intestazioni, dovrebbero essere utilizzati elementi di intestazione\",\n      \"incomplete\": \"Non è possibile determinare se gli elementi <p> sono stati disegnati come intestazioni\"\n    },\n    \"region\": {\n      \"pass\": \"Tutto il contenuto della pagina è contenuto in landmark\",\n      \"fail\": \"Alcuni contenuti della pagina non sono contenuti in landmark\"\n    },\n    \"skip-link\": {\n      \"pass\": \"L'elemento a cui lo skip link punta esiste\",\n      \"incomplete\": \"L'elemento a cui lo skip link punta deve diventare visibile quando attivato\",\n      \"fail\": \"L'elemento a cui lo skip link punta non esiste\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"L'attributo title dell'elemento è univoco\",\n      \"fail\": \"L'attributo title dell'elemento non è univoco\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Il documento non ha elementi attivi che condividono lo stesso attributo id\",\n      \"fail\": \"Il documento ha elementi attivi con lo stesso attributo id: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Il documento non ha elementi referenziati con ARIA o etichette che condividono lo stesso attributo id\",\n      \"fail\": \"Il documento ha più elementi referenziati con ARIA con lo stesso attributo id: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Il documento non ha elementi statici che condividono lo stesso attributo id\",\n      \"fail\": \"Il documento ha più elementi statici con lo stesso attributo id: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"L'attributo aria-label esiste e non è vuoto\",\n      \"fail\": \"L'attributo aria-label non esiste o è vuoto\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"L'attributo aria-labelledby esiste e fa riferimento ad elementi che sono visibili agli screen reader\",\n      \"fail\": \"L'attributo aria-labelledby non esiste, fa riferimento ad elementi che non esistono o fa riferimento ad elementi che sono vuoti\",\n      \"incomplete\": \"assicurati che aria-labelledby faccia riferimento a un elemento esistente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Non sono stati specificati stili inline con '!important' che influenzano lo spaziatura del testo\",\n      \"fail\": {\n        \"singular\": \"Rimuovi '!important' dallo stile inline ${data.values}, poiché la sovrascrittura di questo non è supportata dalla maggior parte dei browser\",\n        \"plural\": \"Rimuovi '!important' dagli stili inline ${data.values}, poiché la sovrascrittura di questo non è supportata dalla maggior parte dei browser\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"L'elemento ha del testo interno che è visibile agli screen reader\",\n      \"fail\": \"L'elemento non ha del testo interno che è visibile agli screen reader\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento ha figli\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Il documento ha un elemento <title> non vuoto\",\n      \"fail\": \"Il documento non ha un elemento <title> non vuoto\"\n    },\n    \"exists\": {\n      \"pass\": \"L'elemento non esiste\",\n      \"incomplete\": \"L'elemento esiste\"\n    },\n    \"has-alt\": {\n      \"pass\": \"L'elemento ha un attributo alt\",\n      \"fail\": \"L'elemento non ha un attributo alt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"L'elemento ha del testo che è visibile agli screen reader\",\n      \"fail\": \"L'elemento non ha del testo che è visibile agli screen reader\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento ha figli\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"La spaziatura delle lettere nell'attributo style non è impostato su !important, o rispetta il requisito minimo\",\n      \"fail\": \"La spaziatura delle lettere nell'attributo style non deve utilizzare !important, o deve essere impostata a ${data.minValue}em (attuale ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"l'interlinea nell'attributo style non è impostata su !important, o rispetta il requisito minimo\",\n      \"fail\": \"l'interlinea nell'attributo style non deve utilizzare !important, o deve essere impostata a ${data.minValue}em (attuale ${data.value}em)\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"La spaziatura delle parole nell'attributo style non è impostata su !important, o rispetta il requisito minimo\",\n      \"fail\": \"La spaziatura delle parole nell'attributo style non deve utilizzare !important, o deve essere impostata a ${data.minValue}em (attuale ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"L'elemento non è visibile\",\n      \"fail\": \"L'elemento è visibile\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"L'elemento ha un attributo alt non vuoto\",\n      \"fail\": {\n        \"noAttr\": \"L'elemento non ha un attributo alt\",\n        \"emptyAttr\": \"L'elemento ha un attributo alt vuoto\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"L'elemento non ha un attributo value\",\n        \"has-label\": \"L'elemento ha un attributo value non vuoto\"\n      },\n      \"fail\": \"L'elemento ha un attributo value e l'attributo value è vuoto\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"L'elemento ha un attributo placeholder\",\n      \"fail\": {\n        \"noAttr\": \"L'elemento non ha un attributo placeholder\",\n        \"emptyAttr\": \"L'elemento ha un attributo placeholder vuoto\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"L'elemento ha un attributo title\",\n      \"fail\": {\n        \"noAttr\": \"L'elemento non ha un attributo title\",\n        \"emptyAttr\": \"L'elemento ha un attributo title vuoto\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"L'elemento ha un attributo value non vuoto\",\n      \"fail\": {\n        \"noAttr\": \"L'elemento non ha un attributo value\",\n        \"emptyAttr\": \"L'elemento ha un attributo value vuoto\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"La semantica predefinita dell'elemento è stata sostituita da role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"La semantica predefinita dell'elemento non è stata sostituita da role=\\\"none\\\" o role=\\\"presentation\\\"\",\n        \"globalAria\": \"Il ruolo dell'elemento non è presentazionale perché ha un attributo ARIA globale\",\n        \"focusable\": \"Il ruolo dell'elemento non è presentazionale perché è focusabile\",\n        \"both\": \"Il ruolo dell'elemento non è presentazionale perché ha un attributo ARIA globale ed è focusabile\",\n        \"iframe\": \"L'utilizzo dell'attributo \\\"title\\\" su un elemento ${data.nodeName} con un ruolo presentazionale si comporta in modo inconsistente a seconda dello screen reader\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"La semantica predefinita dell'elemento è stata sostituita da role=\\\"none\\\"\",\n      \"fail\": \"La semantica predefinita dell'elemento non è stata sostituita da role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"La semantica predefinita dell'elemento è stata sostituita da role=\\\"presentation\\\"\",\n      \"fail\": \"La semantica predefinita dell'elemento non è stata sostituita da role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"L'elemento ha un figlio che è un titolo\",\n      \"fail\": {\n        \"noTitle\": \"L'elemento non ha figli che sono titoli\",\n        \"emptyTitle\": \"Il titolo figlio dell'elemento è vuoto\"\n      },\n      \"incomplete\": \"Non è possibile determinare se l'elemento ha un figlio che è un titolo\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"La prima riga di una tabella non viene utilizzata come didascalia\",\n      \"fail\": \"Il primo figlio della tabella dovrebbe essere una didascalia invece di una cella di tabella\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"L'attributo scope viene utilizzato solo sugli elementi di intestazione della tabella (<th>)\",\n      \"fail\": \"In HTML 5, gli attributi scope possono essere utilizzati solo sugli elementi di intestazione della tabella (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Il contenuto dell'attributo summary e dell'elemento <caption> non sono duplicati\",\n      \"fail\": \"Il contenuto dell'attributo summary e dell'elemento <caption> sono identici\",\n      \"incomplete\": \"Non è possibile determinare se l'elemento <table> ha una didascalia\"\n    },\n    \"scope-value\": {\n      \"pass\": \"L'attributo scope viene utilizzato correttamente\",\n      \"fail\": \"Il valore dell'attributo scope può essere solo 'row' o 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Tutte le celle di dati non vuote hanno intestazioni di tabella\",\n      \"fail\": \"Alcune celle di dati non vuote non hanno intestazioni di tabella\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"L'attributo headers viene utilizzato esclusivamente per fare riferimento ad altre celle nella tabella\",\n      \"incomplete\": \"L'attributo headers è vuoto\",\n      \"fail\": \"L'attributo headers non viene utilizzato esclusivamente per fare riferimento ad altre celle nella tabella\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Tutte le celle di intestazione della tabella fanno riferimento a celle di dati\",\n      \"fail\": \"Non tutte le celle di intestazione della tabella fanno riferimento a celle di dati\",\n      \"incomplete\": \"Le celle di dati della tabella sono vuote o mancanti\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Tutto il contenuto della pagina è stato analizzato.\",\n      \"fail\": \"Ci sono stati problemi nell'analizzare il contenuto di questa pagina.\",\n      \"incomplete\": \"C'è del contenuto nascosto nella pagina che non è stato analizzato. Dovrai attivare la visualizzazione di questo contenuto per poterlo analizzare.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Correggi uno dei seguenti:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Correggi tutti i seguenti:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe non è stato in grado di determinare il motivo. È il momento di utilizzare l'element inspector!\"\n}\n"
  },
  {
    "path": "locales/ja.json",
    "content": "{\n  \"lang\": \"ja\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"すべてのaccesskey属性の値が一意であることを確認してください\",\n      \"help\": \"accesskey属性の値は一意でなければなりません\"\n    },\n    \"area-alt\": {\n      \"description\": \"イメージマップの<area>要素に代替テキストが存在することを確認してください\",\n      \"help\": \"アクティブな<area>要素には代替テキストが存在しなければなりません\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"要素のロールがARIA属性をサポートしていることを確認してください\",\n      \"help\": \"要素にはサポートされているARIA属性のみを使用しなければなりません\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"role属性の値が要素に対して適切であることを確認してください\",\n      \"help\": \"ARIAロールは要素に対して適切でなければなりません\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"aria-braillelabelとaria-brailleroledescriptionには、点字以外の同等のものが存在することを確認してください\",\n      \"help\": \"aria-braille属性には、点字以外の同等のものがなければなりません\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"すべてのARIA button、link、menuitemにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIAコマンドにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"ARIA属性が要素のロールの仕様に従って使用されていることを確認してください\",\n      \"help\": \"ARIA属性は要素のロールの仕様に従って使用しなければなりません\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"要素に非推奨のロールが使用されていないことを確認してください\",\n      \"help\": \"非推奨のARIAロールを使用してはなりません\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"すべてのARIA dialog、alertdialogノードにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA dialogとalertdialogノードにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"ドキュメント本体にaria-hidden=\\\"true\\\"が存在しないことを確認してください\",\n      \"help\": \"ドキュメント本体にaria-hidden=\\\"true\\\"が存在してはなりません\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"aria-hiddenが指定されている要素にフォーカスできないこと、その要素にフォーカス可能な要素が含まれていないことを確認してください\",\n      \"help\": \"aria-hiddenが指定されている要素は、フォーカス可能であったり、フォーカス可能な要素を含んでいたりしてはなりません\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"すべてのARIA入力欄にアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA入力欄にはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"すべてのARIA meterノードにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA meterノードにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"すべてのARIA progressbarノードにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA progressbarノードにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"要素のロールでARIA属性が禁止されていないことを確認してください\",\n      \"help\": \"要素には禁止されているARIA属性を使用してはなりません\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"ARIAロールのある要素にすべての必須ARIA属性が存在することを確認してください\",\n      \"help\": \"必須のARIA属性が提供されていなければなりません\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"子ロールを必須とするARIAロールが指定された要素に、それらが含まれていることを確認してください\",\n      \"help\": \"特定のARIAロールには特定の子が含まれていなければなりません\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"親ロールを必須とするARIAロールが指定された要素に、それらが含まれていることを確認してください\",\n      \"help\": \"特定のARIAロールは特定の親に含まれていなければなりません\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"aria-roledescriptionが暗黙的もしくは明示的なロールを持った要素に使用されていることを確認してください\",\n      \"help\": \"aria-roledescriptionはセマンティックなロールを持った要素に使用しなければなりません\"\n    },\n    \"aria-roles\": {\n      \"description\": \"すべてのrole属性が指定された要素で、有効な値が使用されていることを確認してください\",\n      \"help\": \"使用されているARIAロールは有効な値に一致しなければなりません\"\n    },\n    \"aria-text\": {\n      \"description\": \"role=\\\"text\\\"が指定されている要素にフォーカス可能な子孫がないことを確認してください\",\n      \"help\": \"\\\"role=text\\\"が指定されている要素には、フォーカス可能な子孫が含まれていてはなりません\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"すべてのARIAトグル欄にアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIAトグル欄にはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"すべてのARIA tooltipノードにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA tooltipノードにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"すべてのARIA treeitemノードにアクセシブルな名前があることを確認してください\",\n      \"help\": \"ARIA treeitemノードにはアクセシブルな名前がなければなりません\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"すべてのARIA属性に有効な値が存在することを確認してください\",\n      \"help\": \"ARIA属性は有効な値に一致しなければなりません\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"aria- で始まる属性が有効なARIA属性であることを確認してください\",\n      \"help\": \"ARIA属性は有効な名前に一致しなければなりません\"\n    },\n    \"audio-caption\": {\n      \"description\": \"<audio>要素にキャプションが存在することを確認してください\",\n      \"help\": \"<audio>要素にはキャプショントラックが存在しなければなりません\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"autocomplete属性が正しく、かつフォームフィールドに対して適切であることを確認してください\",\n      \"help\": \"autocomplete属性は正しく使用しなければなりません\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"style属性で指定されたテキストの間隔は、カスタムスタイルシートにより調整可能であることを確認してください\",\n      \"help\": \"インラインのテキスト間隔設定はカスタムスタイルシートによって調整可能でなければなりません\"\n    },\n    \"blink\": {\n      \"description\": \"<blink>要素が使用されていないことを確認してください\",\n      \"help\": \"<blink>要素の使用は非推奨で、使用するべきではありません\"\n    },\n    \"button-name\": {\n      \"description\": \"ボタンに認識可能なテキストが存在することを確認してください\",\n      \"help\": \"ボタンには認識可能なテキストが存在しなければなりません\"\n    },\n    \"bypass\": {\n      \"description\": \"各ページに少なくとも1つ、ユーザーがナビゲーション部分をスキップして直接本文へ移動できるメカニズムが存在することを確認してください\",\n      \"help\": \"ページには繰り返されるブロックをスキップする手段が存在しなければなりません\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"前景色と背景色のコントラストがWCAG 2のAAAコントラスト比（高度）のしきい値を満たすことを確認してください\",\n      \"help\": \"要素は色のコントラスト比（高度）の閾値を満たしていなければなりません\"\n    },\n    \"color-contrast\": {\n      \"description\": \"前景色と背景色のコントラストがWCAG 2のAAコントラスト比（最低限）のしきい値を満たすことを確認してください\",\n      \"help\": \"要素は色のコントラスト比（最低限）の閾値を満たしていなければなりません\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"コンテンツが特定のディスプレイの向きに固定されていないこと、およびコンテンツがすべてのディスプレイの向きで操作可能なことを確認してください\",\n      \"help\": \"CSSメディアクエリーはディスプレイの向きを固定するために使用してはなりません\"\n    },\n    \"definition-list\": {\n      \"description\": \"<dl>要素の構造が正しいことを確認してください\",\n      \"help\": \"<dl>要素は、適切な順序で並べられた<dt>および<dd>のグループ、<script>要素、<template>要素またはdiv要素のみを直接含んでいなければなりません\"\n    },\n    \"dlitem\": {\n      \"description\": \"<dt>および<dd>要素が<dl>に含まれていることを確認してください\",\n      \"help\": \"<dt>および<dd>要素は<dl>に含まれていなければなりません\"\n    },\n    \"document-title\": {\n      \"description\": \"各HTMLドキュメントに空ではない<title>要素が含まれていることを確認してください\",\n      \"help\": \"ドキュメントにはナビゲーションを補助するために<title>要素がなければなりません\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"アクティブな要素のid属性の値が一意であることを確認してください\",\n      \"help\": \"アクティブな要素のIDは一意でなければなりません\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"ARIAおよびラベルに使用されているすべてのid属性の値が一意であることを確認してください\",\n      \"help\": \"ARIAおよびラベルに使用されているIDは一意でなければなりません\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"すべてのid属性の値が一意であることを確認してください\",\n      \"help\": \"id属性の値は一意でなければなりません\"\n    },\n    \"empty-heading\": {\n      \"description\": \"見出しに認識可能なテキストが存在することを確認してください\",\n      \"help\": \"見出しは空にしてはなりません\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"テーブルのヘッダーに認識可能なテキストが存在することを確認してください\",\n      \"help\": \"テーブルのヘッダーは空にしてはなりません\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"フォーカス順序に含まれる要素にインタラクティブコンテンツに適したロールがあることを確認してください\",\n      \"help\": \"フォーカス順序に含まれる要素には、適切なロールがなければなりません\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"フォームフィールドに複数のlabel要素が存在しないことを確認してください\",\n      \"help\": \"フォームフィールドに複数のlabel要素を付与してはなりません\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"フォーカス可能な<frame>と<iframe>要素に、tabindex=-1が指定されていないことを確認してください\",\n      \"help\": \"フォーカス可能なコンテンツを含むフレームには、tabindex=-1が指定されていてはなりません\"\n    },\n    \"frame-tested\": {\n      \"description\": \"<iframe>および<frame>要素にaxe-coreスクリプトが含まれていることを確認してください\",\n      \"help\": \"フレームはaxe-coreでテストしなければなりません\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"<iframe>および<frame>要素に一意のtitle属性が含まれていることを確認してください\",\n      \"help\": \"フレームには一意のtitle属性がなければなりません\"\n    },\n    \"frame-title\": {\n      \"description\": \"<iframe>および<frame>要素にアクセシブルな名前が存在することを確認してください\",\n      \"help\": \"フレームにはアクセシブルな名前がなければなりません\"\n    },\n    \"heading-order\": {\n      \"description\": \"見出しの順序が意味的に正しいことを確認してください\",\n      \"help\": \"見出しのレベルは1つずつ増加させなければなりません\"\n    },\n    \"hidden-content\": {\n      \"description\": \"隠れているコンテンツについてユーザーに通知してください\",\n      \"help\": \"ページ上の隠れているコンテンツは分析されなければなりません\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"すべてのHTMLドキュメントにlang属性が存在することを確認してください\",\n      \"help\": \"<html>要素にはlang属性がなければなりません\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"<html>要素のlang属性に有効な値があることを確認してください\",\n      \"help\": \"<html>要素のlang属性には有効な値がなければなりません\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"HTML要素に指定された有効なlangおよびxml:lang属性の両方がページの基本言語と一致することを確認してください\",\n      \"help\": \"HTML要素に指定されたlangおよびxml:lang属性は同じ基本言語を持たなければなりません\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"同じアクセシブルな名前を持つ複数のリンクが類似した目的を果たすことを確認してください\",\n      \"help\": \"同じ名前を持つ複数のリンクは類似した目的を持っていなければなりません\"\n    },\n    \"image-alt\": {\n      \"description\": \"<img>要素に代替テキストが存在する、またはnoneまたはpresentationのロールが存在することを確認してください\",\n      \"help\": \"画像には代替テキストがなければなりません\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"画像の代替がテキストとして繰り返されていないことを確認してください\",\n      \"help\": \"画像の代替テキストはテキストとして繰り返されるべきではありません\"\n    },\n    \"input-button-name\": {\n      \"description\": \"入力ボタンに認識可能なテキストが存在することを確認してください\",\n      \"help\": \"入力ボタンには認識可能なテキストが存在しなければなりません\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"<input type=\\\"image\\\">要素に代替テキストが存在することを確認してください\",\n      \"help\": \"画像ボタンには代替テキストがなければなりません\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"コンテンツによってラベル付けされた要素は、それらの視認できるテキストがアクセシブルな名前の一部になっていることを確認してください\",\n      \"help\": \"要素の視認できるテキストはそれらのアクセシブルな名前の一部でなければなりません\"\n    },\n    \"label-title-only\": {\n      \"description\": \"すべてのフォーム要素に視認できるラベルがあり、非表示のラベル、titleまたはaria-describedby属性のみを使用してラベル付けされていないことを確認してください\",\n      \"help\": \"フォーム要素には視認できるラベルがなければなりません\"\n    },\n    \"label\": {\n      \"description\": \"すべてのフォーム要素にラベルが存在することを確認してください\",\n      \"help\": \"フォーム要素にはラベルがなければなりません\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"bannerランドマークがトップレベルにあることを確認してください\",\n      \"help\": \"bannerランドマークは他のランドマークに含まれるべきではありません\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"complementaryランドマークあるいはasideがトップレベルにあることを確認してください\",\n      \"help\": \"asideは他の要素に含まれるべきではありません\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"contentinfoランドマークがトップレベルにあることを確認してください\",\n      \"help\": \"contentinfoランドマークは他のランドマークに含まれるべきではありません\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"mainランドマークがトップレベルにあることを確認してください\",\n      \"help\": \"mainランドマークは他のランドマークに含まれるべきではありません\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"ドキュメント内のbannerランドマークが最大で1つのみであることを確認してください\",\n      \"help\": \"ドキュメントに複数のbannerランドマークが存在してはなりません\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"ドキュメント内のcontentinfoランドマークが最大で1つのみであることを確認してください\",\n      \"help\": \"ドキュメントに複数のcontentinfoランドマークが存在してはなりません\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"ドキュメント内のmainランドマークが最大で1つのみであることを確認してください\",\n      \"help\": \"ドキュメントに複数のmainランドマークが存在してはなりません\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"ドキュメントにmainランドマークが含まれていることを確認してください\",\n      \"help\": \"ドキュメントにはmainランドマークが1つ含まれていなければなりません\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"ランドマークが一意であることを確認してください\",\n      \"help\": \"ランドマークには一意のロール又はロール／ラベル／タイトル (すなわちアクセシブルな名前) の組み合わせがなければなりません\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"リンクが色に依存しない形で周囲のテキストと区別できることを確認してください\",\n      \"help\": \"リンクは色に依存しない形で区別できなければなりません\"\n    },\n    \"link-name\": {\n      \"description\": \"リンクに認識可能なテキストが存在することを確認してください\",\n      \"help\": \"リンクには認識可能なテキストがなければなりません\"\n    },\n    \"list\": {\n      \"description\": \"リストが正しく構造化されていることを確認してください\",\n      \"help\": \"<ul>および<ol>の直下には<li>、<script>または<template>要素のみを含まなければなりません\"\n    },\n    \"listitem\": {\n      \"description\": \"<li>要素がセマンティックに使用されていることを確認してください\",\n      \"help\": \"<li>要素は<ul>または<ol>内に含まれていなければなりません\"\n    },\n    \"marquee\": {\n      \"description\": \"<marquee>要素が使用されていないことを確認してください\",\n      \"help\": \"<marquee>要素は非推奨のため、使用してはなりません\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"一定時間経過後のページの自動リロードのために<meta http-equiv=\\\"refresh\\\">が使用されていないことを確認してください\",\n      \"help\": \"一定時間経過後のページの自動リロードを使用してはなりません\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"一定時間経過後のページの自動リロードのために<meta http-equiv=\\\"refresh\\\">が使用されていないことを確認してください\",\n      \"help\": \"20時間より短い時間経過後のページの自動リロードを使用してはなりません\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"<meta name=\\\"viewport\\\">で大幅に拡大縮小できることを確認してください\",\n      \"help\": \"ユーザーがズームをしてテキストを最大500％まで拡大できなければなりません\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"<meta name=\\\"viewport\\\">がテキストのサイズ変更やズームを無効化しないことを確認してください\",\n      \"help\": \"ズーム機能やテキストのサイズ変更は無効にしてはなりません\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"スクリーン・リーダーで必ずしもよみあげられなかったり支援技術のフォーカスに関する問題を引き起こす可能性があったりするため、対話的なコントロールがネストされていないことを確認してください\",\n      \"help\": \"対話的なコントロールはネストされていてはなりません\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"<video> または <audio> 要素が音声を停止またはミュートするコントロールなしに音声を3秒より長く自動再生しないことを確認してください\",\n      \"help\": \"<video> または <audio> 要素は音声を自動再生してはなりません\"\n    },\n    \"object-alt\": {\n      \"description\": \"<object>要素に代替テキストが存在することを確認してください\",\n      \"help\": \"<object>要素には代替テキストがなければなりません\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"<p>要素を見出しとしてスタイル付けするために太字、イタリック体、およびフォントサイズが使用されていないことを確認してください\",\n      \"help\": \"スタイル付けした<p>要素を見出しとして使用してはなりません\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"ページ、またはそのページ中のフレームの少なくとも1つにはレベル1の見出しが含まれていることを確認してください\",\n      \"help\": \"ページにはレベル1の見出しが含まれていなければなりません\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"プレゼンテーション目的とされている要素には、グローバルなARIAまたはtabindexが指定されておらず、すべてのスクリーン・リーダーが無視するようになっていることを確認してください\",\n      \"help\": \"プレゼンテーション目的とされている要素は一貫して無視されなければなりません\"\n    },\n    \"region\": {\n      \"description\": \"ページのすべてのコンテンツがlandmarkに含まれていることを確認してください\",\n      \"help\": \"ページのすべてのコンテンツはlandmarkに含まれていなければなりません\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"[role=\\\"img\\\"] の要素に代替テキストが存在することを確認してください\",\n      \"help\": \"[role=\\\"img\\\"] の要素には代替テキストがなければなりません\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"scope属性がテーブルで正しく使用されていることを確認してください\",\n      \"help\": \"scope属性は正しく使用されなければなりません\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"スクロール可能なコンテンツを持つ要素がキーボードでアクセスできることを確認してください\",\n      \"help\": \"スクロール可能な領域はキーボードでアクセスできなければなりません\"\n    },\n    \"select-name\": {\n      \"description\": \"select要素にはアクセシブルな名前があることを確認してください\",\n      \"help\": \"select要素にはアクセシブルな名前がなければなりません\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"サーバーサイドのイメージマップが使用されていないことを確認してください\",\n      \"help\": \"サーバーサイドのイメージマップを使用してはなりません\"\n    },\n    \"skip-link\": {\n      \"description\": \"すべてのスキップリンクにフォーカス可能なターゲットがあることを確認してください\",\n      \"help\": \"スキップリンクのターゲットが存在し、フォーカス可能でなければなりません\"\n    },\n    \"summary-name\": {\n      \"description\": \"summary要素に認識可能なテキストが存在することを確認してください\",\n      \"help\": \"summary要素には認識可能なテキストがなければなりません\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"img、graphics-documentまたはgraphics-symbolロールを持つsvg要素にアクセシブルなテキストがあることを確認してください\",\n      \"help\": \"imgロールを持つ<svg>要素には代替テキストが存在しなければなりません\"\n    },\n    \"tabindex\": {\n      \"description\": \"tabindex属性値が0より大きくないことを確認してください\",\n      \"help\": \"要素に指定するtabindexは0より大きい値であってはなりません\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"<caption>要素の内容がsummary属性のテキストと同一ではないことを確認してください\",\n      \"help\": \"テーブルのキャプションとサマリーは同一であってはなりません\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"キャプション付きのテーブルが<caption>要素を用いていることを確認してください\",\n      \"help\": \"データテーブルにキャプションをつけるためにデータまたはヘッダーセルを用いてはなりません\"\n    },\n    \"target-size\": {\n      \"description\": \"タッチターゲットのサイズとスペースが十分にあることを確認してください\",\n      \"help\": \"すべてのタッチターゲットは24pxの大きさか、十分なスペースがなければなりません\"\n    },\n    \"td-has-header\": {\n      \"description\": \"3×3より大きい<table>の空ではないデータセルにはそれぞれ1つ以上のテーブルヘッダーが存在することを確認してください\",\n      \"help\": \"大きい<table>の空ではない<td>要素は対応するテーブルヘッダーと関連づけられていなければなりません\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"テーブルでheaders属性を使用している各セルの参照先が、同じテーブル内の他の<th>要素であることを確認してください\",\n      \"help\": \"テーブルのheaders属性を使用するすべてのセルは、同じ表内の他の<th>要素を参照しなければなりません\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"すべての<th>要素およびrole=columnheader/rowheaderを持つ要素にはそれらが説明するデータセルがあることを確認してください\",\n      \"help\": \"データテーブルのテーブルヘッダーはデータセルを参照していなければなりません\"\n    },\n    \"valid-lang\": {\n      \"description\": \"lang属性に有効な値が存在することを確認してください\",\n      \"help\": \"lang属性には有効な値がなければなりません\"\n    },\n    \"video-caption\": {\n      \"description\": \"<video>要素にキャプションが存在することを確認してください\",\n      \"help\": \"<video>要素にはキャプションがなければなりません\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"抽象ロールは使用されていません\",\n      \"fail\": {\n        \"singular\": \"抽象ロールは直接使用できません: ${data.values}\",\n        \"plural\": \"抽象ロールは直接使用できません: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA属性が定義されたロールに対して正しく使用されています\",\n      \"fail\": {\n        \"singular\": \"ARIA属性は許可されていません: ${data.values}\",\n        \"plural\": \"ARIA属性は許可されていません: ${data.values}\"\n      },\n      \"incomplete\": \"この要素のARIA属性が無視されても問題ないことを確認しましょう: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIAロールは指定された要素に対して許可されています\",\n      \"fail\": {\n        \"singular\": \"ARIAロール${data.values}は指定された要素では許可されていません\",\n        \"plural\": \"ARIAロール${data.values}は指定された要素では許可されていません\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIAロール${data.values}はこの要素では許可されていないため、要素が表示されたときはARIAロールを削除する必要があります\",\n        \"plural\": \"ARIAロール${data.values}はこの要素では許可されていないため、要素が表示されたときはARIAロールを削除する必要があります\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"要素にはaria-busy属性が存在します\",\n      \"fail\": \"ローダーを表示中に、要素aria-busy=\\\"true\\\"が指定されています\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"ARIA 属性が許可されています\",\n      \"fail\": {\n        \"checkbox\": \"aria-checkedを削除するか\\\"${data.checkState}\\\"を設定して、チェックボックスの実際の状態と合うようにしましょう\",\n        \"rowSingular\": \"treegridの行でサポートされている属性ですが、${data.ownerRole}: ${data.invalidAttrs}の場合はこの限りではありません\",\n        \"rowPlural\": \"treegridの行でサポートされている属性ですが、${data.ownerRole}: ${data.invalidAttrs}の場合はこの限りではありません\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessageが存在して、サポートされているaria-errormessageの手法を用いているスクリーン・リーダーが認識できる要素を参照しています\",\n      \"fail\": {\n        \"singular\": \"aria-errormessageの値`${data.values}`はメッセージを通知する方法を使用しなければなりません (例えば、aria-live、aria-describedby、role=alert等)\",\n        \"plural\": \"aria-errormessageの値`${data.values}`はメッセージを通知する方法を使用しなければなりません (例えば、aria-live、aria-describedby、role=alert等)\",\n        \"hidden\": \"aria-errormessageの値 `${data.values}`は非表示の要素を参照することはできません\"\n      },\n      \"incomplete\": {\n        \"singular\": \"aria-errormessageの値 `${data.values}`は存在する要素を参照していることを確認しましょう\",\n        \"plural\": \"aria-errormessageの値 `${data.values}`は存在する要素を参照していることを確認しましょう\",\n        \"idrefs\": \"aria-errormessage要素がページ上に存在するかどうか判定できません: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"ドキュメント本体にaria-hidden属性は存在しません\",\n      \"fail\": \"aria-hidden=trueはドキュメント本体に存在してはなりません\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria-levelの値は有効です\",\n      \"incomplete\": \"6より大きいaria-levelの値は、どのスクリーンリーダーとブラウザーの組み合わせでもサポートされていません\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"ARIA属性は使用できます\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited}属性は\\\"${data.role}\\\"ロールでは使用できません\",\n        \"hasRoleSingular\": \"${data.prohibited}属性は\\\"${data.role}\\\"ロールでは使用できません\",\n        \"noRolePlural\": \"${data.prohibited}属性は、有効なrole属性のない${data.nodeName}では使用できません\",\n        \"noRoleSingular\": \"${data.prohibited}属性は、有効なrole属性のない${data.nodeName}では使用できません\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"${data.prohibited}属性はロール\\\"${data.role}\\\"では十分にサポートされていません\",\n        \"hasRolePlural\": \"${data.prohibited}属性はロール\\\"${data.role}\\\"では十分にサポートされていません\",\n        \"noRoleSingular\": \"${data.prohibited}属性は、有効なrole属性のない${data.nodeName}では十分にサポートされていません\",\n        \"noRolePlural\": \"${data.prohibited}属性は、有効なrole属性のない${data.nodeName}では十分にサポートされていません\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"すべての必須のARIA属性が存在します\",\n      \"fail\": {\n        \"singular\": \"必須のARIA属性が提供されていません: ${data.values}\",\n        \"plural\": \"必須のARIA属性が提供されていません: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"必須のARIA子ロールが存在します\",\n        \"aria-busy\": \"要素にはaria-busy属性が指定されているため、必要な子要素を省略することが許可されています\"\n      },\n      \"fail\": {\n        \"singular\": \"必須のARIA子ロールが提供されていません: ${data.values}\",\n        \"plural\": \"必須のARIA子ロールが提供されていません: ${data.values}\",\n        \"unallowed\": \"要素には許可されていないARIA子ロールがあります: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIAの子ロールを追加することが求められます: ${data.values}\",\n        \"plural\": \"ARIAの子ロールを追加することが求められます: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"必須のARIA親ロールが存在します\",\n      \"fail\": {\n        \"singular\": \"必須のARIA親ロールが提供されていません: ${data.values}\",\n        \"plural\": \"必須のARIA親ロールが提供されていません: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescriptionはサポートされたセマンティックなロールに使用されています\",\n      \"incomplete\": \"aria-roledescriptionがサポートされたスクリーン・リーダーで読み上げられることを確認しましょう\",\n      \"fail\": \"aria-roledescriptionをサポートするロールを要素に付与しましょう\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA属性はサポートされています\",\n      \"fail\": \"ARIA属性はスクリーン・リーダーや支援技術に広くサポートされていません: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA属性値が有効です\",\n      \"fail\": {\n        \"singular\": \"無効なARIA属性値です: ${data.values}\",\n        \"plural\": \"無効なARIA属性値です: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"ARIA属性で指定されている要素のIDがページ上に存在しません: ${data.needsReview}\",\n        \"noIdShadow\": \"ARIA属性で指定されている要素のIDがページ上に存在しないか、別のshadow DOMツリーの小要素です: ${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA属性値が無効であるため、\\\"aria-current=true\\\"として扱われます: ${data.needsReview}\",\n        \"idrefs\": \"ARIA属性で指定されている要素のIDがページ上に存在するかどうか判定できません: ${data.needsReview}\",\n        \"empty\": \"ARIA属性値が空のときは無視されます: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"aria-haspopupを使用している際にaria-controlsが参照するIDがページ上に存在するかどうかを判断できません: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"ARIA属性名が有効です\",\n      \"fail\": {\n        \"singular\": \"無効なARIA属性名です: ${data.values}\",\n        \"plural\": \"無効なARIA属性名です: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabelはアクセシブルなテキストがある要素に対して使用されています\",\n      \"fail\": \"aria-braillelabelがアクセシブルなテキストがない要素に対して使用されています\",\n      \"incomplete\": \"アクセシブルなテキストを算出することができません\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescriptionはaria-roledescriptionが指定されている要素に対して使用されています\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescriptionがaria-roledescriptionが指定されていない要素に対して使用されています\",\n        \"emptyRoleDescription\": \"aria-brailleroledescriptionが空のaria-roledescriptionが指定されている要素に対して使用されています\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"非推奨のARIAロールではありません\",\n      \"fail\": \"非推奨のロールが使用されています: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"1つのロール値のみ使用されています\",\n      \"fail\": \"古いブラウザーではフォールバックロールがサポートされていないため、ロール値は1つのみ使用します\",\n      \"incomplete\": \"'presentation'と'none'のロールは同義なので、どちらか一方のみを使用します。\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"要素にはグローバルなARIA属性が指定されています: ${data.values}\",\n        \"plural\": \"要素にはグローバルなARIA属性が指定されています: ${data.values}\"\n      },\n      \"fail\": \"要素にはグローバルなARIA属性が指定されていません\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"要素にはwidgetロールが存在します\",\n      \"fail\": \"要素にはwidgetロールが存在しません\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIAロールが有効です\",\n      \"fail\": {\n        \"singular\": \"ロールは有効なARIAロールのうちの1つでなければなりません: ${data.values}\",\n        \"plural\": \"ロールは有効なARIAロールのうちの1つでなければなりません: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"フォーカス可能な要素です\",\n      \"fail\": \"フォーカス不可能な要素です\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"<label> とアクセシブルな名前の間に不一致はありません\",\n      \"incomplete\": \"<label>がARIA ${data}欄の名前の一部である必要がないことを確認しましょう\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIAロールはサポートされています\",\n      \"fail\": \"使用されているロールはスクリーン・リーダーや支援技術に広くサポートされていません: ${data.values}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"要素はフォーカス順序に含まれる要素に対して有効なセマンティクスを持ちます\",\n      \"fail\": \"要素はフォーカス順序に含まれる要素に対して無効なセマンティクスを持ちます\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"要素には${data.contrastRatio}の十分なコントラスト比があります\",\n      \"fail\": {\n        \"default\": \"要素のコントラスト比が不十分です ${data.contrastRatio}（前景色: ${data.fgColor}、背景色: ${data.bgColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\",\n        \"fgOnShadowColor\": \"要素の前景色と影（text-shadow）の色のコントラスト比が不十分です ${data.contrastRatio}（前景色: ${data.fgColor}、影（text-shadow）の色: ${data.shadowColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\",\n        \"shadowOnBgColor\": \"要素の影（text-shadow）の色と背景色のコントラスト比が不十分です ${data.contrastRatio}（影（text-shadow）の色: ${data.shadowColor}、背景色: ${data.bgColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\"\n      },\n      \"incomplete\": {\n        \"default\": \"コントラスト比を判定できません\",\n        \"bgImage\": \"背景画像のため、要素の背景色を判定できません\",\n        \"bgGradient\": \"背景グラデーションのため、要素の背景色を判定できません\",\n        \"imgNode\": \"画像ノードが含まれるため、要素の背景色を判定できません\",\n        \"bgOverlap\": \"他の要素と重なっているため、要素の背景色を判定できません\",\n        \"fgAlpha\": \"アルファ透明度により、要素の前景色を判定できません\",\n        \"elmPartiallyObscured\": \"他の要素により部分的に不明瞭なため、要素の背景色を判定できません\",\n        \"elmPartiallyObscuring\": \"他の要素と部分的に重なっているため、要素の背景色を判定できません\",\n        \"outsideViewport\": \"ビューポートの外にあるため、要素の背景色を判定できません\",\n        \"equalRatio\": \"要素のコントラスト比が背景と1:1です\",\n        \"shortTextContent\": \"実際のテキストコンテンツであるかを判断するには要素のコンテンツが短すぎます\",\n        \"nonBmp\": \"要素のコンテンツはテキストではない文字のみを含んでいます\",\n        \"pseudoContent\": \"擬似要素のため、要素の背景色を判定することができません\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"要素には${data.contrastRatio}の十分なコントラスト比があります\",\n        \"hidden\": \"要素は非表示です\"\n      },\n      \"fail\": {\n        \"default\": \"要素のコントラスト比が不十分です ${data.contrastRatio}（前景色: ${data.fgColor}、背景色: ${data.bgColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\",\n        \"fgOnShadowColor\": \"要素の前景色と影（text-shadow）の色のコントラスト比が不十分です ${data.contrastRatio}（前景色: ${data.fgColor}、影（text-shadow）の色: ${data.shadowColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\",\n        \"shadowOnBgColor\": \"要素の影（text-shadow）の色と背景色のコントラスト比が不十分です ${data.contrastRatio}（影（text-shadow）の色: ${data.shadowColor}、背景色: ${data.bgColor}、フォントサイズ: ${data.fontSize}、フォントの太さ: ${data.fontWeight}）。コントラスト比${data.expectedContrastRatio}を必要とします\"\n      },\n      \"incomplete\": {\n        \"default\": \"コントラスト比を判定できません\",\n        \"bgImage\": \"背景画像のため、要素の背景色を判定できません\",\n        \"bgGradient\": \"背景グラデーションのため、要素の背景色を判定できません\",\n        \"imgNode\": \"画像ノードが含まれるため、要素の背景色を判定できません\",\n        \"bgOverlap\": \"他の要素と重なっているため、要素の背景色を判定できません\",\n        \"complexTextShadows\": \"複雑なtext-shadowが用いられているため、要素のコントラスト比を判定できません\",\n        \"fgAlpha\": \"アルファ透明度により、要素の前景色を判定できません\",\n        \"elmPartiallyObscured\": \"他の要素により部分的に不明瞭なため、要素の背景色を判定できません\",\n        \"elmPartiallyObscuring\": \"他の要素と部分的に重なっているため、要素の背景色を判定できません\",\n        \"outsideViewport\": \"ビューポートの外にあるため、要素の背景色を判定できません\",\n        \"equalRatio\": \"要素のコントラスト比が背景と1:1です\",\n        \"shortTextContent\": \"実際のテキストコンテンツであるかを判断するには要素のコンテンツが短すぎます\",\n        \"nonBmp\": \"要素のコンテンツはテキストではない文字のみを含んでいます\",\n        \"pseudoContent\": \"擬似要素のため、要素の背景色を判定することができません\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"リンクは視覚的なスタイル付けによって周囲のテキストと区別できます\",\n      \"incomplete\": {\n        \"default\": \"リンクを近接したテキストと区別するために、スタイル付けが必要か確認しましょう。\",\n        \"pseudoContent\": \"リンクの擬似スタイルが、周囲のテキストと区別するのに充分か確認しましょう。\"\n      },\n      \"fail\": \"リンクには、周囲のテキストと区別するためのスタイル (下線など) がありません\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"リンクは色以外の方法で周囲のテキストと区別できます\",\n      \"fail\": {\n        \"fgContrast\": \"このリンクは、周囲のテキストとの${data.contrastRatio}:1の色のコントラストが不十分です。(最小コントラストは${data.requiredContrastRatio}:1、リンクテキスト: ${data.nodeColor}、周囲のテキスト: ${data.parentColor})\",\n        \"bgContrast\": \"リンクの背景の色コントラストが${data.contrastRatio}で十分ではありません (最小コントラストは${data.requiredContrastRatio}:1、リンク背景色: ${data.nodeBackgroundColor}、周囲の背景色: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"要素の前景コントラスト比を判定できません\",\n        \"bgContrast\": \"要素の背景コントラスト比を判定できません\",\n        \"bgImage\": \"背景画像のため、要素のコントラスト比を判定できません\",\n        \"bgGradient\": \"背景グラデーションのため、要素のコントラスト比を判定できません\",\n        \"imgNode\": \"画像ノードが含まれるため、要素のコントラスト比を判定できません\",\n        \"bgOverlap\": \"要素の重なりにより、要素のコントラスト比を判定できません\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"オートコンプリートの値は適切な要素に指定されています\",\n      \"fail\": \"オートコンプリートの値はこの種類の入力項目には適切ではありません\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"autocomplete属性は正しくフォーマットされています\",\n      \"fail\": \"autocomplete属性は誤ってフォーマットされています\",\n      \"incomplete\": \"autocomplete属性に、標準的ではない値が指定されています。代わりに指定できる標準的な値がないか確認してください。\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"accesskey属性の値は一意です\",\n      \"fail\": \"ドキュメントに同じaccesskeyを持った複数の要素が存在します\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"要素内にフォーカス可能な要素が含まれています\",\n      \"fail\": \"要素にフォーカス可能なコンテンツが存在するべきです\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"要素内にフォーカス可能な要素は含まれていません\",\n      \"incomplete\": \"フォーカス可能な要素がすぐにフォーカスインジケータを動かすかどうか確認しましょう\",\n      \"fail\": \"フォーカス可能なコンテンツは無効にするか、DOMから削除するべきです\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"要素はフォーカス可能です\",\n      \"fail\": \"要素はフォーカス可能であるべきです\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"モーダルが開いている時に、フォーカス可能な要素はありません\",\n      \"incomplete\": \"現在の状態で、フォーカス可能な要素にタブ移動可能になっていないことを確認しましょう\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"要素がタブ順序にない、またはアクセシブルなテキストが存在します\",\n      \"fail\": \"要素がタブ順序にあり、アクセシブルなテキストが存在しません\",\n      \"incomplete\": \"要素にアクセシブルな名前があるか判定できません\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"要素内にフォーカス不可能な要素は含まれていません\",\n      \"incomplete\": \"フォーカス可能な要素がすぐにフォーカスインジケータを動かすかどうか確認しましょう\",\n      \"fail\": \"フォーカス可能なコンテンツはtabindex=\\\"-1\\\"を指定するか、DOMから削除するべきです\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"要素の子孫にフォーカス可能なものはありません\",\n      \"fail\": \"要素の子孫にフォーカス可能なものがあります\",\n      \"incomplete\": \"要素に子孫があるか判定できませんでした\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role}ランドマークはトップレベルにあります\",\n      \"fail\": \"${data.role}ランドマークは他のランドマークに含まれています\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"要素の子孫にフォーカス可能なものはありません\",\n      \"fail\": {\n        \"default\": \"要素の子孫にフォーカス可能なものがあります\",\n        \"notHidden\": \"インタラクティブなコントロールの内側の要素に負のtabindexを指定しても、（aria-hidden=\\\"true\\\"が指定されている場合も）支援技術がその要素にフォーカスできないようにはなりません\"\n      },\n      \"incomplete\": \"要素に子孫があるか判定できませんでした\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"ページには少なくとも1つのレベル1の見出しがあります\",\n      \"fail\": \"ページにはレベル1の見出しがなければなりません\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"ドキュメントには少なくとも1つのmainランドマークがあります\",\n      \"fail\": \"ドキュメントにmainランドマークがありません\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"ドキュメントにbannerランドマークは複数ありません\",\n      \"fail\": \"ドキュメントに複数のbannerランドマークがあります\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"ドキュメントにcontentinfoランドマークは複数ありません\",\n      \"fail\": \"ドキュメントに複数のcontentinfoランドマークがあります\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"ドキュメントにmainランドマークは複数ありません\",\n      \"fail\": \"ドキュメントに複数のmainランドマークがあります\"\n    },\n    \"tabindex\": {\n      \"pass\": \"要素に0より大きいtabindexは存在しません\",\n      \"fail\": \"要素に0より大きいtabindexが存在します\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"要素に妥当なalt属性値があります\",\n      \"fail\": \"要素にスペース文字のみを含んだalt属性があり、これは必ずしもすべてのスクリーン・リーダーに無視されません\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"要素の既存のテキストと<img>の代替テキストは重複していません\",\n      \"fail\": \"要素が既存のテキストと重複した代替テキストの存在する<img>要素を含んでいます\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"要素に明示的な<label>が存在します\",\n      \"fail\": \"要素に明示的な<label>が存在しません\",\n      \"incomplete\": \"フォームの要素に明示的な <label> があるか判定できませんでした\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"ヘルプテキスト（titleまたはaria-describedby）はラベルのテキストと重複していません\",\n      \"fail\": \"ヘルプテキスト（titleまたはaria-describedby）がラベルのテキストと同じです\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"フォームの要素に視認可能で明示的な<label>があります\",\n      \"fail\": \"フォームの要素に非表示の明示的な<label>があります\",\n      \"incomplete\": \"フォームの要素に明示的で非表示の <label> があるか判定できませんでした\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"要素に暗黙の（包含された）<label>が存在します\",\n      \"fail\": \"要素に暗黙の（包含された）<label>が存在しません\",\n      \"incomplete\": \"フォームの要素に暗黙の（包含された）<label>が存在するか判定できません\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"視認可能なテキストが要素のアクセシブルな名前の一部に含まれています\",\n      \"fail\": \"要素内のテキストがアクセシブルな名前の一部に含まれていません\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"フォームフィールドにlabel要素は複数ありません\",\n      \"incomplete\": \"複数のlabel要素は支援技術に広くサポートされていません。最初のラベルがすべての必要な情報を含んでいることを確認しましょう。\"\n    },\n    \"title-only\": {\n      \"pass\": \"フォーム要素はラベルにtitle属性だけを用いていません\",\n      \"fail\": \"フォーム要素のラベルにtitle属性だけを用いています\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"ランドマークには一意のロール又はロール／ラベル／タイトル (例: アクセシブルな名前) の組み合わせがなければなりません\",\n      \"fail\": \"ランドマークを識別可能にするため、ランドマークには一意のaria-label、aria-labelledby、またはtitleがなければなりません。\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html>要素にlang属性が存在します\",\n      \"fail\": {\n        \"noXHTML\": \"HTMLページでxml:lang属性は有効ではありません、lang属性を使用してください。\",\n        \"noLang\": \"<html>要素にlang属性が指定されていません\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"lang属性の値が有効な言語の一覧に含まれています\",\n      \"fail\": \"lang属性の値が有効な言語の一覧に含まれていません\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"langおよびxml:lang属性に同じ基本言語を指定しています\",\n      \"fail\": \"langおよびxml:lang属性に同じ基本言語を指定していません\"\n    },\n    \"dlitem\": {\n      \"pass\": \"説明リスト項目に<dl>親要素が存在します\",\n      \"fail\": \"説明リスト項目に<dl>親要素が存在しません\"\n    },\n    \"listitem\": {\n      \"pass\": \"リスト項目に<ul>、<ol>、またはrole=\\\"list\\\"の親要素が存在します\",\n      \"fail\": {\n        \"default\": \"リスト項目に <ul>、<ol> 親要素が存在しません\",\n        \"roleNotValid\": \"リスト項目の親要素に、role=\\\"list\\\"以外のロールが指定されています\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"dl要素内には許可されている直接の子要素、<dt>、<dd>および<div>要素のみが存在します\",\n      \"fail\": \"dl要素内に許可されていない直接の子要素が存在します: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"リスト要素内には許可されている直接の子要素、<li>要素のみが存在します\",\n      \"fail\": \"リスト要素内に許可されていない直接の子要素が存在します: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"空ではない場合、要素に<dt>および<dd>要素の両方が存在します\",\n      \"fail\": \"空ではない場合、要素に少なくとも1つの<dt>要素に続く、少なくとも1つの<dd>要素が存在しません\"\n    },\n    \"caption\": {\n      \"pass\": \"マルチメディア要素にキャプショントラックが存在します\",\n      \"incomplete\": \"要素にキャプションが存在することを確認しましょう\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"iframeはaxe-coreでテストされました\",\n      \"fail\": \"iframeはaxe-coreでテストできませんでした\",\n      \"incomplete\": \"iframeはまだaxe-coreでテストする必要があります\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> または <audio> は許可された時間より長く音声を出力しない、もしくはコントロールするメカニズムを持っています\",\n      \"fail\": \"<video> または <audio> は許可された時間より長く音声を出力し、かつコントロールするメカニズムを持っていません\",\n      \"incomplete\": \"<video> または <audio> が許可された時間より長く音声を出力しない、またはコントロールするメカニズムを提供していることを確認しましょう\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"ディスプレイは操作可能で、向きは固定されていません\",\n      \"fail\": \"CSSにより向きが固定されており、ディスプレイ操作を不可能にしています\",\n      \"incomplete\": \"CSSで向きが固定されているかどうか特定できません\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta>タグはモバイルデバイスでの大幅な拡大を阻止しません\",\n      \"fail\": \"<meta>タグはモバイルデバイスでの拡大を制限します\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta>タグはモバイルデバイスでの拡大を無効にしません\",\n      \"fail\": \"<meta>タグの${data}がモバイルデバイスでの拡大を無効にします\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"ターゲットは最も近い隣接する要素との間に充分な大きさの空白があります。クリッカブルな領域として十分な大きさは、${data.minOffset}px以上で直径${data.closestOffset}pxです。\",\n        \"large\": \"ターゲットの大きさは、最低${data.minOffset}pxの基準を大きく上回っています。\"\n      },\n      \"fail\": \"ターゲットに最も近い隣接要素との間の空白の大きさが不十分です。クリッカブルな領域として十分な大きさは、${data.minOffset}px以上ではなく、直径${data.closestOffset}pxです。\",\n      \"incomplete\": {\n        \"default\": \"tabindexが負の要素において、最も近い隣接要素との間の空白の大きさが不十分です。クリッカブルな領域として十分な大きさは、${data.minOffset}px以上ではなく、直径${data.closestOffset}pxです。これがターゲットであるか確認しましょう。\",\n        \"nonTabbableNeighbor\": \"ターゲットに最も近い隣接要素との間の空白の大きさが不十分です。クリッカブルな領域として十分な大きさは、${data.minOffset}px以上ではなく、直径${data.closestOffset}pxです。隣接要素がターゲットであるか確認しましょう。\",\n        \"tooManyRects\": \"重複する要素が多すぎるため、ターゲットの大きさを取得できません。\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"コントロールには十分なサイズがあります (${data.width}px x ${data.height}pxであり、${data.minSize}px x ${data.minSize}px以上です)\",\n        \"obscured\": \"コントロールは完全に隠されていてクリックできないため無視されます\",\n        \"large\": \"ターゲットの大きさは、最低${data.minSize}pxの基準を大きく上回っています。\"\n      },\n      \"fail\": {\n        \"default\": \"ターゲットのサイズが不十分です (${data.width}px x ${data.height}pxですが、${data.minSize}px x ${data.minSize}px以上でなければなりません)\",\n        \"partiallyObscured\": \"ターゲットは部分的に隠れているためサイズが不十分です (最小スペースは${data.width}px x ${data.height}pxですが、少なくとも${data.minSize}px x ${data.minSize}pxでなければなりません)\"\n      },\n      \"incomplete\": {\n        \"default\": \"tabindexが負の要素のサイズが不十分です (${data.width}px x ${data.height}pxですが、${data.minSize}px x ${data.minSize}px以上でなければなりません)。これがターゲットであるか確認しましょう\",\n        \"contentOverflow\": \"コンテンツがオーバーフローしたため、要素のサイズを正確に決定できませんでした\",\n        \"partiallyObscured\": \"tabindexが負の要素は、部分的に隠れているためサイズが不十分です (最小のスペースは${data.width}px x ${data.height}pxですが、${data.minSize}px x ${data.minSize}px以上でなければなりません)。これがターゲットであるか確認しましょう\",\n        \"partiallyObscuredNonTabbable\": \"ターゲットのサイズが不十分です。これはtabindexが負の隣接オブジェクトによって部分的に隠されているためです (最小のスペースは${data.width}px x ${data.height}pxですが、${data.minSize}px x ${data.minSize}px以上でなければなりません)。隣接要素がターゲットであるか確認しましょう\",\n        \"tooManyRects\": \"重複する要素が多すぎるため、ターゲットの大きさを取得できません。\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"ページに見出しが存在します\",\n      \"fail\": \"ページに見出しが存在しません\"\n    },\n    \"heading-order\": {\n      \"pass\": \"見出しの順序が有効です\",\n      \"fail\": \"見出しの順序が無効です\",\n      \"incomplete\": \"1つ前の見出しを特定できません\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"同じ名前で異なるURLに遷移する他のリンクはありません\",\n      \"incomplete\": \"リンクが同じ目的を持っていること、または意図的に不明瞭になっていることを確認しましょう\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"有効なスキップリンクが存在します\",\n      \"fail\": \"有効なスキップリンクが存在しません\"\n    },\n    \"landmark\": {\n      \"pass\": \"ページにランドマーク領域が存在します\",\n      \"fail\": \"ページにランドマーク領域が存在しません\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"<meta>タグはすぐにページを更新しません\",\n      \"fail\": \"<meta>タグは一定時間経過後に強制的にページを更新します\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta>タグはすぐにページを更新しません\",\n      \"fail\": \"<meta>タグは一定時間経過後に強制的にページを更新します (20時間未満)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p>要素が見出しとしてスタイル付けされていません\",\n      \"fail\": \"スタイル付けされた<p>要素ではなく、見出し要素を使用すべきです\",\n      \"incomplete\": \"<p> 要素に見出しのスタイル付けがされているかどうか判定できません\"\n    },\n    \"region\": {\n      \"pass\": \"ページのすべてのコンテンツがランドマークに含まれています\",\n      \"fail\": \"ページの一部のコンテンツがランドマークに含まれていません\"\n    },\n    \"skip-link\": {\n      \"pass\": \"スキップリンクのターゲットが存在します\",\n      \"incomplete\": \"スキップリンクのターゲットは活性化された時に表示されるべきです\",\n      \"fail\": \"スキップリンクのターゲットがありません\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"要素のtitle属性が一意です\",\n      \"fail\": \"要素のtitle属性が一意ではありません\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"ドキュメントに同じid属性を持つ有効な要素はありません\",\n      \"fail\": \"ドキュメントに同じid属性を持つ有効な要素があります: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"ARIAまたはラベルが参照している要素で、同じid属性を持つものはありません\",\n      \"fail\": \"ARIAが参照している要素で、複数の要素で同じid属性を持つものがあります: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"ドキュメントにid属性が同じ静的な要素はありません\",\n      \"fail\": \"ドキュメントにid属性が同じ静的な要素が複数存在します\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label属性が存在し、空ではありません\",\n      \"fail\": \"aria-label属性が存在しない、または空です\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby属性が存在し、スクリーン・リーダーに認識可能な要素を参照しています\",\n      \"fail\": \"aria-labelledby属性が存在しない、存在しない要素を参照している、または空の要素を参照しています\",\n      \"incomplete\": \"aria-labelledby属性が、存在する要素を参照していることを確認しましょう\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"テキストの間隔に影響する'!important'のついたインラインスタイルは指定されていません\",\n      \"fail\": {\n        \"singular\": \"ほとんどのブラウザーで上書きすることはサポートされていないため、インラインスタイル${data.values}から'!important'を削除します\",\n        \"plural\": \"ほとんどのブラウザーで上書きすることはサポートされていないため、インラインスタイル${data.values}から'!important'を削除します\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"要素内にスクリーン・リーダーが認識可能なテキストが存在します\",\n      \"fail\": \"要素内にスクリーン・リーダーが認識可能なテキストが存在しません\",\n      \"incomplete\": \"要素に子ノードがあるか判定できません\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"ドキュメントに空ではない<title>要素が存在します\",\n      \"fail\": \"ドキュメントに空ではない<title>要素が存在しません\"\n    },\n    \"exists\": {\n      \"pass\": \"要素は存在しません\",\n      \"incomplete\": \"要素が存在します\"\n    },\n    \"has-alt\": {\n      \"pass\": \"要素にalt属性が指定されています\",\n      \"fail\": \"要素にalt属性が指定されていません\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"要素にスクリーン・リーダーが認識可能なテキストが存在します\",\n      \"fail\": \"要素にスクリーン・リーダーが認識可能なテキストが存在しません\",\n      \"incomplete\": \"要素に子ノードがあるか判定できません\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"style属性中のletter-spacingに'!important'が指定されていない、または最低条件を満たしています\",\n      \"fail\": \"style属性中のletter-spacingには'!important'を指定しない、または${data.minValue}em以上（現在は${data.value}em）にしてください\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"style属性中のline-heightに'!important'が指定されていない、または最低条件を満たしています\",\n      \"fail\": \"style属性中のline-heightには'!important'を指定しない、または${data.minValue}em以上（現在は${data.value}em）にしてください\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"style属性中のword-spacingに'!important'が指定されていない、または最低条件を満たしています\",\n      \"fail\": \"style属性中のword-spacingには'!important'を指定しない、または${data.minValue}em以上（現在は${data.value}em）にしてください\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"要素が表示されていません\",\n      \"fail\": \"要素が表示されています\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"要素に空ではないalt属性が指定されています\",\n      \"fail\": {\n        \"noAttr\": \"要素にalt属性が指定されていません\",\n        \"emptyAttr\": \"要素に空のalt属性が指定されています\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"要素にvalue属性が指定されていません\",\n        \"has-label\": \"要素に空ではないvalue属性が指定されています\"\n      },\n      \"fail\": \"要素にvalue属性が指定されていますが、value属性が空です\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"要素にplaceholder属性が指定されています\",\n      \"fail\": {\n        \"noAttr\": \"要素にplaceholder属性が指定されていません\",\n        \"emptyAttr\": \"要素に空のplaceholder属性が指定されています\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"要素にtitle属性が指定されています\",\n      \"fail\": {\n        \"noAttr\": \"要素にtitle属性が指定されていません\",\n        \"emptyAttr\": \"要素に空のtitle属性が指定されています\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"要素に空ではないvalue属性が指定されています\",\n      \"fail\": {\n        \"noAttr\": \"要素にvalue属性が指定されていません\",\n        \"emptyAttr\": \"要素に空のvalue属性が指定されています\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"要素のデフォルトのセマンティクスはrole=\\\"${data.role}\\\"で上書きされました\",\n      \"fail\": {\n        \"default\": \"要素のデフォルトのセマンティクスはrole=\\\"none\\\"またはrole=\\\"presentation\\\"で上書きされませんでした\",\n        \"globalAria\": \"要素にはグローバルなARIA属性が指定されているため、role=\\\"none\\\"またはrole=\\\"presentation\\\"にはなりません\",\n        \"focusable\": \"要素がフォーカス可能なため、role=\\\"none\\\"またはrole=\\\"presentation\\\"にはなりません\",\n        \"both\": \"要素にはグローバルなARIA属性が指定されており、フォーカス可能なため、role=\\\"none\\\"またはrole=\\\"presentation\\\"にはなりません\",\n        \"iframe\": \"プレゼンテーション用のロールを指定した${data.nodeName}要素に'title'属性を使用すると、スクリーンリーダー間で動作が一貫しません\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"要素のデフォルトのセマンティクスがrole=\\\"none\\\"で上書きされました\",\n      \"fail\": \"要素のデフォルトのセマンティクスはrole=\\\"none\\\"で上書きされませんでした\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"要素のデフォルトのセマンティクスがrole=\\\"presentation\\\"で上書きされました\",\n      \"fail\": \"要素のデフォルトのセマンティクスはrole=\\\"presentation\\\"で上書きされませんでした\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"要素にタイトルを示す子要素が存在します\",\n      \"fail\": {\n        \"noTitle\": \"要素にタイトルを示す子要素が存在しません\",\n        \"emptyTitle\": \"子要素のtitle要素が空です\"\n      },\n      \"incomplete\": \"要素にタイトルを示す子要素が存在するか判定できません\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"テーブルの1行目がキャプションとして使用されていません\",\n      \"fail\": \"テーブルの最初の子はテーブルセルではなく、キャプションであるべきです\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"scope属性がテーブルヘッダー要素（<th>）にのみ使用されています\",\n      \"fail\": \"HTML5では、scope属性はテーブルヘッダー要素（<th>）にのみ使用できます\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"summary属性および<caption>のコンテンツは重複していません\",\n      \"fail\": \"summary属性および<caption>のコンテンツが同一です\",\n      \"incomplete\": \"<table>にキャプションがあるかどうかを確認できません\"\n    },\n    \"scope-value\": {\n      \"pass\": \"scope属性は正しく使用されています\",\n      \"fail\": \"scope属性の値は'row'または'col'のみです\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"すべての空ではないデータセルにテーブルヘッダーが存在します\",\n      \"fail\": \"いくつかの空ではないデータセルにテーブルヘッダーが存在しません\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"headers属性はテーブル内の他のヘッダーセルを参照するためだけに使用されています\",\n      \"incomplete\": \"headers属性が空です\",\n      \"fail\": {\n        \"cell-header-not-in-table\": \"headers属性がテーブル内の他のヘッダーセルのみを参照するために使用されていません\",\n        \"cell-header-not-th\": \"headers属性はデータセルではなく、ヘッダーセルを参照しなければなりません\",\n        \"header-refs-self\": \"headers属性を持つ要素が自分自身を参照しています\"\n      }\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"すべてのテーブルヘッダーセルがデータセルを参照しています\",\n      \"fail\": \"一部のテーブルヘッダーセルがデータセルを参照していません\",\n      \"incomplete\": \"テーブルデータセルが存在しないまたは空です\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"ページ内のすべてのコンテンツが分析されました\",\n      \"fail\": \"このページのコンテンツを分析中に問題が発生しました\",\n      \"incomplete\": \"ページ内に分析されなかった非表示のコンテンツが存在します。分析するには、そのコンテンツを表示させる必要があります\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"次のいずれかを修正します:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"次のすべてを修正します:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axeは原因を特定できませんでした。要素のインスペクターを使いましょう!\"\n}\n"
  },
  {
    "path": "locales/ko.json",
    "content": "{\n  \"lang\": \"ko\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"모든 accesskey 어트리뷰트 값이 고유한지 확인하세요.\",\n      \"help\": \"accesskey 어트리뷰트 값은 고유해야 합니다.\"\n    },\n    \"area-alt\": {\n      \"description\": \"이미지 맵의 <area> 엘리먼트가 대체텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"활성 <area> 엘리먼트는 반드시 대체텍스트를 가져야 합니다.\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"ARIA 어트리뷰트가 엘리먼트의 역할(role)에 허용되었는지 확인하세요.\",\n      \"help\": \"엘리먼트는 반드시 허용된 ARIA 어트리뷰트만 사용해야 합니다.\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"역할(role) 어트리뷰트가 엘리먼트에 적절한 값을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA 역할(role)은 엘리먼트에 적절해야 합니다.\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"모든 ARIA 버튼, 링크, 메뉴 아이템이 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA 명령 엘리먼트에는 반드시 접근 가능한 이름이 있어야 합니다.\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"모든 ARIA dialog와 alertdialog 노드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA dialog와 alertdialog 노드는 접근 가능한 이름을 가져야 합니다.\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"문서 body에 aria-hidden='true'가 없게 하세요.\",\n      \"help\": \"aria-hidden='true'는 반드시 문서 body에 없어야 합니다.\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"aria-hidden 엘리먼트가 초점을 얻을 수 있는(focusable) 엘리먼트를 포함하지 않도록 하세요.\",\n      \"help\": \"ARIA hidden 엘리먼트는 반드시 초점을 얻을 수 있는(focusable) 엘리먼트를 포함하지 않아야 합니다.\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"모든 ARIA 입력 필드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA 입력 필드에는 반드시 접근 가능한 이름이 있어야 합니다.\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"모든 ARIA meter 노드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA meter 노드에는 반드시 접근 가능한 이름이 있어야 합니다.\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"모든 ARIA progressbar 노드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA progressbar 노드에는 반드시 접근 가능한 이름이 있어야 합니다.\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"ARIA 역할(role)을 가진 엘리먼트가 필수 ARIA 어트리뷰트를 모두 가지고 있는지 확인하세요\",\n      \"help\": \"필수 ARIA 어트리뷰트는 반드시 제공되어야 합니다.\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"하위 역할(child role)이 필요한 ARIA 역할(role)을 가진 엘리먼트가 해당 역할(role)을 포함하고 있는지 확인하세요.\",\n      \"help\": \"일부 ARIA 역할(role)은 반드시 특정한 하위 항목들을 포함해야 합니다.\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"상위 역할(parent role)이 필요한 ARIA 역할(role)을 가진 엘리먼트가 해당 역할(role)에 포함되어 있는지 확인하세요.\",\n      \"help\": \"일부 ARIA 역할(role)은 반드시 특정한 상위 항목들에 포함되어야 합니다.\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"aria-roledescription이 암묵적 혹은 명시적 역할(role)을 가진 엘리먼트에만 사용되었는지 확인하세요.\",\n      \"help\": \"aria-roledescription은 의미론적 역할(role)을 가진 엘리먼트에 사용하세요.\"\n    },\n    \"aria-roles\": {\n      \"description\": \"역할(role) 어트리뷰트를 가진 모든 엘리먼트가 유효한 값을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA 역할(role)은 반드시 유효한 값을 준수해야 합니다.\"\n    },\n    \"aria-text\": {\n      \"description\": \"\\\"role=text\\\"가 초점을 얻을 수 있는(focusable) 후손을 가지지 않는 엘리먼트에 사용되었는지 확인하세요.\",\n      \"help\": \"\\\"role=text\\\"는 초점을 얻을 수 있는(focusable) 후손을 가지지 않아야 합니다.\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"모든 ARIA toggle 필드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA toggle 필드는 접근 가능한 이름을 가져야 합니다.\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"모든 ARIA tooltip 노드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA tooltip 노드는 반드시 접근 가능한 이름을 가져야 합니다.\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"모든 ARIA treeitem 노드가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA treeitem 노드는 접근 가능한 이름을 가져야 합니다.\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"모든 ARIA 어트리뷰트가 유효한 값을 가지고 있는지 확인하세요.\",\n      \"help\": \"ARIA 어트리뷰트는 반드시 유효한 값을 준수해야 합니다.\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"aria- 로 시작하는 어트리뷰트가 유효한 ARIA 어트리뷰트인지 확인하세요.\",\n      \"help\": \"ARIA 어트리뷰트는 반드시 유효한 이름을 준수해야 합니다.\"\n    },\n    \"audio-caption\": {\n      \"description\": \"<audio> 엘리먼트가 캡션(자막)을 가지고 있는지 확인하세요.\",\n      \"help\": \"<audio> 엘리먼트는 반드시 캡션(자막) 트랙을 가져야 합니다.\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"autocomplete 어트리뷰트가 올바르고 form 필드에 적합한지 확인하세요.\",\n      \"help\": \"autocomplete 어트리뷰트는 반드시 올바르게 사용되어야 합니다.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"style 어트리뷰트를 통해 설정된 텍스트 간격이 사용자 정의 스타일시트를 통해 조정될 수 있는지 확인하세요.\",\n      \"help\": \"인라인 텍스트 간격은 반드시 사용자 정의 스타일시트로 조정 될 수 있어야 합니다.\"\n    },\n    \"blink\": {\n      \"description\": \"<blink> 엘리먼트가 사용되지 않도록 하세요.\",\n      \"help\": \"<blink> 엘리먼트는 더 이상 사용되지 않으며 반드시 사용되지 않아야 합니다.\"\n    },\n    \"button-name\": {\n      \"description\": \"버튼이 인식 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"버튼에 반드시 인식 가능한 텍스트가 있어야 합니다.\"\n    },\n    \"bypass\": {\n      \"description\": \"각 페이지에 사용자가 내비게이션을 건너뛰고 콘텐츠로 바로 이동할 수 있는 최소 하나의 메커니즘이 있는지 확인하세요.\",\n      \"help\": \"페이지에는 반드시 반복 되는 블럭을 건너 뛸 수 있는 수단이 있어야 합니다.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"전경색과 배경색 사이의 대비가 WCAG 2 AAA 명암비 기준치를 충족하는지 확인하세요.\",\n      \"help\": \"엘리먼트는 반드시 충분한 명도 대비를 가져야 합니다.\"\n    },\n    \"color-contrast\": {\n      \"description\": \"전경색과 배경색 사이의 대비가 WCAG 2 AA 명암비 기준치를 충족하는지 확인하세요.\",\n      \"help\": \"엘리먼트는 반드시 충분한 명도 대비를 가져야 합니다.\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"콘텐츠가 특정 디스플레이 방향으로 고정되지 않고, 콘텐츠가 모든 디스플레이 방향에서 사용 가능한지 확인하세요.\",\n      \"help\": \"CSS 미디어쿼리가 디스플레이 방향을 고정하기 위해 사용되지 않아야 합니다.\"\n    },\n    \"definition-list\": {\n      \"description\": \"<dl> 엘리먼트가 올바르게 구조화되어 있는지 확인하세요.\",\n      \"help\": \"<dl> 엘리먼트는 반드시 올바르게 정렬된 <dt>와 <dd> 그룹, <script>, <template>, <div> 엘리먼트만 바로 포함해야 합니다.\"\n    },\n    \"dlitem\": {\n      \"description\": \"<dt>와 <dd> 엘리먼트가 <dl>에 포함되어 있는지 확인하세요.\",\n      \"help\": \"<dt>와 <dd> 엘리먼트는 반드시 <dl>에 포함되어야 합니다.\"\n    },\n    \"document-title\": {\n      \"description\": \"각 HTML 문서가 비어 있지 않은 <title> 엘리먼트를 포함하고 있는지 확인하세요.\",\n      \"help\": \"탐색에 도움이 되도록 문서에는 반드시 <title>이 있어야 합니다.\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"활성 엘리먼트의 모든 id 어트리뷰트 값이 고유한지 확인하세요.\",\n      \"help\": \"활성 엘리먼트의 ID는 반드시 고유해야 합니다.\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"ARIA 및 label에 사용된 모든 id 어트리뷰트 값이 고유한지 확인하세요.\",\n      \"help\": \"ARIA 및 label에 사용된 ID는 반드시 고유해야 합니다.\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"모든 id 어트리뷰트 값이 고유한지 확인하세요.\",\n      \"help\": \"id 어트리뷰트 값은 반드시 고유해야 합니다.\"\n    },\n    \"empty-heading\": {\n      \"description\": \"제목이 인식 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"제목은 비어있지 않아야 합니다.\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"테이블 헤더가 인식 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"테이블 헤더 텍스트는 반드시 비어있지 않아야 합니다.\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"초점 순서(focus order)에 있는 엘리먼트가 적절한 역할(role)을 가지고 있는지 확인하세요.\",\n      \"help\": \"초점 순서(focus order)에 있는 엘리먼트는 대화형 콘텐츠(interactive contents)에 적합한 역할(role)이 필요합니다.\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"form 필드가 여러 개의 레이블 엘리먼트를 가지지 않도록 하세요.\",\n      \"help\": \"form 필드는 반드시 여러 개의 레이블 엘리먼트를 가지지 않아야 합니다.\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"초점을 얻을 수 있는(focusable) 콘텐츠를 가진 <frame>과 <iframe> 엘리먼트에 tabindex=-1이 없게 하세요.\",\n      \"help\": \"초점을 얻을 수 있는(focusable) 콘텐츠를 가진 프레임에는 반드시 tabindex=-1 이 없어야 합니다.\"\n    },\n    \"frame-tested\": {\n      \"description\": \"<iframe>과 <frame> 엘리먼트가 axe-core 스크립트를 포함하고 있는지 확인하세요.\",\n      \"help\": \"프레임이 axe-core로 테스트되어야 합니다.\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"<iframe>과 <frame> 엘리먼트가 고유한 title 어트리뷰트를 포함하고 있는지 확인하세요.\",\n      \"help\": \"프레임에는 고유한 title 어트리뷰트가 있어야 합니다.\"\n    },\n    \"frame-title\": {\n      \"description\": \"<iframe>과 <frame> 엘리먼트가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"프레임에는 반드시 접근 가능한 이름이 있어야 합니다.\"\n    },\n    \"heading-order\": {\n      \"description\": \"제목 순서가 의미론적으로 올바른지 확인하세요.\",\n      \"help\": \"제목 수준은 한 단계씩 증가해야만 합니다.\"\n    },\n    \"hidden-content\": {\n      \"description\": \"숨겨진 콘텐츠에 대해 사용자에게 알리세요.\",\n      \"help\": \"페이지의 숨겨진 콘텐츠는 분석 될 수 없습니다.\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"모든 HTML 문서가 lang 어트리뷰트를 가지고 있는지 확인하세요.\",\n      \"help\": \"<html> 엘리먼트는 반드시 lang 어트리뷰트를 가져야 합니다.\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"<html> 엘리먼트의 lang 어트리뷰트가 유효한 값을 가지고 있는지 확인하세요.\",\n      \"help\": \"<html> 엘리먼트는 반드시 lang 어트리뷰트에 유효한 값을 가져야 합니다.\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"유효한 lang과 xml:lang 어트리뷰트를 가진 HTML 엘리먼트가 페이지의 기본 언어와 일치하는지 확인하세요.\",\n      \"help\": \"lang과 xml:lang을 가진 HTML 엘리먼트는 반드시 동일한 기본 언어를 가져야 합니다.\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"동일한 접근 가능한 이름을 가지는 링크가 비슷한 용도로 제공되고 있는지 확인하세요.\",\n      \"help\": \"동일한 이름을 가진 링크는 비슷한 용도를 가집니다.\"\n    },\n    \"image-alt\": {\n      \"description\": \"<img> 엘리먼트가 대체텍스트를 가지고 있거나 none 또는 presentation 역할(role)을 가지고 있는지 확인하세요.\",\n      \"help\": \"이미지는 반드시 대체텍스트를 가져야 합니다.\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"이미지 대체텍스트가 텍스트와 반복되지 않도록 하세요.\",\n      \"help\": \"이미지의 대체텍스트는 텍스트와 반복되지 않아야 합니다.\"\n    },\n    \"input-button-name\": {\n      \"description\": \"입력 버튼이 인식 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"입력 버튼은 반드시 인식 가능한 텍스트를 가져야 합니다.\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"<input type=\\\"image\\\"> 엘리먼트가 대체텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"이미지 버튼은 반드시 대체텍스트를 가져야 합니다.\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"콘텐츠로부터 레이블이 지정되는 엘리먼트가 접근 가능한 이름의 일부로 눈에 보이는 텍스트를 반드시 가지도록 하세요.\",\n      \"help\": \"엘리먼트는 반드시 접근 가능한 이름의 일부로 눈에 보이는 텍스트를 가져야 합니다.\"\n    },\n    \"label-title-only\": {\n      \"description\": \"모든 form 엘리먼트가 title이나 aria-describedby 어트리뷰트를 단독으로 사용하여 레이블이 지정되지 않도록 하세요.\",\n      \"help\": \"form 엘리먼트는 눈에 보이는 레이블을 가져야 합니다.\"\n    },\n    \"label\": {\n      \"description\": \"모든 form 엘리먼트가 레이블을 가지고 있는지 확인하세요.\",\n      \"help\": \"form 엘리먼트는 반드시 레이블을 가져야 합니다.\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"banner 랜드마크가 최상위 레벨에 있는지 확인하세요.\",\n      \"help\": \"banner 랜드마크는 다른 랜드마크 안에 포함되지 않아야 합니다.\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"complementary나 aside 랜드마크가 최상위 레벨에 있는지 확인하세요.\",\n      \"help\": \"aside는 다른 랜드마크 안에 포함되지 않아야 합니다.\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"contentinfo 랜드마크가 최상위 레벨에 있는지 확인하세요.\",\n      \"help\": \"contentinfo 랜드마크는 다른 랜드마크 안에 포함되지 않아야 합니다.\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"main 랜드마크가 최상위 레벨에 있는지 확인하세요.\",\n      \"help\": \"main 랜드마크는 다른 랜드마크 안에 포함되지 않아야 합니다.\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"문서가 최대 하나의 banner 랜드마크를 가지고 있는지 확인하세요.\",\n      \"help\": \"문서는 하나를 초과하는 banner 랜드마크를 가지지 않아야 합니다.\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"문서가 최대 하나의 contentinfo 랜드마크를 가지고 있는지 확인하세요.\",\n      \"help\": \"문서는 하나를 초과하는 contentinfo 랜드마크를 가지지 않아야 합니다.\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"문서가 최대 하나의 main 랜드마크를 가지고 있는지 확인하세요.\",\n      \"help\": \"문서는 하나를 초과하는 main 랜드마크를 가지지 않아야 합니다.\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"문서가 main 랜드마크를 가지고 있는지 확인하세요.\",\n      \"help\": \"문서는 하나의 main 랜드마크를 가져야 합니다.\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"랜드마크가 고유한지 확인하세요.\",\n      \"description\": \"랜드마크는 고유한 역할(role) 또는 역할(role)/레이블/제목 조합(즉, 접근 가능한 이름)을 가져야 합니다.\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"링크가 색상에 의존하지 않고 구별 될 수 있어야 합니다.\",\n      \"help\": \"링크는 반드시 색상에 의존하지 않는 방식으로 주변 텍스트로부터 구별되어야 합니다.\"\n    },\n    \"link-name\": {\n      \"description\": \"링크가 인식 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"링크는 반드시 인식 가능한 텍스트를 가져야 합니다.\"\n    },\n    \"list\": {\n      \"description\": \"목록이 올바르게 구조화되어 있는지 확인하세요.\",\n      \"help\": \"<ul>과 <ol>은 반드시 <li>, <script> 또는 <template> 엘리먼트만을 바로 포함해야 합니다.\"\n    },\n    \"listitem\": {\n      \"description\": \"<li> 엘리먼트가 의미론적으로 사용되었는지 확인하세요.\",\n      \"help\": \"<li> 엘리먼트는 반드시 <ul>이나 <ol>에 포함되어야 합니다.\"\n    },\n    \"marquee\": {\n      \"description\": \"<marquee> 엘리먼트가 사용되지 않도록 하세요.\",\n      \"help\": \"<marquee> 엘리먼트는 더 이상 사용되지 않으며 반드시 사용되지 않아야 합니다.\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"<meta http-equiv=\\\"refresh\\\">가 사용되지 않도록 하세요.\",\n      \"help\": \"시간 제한 새로고침은 존재하지 않아야 합니다.\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"<meta name=\\\"viewport\\\">가 크기를 조정할 수 있는지 확인하세요.\",\n      \"help\": \"사용자는 텍스트를 최대 500%까지 확대/축소 또는 크기를 조정할 수 있어야 합니다.\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"<meta name=\\\"viewport\\\">가 텍스트 크기 조절 및 확대/축소를 비활성화되지 않게 하세요.\",\n      \"help\": \"확대/축소 및 크기 조정은 비활성화되지 않아야 합니다.\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"중첩된 대화형 컨트롤은 스크린리더에 의해 낭독되지 않습니다.\",\n      \"help\": \"대화형 컨트롤이 중첩되지 않게 하세요.\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"<video>나 <audio> 엘리먼트가 중지 시키거나 오디오를 음소거하는 제어 메커니즘 없이 3초를 초과하여 오디오를 자동 재생하지 않게 하세요.\",\n      \"help\": \"<video>나 <audio> 엘리먼트는 오디오를 자동 재생하지 않아야 합니다.\"\n    },\n    \"object-alt\": {\n      \"description\": \"<object> 엘리먼트가 대체텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"<object> 엘리먼트는 반드시 대체텍스트를 가져야 합니다.\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"p 엘리먼트를 스타일링하여 제목으로 사용되지 않도록 하세요.\",\n      \"help\": \"굵은 텍스트, 기울임 꼴, 글꼴 크기를 p 엘리먼트를 제목으로 스타일링하는데 사용하지 않아야 합니다.\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"페이지 또는 프레임 중 최소 하나의 프레임이 1 레벨 제목을 포함하고 있는지 확인하세요.\",\n      \"help\": \"페이지는 1 레벨 제목을 포함해야 합니다.\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"역할(role)이 none이거나 presentation이고 역할(role) 충돌 해결이 필요한 엘리먼트를 표시해 두세요.\",\n      \"help\": \"none이나 presentation 역할(role) 엘리먼트가 표시되어야 합니다.\"\n    },\n    \"region\": {\n      \"description\": \"모든 페이지 콘텐츠가 랜드마크에 포함되어 있는지 확인하세요.\",\n      \"help\": \"모든 페이지 콘텐츠는 랜드마크에 포함되어야 합니다.\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"[role='img'] 엘리먼트가 대체텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"[role='img'] 엘리먼트는 대체텍스트를 가저야 합니다.\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"scope 어트리뷰트가 테이블에 올바르게 사용되고 있는지 확인하세요.\",\n      \"help\": \"scope 어트리뷰트는 올바르게 사용되어야 합니다.\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"스크롤 가능한 콘텐츠를 가진 엘리먼트는 반드시 키보드로 접근 가능해야 합니다.\",\n      \"help\": \"스크롤 가능한 영역이 키보드 접근을 가져야 합니다.\"\n    },\n    \"select-name\": {\n      \"description\": \"select 엘리먼트가 접근 가능한 이름을 가지고 있는지 확인하세요.\",\n      \"help\": \"select 엘리먼트는 반드시 접근 가능한 이름을 가져야 합니다.\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"서버 사이드 이미지 맵이 사용되지 않도록 하세요.\",\n      \"help\": \"서버 사이드 이미지 맵은 반드시 사용되지 않아야 합니다.\"\n    },\n    \"skip-link\": {\n      \"description\": \"모든 건너뛰기 링크가 초점을 얻을 수 있는(focusable) 대상을 가지는지 확인하세요.\",\n      \"help\": \"건너뛰기 링크 대상이 존재하고 초점을 얻을 수 있어야(focusable) 합니다.\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"이미지, 그래픽 문서, 그래픽 심볼 역할(role)을 가진 svg 엘리먼트가 접근 가능한 텍스트를 가지고 있는지 확인하세요.\",\n      \"help\": \"img 역할(role)을 가진 svg 엘리먼트는 접근 가능한 텍스트를 가져야 합니다.\"\n    },\n    \"tabindex\": {\n      \"description\": \"tabindex 어트리뷰트 값이 0보다 크지 않게 하세요.\",\n      \"help\": \"엘리먼트는 0보다 큰 tabindex를 가지지 않아야 합니다.\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"테이블이 동일한 summary와 caption을 가지지 않게 하세요.\",\n      \"help\": \"<caption> 엘리먼트는 summary 어트리뷰트와 동일한 텍스트를 포함하지 않아야 합니다.\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"캡션이 있는 테이블이 <caption> 엘리먼트를 사용하고 있는지 확인하세요.\",\n      \"help\": \"데이터 테이블에 캡션을 제공하는데 데이터 셀이나 헤더 셀이 반드시 사용되지 않아야 합니다.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"큰 테이블의 비어 있지 않은 각 데이터 셀이 하나 또는 그 이상의 테이블 헤더를 가지고 있는지 확인하세요.\",\n      \"help\": \"3x3보다 큰 테이블에서 모든 비어있지 않은 td 엘리먼트는 반드시 연관된 테이블 헤더를 가져야 합니다.\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"headers를 사용하는 테이블의 각 셀이 그 테이블의 다른 셀을 참조하고 있는지 확인하세요.\",\n      \"help\": \"headers 어트리뷰트를 사용하는 table 엘리먼트의 모든 셀은 반드시 그 동일한 테이블의 다른 셀만 참조해야 합니다.\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"데이터 테이블의 각 테이블 헤더가 데이터 셀을 참조하고 있는지 확인하세요.\",\n      \"help\": \"모든 th 엘리먼트와 role=columnheader/rowheader를 가진 엘리먼트는 반드시 그것들이 설명하는 데이터 셀을 가져야 합니다.\"\n    },\n    \"valid-lang\": {\n      \"description\": \"lang 어트리뷰트가 유효한 값을 가지고 있는지 확인하세요.\",\n      \"help\": \"lang 어트리뷰트는 반드시 유효한 값을 가져야 합니다.\"\n    },\n    \"video-caption\": {\n      \"description\": \"<video> 엘리먼트가 캡션(자막)을 가지고 있는지 확인하세요.\",\n      \"help\": \"<video> 엘리먼트는 반드시 캡션(자막)을 가져야 합니다.\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"abstract 역할(role)이 사용되지 않았습니다.\",\n      \"fail\": {\n        \"singular\": \"abstract 역할(role)은 직접 사용될 수 없습니다: ${data.values}\",\n        \"plural\": \"abstract 역할(role)들은 직접 사용될 수 없습니다: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA 어트리뷰트가 정의된 역할(role)에 대해 올바르게 사용되었습니다.\",\n      \"fail\": {\n        \"singular\": \"ARIA 어트리뷰트가 허용되지 않았습니다: ${data.values}\",\n        \"plural\": \"ARIA 어트리뷰트들이 허용되지 않았습니다: ${data.values}\"\n      },\n      \"incomplete\": \"이 엘리먼트에 ARIA 어트리뷰트가 무시되어도 문제가 없는지 확안하세요: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA 역할(role)이 주어진 엘리먼트에 허용되었습니다.\",\n      \"fail\": {\n        \"singular\": \"ARIA 역할(role) ${data.values}이(가) 주어진 엘리먼트에 허용되지 않았습니다.\",\n        \"plural\": \"ARIA 역할(role)들 ${data.values}이(가) 주어진 엘리먼트에 허용되지 않았습니다.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"엘리먼트에 허용되지 않았기 때문에, 엘리먼트가 노출 될 때 ARIA 역할(role) ${data.values}이(가) 반드시 제거되어야 합니다.\",\n        \"plural\": \"엘리먼트에 허용되지 않았기 때문에, 엘리먼트가 노출 될 때 ARIA 역할(role)들 ${data.values}이(가) 반드시 제거되어야 합니다.\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage가 존재하고, 지원된 aria-errormessage 기법을 사용하는 스크린리더에 노출되는 엘리먼트를 참조합니다.\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage 값 `${data.values}`은(는) 반드시 메세지를 낭독하기 위한 기법을 (예를 들어, aria-live, aria-describedby, role=alert 등) 사용해야 합니다.\",\n        \"plural\": \"aria-errormessage 값들 `${data.values}`은(는) 반드시 메세지를 낭독하기 위한 기법을 (예를 들어, aria-live, aria-describedby, role=alert 등) 사용해야 합니다.\",\n        \"hidden\": \"aria-errormessage 값 `${data.values}`은(는) 숨겨진 엘리먼트를 참조할 수 없습니다.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"aria-errormessage 값 `${data.values}`이(가) 기존 엘리먼트를 참조하는지 확인하세요.\",\n        \"plural\": \"aria-errormessage 값들 `${data.values}`이(가) 기존 엘리먼트를 참조하는지 확인하세요.\",\n        \"idrefs\": \"aria-errormessage 엘리먼트가 페이지에 존재하는지 확인할 수 없습니다: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"문서 body에 aria-hidden 어트리뷰트가 없습니다.\",\n      \"fail\": \"문서 body에 aria-hidden=true가 없어야 합니다.\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria-level 값이 유효합니다.\",\n      \"incomplete\": \"6보다 큰 aria-level 값은 모든 스크린리더와 브라우저 조합에서 지원되지 않습니다.\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"ARIA 어트리뷰트가 혀용되었습니다.\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} 어트리뷰트는 \\\"${data.role}\\\" 역할(role)과 함께 사용될 수 없습니다.\",\n        \"hasRoleSingular\": \"${data.prohibited} 어트리뷰트는 \\\"${data.role}\\\" 역할(role)과 함께 사용될 수 없습니다.\",\n        \"noRolePlural\": \"${data.prohibited} 어트리뷰트는 유효하지 않은 역할(role)을 가진 ${data.nodeName}에 사용될 수 없습니다.\",\n        \"noRoleSingular\": \"${data.prohibited} 어트리뷰트는 유효하지 않은 역할(role)을 가진 ${data.nodeName}에 사용될 수 없습니다.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"${data.prohibited} 어트리뷰트는 \\\"${data.role}\\\" 역할(role)에서 제대로 지원되지 않습니다.\",\n        \"hasRolePlural\": \"${data.prohibited} 어트리뷰트는 \\\"${data.role}\\\" 역할(role)에서 제대로 지원되지 않습니다.\",\n        \"noRoleSingular\": \"${data.prohibited} 어트리뷰트는 유효한 역할(role) 어트리뷰트가 없는 ${data.nodeName}에서는 제대로 지원되지 않습니다.\",\n        \"noRolePlural\": \"${data.prohibited} 어트리뷰트는 유효한 역할(role) 어트리뷰트가 없는 ${data.nodeName}에서는 제대로 지원되지 않습니다.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"모든 필수 ARIA 어트리뷰트가 존재합니다.\",\n      \"fail\": {\n        \"singular\": \"필수 ARIA 어트리뷰트가 없습니다: ${data.values}\",\n        \"plural\": \"필수 ARIA 어트리뷰트들이 없습니다: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"필수 ARIA 하위 항목들이 존재합니다.\"\n      },\n      \"fail\": {\n        \"singular\": \"필수 ARIA 하위 역할(role)이 없습니다: ${data.values}\",\n        \"plural\": \"필수 ARIA 하위 역할(role)들이 없습니다: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"추가 될 예상 ARIA 하위 역할(role): ${data.values}\",\n        \"plural\": \"추가 될 예상 ARIA 하위 역할(role)들: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"필수 ARIA 상위 역할(role)이 존재합니다.\",\n      \"fail\": {\n        \"singular\": \"필수 ARIA 상위 역할(role)이 없습니다: ${data.values}\",\n        \"plural\": \"필수 ARIA 상위 역할(role)들이 없습니다: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription이 지원되는 의미론적 역할(role)에 사용되었습니다.\",\n      \"incomplete\": \"지원되는 스크린리더에서 aria-roledescription이 낭독되는지 검사하세요.\",\n      \"fail\": \"엘리먼트에 aria-roledescription을 지원하는 역할(role)을 제공되지 않았습니다.\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA 어트리뷰트가 지원됩니다.\",\n      \"fail\": \"ARIA 어트리뷰트가 스크린리더와 보조기술에서 널리 지원되지 않습니다: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA 어트리뷰트 값이 유효합니다.\",\n      \"fail\": {\n        \"singular\": \"유효하지 않은 ARIA 어트리뷰트 값: ${data.values}\",\n        \"plural\": \"유효하지 않은  ARIA 어트리뷰트 값들: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"ARIA 어트리뷰트 엘리먼트 ID가 페이지에 없습니다: ${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA 어트리뷰트 값이 유효하지 않으며 \\\"aria-current=true\\\"로 취급 될 것입니다: ${data.needsReview}\",\n        \"idrefs\": \"페이지에 ARIA 어트리뷰트 엘리먼트 ID가 존재하는지 확인할 수 없습니다: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"ARIA 어트리뷰트 이름이 유효합니다.\",\n      \"fail\": {\n        \"singular\": \"유효하지 않은 ARIA 어트리뷰트 이름: ${data.values}\",\n        \"plural\": \"유효하지 않은 ARIA 어트리뷰트 이름들: ${data.values}\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA 역할(role)은 용도 폐기(deprecated) 되지 않았습니다.\",\n      \"fail\": \"사용된 역할(role)은 용도 폐기(deprecated) 되었습니다: ${data.values}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"하나의 역할(role) 값만 사용되었습니다.\",\n      \"fail\": \"폴백 역할(role)들은 구형 브라우저들에서 지원되지 않으므로, 단 하나의 값만 사용해야 합니다.\",\n      \"incomplete\": \"'presentation'이나 'none' 역할(role)만 사용하세요, 둘은 동의어입니다.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있습니다: ${data.values}\",\n        \"plural\": \"엘리먼트가 전역 ARIA 어트리뷰트들을 가지고 있습니다: ${data.values}\"\n      },\n      \"fail\": \"엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있지 않습니다.\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"엘리먼트가 위젯 역할(role)을 가지고 있습니다.\",\n      \"fail\": \"엘리먼트가 위젯 역할(role)을 가지고 있지 않습니다.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA 역할(role)이 유효합니다.\",\n      \"fail\": {\n        \"singular\": \"역할(role)은 반드시 유효한 ARIA 역할(role)들 중 하나여야 합니다: ${data.values}\",\n        \"plural\": \"역할(role)들은 반드시 유효한 ARIA 역할(role)들 중 하나여야 합니다: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"엘리먼트가 초점을 얻을 수 있습니다(focusable).\",\n      \"fail\": \"엘리먼트가 초점을 얻을 수 없습니다(not focusable).\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"<label>과 접근 가능한 이름이 일치합니다.\",\n      \"incomplete\": \"<label>이 ARIA ${data} 필드의 이름의 일부일 필요가 없는지 확인하세요.\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA 역할(role)이 지원됩니다.\",\n      \"fail\": \"사용된 역할(role)이 스크린리더와 보조기술에서 널리 지원되지 않습니다: ${data.values}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"엘리먼트가 초점 순서(focus order)의 엘리먼트에 유효한 의미론을 가집니다.\",\n      \"fail\": \"엘리먼트가 초점 순서(focus order)의 엘리먼트에 유효하지 않은 의미론을 가집니다.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"엘리먼트가 ${data.contrastRatio}의 충분한 명도 대비를 가집니다.\",\n      \"fail\": {\n        \"default\": \"엘리먼트가 ${data.contrastRatio} (전경색: ${data.fgColor}, 배경색: ${data.bgColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight})의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"엘리먼트가 전경색과 그림자 색상 (전경색: ${data.fgColor}, 텍스트 그림자 색상: ${data.shadowColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight}) 사이에 ${data.contrastRatio}의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"엘리먼트가 그림자 색상과 배경색 (텍스트 그림자 색상: ${data.shadowColor}, 배경색: ${data.bgColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight}) 사이에 ${data.contrastRatio}의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"명암비를 확인할 수 없습니다.\",\n        \"bgImage\": \"배경 이미지로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"bgGradient\": \"배경 그라데이션으로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"imgNode\": \"엘리먼트가 이미지 노드를 포함하기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"bgOverlap\": \"다른 엘리먼트로 겹쳐 있기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"fgAlpha\": \"알파 투명도 때문에 엘리먼트의 전경색이 확인될 수 없습니다.\",\n        \"elmPartiallyObscured\": \"다른 엘리먼트에 의해 부분적으로 가려 있기 때문에 배경색이 확인될 수 없습니다.\",\n        \"elmPartiallyObscuring\": \"다른 엘리먼트에 의해 부분적으로 겹쳐 있기 때문에 배경색이 확인될 수 없습니다.\",\n        \"outsideViewport\": \"엘리먼트가 뷰포트 밖에 있기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"equalRatio\": \"엘리먼트가 배경색과 1:1의 명암비를 가집니다.\",\n        \"shortTextContent\": \"엘리먼트 콘텐츠가 너무 짧아 실제 텍스트 콘텐츠인지 확인될 수 없습니다.\",\n        \"nonBmp\": \"엘리먼트 콘텐츠가 비텍스트 문자만 포함합니다.\",\n        \"pseudoContent\": \"가상 엘리먼트로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": \"엘리먼트가 ${data.contrastRatio}의 충분한 명도 대비를 가집니다.\",\n      \"fail\": {\n        \"default\": \"엘리먼트가 ${data.contrastRatio} (전경색: ${data.fgColor}, 배경색: ${data.bgColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight})의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"엘리먼트가 전경색과 그림자 색상 (전경색: ${data.fgColor}, 텍스트 그림자 색상: ${data.shadowColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight}) 사이에 ${data.contrastRatio}의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"엘리먼트가 그림자 색상과 배경색 (텍스트 그림자 색상: ${data.shadowColor}, 배경색: ${data.bgColor}, 글꼴 크기: ${data.fontSize}, 글꼴 두께: ${data.fontWeight}) 사이에 ${data.contrastRatio}의 불충분한 명도 대비를 가집니다. 기대 명암비: ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"명암비를 확인할 수 없습니다.\",\n        \"bgImage\": \"배경 이미지로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"bgGradient\": \"배경 그라데이션으로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"imgNode\": \"엘리먼트가 이미지 노드를 포함하기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"bgOverlap\": \"다른 엘리먼트로 겹쳐 있기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"fgAlpha\": \"알파 투명도 때문에 엘리먼트의 전경색이 확인될 수 없습니다.\",\n        \"elmPartiallyObscured\": \"다른 엘리먼트에 의해 부분적으로 가려 있기 때문에 배경색이 확인될 수 없습니다.\",\n        \"elmPartiallyObscuring\": \"다른 엘리먼트에 의해 부분적으로 겹쳐 있기 때문에 배경색이 확인될 수 없습니다.\",\n        \"outsideViewport\": \"엘리먼트가 뷰포트 밖에 있기 때문에 엘리먼트의 배경색이 확인될 수 없습니다.\",\n        \"equalRatio\": \"엘리먼트가 배경색과 1:1의 명암비를 가집니다.\",\n        \"shortTextContent\": \"엘리먼트 콘텐츠가 너무 짧아 실제 텍스트 콘텐츠인지 확인될 수 없습니다.\",\n        \"nonBmp\": \"엘리먼트 콘텐츠가 비텍스트 문자만 포함합니다.\",\n        \"pseudoContent\": \"가상 엘리먼트로 인해 엘리먼트의 배경색이 확인될 수 없습니다.\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"링크가 색상 외 다른 방법으로 주변 텍스트와 구별 될 수 있습니다.\",\n      \"fail\": \"링크가 색상 외 다른 방법으로 주변 텍스트와 구별 될 필요가 있습니다.\",\n      \"incomplete\": {\n        \"default\": \"명암비를 확인할 수 없습니다.\",\n        \"bgContrast\": \"엘리먼트의 명암비가 확인될 수 없습니다. 뚜렷이 구별되는 hover/focus 스타일을 확인하세요.\",\n        \"bgImage\": \"'배경 이미지로 인해 엘리먼트의 명암비가 확인될 수 없습니다.\",\n        \"bgGradient\": \"배경 그라데이션으로 인해 엘리먼트의 명암비가 확인될 수 없습니다.\",\n        \"imgNode\": \"엘리먼트가 이미지 노드를 포함하기 때문에 엘리먼트의 명암비가 확인될 수 없습니다.\",\n        \"bgOverlap\": \"엘리먼트 겹침으로 인해 엘리먼트의 명암비가 확인될 수 없습니다.\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"autocomplete 값이 적절한 엘리먼트에 있습니다.\",\n      \"fail\": \"autocomplete 값은 이 유형의 입력에는 적절하지 않습니다.\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"autocomplete 어트리뷰트가 올바르게 구성되었습니다.\",\n      \"fail\": \"autocomplete 어트리뷰트가 올바르지 않게 구성되었습니다.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"accesskey 어트리뷰트 값이 고유합니다.\",\n      \"fail\": \"문서에 동일한 accesskey를 가진 여러 엘리먼트가 있습니다.\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"엘리먼트가 초점을 얻을 수 있는(focusable) 엘리먼트를 포함합니다.\",\n      \"fail\": \"엘리먼트가 초점을 얻을 수 있는(focusable) 콘텐츠를 가져야 합니다.\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"엘리먼트 안에 초점을 얻을 수 있는(focusable) 엘리먼트가 없습니다.\",\n      \"fail\": \"초점을 얻을 수 있는(focusable) 콘텐츠는 비활성 되거나 DOM에서 제거되어야 합니다.\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"엘리먼트가 초점을 얻을 수 있습니다(focusable).\",\n      \"fail\": \"엘리먼트가 초점을 얻을 수 있어야(focusable) 합니다.\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"modal이 열려 있는 동안 초점을 얻을 수 있는(focusable) 엘리먼트가 없습니다.\",\n      \"incomplete\": \"현재 상태에서 초점을 얻을 수 있는(focusable) 엘리먼트가 키보드로 초점을 얻을 수(tabbable) 없는지 확인하세요.\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"엘리먼트가 탭 순서(tab order)에 없거나 접근 가능한 텍스트를 가지고 있습니다.\",\n      \"fail\": \"엘리먼트가 탭 순서(tab order)에 있지만 접근 가능한 텍스트를 가지고 있지 않습니다.\",\n      \"incomplete\": \"엘리먼트가 접근 가능한 이름을 가지고 있는지 확인할 수 없습니다.\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"엘리먼트 안에 초점을 얻을 수 있는(focusable) 엘리먼트가 없습니다.\",\n      \"fail\": \"초점을 얻을 수 있는(focusable) 콘텐츠는 tabindex='-1'을 가지거나 DOM에서 제거되어야 합니다.\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"엘리먼트에 초점을 얻을 수 있는(focusable) 후손 항목이 없습니다.\",\n      \"fail\": \"엘리먼트에 초점을 얻을 수 있는(focusable) 후손 항목이 있습니다.\",\n      \"incomplete\": \"엘리먼트에 후손 항목이 있는지 확인할 수 없습니다.\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} 랜드마크가 최상위에 있습니다.\",\n      \"fail\": \"${data.role} 랜드마크가 다른 랜드마크에 포함되어 있습니다.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"엘리먼트에 초점을 얻을 수 있는(focusable) 후손 항목이 없습니다.\",\n      \"fail\": {\n        \"default\": \"엘리먼트에 초점을 얻을 수 있는(focusable) 후손 항목이 있습니다.\",\n        \"notHidden\": \"대화형 컨트롤 내부의 엘리먼트에 음수 tabindex를 사용하는 것은 ('aria-hidden=true'인 경우에도) 보조기술에서 엘리먼트가 초점을 얻는 것을 막지 않습니다.\"\n      },\n      \"incomplete\": \"엘리먼트에 후손 항목이 있는지 확인할 수 없습니다.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"페이지가 최소 한 개의 1 레벨 제목을 가지고 있습니다.\",\n      \"fail\": \"페이지가 반드시 1 레벨 제목을 가져야 합니다.\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"문서에 최소 하나의 main 랜드마크가 있습니다.\",\n      \"fail\": \"문서에 main 랜드마크가 없습니다.\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"문서가 banner 랜드마크를 하나를 초과하여 가지고 있지 않습니다.\",\n      \"fail\": \"문서가 banner 랜드마크를 하나를 초과하여 가지고 있습니다.\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"문서가 contentinfo 랜드마크를 하나를 초과하여 가지고 있지 않습니다.\",\n      \"fail\": \"문서가 contentinfo 랜드마크를 하나를 초과하여 가지고 있습니다.\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"문서가 main 랜드마크를 하나를 초과하여 가지고 있지 않습니다.\",\n      \"fail\": \"문서가 main 랜드마크를 하나를 초과하여 가지고 있습니다.\"\n    },\n    \"tabindex\": {\n      \"pass\": \"엘리먼트가 0보다 큰 tabindex를 가지고 있지 않습니다.\",\n      \"fail\": \"엘리먼트가 0보다 큰 tabindex를 가지고 있습니다.\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"엘리먼트가 유효한 alt 어트리뷰트 값을 가지고 있습니다.\",\n      \"fail\": \"엘리먼트가 공백 문자만으로 구성 되는 alt 어트리뷰트를 가지고 있고, 이는 모든 스크린리더가 무시하지 않습니다.\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"엘리먼트가 <img> alt 텍스트에 기존 텍스트를 중복하지 않습니다.\",\n      \"fail\": \"엘리먼트가 기존 텍스트와 중복되는 alt 텍스트를 가진 <img> 엘리먼트를 포함합니다.\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"form 엘리먼트가 명시적인 <label>을 가지고 있습니다.\",\n      \"fail\": \"form 엘리먼트에 명시적인 <label>이 없습니다.\",\n      \"incomplete\": \"form 엘리먼트가 명시적인 <label>을 가지고 있는지 확인할 수 없습니다.\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"도움말 텍스트(title이나 aria-describedby)가 레이블 텍스트를 중복하지 않습니다.\",\n      \"fail\": \"도움말 텍스트(title이나 aria-describedby)가 레이블 텍스트와 동일합니다.\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"form 엘리먼트가 눈에 보이는 명시적인 <label>을 가집니다.\",\n      \"fail\": \"form 엘리먼트가 숨겨진 명시적인 <label>을 가집니다.\",\n      \"incomplete\": \"form 엘리먼트가 숨겨진 명시적인 <label>을 가지는지 확인할 수 없습니다.\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"form 엘리먼트가 암묵적인(감싸는) <label>을 가집니다.\",\n      \"fail\": \"form 엘리먼트에 암묵적인(감싸는) <label>이 없습니다.\",\n      \"incomplete\": \"form 엘리먼트가 암묵적인(감싸는) <label>을 가지는지 확인할 수 없습니다.\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"엘리먼트가 접근 가능한 이름의 일부로 눈에 보이는 텍스트를 포함하고 있습니다.\",\n      \"fail\": \"엘리먼트 내부 텍스트가 접근 가능한 이름에 포함되어 있지 않습니다.\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"form 필드가 여러 레이블 엘리먼트를 가지고 있지 않습니다.\",\n      \"incomplete\": \"여러 레이블 엘리먼트는 보조기술에서 널리 지원되지 않습니다. 첫 번째 레이블이 모든 필요한 정보를 포함하는지 확인하세요.\"\n    },\n    \"title-only\": {\n      \"pass\": \"form 엘리먼트가 레이블을 위해 title 어트리뷰트만 단독으로 사용하지 않습니다.\",\n      \"fail\": \"form 엘리먼트의 레이블을 생성하는데 title만 사용되었습니다.\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"랜드마크는 반드시 고유한 역할(role)이나 role/label/title 조합(즉, 접근 가능한 이름)을 가져야 합니다.\",\n      \"fail\": \"랜드마크는 랜드마크를 구별할 수 있게 만들기 위해 반드시 고유한 aria-label, aria-labelledby 또는 title을 가져야 합니다.\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> 엘리먼트에 lang 어트리뷰트가 있습니다.\",\n      \"fail\": {\n        \"noXHTML\": \"xml:lang 어트리뷰트는 HTML 페이지에 유효하지 않습니다, lang 어트리뷰트를 사용하세요.\",\n        \"noLang\": \"<html> 어트리뷰트에 lang 어트리뷰트가 없습니다.\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"lang 어트리뷰트의 값이 유효한 언어 목록에 포함되어 있습니다.\",\n      \"fail\": \"lang 어트리뷰트의 값이 유효한 언어 목록에 포함되어 있지 않습니다.\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"lang과 xml:lang 어트리뷰트가 동일한 기본 언어를 가지고 있습니다.\",\n      \"fail\": \"lang과 xml:lang 어트리뷰트가 동일한 기본 언어를 가지고 있지 않습니다.\"\n    },\n    \"dlitem\": {\n      \"pass\": \"설명 목록 항목이 <dl> 상위 엘리먼트를 가지고 있습니다.\",\n      \"fail\": \"설명 목록 항목이 <dl> 상위 엘리먼트를 가지고 있지 않습니다.\"\n    },\n    \"listitem\": {\n      \"pass\": \"목록 항목이 <ul>, <ol> 또는 role=\\\"list\\\" 상위 엘리먼트를 가지고 있습니다.\",\n      \"fail\": {\n        \"default\": \"목록 항목이 <ul>, <ol> 상위 엘리먼트를 가지고 있지 않습니다.\",\n        \"roleNotValid\": \"목록 항목이 역할(role)이 없는 <ul>, <ol> 상위 엘리먼트나 role=\\\"list\\\" 상위 엘리먼트를 가지고 있지 않습니다.\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"목록 엘리먼트가 허용된 내부 <dt>나 <dd> 엘리먼트 직속 자식만 가지고 있습니다.\",\n      \"fail\": \"목록 엘리먼트가 허용된 내부 <dt>나 <dd> 엘리먼트가 아닌 직속 자식을 가지고 있습니다.\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"목록 엘리먼트가 허용된 내부 <li> 엘리먼트 직속 자식만 가지고 있습니다.\",\n      \"fail\": {\n        \"default\": \"목록 엘리먼트가 허용된 내부 <li> 엘리먼트가 아닌 직속 자식을 가지고 있습니다.\",\n        \"roleNotValid\": \"목록 엘리먼트가 허용되지 않은 역할(role)을 가진 직속 자식을 가지고 있습니다: ${data.roles}\"\n      }\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"비어있지 않은 경우, 엘리먼트가 <dt>와 <dd> 엘리먼트를 모두 가지고 있습니다.\",\n      \"fail\": \"비어있지 않은 경우, 엘리먼트가 최소 하나의 <dd> 엘리먼트가 뒤따르는 최소 하나의 <dt> 엘리먼트를 가지고 있지 않습니다.\"\n    },\n    \"caption\": {\n      \"pass\": \"멀티미디어 엘리먼트가 캡션(자막) 트랙을 가지고 있습니다.\",\n      \"incomplete\": \"엘리먼트에 캡션(자막)이 사용 가능한지 확인하세요.\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"iframe이 axe-core로 테스트 되었습니다.\",\n      \"fail\": \"iframe이 axe-core로 테스트 될 수 없습니다.\",\n      \"incomplete\": \"iframe은 아직 axe-core로 테스트 되지 않았습니다.\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video>나 <audio>가 허용된 지속 시간을 초과하여 오디오를 출력하지 않거나 제어 메커니즘을 가지고 있습니다.\",\n      \"fail\": \"<video>나 <audio>가 허용된 지속 시간을 초과하여 오디오를 출력하거나 제어 메커니즘을 가지고 있지 않습니다.\",\n      \"incomplete\": \"<video>나 <audio>가 허용된 지속 시간을 초과하여 오디오를 출력하지 않거나 제어 메커니즘을 제공하고 있는지 확인하세요.\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"디스플레이를 조작할 수 있고, 방향 잠금이 존재하지 않습니다.\",\n      \"fail\": \"CSS 방향 잠금이 적용되어 있어, 디스플레이를 조작할 수 없습니다.\",\n      \"incomplete\": \"CSS 방향 잠금이 확인될 수 없습니다.\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta> 태그가 모바일 기기에서 확대/축소하는 것을 제한하지 않습니다.\",\n      \"fail\": \"<meta> 태그가 모바일 기기에서 확대/축소하는 것을 제한하고 있습니다.\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta> 태그가 모바일 기기에서 확대/축소하는 것을 비활성화하지 않았습니다.\",\n      \"fail\": \"모바일 기기에서 <meta> 태그의 ${data}이(가) 확대/축소를 할 수 없게 만듭니다.\"\n    },\n    \"header-present\": {\n      \"pass\": \"페이지가 제목을 가지고 있습니다.\",\n      \"fail\": \"페이지에 제목이 없습니다.\"\n    },\n    \"heading-order\": {\n      \"pass\": \"제목 순서가 유효합니다.\",\n      \"fail\": \"제목 순서가 잘못되었습니다.\",\n      \"incomplete\": \"이전 제목을 확인 할 수 없습니다.\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"다른 URL로 이동하는 동일한 이름의 다른 링크가 없습니다.\",\n      \"incomplete\": \"링크가 동일한 용도를 가지고 있는지, 또는 의도적으로 모호하게 한 것인지 확인하세요.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"유효한 건너뛰기 링크가 발견되었습니다.\",\n      \"fail\": \"유효한 건너뛰기 링크를 찾을 수 없습니다.\"\n    },\n    \"landmark\": {\n      \"pass\": \"페이지가 랜드마크 영역을 가지고 있습니다.\",\n      \"fail\": \"페이지에 랜드마크 영역이 없습니다.\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta> 태그가 페이지를 즉시 새로고침하지 않습니다.\",\n      \"fail\": \"<meta> 태그가 페이지의 시간 제한 새로고침을 강요합니다.\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> 엘리먼트가 제목으로 스타일되지 않았습니다.\",\n      \"fail\": \"스타일링된 p 엘리먼트는 제목 엘리먼트 대신 사용되지 않아야 합니다.\",\n      \"incomplete\": \"<p> 엘리먼트가 제목으로 스타일되었는지 확인할 수 없습니다.\"\n    },\n    \"region\": {\n      \"pass\": \"모든 페이지 콘텐츠가 랜드마크에 포함되어 있습니다.\",\n      \"fail\": \"일부 페이지 콘텐츠가 랜드마크에 포함되어 있지 않습니다.\"\n    },\n    \"skip-link\": {\n      \"pass\": \"건너뛰기 링크 대상이 존재합니다.\",\n      \"incomplete\": \"건너뛰기 링크 대상이 활성화 시 표시되어야 합니다.\",\n      \"fail\": \"건너뛰기 링크 대상이 없습니다.\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"엘리먼트의 title 어트리뷰트가 고유합니다.\",\n      \"fail\": \"엘리먼트의 title 어트리뷰트가 고유하지 않습니다.\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"문서에 동일한 id 어트리뷰트를 공유하는 활성 엘리먼트가 없습니다.\",\n      \"fail\": \"문서에 동일한 id 어트리뷰트를 가진 활성 엘리먼트가 있습니다: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"문서에 동일한 id 어트리뷰트를 공유하는 ARIA로 참조된 엘리먼트 및 레이블이 없습니다.\",\n      \"fail\": \"문서에 동일한 id 어트리뷰트를 가진 ARIA로 참조된 여러 엘리먼트가 있습니다: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"문서에 동일한 id 어트리뷰트를 공유하는 정적 엘리먼트가 없습니다.\",\n      \"fail\": \"문서에 동일한 id 어트리뷰트를 공유하는 여러 정적 엘리먼트가 있습니다: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label 어트리뷰트가 존재하고 비어있지 않습니다.\",\n      \"fail\": \"aria-label 어트리뷰트가 없거나 비어있습니다.\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby 어트리뷰트가 존재하고 스크린리더에 노출되는 엘리먼트를 참조하고 있습니다.\",\n      \"fail\": \"aria-labelledby 어트리뷰트가 없거나, 존재하지 않는 엘리먼트를 참조하고 있거나, 비어있는 엘리먼트를 참조하고 있습니다.\",\n      \"incomplete\": \"aria-labelledby가 존재하고 있는 엘리먼트를 참조하는지 확인하세요.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"텍스트 간격에 영향을 주는 '!important'를 가진 명시된 인라인 스타일이 없습니다.\",\n      \"fail\": {\n        \"singular\": \"인라인 스타일을 재정의 하는 것을 대부분의 브라우저가 지원하지 않으므로, 인라인 스타일 ${data.values}에서 '!important'를 제거하세요.\",\n        \"plural\": \"인라인 스타일들을 재정의 하는 것을 대부분의 브라우저가 지원하지 않으므로, 인라인 스타일들 ${data.values}에서 '!important'를 제거하세요.\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"엘리먼트가 스크린리더에 노출되는 내부 텍스트를 가지고 있습니다.\",\n      \"fail\": \"엘리먼트에 스크린리더에 노출되는 내부 텍스트가 없습니다.\",\n      \"incomplete\": \"엘리먼트가 하위 항목을 가지고 있는지 확인할 수 없습니다.\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"문서가 비어있지 않은 <title> 엘리먼트를 가지고 있습니다.\",\n      \"fail\": \"문서에 비어있지 않은 <title> 엘리먼트가 없습니다.\"\n    },\n    \"exists\": {\n      \"pass\": \"엘리먼트가 존재하지 않습니다.\",\n      \"incomplete\": \"엘리먼트가 존재합니다.\"\n    },\n    \"has-alt\": {\n      \"pass\": \"엘리먼트가 alt 어트리뷰트를 가지고 있습니다.\",\n      \"fail\": \"엘리먼트에 alt 어트리뷰트가 없습니다.\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"엘리먼트가 스크린리더에 노출되는 텍스트를 가지고 있습니다.\",\n      \"fail\": \"엘리먼트가 스크린리더에 노출되는 텍스트를 가지고 있지 않습니다.\",\n      \"incomplete\": \"엘리먼트가 하위 항목을 가지고 있는지 확인할 수 없습니다.\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"엘리먼트가 보이지 않습니다.\",\n      \"fail\": \"엘리먼트가 노출되어 있습니다.\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"엘리먼트가 비어있지 않은 alt 어트리뷰트를 가지고 있습니다.\",\n      \"fail\": {\n        \"noAttr\": \"엘리먼트에 alt 어트리뷰트가 없습니다.\",\n        \"emptyAttr\": \"엘리먼트가 빈 alt 어트리뷰트를 가지고 있습니다.\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"엘리먼트에 value 어트리뷰트가 없습니다.\",\n        \"has-label\": \"엘리먼트가 비어있지 않은 value 어트리뷰트를 가지고 있습니다.\"\n      },\n      \"fail\": \"엘리먼트가 value 어트리뷰트를 가지고 있고 value 어트리뷰트가 비어있습니다.\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"엘리먼트가 placeholder 어트리뷰트를 가지고 있습니다.\",\n      \"fail\": {\n        \"noAttr\": \"엘리먼트에 placeholder 어트리뷰트가 없습니다.\",\n        \"emptyAttr\": \"엘리먼트가 빈 placeholder 어트리뷰트를 가지고 있습니다.\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"엘리먼트가 title 어트리뷰트를 가지고 있습니다.\",\n      \"fail\": {\n        \"noAttr\": \"엘리먼트에 title 어트리뷰트가 없습니다.\",\n        \"emptyAttr\": \"엘리먼트가 빈 title 어트리뷰트를 가지고 있습니다.\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"엘리먼트가 비어있지 않은 value 어트리뷰트를 가지고 있습니다.\",\n      \"fail\": {\n        \"noAttr\": \"엘리먼트에 value 어트리뷰트가 없습니다.\",\n        \"emptyAttr\": \"엘리먼트가 빈 value 어트리뷰트를 가지고 있습니다.\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"엘리먼트의 기본 의미론이 role=\\\"${data.role}\\\"(으)로 재정의되었습니다.\",\n      \"fail\": {\n        \"default\": \"엘리먼트의 기본 의미론이 role=\\\"none\\\"이나 role=\\\"presentation\\\"으로 재정의되지 않았습니다.\",\n        \"globalAria\": \"엘리먼트가 전역 ARIA 어트리뷰트를 가지고 있기 때문에 엘리먼트의 역할(role)이 표현적이지 않습니다.\",\n        \"focusable\": \"엘리먼트가 초점을 얻을 수 있기(focusable) 때문에 엘리먼트의 역할(role)은 표현적이지 않습니다.\",\n        \"both\": \"전역 ARIA 어트리뷰트를 가지고 있고 초점을 얻을 수 있기(focusable) 때문에 엘리먼트의 역할(role)은 표현적이지 않습니다.\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"엘리먼트의 기본 의미론이 role=\\\"none\\\"으로 재정의되었습니다.\",\n      \"fail\": \"엘리먼트의 기본 의미론이 role=\\\"none\\\"으로 재정의되지 않았습니다.\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"엘리먼트의 기본 의미론이 role=\\\"presentation\\\"으로 재정의되었습니다.\",\n      \"fail\": \"엘리먼트의 기본 의미론이 role=\\\"presentation\\\"으로 재정의되지 않았습니다.\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"엘리먼트가 title인 하위 항목을 가지고 있습니다.\",\n      \"fail\": {\n        \"noTitle\": \"엘리먼트에 title인 하위 항목이 없습니다.\",\n        \"emptyTitle\": \"엘리먼트 하위 title이 비어있습니다.\"\n      },\n      \"incomplete\": \"엘리먼트가 title인 하위 항목을 가지고 있는지 확인할 수 없습니다.\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"테이블의 첫 번째 행이 캡션으로 사용되지 않았습니다.\",\n      \"fail\": \"테이블의 첫 번째 자식은 테이블 셀 대신 caption이어야 합니다.\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"scope 어트리뷰트가 테이블 헤더 엘리먼트(<th>)에만 사용되었습니다.\",\n      \"fail\": \"HTML 5에서, scope 어트리뷰트는 테이블 헤더 엘리먼트(<th>)에만 사용 될 수 있습니다.\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"summary 어트리뷰트의 내용과 <caption>이 중복되지 않습니다.\",\n      \"fail\": \"summary 어트리뷰트의 내용과 <caption> 엘리먼트가 동일합니다.\"\n    },\n    \"scope-value\": {\n      \"pass\": \"scope 어트리뷰트가 올바르게 사용되었습니다.\",\n      \"fail\": \"어트리뷰트의 값은 오직 'row'나 'col'만 될 수 있습니다.\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"모든 비어있지 않은 데이터 셀이 테이블 헤더를 가지고 있습니다.\",\n      \"fail\": \"일부 비어있지 않은 데이터 셀이 테이블 헤더를 가지고 있지 않습니다.\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"headers 어트리뷰트는 오직 테이블의 다른 셀을 참조하는데에만 사용되고 있습니다.\",\n      \"incomplete\": \"headers 어트리뷰트가 비어있습니다.\",\n      \"fail\": \"headers 어트리뷰트가 오직 테이블의 다른 셀을 참조하는데에만 사용되지 않고 있습니다.\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"모든 테이블 헤더 셀이 데이터 셀을 참조하고 있습니다.\",\n      \"fail\": \"모든 테이블 헤더 셀이 데이터 셀을 참조하고 있지 않습니다.\",\n      \"incomplete\": \"테이블 데이터 셀이 누락되었거나 비어있습니다.\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"페이지의 모든 콘텐츠가 분석 되었습니다.\",\n      \"fail\": \"이 페이지의 콘텐츠를 분석하는데 문제가 발생했습니다.\",\n      \"incomplete\": \"페이지에 분석될 수 없는 숨겨진 콘텐츠가 있습니다. 이 콘텐츠를 분석하려면 콘텐츠의 노출을 동작시킬 필요가 있습니다.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"다음 중 하나를 해결하세요:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"다음 사항을 모두 해결하세요:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe가 원인을 제시할 수 없습니다. 엘리먼트 검사기를 열어 볼 시간입니다!\"\n}\n"
  },
  {
    "path": "locales/nl.json",
    "content": "{\n  \"lang\": \"nl\",\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Er zijn geen abstracte rollen (role) gebruikt\",\n      \"fail\": \"Gebruik geen abstracte rollen (role)\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"Element heeft voldoende contrast, namelijk ${data.contrastRatio}\",\n      \"fail\": \"Element heeft onvoldoende contrast, ${data.contrastRatio} (Voorgrondkleur: ${data.fgColor}, achtergrondkleur: ${data.bgColor}, tekstgrootte: ${data.fontSize}, tekstdikte: ${data.fontWeight})\",\n      \"incomplete\": {\n        \"bgImage\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een achtergrondafbeelding\",\n        \"bgGradient\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een gradient kleur\",\n        \"imgNode\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een image node\",\n        \"bgOverlap\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een overlappend element\",\n        \"fgAlpha\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen alpha transparency\",\n        \"default\": \"Contrastkleur kon niet bepaald worden\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Element heeft voldoende contrast, namelijk ${data.contrastRatio}\",\n      \"fail\": \"Element heeft onvoldoende contrast, ${data.contrastRatio} (Voorgrondkleur: ${data.fgColor}, achtergrondkleur: ${data.bgColor}, tekstgrootte: ${data.fontSize}, tekstdikte: ${data.fontWeight})\",\n      \"incomplete\": {\n        \"bgImage\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een achtergrondafbeelding\",\n        \"bgGradient\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een gradient kleur\",\n        \"imgNode\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een image node\",\n        \"bgOverlap\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen een overlappend element\",\n        \"fgAlpha\": \"Element's achtergrondkleur kon niet worden bepaald vanwegen alpha transparency\",\n        \"default\": \"Contrastkleur kon niet bepaald worden\"\n      }\n    }\n  },\n  \"rules\": {\n    \"aria-required-attr\": {\n      \"description\": \"Zorg dat elementen met ARIA rollen (role) de vereiste ARIA attributen hebben\",\n      \"help\": \"Voorzien de vereiste ARIA attributen\"\n    }\n  },\n  \"failureSummaries\": {\n    \"none\": {\n      \"failureMessage\": \"Los al het volgende op:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"any\": {\n      \"failureMessage\": \"Gebruik een van de volgende oplossingen:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe kon de reden niet vertellen. Tijd om de element inspecteur uit te breken!\"\n}\n"
  },
  {
    "path": "locales/no_NB.json",
    "content": "{\n  \"lang\": \"nb-no\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"\",\n      \"help\": \"Verdien for hver 'accesskey' attributt skal være unik\"\n    },\n    \"area-alt\": {\n      \"description\": \"\",\n      \"help\": \"Aktive <area> elementer skal ha alternativ tekst\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"\",\n      \"help\": \"Elementer må kun bruke tillate ARIA-attributter\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributtet 'role' skal være passende for elementet\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"\",\n      \"help\": \"aria-hidden='true' skal ikke brukes på dokumentets <body> element\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"\",\n      \"help\": \"Elementer med ARIA-'hidden' skal ikke inneholde fokuserbare elementer\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-input-felter skal ha et tilgjengelig navn\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"\",\n      \"help\": \"Alle påkrevde ARIA-attributter skal være angitt i henhold til elementets rolle\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"\",\n      \"help\": \"Bestemte ARIA-roller skal inneholde spesifikke under-elementer\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"\",\n      \"help\": \"Bestemte ARIA-roller skal være under-element til spesifikke over-elementer\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"\",\n      \"help\": \"Bruk 'aria-roledescription' på elementer med en semantisk rolle\"\n    },\n    \"aria-roles\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-roller skal ha en korrekt verdi\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"\",\n      \"help\": \"ARIA avkrysningsboks skal ha et tilgjengelig navn\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributter skal ha en gyldig verdi\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"\",\n      \"help\": \"ARIA-attributter skal ha et gyldig navn\"\n    },\n    \"audio-caption\": {\n      \"description\": \"\",\n      \"help\": \"<audio> elementer skal ha en transkripsjon ('captions track')\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"\",\n      \"help\": \"Attributtet 'autocomplete' skal benyttes korrekt\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"\",\n      \"help\": \"Inline tekst-avstand skal være justerbare med brukerdefinerte stylesheets\"\n    },\n    \"blink\": {\n      \"description\": \"\",\n      \"help\": \"Elementet <blink> er utfaset og skal ikke brukes\"\n    },\n    \"button-name\": {\n      \"description\": \"\",\n      \"help\": \"Knapper skal ha forståelig (dvs. detekterbar) tekst\"\n    },\n    \"bypass\": {\n      \"description\": \"\",\n      \"help\": \"Sider skal ha en metode for å hoppe over navigasjon og komme direkte til innholdet\"\n    },\n    \"color-contrast\": {\n      \"description\": \"\",\n      \"help\": \"Elementer skal ha tilstrekkelig fargekontrast\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"\",\n      \"help\": \"Elementer skal ha tilstrekkelig fargekontrast\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"\",\n      \"help\": \"'CSS Media queries' skal ikke brukes til å låse skjermretningen ('orientation')\"\n    },\n    \"definition-list\": {\n      \"description\": \"\",\n      \"help\": \"<dl> elementer skal kun direkte inneholde velsorterte <dt> og <dd> grupper, <script> eller <template> elementer\"\n    },\n    \"dlitem\": {\n      \"description\": \"\",\n      \"help\": \"<dt> og <dd> elementer skal være under-element til et <dl> element\"\n    },\n    \"document-title\": {\n      \"description\": \"\",\n      \"help\": \"Dokumenter skal ha et <title> element med en verdi som ikke er tom\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"\",\n      \"help\": \"'id'-attributtet for aktive elementer skal være unik\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"\",\n      \"help\": \"'id'-attributtet brukt på ARIA-elementer og -labels skal være unikt\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"\",\n      \"help\": \"Verdien for 'id'-attributtet skal være unik\"\n    },\n    \"empty-heading\": {\n      \"description\": \"\",\n      \"help\": \"Overskrifter skal ikke være tomme\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"\",\n      \"help\": \"Elementer i fokus-rekkefølgen skal ha en 'role'-attributt, som er passende for det interaktive innholdet\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"\",\n      \"help\": \"Form-feltet skal ikke ha flere label-elementer\"\n    },\n    \"frame-tested\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal være testet med axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal ha en unik 'title'-attributt\"\n    },\n    \"frame-title\": {\n      \"description\": \"\",\n      \"help\": \"Frame-elementer skal ha 'title'-attributt\"\n    },\n    \"heading-order\": {\n      \"description\": \"\",\n      \"help\": \"Rekkefølgen for overksriftsnivåer skal være semtantisk korrekt\"\n    },\n    \"hidden-content\": {\n      \"description\": \"\",\n      \"help\": \"Skjult innhold på siden kunne ikke analyseres\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementet skal ha en 'lang'-attributt\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementet skal ha en gyldig verdi for 'lang'-attributtet\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"\",\n      \"help\": \"<html> elementer med 'lang' og 'xml:lang' skal ha samme språk / dialekt\"\n    },\n    \"image-alt\": {\n      \"description\": \"\",\n      \"help\": \"Bilder skal ha en alternativ tekst\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"\",\n      \"help\": \"Alternativ tekst til bilder (alt-tekst) skal ikke gjentas som tekst\"\n    },\n    \"input-button-name\": {\n      \"description\": \"\",\n      \"help\": \"Input-knapper skal ha en forståelig tekst\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"\",\n      \"help\": \"Bilde-knapper skal ha en alternativ tekst\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"\",\n      \"help\": \"Elementer sin synlige tekst skal være del av deres tilgjengelige navn\"\n    },\n    \"label-title-only\": {\n      \"description\": \"\",\n      \"help\": \"Skjemaalement skal ha en synlig ledetekst\"\n    },\n    \"label\": {\n      \"description\": \"\",\n      \"help\": \"Alle skjemaelement skal ha en tilknyttet ledetekst eller instruksjon\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'banner'-landemerke skal ikke være inne i et annet landemerke\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'aside'- eller 'complimentary'-landemerke skal ikke være inne i et annet landemerke\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'contentinfo'-landemerke skal ikke være inne i et annet landemerke\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"\",\n      \"help\": \"Et 'main'-landemerke skal ikke være inne i et annet landemerke\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet skal ikke ha mer enn ett 'banner'-landemerke\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet skal ikke ha mer enn ett 'contentinfo'-landemerke\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"\",\n      \"help\": \"Dokumentet skal ha ett 'main'-landemerke\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"\",\n      \"help\": \"Sikre at landemerker er unike\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"\",\n      \"help\": \"Lenker skal være fremtredende fra den omkringliggende teksten på en måte som ikke avhenger av farge\"\n    },\n    \"link-name\": {\n      \"description\": \"\",\n      \"help\": \"Lenker skal ha forståelig (detekterbar) tekst\"\n    },\n    \"list\": {\n      \"description\": \"\",\n      \"help\": \"<ul> og <ol> skal kun direkte inneholde <li>, <script> eller <template> elementer\"\n    },\n    \"listitem\": {\n      \"description\": \"\",\n      \"help\": \"<li> elementer skal være inne i et <ul> eller <ol> element\"\n    },\n    \"marquee\": {\n      \"description\": \"\",\n      \"help\": \"<marquee> elementer er utfaset og skal ikke brukes\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"\",\n      \"help\": \"Tidsinnstilt 'refresh' skal ikke brukes\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"\",\n      \"help\": \"Brukere skal kunne zoome og skalere tekst opp til 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"\",\n      \"help\": \"Zoom og skalering skal ikke være slått av\"\n    },\n    \"object-alt\": {\n      \"description\": \"\",\n      \"help\": \"<object> elementer skal ha en alternativ tekst\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"\",\n      \"help\": \"Fremhevelse med fet, kursiv og skriftstørrelse (font-size) skal ikke brukes til å 'style' <p> elementer som en overskrift\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"\",\n      \"help\": \"Siden skal inneholde en overskrift på øverste nivå\"\n    },\n    \"region\": {\n      \"description\": \"\",\n      \"help\": \"Alt innholdet på siden skal være inne i landemerker\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"\",\n      \"help\": \"Elementer med 'role' attributtverdien 'img' skal ha en alternativ tekst\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"\",\n      \"help\": \"'scope'-attributtet skal brukes korrekt i tabeller\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"\",\n      \"help\": \"Sørg for at en scrollbar region er tilgjengelig via tastatur\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"\",\n      \"help\": \"Såkalte 'server-side image-maps' skal ikke brukes\"\n    },\n    \"skip-link\": {\n      \"description\": \"\",\n      \"help\": \"En 'skip-link' skal peke på et eksisterende og fokuserbart element\"\n    },\n    \"tabindex\": {\n      \"description\": \"\",\n      \"help\": \"Elementer skal ikke ha et 'tabindex' høyere enn 0\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"\",\n      \"help\": \"Elementet <caption> skal ikke inneholde samme tekst som 'summary'-attributtet\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"\",\n      \"help\": \"Data- eller overskrifts-celler skal ikke brukes til å beskrive innholdet av en data-tabell\"\n    },\n    \"td-has-header\": {\n      \"description\": \"\",\n      \"help\": \"Alle ikke-tomme <td> elementer i en tabel større enn 3x3 skal ha en tabelloverskrift (<th>)\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"\",\n      \"help\": \"Alle celler i en tabel, som  bruker 'header'-attributtet skal kun referere til andre celler i samme tabel\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"\",\n      \"help\": \"Alle <th> elementer og elementer med 'role=columnheader/rowheader' skal referere til de data-celler, som de beskriver\"\n    },\n    \"valid-lang\": {\n      \"description\": \"\",\n      \"help\": \"'lang'-attributtet skal ha en gyldig verdi\"\n    },\n    \"video-caption\": {\n      \"description\": \"\",\n      \"help\": \"<video> elementer skal ha undertekster ('captions')\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Abstrakte roller er ikke brukt\",\n      \"fail\": \"Abstrakte roller skal ikke brukes\"\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA-attributt er brukt korrekt for den angitte rolle\",\n      \"fail\": {\n        \"singular\": \"ARIA-attributtet er ikke tillatt: ${data.values}\",\n        \"plural\": \"ARIA-attributtene er ikke tillatt: ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA-rollen er tillatt for det gitte element\",\n      \"fail\": {\n        \"singular\": \"ARIA-rollen ${data.values} er ikke tillatt for det gitte element\",\n        \"plural\": \"ARIA-rollene ${data.values} er ikke tillatt for det gitte element\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA-rollen ${data.values} skal være fjernet når elementet er synlig, da de ikke er tillatt for elementet\",\n        \"plural\": \"ARIA-rollene ${data.values} skal være fjernet når elementet er synlig, da det ikke er tillatt for elementet\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Kunne ikke finne noen 'aria-hidden'-attributt i dokumentets <body> element\",\n      \"fail\": \"'aria-hidden=true' skal ikke brukes på dokumentets <body> element\"\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"'aria-roledescription' brukes på en understøttet semantisk rolle\",\n      \"incomplete\": \"Sjekk at 'aria-roledescription' blir lest opp av understøttet skjermleser\",\n      \"fail\": \"Gi elementet en rolle som understøtter 'aria-roledescription'\"\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Bruker en understøttet 'aria-errormessage'-metode\",\n      \"fail\": {\n        \"singular\": \"'aria-errormessage'-verdi ${data.values}` skal bruke en metode for å annonsere beskeden (f.eks. 'aria-live', 'aria-describedby', 'role=alert', osv.)\",\n        \"plural\": \"'aria-errormessage'-verdier ${data.values}` skal bruke en metode for å annonsere beskeden (f.eks. 'aria-live', 'aria-describedby', 'role=alert', osv.)\"\n      }\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Elementet har en 'widget'-rolle.\",\n      \"fail\": \"Elementet har ikke en 'widget'-rolle.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA-rollen er korrekt\",\n      \"fail\": \"Rollen skal være en av de mulige ARIA-roller\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Det er uoverensstemmelse mellom <label> og det tilgjengelige navnet\",\n      \"incomplete\": \"Sjekk at <label> elementet ikke behøver å være en del av ${data}-feltets navn\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Alle påkrevde ARIA-attributter er tilstede\",\n      \"fail\": {\n        \"singular\": \"Påkrevd ARIA-attributt er ikke til stede: ${data.values}\",\n        \"plural\": \"Påkrevde ARIA-attributter er ikke til stede: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Påkrevde ARIA-under-elementer er til stede\"\n      },\n      \"fail\": {\n        \"singular\": \"Påkrevd ARIA-under-element sin rolle er ikke til stede: ${data.values}\",\n        \"plural\": \"Påkrevde ARIA-under-elementer sine roller er ikke til stede: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Forventer at ARIA under-element sin rolle blir lagt til: ${data.values}\",\n        \"plural\": \"Forventer at ARIA under-elementer sine roller blir lagt til: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Påkrevde ARIA-over-elements rolle er til stede\",\n      \"fail\": {\n        \"singular\": \"Påkrevd ARIA-over-element sin rolle er ikke til stede: ${data.values}\",\n        \"plural\": \"Påkrevde ARIA-over-elementer sine roller er ikke til stede: ${data.values}\"\n      }\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA-attributt er understøttet\",\n      \"fail\": \"ARIA-attributt er ikke bredt understøttet i skjermlesere og tilgjengelighetsteknologier:  ${data.values}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA rollen er understøttet\",\n      \"fail\": \"Den brukte rolle er ikke bredt understøttet i skjermlesere og tilgjengelighetsteknologier:  ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA-attributverdien er gyldig\",\n      \"fail\": {\n        \"singular\": \"Ugyldig ARIA-attributtverdi: ${data.values}\",\n        \"plural\": \"Ugyldige ARIA-attributtverdier: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Kunne ikke finne ARIA-attributtet sitt element 'id' på siden: ${data.values}\",\n        \"plural\": \"Kunne ikke finne ARIA-attributtene sine element 'id' på siden: ${data.values}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"ARIA-attributnavnet er korrekt\",\n        \"plural\": \"ARIA-attributnavnene er korrekt\"\n      },\n      \"fail\": {\n        \"singular\": \"Ugyldig ARIA-attributtnavn: ${data.values}\",\n        \"plural\": \"Ugyldige ARIA-attributtnavn: ${data.values}\"\n      }\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Elementet har korrekt semantikk for et element i fokus-rekkefølgen.\",\n      \"fail\": \"Elementet har ikke korrekt semantikk for et element i fokus-rekkefølgen.\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"Elementet har tilstrekkelig fargekontrast. Den er ${data.contrastRatio}\",\n      \"fail\": \"Elementet har ikke god nok fargekontrast. Den er ${data.contrastRatio} (forgrunnsfarge: ${data.fgColor}, bakgrunnsfarge: ${data.bgColor}, tekststørrelse: ${data.fontSize}, teksttykkelse: ${data.fontWeight}). Forventet kontrastforhold er ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementets bakgrunnsfarge kunne ikke detekteres på grunn av et bakgrunnsbildet\",\n        \"bgGradient\": \"Elementets bakgrunnsfarge kunne ikke detekteres på grunn av en bakgrunnsgradient\",\n        \"imgNode\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi elementet inneholder et bildeelement\",\n        \"bgOverlap\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er overlappet av et annet element\",\n        \"fgAlpha\": \"Elementets forgrunnsfarge kunne ikke detekteres på grunn av dets gjennomsiktighet\",\n        \"elmPartiallyObscured\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er delvist dekket av et annet element\",\n        \"elmPartiallyObscuring\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det delvist dekker et annet element\",\n        \"outsideViewport\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er utenfor sidens 'viewport'\",\n        \"equalRatio\": \"Elementet har et 1:1-kontrastforhold med bakgrunnen\",\n        \"shortTextContent\": \"Elementets innhold er for kort til å kunne avgjøre om innholdet faktisk er tekst\",\n        \"default\": \"Kan ikke regne ut kontrastforhold\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Elementet har tilstrekkelig fargekontrast, den er ${data.contrastRatio}\",\n      \"fail\": \"Elementet har ikke god nok fargekontrast, den er ${data.contrastRatio} (forgrunnsfarge: ${data.fgColor}, bakgrunnsfarge: ${data.bgColor}, tekststørrelse: ${data.fontSize}, teksttykkelse: ${data.fontWeight}). Forventet kontrastforhold er ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"bgImage\": \"Elementets bakgrunnsfarge kunne ikke detekteres på grunn av et bakgrunnsbilde\",\n        \"bgGradient\": \"Elementets bakgrunnsfarge kunne ikke detekteres på grunn av en bakgrunnsgradient\",\n        \"imgNode\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi elementet inneholder et bildeelement\",\n        \"bgOverlap\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er overlappet av et annet element\",\n        \"fgAlpha\": \"Elementets forgrunnsfarge kunne ikke detekteres på grunn av dets gjennomsiktighet\",\n        \"elmPartiallyObscured\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er delvist dekket av et annet element\",\n        \"elmPartiallyObscuring\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det delvist dekker et annet element\",\n        \"outsideViewport\": \"Elementets bakgrunnsfarge kunne ikke detekteres, fordi det er utenfor sidens 'viewport'\",\n        \"equalRatio\": \"Elementet har et 1:1-kontrastforhold med bakgrunnen\",\n        \"shortTextContent\": \"Elementets innhold er for kort til å kunne avgjøre om innholdet faktisk er tekst\",\n        \"default\": \"Kan ikke regne ut kontrastforhold\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Lenker kan adskilles fra den omkringliggende tekst på annen måte enn med farge\",\n      \"fail\": \"Lenker må skille seg ut fra den omkringliggende tekst på annen måte enn med farge\",\n      \"incomplete\": {\n        \"bgContrast\": \"Elementets kontrastforhold kunne ikke detekteres. Sjekk for spesifikk 'hover'/'focus' styling\",\n        \"bgImage\": \"Elementets kontrastforhold kunne ikke detekteres på grunn av et bakgrunnsbilde\",\n        \"bgGradient\": \"Elementets kontrastforhold kunne ikke detekteres på grunn av en bakgrunnsgradient\",\n        \"imgNode\": \"Elementets kontrastforhold kunne ikke detekteres, fordi elementet inneholder et bildeelement\",\n        \"bgOverlap\": \"Elementets kontrastforhold kunne ikke detekteres på grunn av overlappende elementer\",\n        \"default\": \"Kan ikke regne ut kontrastforhold\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"'autocomplete'-verdien er brukt på et passende element\",\n      \"fail\": \"'autocomplete'-verdien er ikke passende for denne type input\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"'autocomplete'-attributtet er korrekt formatert\",\n      \"fail\": \"'autocomplete'-attributtet er ikke formatert korrekt\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"'Accesskey' attributtverdien er unik\",\n      \"fail\": \"Dokumentet har flere elementer med den samme 'accesskey'-attributtet\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Elementet inneholder fokuserbare elementer\",\n      \"fail\": \"Elementet skal ha fokuserbart innhold\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Ingen fokuserbare elementer inne i elementet\",\n      \"fail\": \"Fokuserbart innhold skal slås av eller fjernes fra sidens DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Elementet er fokuserbart\",\n      \"fail\": \"Elementet skal være fokuserbart\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Elementet er ikke i sidens tabulerings-rekkefølge ('tab order') eller har tilgjengelig tekst\",\n      \"fail\": \"Elementet er i sidens tabulerings-rekkefølge ('tab order') og har ikke tilgjengelig tekst\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Ingen fokuserbare elementer inne i element\",\n      \"fail\": \"Fokuserbart innholdet skal ha tabindex='-1' eller fjernes fra sidens DOM\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} landemerke er på det øverste nivå.\",\n      \"fail\": \"${data.role} landemerke er innholdet i et annet landemerke.\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Siden har minst én overskrift på nivå 1\",\n      \"fail\": \"Siden skal ha minst én overskrift på nivå 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Dokumentet har minst ét 'main'-landemerke\",\n      \"fail\": \"Dokumentet har ikke et 'main'-landemerke\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"Dokumentet har ikke mer enn ett 'banner'-landemerke\",\n      \"fail\": \"Dokumentet har mer enn ett 'banner-'landemerke\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"Dokumentet har ikke mer enn ett 'contentinfo'-landemerke\",\n      \"fail\": \"Dokumentet har mer enn ett 'contentinfo'-landemerke\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"Dokumentet har ikke mer enn ett 'main'-landemerke\",\n      \"fail\": \"Dokumentet har mer enn ett 'main'-landemerke\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Elementet har ikke en 'tabindex' som er større enn 0\",\n      \"fail\": \"Elementet har en 'tabindex' som er større enn 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Elementet har en alt-attributt med gyldig verdi\",\n      \"fail\": \"Elementet har en alt-attributt som kun inneholder et mellomrom, som ikke ignoreres av alle skjermlesere\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Elementet duplisere ikke den eksisterende tekst fra <img> elementets alt-tekst\",\n      \"fail\": \"Elementet inneholder et <img> element med alt-tekst, som duplisere den eksisterende tekst\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Form-elementet har en eksplisitt <label>\",\n      \"fail\": \"Form-elementet har ikke en eksplisitt <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Hjelpeteksten ('title' eller 'aria-describedby') duplisere ikke label-teksten\",\n      \"fail\": \"Hjelpeteksten ('title' eller 'aria-describedby') er den samme som label-teksten\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Form-elementet har en synlig og eksplisitt <label>\",\n      \"fail\": \"Form-elementet har en eksplisitt <label>, som er skjult\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Form-elementet har en implisitt (inne i en) <label>\",\n      \"fail\": \"Form-elementet har ikke en implisitt (inne i en) <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Elementet inneholder synlig tekst som del av dets tilgjengelige navn\",\n      \"fail\": \"Tekst i elementet er ikke inkluderet i det tilgjengelige navn\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Form-feltet har ikke flere label-elementer\",\n      \"incomplete\": \"Flere label-elementer er ikke bredt understøttet i tilgjengelighetsteknologier. Sørg for at det første label-element inneholder all nødvendige informasjon.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Form-elementet bruker ikke utelukkende 'title'-attributtet som label\",\n      \"fail\": \"Kun 'title'-attributtet er brukt som label for form-elementet\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Landemerker skal ha en unik rolle eller 'role'/'label'/'title'-attributtkombinasjon (som tilgjengelig navn)\",\n      \"fail\": \"Et landemerke skal ha en unik 'aria-label', 'aria-labelledby', eller 'title' for å gjøre landemerker atskillelige\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> elementet har en 'lang'-attributt\",\n      \"fail\": \"<html> elementet har ikke en 'lang'-attributt\"\n    },\n    \"valid-lang\": {\n      \"pass\": \"'lang'-attributtet sin verdi er inkluderet i listen over godkjente språk\",\n      \"fail\": \"'lang'-attributtet sin verdi er ikke inkluderet i listen over gyldige språk\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Attributtene 'lang' og 'xml:lang' har samme språk / dialekt\",\n      \"fail\": \"Attributtene 'lang' og 'xml:lang' har ikke samme språk / dialekt\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Beskrivelsesliste-elementet har et <dl>-over-element\",\n      \"fail\": \"Beskrivelsesliste-elementet har ikke et <dl>-over-element\"\n    },\n    \"listitem\": {\n      \"pass\": \"Elementet i listen har et <ul>, <ol> eller 'role=\\\"list\\\"'-over-element\",\n      \"fail\": \"Elementet i listen har ikke et <ul>, <ol> eller 'role=\\\"list\\\"'-over-element\"\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Elementet i listen har kun direkte under-elementer, som er tillatt i <dt> eller <dd> elementer\",\n      \"fail\": \"Elementet i listen har direkte under-elementer, som ikke er tillatt inde i <dt> eller <dd> elementer\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Elementet i listen har kun direkte under-elementer, som er tillatt i <li> elementer\",\n      \"fail\": \"Elementet i listen har direkte under-elementer, som ikke er tillatt inne i <li> elementer\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Det ikke-tomme elementet har både <dt> og <dd> elementer\",\n      \"fail\": \"Det ikke-tomme elementet mangler minst ett <dt> element etterfulgt av minst ett <dd> element\"\n    },\n    \"caption\": {\n      \"pass\": \"Multimedia-elementet har et track med undertekster\",\n      \"incomplete\": \"Sjekk at undertekster er tilgjengelige for elementet\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Denne <iframe> ble testet av axe-core\",\n      \"fail\": \"Denne <iframe> kunne ikke testes av axe-core\",\n      \"incomplete\": \"Denne <iframe> er enda ikke testet av axe-core\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Skjermen kan styres, og sidens skjermretning er ikke låst med med 'css-orientation-lock'\",\n      \"fail\": \"Skjermretningen ('css-orientation-lock') er låst, hvilket gjør sidevisning vanskelig å styre på skjermen\",\n      \"incomplete\": \"Det kan ikke detekteres om skjermretning er låst (med 'css-orientation-lock')\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta>-tagget begrenser ikke høy zoom på mobile enheter\",\n      \"fail\": \"<meta>-tagget begrenser zoom på mobile enheter\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta>-tagget slår ikke zoom av på mobile enheter\",\n      \"fail\": \"${data} på <meta>-tagget slår zoom av på mobile enheter\"\n    },\n    \"header-present\": {\n      \"pass\": \"Siden har en overskrift (header)\",\n      \"fail\": \"Siden har ikke en overskrift (header)\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Rekkefølgen av overskiftsnivåer er korrekt\",\n      \"fail\": \"Rekkefølgen av overskiftsnivåer er ikke korrekt\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Passende 'skip link' funnet\",\n      \"fail\": \"Ingen passende 'skip link' funnet\"\n    },\n    \"landmark\": {\n      \"pass\": \"Siden har en 'landemerke' region\",\n      \"fail\": \"Siden har ikke en 'landemerke' region\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta>-tagget oppdaterer ikke siden med det samme\",\n      \"fail\": \"<meta>-tagget oppdaterer siden med det samme\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> elementer er ikke stylet som en overskrift\",\n      \"fail\": \"Overskrifts-elementer (<h1>, <h2>, osv.) skal brukes i stedet for stylet <p> elementer\"\n    },\n    \"region\": {\n      \"pass\": \"Alt innholdet på siden er lagt inn under landemerker\",\n      \"fail\": \"Deler av sidens innholdet er ikke lagt inn under et landemerke\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Elementet som 'skip link' refererer til, eksisterer\",\n      \"incomplete\": \"Element som 'skip link' refererer til, skal bli synlig ved aktivering\",\n      \"fail\": \"Manglende 'skip link' 'target'-attributt\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Elementets 'title'-attributt er unik\",\n      \"fail\": \"Elementets 'title'-attributt er ikke unik\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Dokumentet har ingen aktive elementer som deler den samme 'id'-attributt\",\n      \"fail\": \"Dokumentet har aktive elementer som deler den samme 'id'-attributten: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Dokumentet har ingen elementer referert med ARIA eller labels, som deler den samme 'id'-attributten\",\n      \"fail\": \"Dokumentet har flere elementer referert med ARIA med den samme 'id'-attributten: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Dokumentet har ingen statiske elementer som deler den samme 'id'-attributtet\",\n      \"fail\": \"Dokumentet har flere statiske elementer med den samme 'id'-attributtet\"\n    },\n    \"aria-label\": {\n      \"pass\": \"'aria-label'-attributt er til stede og er ikke tomt\",\n      \"fail\": \"'aria-label'-attributt eksisterer ikke eller er tomt\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"'aria-labelledby'-attributt eksisterer og refererer elementer som er synlige for skjermlesere\",\n      \"fail\": \"'aria-labelledby'-attributt eksisterer ikke, eller refererer elementer, som ikke eksisterer - eller refererer til elementer, som er tomme\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Ingen inline styling med '!important', som påvirker tekst-mellomrom-avstand er spesifisert\",\n      \"fail\": {\n        \"singular\": \"Fjern '!important' fra inline stylings ${data.values}, da overskrivning av dette ikke er understøttet i de fleste nettlesere\",\n        \"plural\": \"Fjern '!important' fra inline styling ${data.values}, da overskrivning av dette ikke er understøttet i de fleste nettlesere\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Elementet har indre tekst, som er synlig for skjermlesere\",\n      \"fail\": \"Elementet har ingen indre tekst, som er synlig for skjermlesere\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Dokumentet har et <title> element som ikke er tomt\",\n      \"fail\": \"Dokumentet mangler et <title> element med innhold\"\n    },\n    \"exists\": {\n      \"pass\": \"Elementet eksisterer ikke\",\n      \"fail\": \"Elementet eksisterer\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Elementet har en 'alt'-attributt\",\n      \"fail\": \"Elementet har ikke en 'alt'-attributt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Elementet har tekst som er synlig for skjermlesere\",\n      \"fail\": \"Elementet har ikke tekst som er synlig for skjermlesere\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Elementet er ikke synlig\",\n      \"fail\": \"Elementet er synlig\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Elementet har 'alt'-attributt med innhold\",\n      \"fail\": \"Elementet har ingen 'alt'-attributt, eller 'alt'-attributtet er tomt\"\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Elementet har ikke en 'value'-attributt\",\n        \"has-label\": \"Elementet har en 'verdi'-attributt med innhold\"\n      },\n      \"fail\": \"Elementet har en 'value'-attributt, og 'verdi'-attributtet er tomt\"\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Elementet har en 'title'-attributt\",\n      \"fail\": \"Elementet har ingen 'title'-attributt, eller 'title'-attributtet er tomt\"\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Elementet har 'value'-attributt med innholdet\",\n      \"fail\": \"Elementet har ingen 'value'-attributt, eller 'value'-attributtet er tomt\"\n    },\n    \"role-none\": {\n      \"pass\": \"Elementets standard semantikk ble overskrevet med attributtet 'role=\\\"none\\\"'\",\n      \"fail\": \"Elementets standard semantikk ble ikke overskrevet med attributtet 'role=\\\"none\\\"'\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Elementets standard semantikk ble overskrevet med attributtet 'role=\\\"presentation\\\"'\",\n      \"fail\": \"Elementets standard semantikk ble ikke overskrevet med attributtet 'role=\\\"presentation\\\"'\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Den første raden i tabellen er ikke brukt som en beskrivelse ('caption')\",\n      \"fail\": \"Det første elementet i tabellen skal være en beskrivelse (<caption>) i stedet for en celle\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"'Scope'-attributtet skal kun brukes på tabellens header-elementer (<th>)\",\n      \"fail\": \"'Scope'-attributtet skal kun brukes på tabellens header-elementer (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"innholdet av 'summary'-attributtet og <caption> elementet er ikke identisk\",\n      \"fail\": \"innholdet av 'summary'-attributtet og <caption> elementet er identisk\"\n    },\n    \"scope-value\": {\n      \"pass\": \"'scope'-attributtet brukes korrekt\",\n      \"fail\": \"Verdien av 'scope'-attributtet skal kun være 'row' eller 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Alle data-celler med innhold har en tabell-header\",\n      \"fail\": \"Noen data-celler med innhold har ikke tabell-headers\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Header-attributtet brukes kun til å referere til andre celler i den samme tabel\",\n      \"fail\": \"Header-attributtet brukes ikke kun til å referere til andre celler i den samme tabel\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Alle tabellens header-celler refererer til data-celler\",\n      \"fail\": \"Ikke alle tabellens header-celler refererer til data-celler\",\n      \"incomplete\": \"Noen av tabellens data-celler mangler eller er tomme\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Alt innholdet på siden er blitt analyseret.\",\n      \"fail\": \"Der var problemer med å analysere deler av innholdet på denne side.\",\n      \"incomplete\": \"Der er skjult innhold på siden som ikke ble analyseret. Du må gjøre dette innholdet synlig for å kunne analysere det.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Rett en av følgende: {{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Rett alle de følgende: {{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe kunne ikke finne årsaken. Tid for å finne frem utviklerverktøyet(element inspector)!\"\n}\n"
  },
  {
    "path": "locales/pl.json",
    "content": "{\n  \"lang\": \"pl\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Wartość każdego atrybutu accessskey jest unikalna.\",\n      \"help\": \"Wartość atrybutu accessskey musi być unikalna.\"\n    },\n    \"area-alt\": {\n      \"description\": \"Elementy <area> w graficznych mapach odnośników mają tekst zastępczy.\",\n      \"help\": \"Elementy aktywne <area> muszą mieć tekst alternatywny.\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Użyte atrybuty ARIA są dozwolone dla elementu z określoną rolą.\",\n      \"help\": \"Elementy mogą używać tylko dozwolonych atrybutów ARIA.\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Atrybut role ma odpowiednią wartość dla danego elementu.\",\n      \"help\": \"Rola ARIA musi być odpowiednia dla danego elementu.\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Upewnij się, że aria-braillelabel i aria-brailleroledescription mają odpowiednik niebrajlowski\",\n      \"help\": \"Atrybuty aria-braille muszą mieć odpowiednik niebrajlowski.\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Każdy element button, link i menuitem ARIA ma dostępną nazwę.\",\n      \"help\": \"Przyciski, łącza, pozycje menu ARIA muszą mieć dostępną nazwę.\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Upewnij się, że atrybuty ARIA są używane zgodnie ze specyfikacją roli elementu.\",\n      \"help\": \"Atrybuty ARIA muszą być używane w sposób określony dla roli elementu\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Upewnij się, że elementy nie używają przestarzałych ról.\",\n      \"help\": \"Przestarzałe role ARIA nie mogą być używane.\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Każde okno dialogowe ARIA i węzeł alertdialog ma dostępną nazwę.\",\n      \"help\": \"Okno dialogowe ARIA i węzły alertdialog muszą mieć dostępną nazwę.\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Element body nie ma atrybutu aria-hidden='true'.\",\n      \"help\": \"Element body nie może mieć atrybutu aria-hidden='true'.\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Elementy z aria-hidden=true nie mogą obejmować elementów przyjmujących fokus.\",\n      \"help\": \"Ukryty element ARIA nie może zawierać elementów przyjmujących fokus.\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Każde pole ARIA do wprowadzania danych ma dostępną nazwę.\",\n      \"help\": \"Pola ARIA do wprowadzania danych muszą mieć dostępną nazwę.\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Każdy element ARIA meter (licznik) ma dostępną nazwę.\",\n      \"help\": \"Liczniki ARIA (meter) muszą mieć dostępne nazwy.\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Każdy element ARIA progressbar (pasek postępu) ma dostępną nazwę.\",\n      \"help\": \"Paski postępu ARIA (progressbar) muszą mieć dostępne nazwy.\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Upewnij się, że atrybuty ARIA nie są zabronione dla roli elementu.\",\n      \"help\": \"Elementy mogą używać tylko dozwolonych atrybutów ARIA.\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Elementy z rolą ARIA mają wszystkie wymagane atrybuty aria-*\",\n      \"help\": \"Wymagane atrybuty ARIA muszą istnieć.\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Elementy z atrybutem ARIA role, które muszą zawierać elementy potomne z wymaganym atrybutem role, zawierają je.\",\n      \"help\": \"Niektóre role ARIA muszą obejmować określone dzieci.\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Elementy z atrybutem ARIA role, które wymagają elementu rodzica z atrybutem role, są zawarte w elementach z takimi rolami.\",\n      \"help\": \"Elementy z niektórymi atrybutami role ARIA muszą znajdować się wewnątrz nadrzędnego elementu rodzica z wymaganym atrybutem role.\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Atrybut aria-roledescription jest używany tylko w elementach, które mają rolę określoną domyślnie lub jawnie.\",\n      \"help\": \"Użyj aria-roledescription w elementach o roli semantycznej.\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Wartości atrybutu role są poprawne.\",\n      \"help\": \"Stosowane role ARIA muszą mieć poprawne wartości.\"\n    },\n    \"aria-text\": {\n      \"description\": \"Atrybut role=\\\"text\\\" jest używany dla elementów, które nie mają potomków przyjmujących fokus\",\n      \"help\": \"Element z atrybutem role=\\\"text\\\" nie może mieć potomków przyjmujących fokus.\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Każdy element ARIA toggle (przełącznik) ma dostępną nazwę.\",\n      \"help\": \"Przełączniki ARIA  (toggle) muszą mieć dostępną nazwę.\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Każdy element ARIA tooltip (podpowiedź) ma dostępną nazwę.\",\n      \"help\": \"Podpowiedzi ARIA (tooltip) muszą mieć dostępną nazwę.\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Każdy element ARIA treeitem (węzeł drzewa) ma dostępną nazwę.\",\n      \"help\": \"Węzły drzewa elementów ARIA (treeitem) muszą mieć dostępną nazwę.\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Wszystkie atrybuty ARIA mają poprawne wartości.\",\n      \"help\": \"Atrybuty ARIA muszą mieć poprawne wartości.\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Wszystkie atrybuty aria-* mają poprawne nazwy.\",\n      \"help\": \"Atrybuty ARIA muszą mieć poprawne nazwy.\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Elementy <audio> mają napisy rozszerzone.\",\n      \"help\": \"Elementy <audio> muszą mieć ścieżkę z napisami.\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Pola formularza, które zbierają dane osobowe, mają poprawne atrybuty autocomplete\",\n      \"help\": \"Atrybut autocomplete musi być użyty poprawnie.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Odstępy w tekście można regulować za pomocą własnych arkuszy stylów.\",\n      \"help\": \"Odstępy w tekście muszą być regulowane za pomocą własnych arkuszy stylów.\"\n    },\n    \"blink\": {\n      \"description\": \"Elementy <blink> nie są używane.\",\n      \"help\": \"Elementy <blink> są przestarzałe i nie mogą być używane.\"\n    },\n    \"button-name\": {\n      \"description\": \"Każdy przycisk ma odróżniającą go dostępną nazwę.\",\n      \"help\": \"Przyciski muszą mieć odróżniający je tekst nazwy.\"\n    },\n    \"bypass\": {\n      \"description\": \"Każda strona ma co najmniej jeden mechanizm, który pozwala ominąć nawigację i przejść od razu do treści.\",\n      \"help\": \"Strona musi mieć środki do ominięcia powtarzających bloków treści.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Kontrast między kolorami pierwszego planu i tła spełnia wyższe progi współczynnika kontrastu WCAG 2 AAA.\",\n      \"help\": \"Elementy muszą spełniać wyższe progi współczynnika kontrastu kolorów\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Kontrast między kolorami pierwszego planu i tła spełnia progi kontrastu WCAG 2 AA.\",\n      \"help\": \"Elementy muszą mieć wystarczający kontrast kolorów.\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Treść nie jest przypisana do żadnej konkretnej orientacji wyświetlacza i można ją obsługiwać we wszystkich orientacjach wyświetlacza.\",\n      \"help\": \"Zapytania medialne nie są wykorzystywane do blokowania orientacji wyświetlacza.\"\n    },\n    \"definition-list\": {\n      \"description\": \"Elementy <dl> mają poprawną strukturę.\",\n      \"help\": \"Elementy <dl> mogą bezpośrednio zawierać tylko odpowiednio uporządkowane grupy <dt> i <dd> oraz elementy <script>, <template> lub <div>.\"\n    },\n    \"dlitem\": {\n      \"description\": \"Elementy <dt> i <dd> znajdują się bezpośrednio w <dl>.\",\n      \"help\": \"Elementy <dt> i <dd> są wewnątrz elementu <dl>.\"\n    },\n    \"document-title\": {\n      \"description\": \"Każdy dokument HTML ma niepusty element <title>.\",\n      \"help\": \"Dokumenty muszą mieć element <title> pomagający w nawigacji.\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Wartość każdego atrybutu id aktywnych elementów jest unikalna.\",\n      \"help\": \"ID aktywnych elementów, które otrzymują fokus, muszą być unikalne.\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Każdy atrybut id używany w ARIA i w etykietach jest unikalny.\",\n      \"help\": \"Identyfikatory stosowane w ARIA i etykietach muszą być unikalne.\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Wartość każdego atrybutu id jest unikalna.\",\n      \"help\": \"Wartość atrybutu id musi być unikalna.\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Nagłówki mają odróżniający je tekst.\",\n      \"help\": \"Nagłówki nie mogą być puste.\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Upewnij się, że nagłówki tabel mają opisowy tekst.\",\n      \"help\": \"Tekst nagłówka tabeli nie może być pusty.\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Elementy w porządku otrzymywania fokusu mają odpowiednią rolę.\",\n      \"help\": \"Elementy w porządku otrzymywania fokusu muszą mieć rolę odpowiednią dla treści interaktywnych.\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Żadne pole formularza nie ma wielu etykiet (elementów label).\",\n      \"help\": \"Pole formularza nie może mieć wielu elementów label.\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Elementy <frame> i <iframe> z treścią przyjmującą fokus nie mają tabindex=-1.\",\n      \"help\": \"Ramki z treścią przyjmującą fokus nie mogą mieć tabindex=-1.\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Elementy <iframe> i <frame> muszą być testowane ze skryptem axe-core.\",\n      \"help\": \"Ramki muszą być testowane ze skryptem axe-core.\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Elementy <iframe> i <frame> mają unikalny atrybut title.\",\n      \"help\": \"Ramki muszą mieć unikalny atrybut title.\"\n    },\n    \"frame-title\": {\n      \"description\": \"Elementy <iframe> i <frame> mają niepusty atrybut title.\",\n      \"help\": \"Ramki muszą mieć niepusty atrybut title.\"\n    },\n    \"heading-order\": {\n      \"description\": \"Kolejność nagłówków jest semantycznie poprawna.\",\n      \"help\": \"Poziomy nagłówków powinny wzrastać tylko o jeden.\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Na stronie są ukryte treści.\",\n      \"help\": \"Ukrytych treści na stronie nie można analizować.\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Każdy dokument HTML ma atrybut lang.\",\n      \"help\": \"Element <html> musi mieć atrybut lang.\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Atrybut lang elementu <html> ma poprawną wartość.\",\n      \"help\": \"Element <html> musi mieć poprawną wartość atrybutu lang.\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Element HTML z poprawnym atrybutem lang xml:lang ma ten sam podstawowy język strony.\",\n      \"help\": \"Element HTML z lang i xml:lang musi mieć ten sam język podstawowy.\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Łącza o tej samej dostępnej nazwie służą temu samemu celowi.\",\n      \"help\": \"Łącza o tej samej nazwie mają ten sam cel.\"\n    },\n    \"image-alt\": {\n      \"description\": \"Elementy <img> mają atrybut alt lub rolę none albo presentation.\",\n      \"help\": \"Obrazy muszą mieć tekst alternatywny.\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Tekst alternatywny obrazu nie jest powtarzany w tekście.\",\n      \"help\": \"Tekst alternatywny obrazów nie powinien być powtarzany w tekście.\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Przyciski input type=button mają odróżniający je tekst.\",\n      \"help\": \"Przyciski input type=button muszą mieć odróżniający je tekst.\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Elementy <input type=\\\"image\\\"> mają tekst alternatywny.\",\n      \"help\": \"Przyciski graficzne muszą mieć tekst alternatywny.\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Elementy oznakowane swoją treścią mają swój widoczny tekst jako część ich dostępnej nazwy\",\n      \"help\": \"Elementy muszą mieć swój widoczny tekst jako część ich dostępnej nazwy.\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Żaden element formularza nie jest oznaczony wyłącznie za pomocą atrybutu title lub aria-describedby.\",\n      \"help\": \"Element formularza powinien mieć widoczną etykietę.\"\n    },\n    \"label\": {\n      \"description\": \"Każdy element formularza ma etykietę.\",\n      \"help\": \"Element formularza musi mieć etykietę.\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Obszar banner jest obszarem kluczowym najwyższego poziomu.\",\n      \"help\": \"Punkt orientacyjny banner nie może być zawarty wewnątrz innego obszaru kluczowego.\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Obszary aside lub z role=complementary są obszarami kluczowymi najwyższego poziomu.\",\n      \"help\": \"Punkt orientacyjny complementary nie może być zawarty wewnątrz innego obszaru kluczowego.\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Obszar kluczowy z role=contentinfo jest obszarem kluczowym najwyższego poziomu.\",\n      \"help\": \"Punkt orientacyjny contentinfo nie może być zawarty wewnątrz innego obszaru kluczowego.\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Obszar main jest obszarem kluczowym najwyższego poziomu.\",\n      \"help\": \"Punkt orientacyjny main nie może być zawarty wewnątrz innego obszaru kluczowego.\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Dokument ma co najwyżej jeden punkt orientacyjny banner.\",\n      \"help\": \"Dokument nie może mieć więcej niż jednego obszaru kluczowego banner.\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Dokument ma co najwyżej jeden punkt orientacyjny contentinfo.\",\n      \"help\": \"Dokument nie może mieć więcej niż jednego obszaru kluczowego contentinfo.\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Dokument ma co najwyżej jeden punkt orientacyjny main.\",\n      \"help\": \"Dokument nie może mieć więcej niż jednego obszaru kluczowego main.\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Dokument ma punkt orientacyjny main.\",\n      \"help\": \"Dokument może mieć tylko jeden obszar kluczowy main.\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Punkty orientacyjne są unikalne\",\n      \"description\": \"Punkty orientacyjne (obszary kluczowe) mają unikalną rolę lub kombinację roli/etykiety/tytułu (tj. dostępną nazwę).\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Łącza można rozróżniać bez opierania się na kolorze.\",\n      \"help\": \"Łącza muszą być odróżnialne od sąsiadującego tekstu w sposób, który nie opiera się na kolorze.\"\n    },\n    \"link-name\": {\n      \"description\": \"Łącza mają odróżniający je tekst.\",\n      \"help\": \"Łącza muszą mieć odróżniający je tekst.\"\n    },\n    \"list\": {\n      \"description\": \"Listy mają poprawną strukturę.\",\n      \"help\": \"Elementy <ul> i <ol> mogą bezpośrednio zawierać tylko elementy <li>, <script> lub <template>.\"\n    },\n    \"listitem\": {\n      \"description\": \"Elementy <li> są używane semantycznie.\",\n      \"help\": \" Elementy <li> muszą być zawarte bezpośrednio w <ul> lub <ol>.\"\n    },\n    \"marquee\": {\n      \"description\": \"Elementy <marquee> nie są używane.\",\n      \"help\": \"Elementy <marquee> są przestarzałe i nie mogą być używane.\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Upewnij się, że <meta http-equiv=\\\"refresh\\\"> nie jest używany do opóźnionego odświeżania\",\n      \"help\": \"Nie wolno stosować opóźnionego odświeżania\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Znacznik <meta http-equiv=\\\"refresh\\\"> nie jest używany do opóźnionego odświeżania.\",\n      \"help\": \"Opóźnione odświeżanie poniżej 20 godzin nie może być stosowane.\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Element <meta name=\\\"viewport\\\"> umożliwia znaczne powiększanie.\",\n      \"help\": \"Użytkownicy mogą powiększać i skalować tekst do 500%.\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Element <meta name=\\\"viewport\\\"> nie wyłącza skalowania i powiększania tekstu.\",\n      \"help\": \"Powiększanie i skalowanie nie może być wyłączone.\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Upewnij się, że kontrolki interaktywne nie są zagnieżdżone, ponieważ nie zawsze są ogłaszane przez czytniki ekranu lub mogą powodować problemy technologii wspomagających z fokusem.\",\n      \"help\": \"Kontrolki interaktywne nie mogą być zagnieżdżone\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Elementy <video> lub <audio> nie odtwarzają automatycznie dźwięku przez dłużej niż 3 sekundy bez mechanizmu, który go zatrzymuje lub wycisza.\",\n      \"help\": \"Elementy <video> lub <audio> nie odtwarzają dźwięku automatycznie.\"\n    },\n    \"object-alt\": {\n      \"description\": \"Elementy <object> mają tekst zastępczy.\",\n      \"help\": \"Elementy <object> muszą mieć alternatywę tekstową.\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Pogrubienie, kursywa i rozmiar czcionki nie są używane do stylizacji elementów <p> jako nagłówków.\",\n      \"help\": \"Stylizowane elementy <p> nie mogą być używane jako nagłówki.\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Strona lub co najmniej jedna z jej ramek, zawiera nagłówek pierwszego poziomu.\",\n      \"help\": \"Strona musi zawierać nagłówek poziomu 1.\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Elementy oznaczone jako prezentacyjne nie powinny mieć globalnego ARIA ani tabindex, aby zapewnić, że wszystkie czytniki ekranu je zignorują.\",\n      \"help\": \"Upewnij się, że elementy oznaczone jako prezentacyjne są konsekwentnie ignorowane.\"\n    },\n    \"region\": {\n      \"description\": \"Cała treść strony jest objęta przez punkty orientacyjne.\",\n      \"help\": \"Cała treść strony musi być zawarta w obszarach kluczowych.\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Elementy z [role='img'] mają tekst alternatywny.\",\n      \"help\": \"Elementy z [role='img'] muszą mieć tekst alternatywny.\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Atrybut scope w tabelach jest stosowany poprawnie.\",\n      \"help\": \"Atrybut scope ma poprawną wartość.\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Elementy, których treść można przewijać, są osiągalne za pomocą klawiatury.\",\n      \"help\": \"Obszary przewijane muszą być osiągalne z klawiatury.\"\n    },\n    \"select-name\": {\n      \"description\": \"Element select ma dostępną nazwę.\",\n      \"help\": \"Element select musi mieć dostępną nazwę.\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Graficzne mapy odnośników (mapy obrazkowe) obsługiwane po stronie serwera nie są używane.\",\n      \"help\": \"Nie wolno używać map odnośników po stronie serwera.\"\n    },\n    \"skip-link\": {\n      \"description\": \"Wszystkie łącza pomijania mają cel przyjmujący fokus.\",\n      \"help\": \"Cel łącza pomijającego powinien istnieć i przyjmować fokus.\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Elementy <svg> z rolą img, graphics-document lub graphics-symbol mają dostępny tekst.\",\n      \"help\": \"Elementy svg z rolą img muszą mieć tekst alternatywny.\"\n    },\n    \"tabindex\": {\n      \"description\": \"Wartości atrybutów tabindex nie są większe niż 0.\",\n      \"help\": \"Elementy nie powinny mieć wartości tabindex większej niż zero.\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Podpis tabeli (element <caption>) nie może zawierać takiego samego tekstu co atrybut summary.\",\n      \"help\": \"Atrybut summary w tabeli ma inny tekst niż element caption.\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Tabele używają jako podpisu elementu <caption>.\",\n      \"help\": \"Komórki danych i nagłówkowe w tabeli danych nie są używane do umieszczania podpisu tabeli.\"\n    },\n    \"target-size\": {\n      \"description\": \"Sprawdź, czy cel dotykowy ma wystarczający rozmiar i przestrzeń wokół.\",\n      \"help\": \"Wszystkie cele dotykowe muszą mieć rozmiar 24px lub pozostawiać wystarczającą ilość miejsca wokół.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Wszystkie niepuste komórki danych w tabelach danych większych niż 3 na 3 mają jeden lub więcej nagłówków tabeli.\",\n      \"help\": \"Każdy niepusty element <td> w dużej tabeli musi mieć powiązany nagłówek tabeli\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Każda komórka tabeli używająca atrybutu headers odwołuje się do innej komórki w tej tabeli.\",\n      \"help\": \"Wszystkie komórki z atrybutem headers odnoszą się tylko do innych komórek tej samej tabeli.\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Wszystkie elementy th i elementy z role=columnheader/rowheader mają komórki danych, które opisują.\",\n      \"help\": \"Każdy nagłówek tabeli w tabeli danych musi odnosić się do komórek danych.\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Atrybuty lang mają poprawne wartości.\",\n      \"help\": \"Atrybuty lang muszą mieć poprawną wartość.\"\n    },\n    \"video-caption\": {\n      \"description\": \"Elementy <video> mają napisy rozszerzone.\",\n      \"help\": \"Elementy <video> muszą mieć napisy rozszerzone.\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Role abstrakcyjne nie są wykorzystywane.\",\n      \"fail\": {\n        \"singular\": \"Rola abstrakcyjna nie może być użyta bezpośrednio: ${data.values}.\",\n        \"plural\": \": Role abstrakcyjne nie mogą być używane bezpośrednio: ${data.values}.\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Atrybuty ARIA są używane poprawnie dla zdefiniowanej roli.\",\n      \"fail\": {\n        \"singular\": \"Atrybut ARIA nie jest dozwolony: ${data.values}.\",\n        \"plural\": \": Atrybuty ARIA nie są dozwolone: ${data.values}.\"\n      },\n      \"incomplete\": \"Sprawdź, czy nie ma problemu, jeśli atrybut ARIA jest ignorowany w tym elemencie: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"Rola ARIA jest dozwolona dla danego elementu.\",\n      \"fail\": {\n        \"singular\": \"Rola ARIA ${data.values} nie jest dozwolona dla tego elementu.\",\n        \"plural\": \": Role ARIA ${data.values} nie są dozwolone dla tego elementu.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Rola ARIA ${data.values} musi być usunięta, gdy element jest widoczny, ponieważ nie jest to dozwolone dla elementu.\",\n        \"plural\": \": Role ARIA ${data.values} muszą być usunięte, gdy element jest widoczny, ponieważ nie są one dozwolone dla elementu.\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"Element ma atrybut aria-busy\",\n      \"fail\": \"Element używa aria-busy=\\\"true\\\" podczas pokazywania ładowania\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"Atrybut ARIA jest dozwolony\",\n      \"fail\": {\n        \"checkbox\": \"Usuń aria-checked lub ustaw jego wartość na \\\"${data.checkState}\\\", aby dopasować ją do rzeczywistego stanu pola wyboru.\",\n        \"rowSingular\": \"Ten atrybut jest obsługiwany przez wiersze siatki, ale nie przez ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"Te atrybuty są obsługiwane przez wiersze siatki, ale nie przez ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Istnieje aria-errormessage oraz elementy referencyjne widoczne dla czytników ekranowych, które wykorzystują wspieraną technikę aria-errormessage.\",\n      \"fail\": {\n        \"singular\": \"Wartość aria-errormessage ${data.values} musi używać techniki ogłoszenia wiadomości (np. aria-live, aria-describedby, role=alert, etc.).\",\n        \"plural\": \"Wartości aria-errormessage ${data.values} muszą używać techniki ogłoszenia wiadomości (np. aria-live, aria-describedby, role=alert, etc.).\",\n        \"hidden\": \"Wartość aria-errormessage ${data.values} nie może odwoływać się do ukrytego elementu.\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Upewnij się, że wartość aria-errormessage ${data.values} odnosi się do istniejącego elementu.\",\n        \"plural\": \"Upewnij się, że wartości aria-errormessage ${data.values} odnoszą się do istniejących elementów.\",\n        \"idrefs\": \"Nie można określić, czy na stronie istnieje element aria-errormessage: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Nie ma żadnego atrybutu aria-hidden w elemencie body dokumentu.\",\n      \"fail\": \"Atrybut aria-hidden=true nie może być użyty w elemencie body dokumentu.\"\n    },\n    \"aria-level\": {\n      \"pass\": \"Wartość aria-level jest poprawna\",\n      \"incomplete\": \"Wartości aria-level większe niż 6 nie są obsługiwane we wszystkich kombinacjach czytników ekranu i przeglądarek\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"Atrybut ARIA jest dozwolony\",\n      \"fail\": {\n        \"hasRolePlural\": \"Atrybuty ${data.prohibited} nie mogą być używane z rolą \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"Atrybut ${data.prohibited} nie może być użyty z rolą \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"Atrybuty ${data.prohibited} nie mogą być używane w ${data.nodeName} bez poprawnego atrybutu roli.\",\n        \"noRoleSingular\": \"Atrybut ${data.prohibited} nie może być użyty w ${data.nodeName} bez poprawnego atrybutu roli.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"Atrybut ${data.prohibited} nie jest dobrze obsługiwany przez rolę \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"Atrybuty ${data.prohibited} nie są dobrze obsługiwane przez role \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"Atrybut ${data.prohibited} nie jest dobrze obsługiwany przez ${data.nodeName} bez poprawnego atrybutu roli.\",\n        \"noRolePlural\": \"Atrybuty ${data.prohibited} nie są dobrze obsługiwane przez ${data.nodeName} bez poprawnego atrybutu roli.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Wszystkie wymagane atrybuty ARIA istnieją.\",\n      \"fail\": {\n        \"singular\": \"Wymagany atrybut ARIA nie istnieje: ${data.values}.\",\n        \"plural\": \"Wymagane atrybuty ARIA nie istnieją: ${data.values}.\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Wymagane dzieci ARIA istnieją.\"\n      },\n      \"fail\": {\n        \"singular\": \"Wymagana rola dziecka ARIA nie istnieje: ${data.values}.\",\n        \"plural\": \"Wymagane role dzieci ARIA nie istnieją: ${data.values\",\n        \"unallowed\": \"Element ma dzieci, które nie są dozwolone: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Należy dodać oczekiwaną rolę dziecka ARIA: ${data.values}.\",\n        \"plural\": \"Należy dodać oczekiwane role dzieci ARIA: ${data.values}.\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Wymagana rola rodzica ARIA istnieje.\",\n      \"fail\": {\n        \"singular\": \"Wymagana rola rodzica ARIA nie istnieje: ${data.values}.\",\n        \"plural\": \"Wymagane role rodziców ARIA nie istnieją: ${data.values}.\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"Atrybut aria-roledescription jest używany z obsługiwaną rolą semantyczną.\",\n      \"incomplete\": \"Sprawdź, czy aria-roledescription jest ogłaszany przez obsługiwane czytniki ekranu.\",\n      \"fail\": \"Nadaj temu elementowi rolę, która obsługuje aria-roledescription.\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"Atrybut ARIA jest obsługiwany.\",\n      \"fail\": \"Atrybut ARIA nie jest dostatecznie obsługiwany przez czytniki ekranu i inne technologie wspomagające: ${data.values}.\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Wartości atrybutu ARIA są poprawne.\",\n      \"fail\": {\n        \"singular\": \"Niepoprawna wartość atrybutu ARIA: ${data.values}\",\n        \"plural\": \"Niepoprawne wartości atrybutu ARIA: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"Identyfikator elementu atrybutu ARIA nie istnieje na stronie: ${data.needsReview}\",\n        \"noIdShadow\": \"ID elementu atrybutu ARIA nie istnieje na stronie lub jest potomkiem innego drzewa shadow DOM: ${data.needsReview}\",\n        \"ariaCurrent\": \"Wartość atrybutu ARIA jest niepoprawna i będzie traktowana jako \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Nie można określić, czy atrybut ARIA element ID istnieje na stronie: ${data.needsReview}\",\n        \"empty\": \"Wartość atrybutu ARIA jest ignorowana, gdy jest pusty: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"Nazwa atrybutu ARIA jest poprawna.\",\n      \"fail\": {\n        \"singular\": \"Niepoprawna nazwa atrybutu ARIA: ${data.values}.\",\n        \"plural\": \"Niepoprawne nazwy atrybutów ARIA: ${data.values}.\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"Atrybut aria-braillelabel jest użyty w elemencie z dostępnym tekstem.\",\n      \"fail\": \"Atrybut aria-braillelabel jest użyty w elemencie, który nie ma dostępnego tekstu.\",\n      \"incomplete\": \"Nie można wyliczyć dostępnego tekstu.\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"Atrybut aria-brailleroledescription nie jest używany w elemencie, który nie ma dostępnego tekstu.\",\n      \"fail\": {\n        \"noRoleDescription\": \"Atrybut aria-brailleroledescription jest użyty w elemencie bez atrybutu aria-roledescription.\",\n        \"emptyRoleDescription\": \"Atrybut aria-brailleroledescription jest użyty w elemencie z pustym atrybutem aria-roledescription.\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"Rola ARIA nie jest przestarzała\",\n      \"fail\": \"Użyta rola jest przestarzała: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Użyto tylko jednej wartości roli.\",\n      \"fail\": \"Użyj tylko jednej wartości roli, ponieważ role rezerwowe nie są obsługiwane w starszych przeglądarkach.\",\n      \"incomplete\": \"Używaj tylko roli 'presentation' lub 'none', ponieważ są one synonimami.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"Element ma ogólny atrybut ARIA: ${data.values}.\",\n        \"plural\": \": Element ma ogólne atrybuty ARIA: ${data.values}.\"\n      },\n      \"fail\": \"Element nie ma ogólnego atrybutu ARIA\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Element ma rolę widżetu.\",\n      \"fail\": \"Element nie ma roli widżetu.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"Rola ARIA jest poprawna.\",\n      \"fail\": {\n        \"singular\": \"Rola musi być jedną z poprawnych ról ARIA: ${data.values}.\",\n        \"plural\": \": Role muszą być jedną z poprawnych ról ARIA: ${data.values}.\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"Element przyjmuje fokus.\",\n      \"fail\": \"Element nie przyjmuje fokusu.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Nie ma rozbieżności między label a dostępną nazwą.\",\n      \"incomplete\": \"Sprawdź, czy label nie musi być częścią nazwy pola ARIA ${data}.\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"Rola ARIA jest obsługiwana.\",\n      \"fail\": \"Zastosowana rola nie jest szeroko obsługiwana w czytnikach ekranu i technologiach wspomagających: ${data.values}.\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Element w porządku otrzymywania fokusu ma poprawną semantykę.\",\n      \"fail\": \"Element w porządku otrzymywania fokusu ma niepoprawną semantykę.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Element ma wystarczający kontrast kolorów ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} (kolor pierwszego planu: ${data.fgColor}, kolor tła: ${data.bgColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} pomiędzy kolorem pierwszego planu a kolorem cienia tekstu (kolor pierwszego planu: ${data.fgColor}, kolor cienia tekstu: ${data.shadowColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} pomiędzy kolorem cienia tekstu a kolorem tła (kolor cienia tekstu: ${data.shadowColor}, kolor tła: ${data.bgColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Nie można określić współczynnika kontrastu.\",\n        \"bgImage\": \"Nie można określić koloru tła elementu, ponieważ element ma obraz tła.\",\n        \"bgGradient\": \"Nie można określić koloru tła elementu, ponieważ element ma gradientowe tło.\",\n        \"imgNode\": \"Nie można określić koloru tła elementu, ponieważ element zawiera węzeł obrazu.\",\n        \"bgOverlap\": \"Nie można określić koloru tła elementu, ponieważ nakłada się na niego inny element.\",\n        \"fgAlpha\": \"Nie można określić koloru pierwszego planu elementu z powodu przezroczystości alfa.\",\n        \"elmPartiallyObscured\": \"Nie można określić koloru tła elementu, ponieważ jest on częściowo zasłonięty przez inny element.\",\n        \"elmPartiallyObscuring\": \"Nie można określić koloru tła elementu, ponieważ częściowo nakłada się on na inne elementy.\",\n        \"outsideViewport\": \"Nie można określić koloru tła elementu, ponieważ znajduje się on poza obszarem operacyjnym.\",\n        \"equalRatio\": \"Element ma współczynnik kontrastu 1:1 z tłem.\",\n        \"shortTextContent\": \"Treść elementu jest zbyt krótka, aby określić, czy jest to rzeczywista treść tekstowa.\",\n        \"nonBmp\": \"Treść elementu zawiera tylko znaki nietekstowe.\",\n        \"pseudoContent\": \"Nie można określić koloru tła elementu, ponieważ jest to pseudoelement.\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"Element ma wystarczający kontrast kolorów  ${data.contrastRatio}\",\n        \"hidden\": \"Element jest ukryty\"\n      },\n      \"fail\": {\n        \"default\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} (kolor pierwszego planu: ${data.fgColor}, kolor tła: ${data.bgColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} pomiędzy kolorem pierwszego planu a kolorem cienia tekstu (kolor pierwszego planu: ${data.fgColor}, kolor cienia tekstu: ${data.shadowColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Element ma niewystarczający kontrast kolorów ${data.contrastRatio} pomiędzy kolorem  cienia tekstu a kolorem tła (kolor cienia tekstu: ${data.shadowColor}, kolor tła: ${data.bgColor}, rozmiar czcionki: ${data.fontSize}, waga czcionki: ${data.fontWeight}). Oczekiwany współczynnik kontrastu: ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Nie można określić współczynnika kontrastu.\",\n        \"bgImage\": \"Nie można określić koloru tła elementu, ponieważ element ma obraz tła.\",\n        \"bgGradient\": \"Nie można określić koloru tła elementu, ponieważ element ma gradientowe tło.\",\n        \"imgNode\": \"Nie można określić koloru tła elementu, ponieważ element zawiera węzeł obrazu.\",\n        \"bgOverlap\": \"Nie można określić koloru tła elementu, ponieważ nakłada się na niego inny element.\",\n        \"fgAlpha\": \"Nie można określić koloru pierwszego planu elementu z powodu przezroczystości alfa.\",\n        \"elmPartiallyObscured\": \"Nie można określić koloru tła elementu, ponieważ jest on częściowo zasłonięty przez inny element.\",\n        \"elmPartiallyObscuring\": \"Nie można określić koloru tła elementu, ponieważ częściowo nakłada się on na inne elementy.\",\n        \"outsideViewport\": \"Nie można określić koloru tła elementu, ponieważ znajduje się on poza obszarem operacyjnym.\",\n        \"equalRatio\": \"Element ma współczynnik kontrastu 1:1 z tłem.\",\n        \"shortTextContent\": \"Treść elementu jest zbyt krótka, aby określić, czy jest to rzeczywista treść tekstowa.\",\n        \"nonBmp\": \"Treść elementu zawiera tylko znaki nietekstowe.\",\n        \"pseudoContent\": \"Nie można określić koloru tła elementu, ponieważ jest to pseudoelement.\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Łącza mogą być odróżnione od sąsiadującego tekstu poprzez wizualną stylizację\",\n      \"incomplete\": {\n        \"default\": \"Sprawdź, czy łącze wymaga stylizacji, aby odróżnić je od sąsiadującego tekstu.\",\n        \"pseudoContent\": \"Sprawdź, czy pseudostyl łącza jest wystarczający, aby odróżnić je od sąsiadującego tekstu.\"\n      },\n      \"fail\": \"Łącze nie ma żadnej stylizacji (np. podkreślenia), która odróżniałaby je od sąsiadującego tekstu\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Łącza można odróżnić od sąsiadującego tekstu w inny sposób niż za pomocą koloru.\",\n      \"fail\": {\n        \"fgContrast\": \"Łącze ma niewystarczający kontrast kolorów ${data.contrastRatio}:1 z sąsiadującym tekstem. (Minimalny kontrast to: ${data.requiredContrastRatio}:1, kolor tekstu łącza: ${data.nodeColor}, kolor sąsiadującego tekstu: ${data.parentColor})\",\n        \"bgContrast\": \"Tło łącza ma niewystarczający kontrast kolorów ${data.contrastRatio} (Minimalny kontrast to:  ${data.requiredContrastRatio}:1, kolor tła łącza: ${data.nodeBackgroundColor}, sąsiadujący kolor tła: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Nie można określić współczynnika kontrastu.\",\n        \"bgContrast\": \"Nie można określić współczynnika kontrastu elementu. Sprawdź, czy nie ma odrębnego stylu stanów hover/fokus.\",\n        \"bgImage\": \"Nie można określić współczynnika kontrastu elementu ze względu na obraz tła.\",\n        \"bgGradient\": \"Nie można określić współczynnika kontrastu elementu ze względu na gradient tła.\",\n        \"imgNode\": \"Nie można określić współczynnika kontrastu elementu, ponieważ element zawiera węzeł obrazu.\",\n        \"bgOverlap\": \"Nie można określić współczynnika kontrastu elementu ze względu na nakładanie się elementów.\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"Wartość autocomplete jest odpowiednia dla tego typu pola formularza.\",\n      \"fail\": \"Wartość autocomplete jest niewłaściwa dla tego typu pola formularza.\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"Atrybut autocomplete jest sformatowany poprawnie.\",\n      \"fail\": \"Atrybut autocomplete jest sformatowany niepoprawnie.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Wartość atrybutu accesskey jest unikalna.\",\n      \"fail\": \"Dokument ma wiele elementów z tym samym klawiszem dostępu.\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Element zawiera elementy przyjmujące fokus.\",\n      \"fail\": \"Element powinien przyjmować fokus.\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"W elemencie nie ma elementów przyjmujących fokus.\",\n      \"incomplete\": \"Sprawdź, czy elementy, na których można ustawić fokus, natychmiast otrzymują wskaźnik fokusu\",\n      \"fail\": \"Treść z możliwością ustawiania fokusu powinna być wyłączona lub usunięta z DOM.\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Element może przyjmować fokus.\",\n      \"fail\": \"Element powinien przyjmować fokus.\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Nie ma elementów przyjmujących fokus, gdy otwarte jest okno modalne.\",\n      \"incomplete\": \"Sprawdź, czy elementy, które mogą przyjmować fokus, nie są tabulowane w aktualnym stanie.\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Element nie znajduje się w porządku tabulacji lub ma dostępny tekst.\",\n      \"fail\": \"Element znajduje się w porządku tabulacji i nie ma dostępnego tekstu.\",\n      \"incomplete\": \"Nie można ustalić, czy element ma dostępną nazwę.\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Nie ma elementów przyjmujących fokus wewnątrz elementu.\",\n      \"incomplete\": \"Sprawdź, czy elementy, na których można ustawić fokus, natychmiast otrzymują wskaźnik fokusu\",\n      \"fail\": \"Treść przyjmująca fokus powinna mieć tabindex=-1 lub być usunięta z DOM.\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"Element nie ma potomków przyjmujących fokus\",\n      \"fail\": \"Element ma elementy potomne przyjmujące fokus\",\n      \"incomplete\": \"Nie można ustalić, czy element ma elementy potomne\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"Punkt orientacyjny ${data.role} jest na najwyższym poziomie.\",\n      \"fail\": \"Punkt orientacyjny {data.role} jest wewnątrz innego obszaru kluczowego.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"Element nie ma potomków przyjmujących fokus\",\n      \"fail\": {\n        \"default\": \"Element ma elementy potomne przyjmujące fokus\",\n        \"notHidden\": \"Użycie ujemnej wartości tabindex  elementu wewnątrz interaktywnej kontrolki nie zapobiega ustawianiu na elemencie fokusu przez technologie wspomagające (nawet z 'aria-hidden=true')\"\n      },\n      \"incomplete\": \"Nie można ustalić, czy element ma elementy potomne\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"Strona ma co najmniej jeden nagłówek 1. poziomu.\",\n      \"fail\": \"Strona musi mieć nagłówek 1. poziomu.\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"Dokument ma co najmniej jeden główny punkt orientacyjny.\",\n      \"fail\": \"Dokument nie ma głównego punktu orientacyjnego.\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"W dokumencie nie ma więcej niż jeden obszar kluczowy banner.\",\n      \"fail\": \"W dokumencie jest więcej niż jeden obszar kluczowy banner.\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"W dokumencie nie ma więcej niż jeden obszar kluczowy contentinfo.\",\n      \"fail\": \"W dokumencie jest więcej niż jeden obszar kluczowy contentinfo.\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"W dokumencie nie ma więcej niż jeden obszar kluczowy main.\",\n      \"fail\": \"W dokumencie jest więcej niż jeden obszar kluczowy main.\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Element nie ma wartości tabindex większej niż 0.\",\n      \"fail\": \"Element nie ma wartości tabindex większą od 0.\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Element ma poprawną wartość atrybutu alt.\",\n      \"fail\": \"Element ma atrybut alt zawierający tylko znak spacji, który nie przez wszystkie czytniki ekranu jest ignorowany .\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Element nie powiela tekstu istniejącego w atrybucie alt elementu img.\",\n      \"fail\": \"Element powiela tekst istniejący w atrybucie alt elementu img.\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Element formularza ma jawnie określoną label.\",\n      \"fail\": \"Element formularza nie ma jawnie określonej label.\",\n      \"incomplete\": \"Nie można określić, czy element formularza ma jawnie określoną label.\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Tekst pomocy (title lub aria-describedby) nie powiela tekstu etykiety.\",\n      \"fail\": \"Tekst pomocy (title lub aria-describedby) jest taki sam jak tekst etykiety.\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Element formularza ma widoczną jednoznaczną etykietę label.\",\n      \"fail\": \"Element formularza ma jednoznaczną etykietę label, która jest ukryta.\",\n      \"incomplete\": \"Nie można określić, czy element formularza ma jawną etykietę (label), gdy jest ukryta.\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Element formularza ma dorozumianą etykietę (jest owinięty w label).\",\n      \"fail\": \"Element formularza nie ma dorozumianej etykiety (nie jest owinięty w label).\",\n      \"incomplete\": \"Nie można określić, czy element formularza ma dorozumianą etykietę (jest owinięty w label).\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Widoczny tekst elementu jest częścią dostępnej nazwy elementu.\",\n      \"fail\": \"Widoczny tekst wewnątrz elementu nie jest częścią dostępnej nazwy.\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Pole formularza nie ma wielu label.\",\n      \"incomplete\": \"Technologie wspomagające nie obsługują wystarczająco dobrze wielu elementów label. Upewnij się, że pierwsza etykieta zawiera wszystkie niezbędne informacje.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Element formularza nie używa wyłącznie atrybutu title jako swojej etykiety.\",\n      \"fail\": \"Tylko atrybut title jest używany do wygenerowania etykiety dla elementu formularza.\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Punkty orientacyjne muszą mieć unikalną kombinację roli lub roli etykiety i tytułu (tj. dostępną nazwę).\",\n      \"fail\": \"Punkt orientacyjny musi mieć unikalną aria-label, aria-labelledby lub title, aby był rozpoznawalny.\"\n    },\n    \"has-lang\": {\n      \"pass\": \"Element <html> ma atrybut lang.\",\n      \"fail\": {\n        \"noXHTML\": \"Atrybut xml:lang nie jest poprawny na stronach HTML, użyj atrybutu lang.\",\n        \"noLang\": \"Element <html> nie ma atrybutu lang.\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Wartość atrybutu lang jest na liście poprawnych kodów języków.\",\n      \"fail\": \"Wartości atrybutu lang nie ma na liście poprawnych kodów języków.\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Atrybuty lang i xml:lang mają ten sam język podstawowy.\",\n      \"fail\": \"Atrybuty lang i xml:lang nie mają tego samego języka podstawowego.\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Element listy opisowej ma element nadrzędny dl.\",\n      \"fail\": \"Pozycja listy opisowej nie ma elementu nadrzędnego dl.\"\n    },\n    \"listitem\": {\n      \"pass\": \"Element listy ma ul, ol lub role=\\\"list\\\" jako bezpośredni element rodzicielski.\",\n      \"fail\": {\n        \"default\": \"Element listy nie ma nadrzędnego elementu ul lub ol.\",\n        \"roleNotValid\": \"Element listy nie ma nadrzędnego elementu ul, ol, ani nadrzędnego elementu z role=\\\"list\\\".\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Element dl ma wewnątrz tylko dozwolone jako bezpośrednie elementy potomne (dzieci) elementy dt lub dd.\",\n      \"fail\": \"Element listy ma wewnątrz bezpośrednie elementy dzieci, które nie są dozwolone wewnątrz listy opisowej.\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Element lista ma wewnątrz tylko dozwolone jako bezpośrednie elementy potomne (dzieci) elementy li.\",\n      \"fail\": \"Element listy ma bezpośrednie elementy potomne, które nie są dozwolone: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Gdy elemet dl nie jest pusty, ma zarówno elementy dt, jak i dd.\",\n      \"fail\": \"Element nie jest pusty, ale nie ma co najmniej jednego elementu dt, po którym następuje co najmniej jeden element dd.\"\n    },\n    \"caption\": {\n      \"pass\": \"Element multimedialny ma ścieżkę z napisami rozszerzonymi.\",\n      \"incomplete\": \"Sprawdź, czy istnieją napisy rozszerzone dla tego elementu.\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Ramka iframe została przetestowana z axe-core.\",\n      \"fail\": \"Ramka iframe nie mogła być przetestowana z axe-core.\",\n      \"incomplete\": \"Nadal wymagane jest przetestowanie ramki iframe z axe-core.\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"Element <video> lub <audio> nie emituje dźwięku przez czas dłuższy niż dozwolony lub posiada mechanizm sterujący.\",\n      \"fail\": \"Element <video> lub <audio> emituje dźwięk przez czas dłuższy niż dozwolony i nie posiada mechanizmu sterowania.\",\n      \"incomplete\": \"Sprawdź, czy <video> lub <audio> nie emituje dźwięku dłuższej niż przez dozwolony czas lub czy ma mechanizm sterujący.\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Wyświetlacz działa sprawnie, orientacja nie jest blokowana.\",\n      \"fail\": \"Zastosowano blokadę orientacji wyświetlacza, w rezultacie wyświetlacz nie działa sprawnie.\",\n      \"incomplete\": \"Nie można blokować orientacji wyświetlacza za pomocą ustawień CSS.\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"Znacznik <meta> nie ogranicza znacznego powiększenia na urządzeniach przenośnych.\",\n      \"fail\": \"Znacznik <meta> znacznik ogranicza powiększanie na urządzeniach przenośnych.\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"Znacznik <meta> nie wyłącza powiększania na urządzeniach przenośnych.\",\n      \"fail\": \"${data} w znaczniku <meta> wyłącza powiększanie na urządzeniach przenośnych.\"\n    },\n    \"target-offset\": {\n      \"pass\": \"Cel ma wystarczające odsunięcie od swojego najbliższego sąsiada: (${data.closestOffset}px, które powinno wynosić co najmniej ${data.minOffset}px)\",\n      \"fail\": \"Cel ma niewystarczające odsunięcie od najbliższego sąsiada: (${data.closestOffset}px, które powinno wynosić co najmniej ${data.minOffset}px)\",\n      \"incomplete\": {\n        \"default\": \"Element z ujemnym tabindeksem ma niewystarczające odsunięcie od najbliższego sąsiada: (${data.closestOffset}px, które powinno wynosić co najmniej${data.minOffset}px). Czy to jest cel?\",\n        \"nonTabbableNeighbor\": \"Cel ma niewystarczające odsunięcie od sąsiada z ujemnym tabindeksem: (${data.closestOffset}px, które powinno wynosić co najmniej ${data.minOffset}px). Czy sąsiad jest celem?\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Kontrolka ma wystarczający rozmiar (${data.width}px na ${data.height}px, który powinnien wynosić co najmniej ${data.minSize}px na ${data.minSize}px)\",\n        \"obscured\": \"Kontrolka jest ignorowana, ponieważ jest całkowicie zasłonięta i nie można jej kliknąć.\"\n      },\n      \"fail\": {\n        \"default\": \"Cel ma niewystarczający rozmiar (${data.width}px na ${data.height}px, który powinnien wynosić co najmniej ${data.minSize}px na ${data.minSize}px)\",\n        \"partiallyObscured\": \"Cel ma niewystarczający rozmiar, ponieważ jest częściowo przesłonięty (najmniejsza przestrzeń to ${data.width}px na ${data.height}px, powinna wynosić ${data.minSize}px na ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"Element z ujemnym tabindex ma niewystarczający rozmiar (${data.width}px na ${data.height}px, powinnien wynosić co najmniej ${data.minSize}px na ${data.minSize}px). Czy to jest cel?\",\n        \"contentOverflow\": \"Rozmiar elementu nie mógł być dokładnie określony z powodu przepełnienia zawartości\",\n        \"partiallyObscured\": \"Element z ujemnym tabindex ma niewystarczający rozmiar, ponieważ jest częściowo przesłonięty (najmniejsza przestrzeń to ${data.width}px na ${data.height}px, powinna wynosić co najmniej ${data.minSize}px na ${data.minSize}px). Czy to jest cel?\",\n        \"partiallyObscuredNonTabbable\": \"Cel ma niewystarczający rozmiar, ponieważ jest częściowo przesłonięty przez sąsiada o ujemnym tabindeksie (najmniejsza przestrzeń to ${data.width}px na ${data.height}px, powinna wynosić co najmniej ${data.minSize}px na ${data.minSize}px). Czy sąsiad jest celem?\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"Strona ma nagłówek.\",\n      \"fail\": \"Strona nie ma nagłówka.\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Kolejność nagłówków jest poprawna.\",\n      \"fail\": \"Kolejność nagłówków jest niepoprawna.\",\n      \"incomplete\": \"Nie można określić poprzedniego nagłówka\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Nie ma żadnych innych łączy o tej samej nazwie, które kierują na inny adres URL.\",\n      \"incomplete\": \"Sprawdź, czy łącza mają ten sam cel lub są celowo niejednoznaczne.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Znaleziono poprawne łącze pomijające.\",\n      \"fail\": \"Nie znaleziono poprawnych łączy pomijających.\"\n    },\n    \"landmark\": {\n      \"pass\": \"Strona ma punkt orientacyjny (obszar kluczowy).\",\n      \"fail\": \"Strona nie ma żadnego punktu orientacyjnego (obszaru kluczowego).\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"Znacznik <meta> nie powoduje natychmiastowego odświeżenia strony\",\n      \"fail\": \"Znacznik <meta> tag wymusza czasowe odświeżenie strony\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Znacznik <meta> nie odświeża od razu strony.\",\n      \"fail\": \"Znacznik <meta> wymusza odświeżenie strony.\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Elementy <p> nie są stylizowane na nagłówki.\",\n      \"fail\": \"Zamiast stylizowanych na nagłówki elementów <p> muszą być użyte nagłówki semantyczne.\",\n      \"incomplete\": \"Nie można określić, czy elementy <p> są stylizowane na nagłówki\"\n    },\n    \"region\": {\n      \"pass\": \"Cała treść strony jest zawarta w obszarach kluczowych.\",\n      \"fail\": \"Pewna część strony znajduje się poza punktami orientacyjnymi.\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Cel łącza pomijajacego istnieje.\",\n      \"incomplete\": \"Cel łącza pomijania powinien być widoczny po aktywacji.\",\n      \"fail\": \"Nie istnieje cel łącza pomijającego.\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Atrybut title elementu jest unikalny.\",\n      \"fail\": \"Atrybut title elementu nie jest unikalny.\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"W dokumencie nie ma aktywnych elementów z takim samym atrybutem id.\",\n      \"fail\": \"W dokumencie są aktywne elementy z tą samą wartością atrybutu id: ${data}.\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"W dokumencie nie ma żadnych elementów, do których odwołuje się ARIA lub <label>, które mają ten sam atrybut id.\",\n      \"fail\": \"W dokumencie jest wiele elementów, do których odnosi się ARIA z tym samym atrybutem id: ${data}.\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"W dokumencie nie ma wielu elementów statycznych z takim samym atrybutem id.\",\n      \"fail\": \"W dokumencie jest wiele elementów statycznych z takim samym atrybutem id: ${data}.\"\n    },\n    \"aria-label\": {\n      \"pass\": \"Atrybut aria-label istnieje i nie jest pusty.\",\n      \"fail\": \"Atrybut aria-label nie istnieje lub jest pusty.\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"Atrybut aria-labelledby oraz elementy, do których się odwołuje, istnieją i są widoczne dla czytników ekranu.\",\n      \"fail\": \"Atrybut aria-labelledby nie istnieje albo  elementy, do których odwołuje atrybut aria-labelledby, nie istnieją lub są puste.\",\n      \"incomplete\": \"Spowoduj, aby istniał element, do którego istnieje odwołanie w atrybucie aria-labelldeby.\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Nie określono żadnych stylów wewnętrznych (inline) z '!important', które wpływają na odstępy w tekście.\",\n      \"fail\": {\n        \"singular\": \"Usuń dyrektywę '!important' ze stylu inline ${data.values}, ponieważ nadpisywanie tego nie jest obsługiwane przez większość przeglądarek.\",\n        \"plural\": \"Usuń dyrektywy '!important' ze stylów inline ${data.values}, ponieważ nadpisywanie tego nie jest obsługiwane przez większość przeglądarek.\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Element ma tekst wewnętrzny widoczny dla czytników ekranu.\",\n      \"fail\": \"Element nie ma wewnętrznego tekstu, który jest widoczny dla czytników ekranu.\",\n      \"incomplete\": \"Nie można określić, czy element ma elementy potomne.\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Dokument ma niepusty element <title>.\",\n      \"fail\": \"Dokument nie ma niepustego elementu <title>.\"\n    },\n    \"exists\": {\n      \"pass\": \"Element nie istnieje.\",\n      \"incomplete\": \"Element istnieje.\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Element ma atrybut alt.\",\n      \"fail\": \"Element nie ma atrybutu alt.\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Element ma tekst, który jest widoczny dla czytników ekranu.\",\n      \"fail\": \"Element nie ma tekstu, który jest widoczny dla czytników ekranu.\",\n      \"incomplete\": \"Nie można określić, czy element ma elementy dzieci.\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"Odstępy między literami (letter-spacing) w atrybucie style nie są ustawione na !important lub spełniają minimum\",\n      \"fail\": \"Odstępy między literami w atrybucie style nie mogą używać !important lub muszą mieć ${data.minValue}em (aktualnie: ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"Właściwość line-height w atrybucie style nie jest ustawiona na !important lub spełnia minimum.\",\n      \"fail\": \"Właściwość line-height w atrybucie style nie może używać !important lub musi mieć ${data.minValue}em (aktualnie: ${data.value}em).\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"Odstępy miedzy wyrazami (word-spacing) w atrybucie style nie są ustawione na !important lub spełniają minimum\",\n      \"fail\": \"Odstępy miedzy wyrazami (word-spacing) w atrybucie style nie mogą używać !important lub muszą mieć ${data.minValue}em (aktualnie: ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Element nie jest widoczny.\",\n      \"fail\": \"Element jest widoczny.\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Element ma niepusty atrybut alt.\",\n      \"fail\": {\n        \"noAttr\": \"Element nie ma atrybutu alt.\",\n        \"emptyAttr\": \"Element ma pusty atrybut alt.\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Element nie posiada atrybutu bez określonej wartości.\",\n        \"has-label\": \"Element ma atrybuty bez określonej wartości.\"\n      },\n      \"fail\": \"Element ma atrybut wartości, a atrybut wartości jest pusty.\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"Element ma atrybut placeholder.\",\n      \"fail\": {\n        \"noAttr\": \"Element nie ma atrybutu placeholder.\",\n        \"emptyAttr\": \"Element ma pusty atrybut placeholder.\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Element ma atrybut title.\",\n      \"fail\": {\n        \"noAttr\": \"Element nie ma atrybutu title.\",\n        \"emptyAttr\": \"Element ma pusty atrybut title.\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Element ma atrybut z niepustą wartością.\",\n      \"fail\": {\n        \"noAttr\": \"Element nie ma atrybutu z wartością.\",\n        \"emptyAttr\": \"Element ma atrybut z pustą wartością.\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"Domyślna semantyka elementu została zastąpiona przez role=\\\"${data.role}\\\".\",\n      \"fail\": {\n        \"default\": \"Domyślna semantyka elementu nie została nadpisana przez role=\\\"none\\\" ani role=\\\"presentation\\\".\",\n        \"globalAria\": \"Rola elementu nie jest prezentacyjna, ponieważ ma on ogólny atrybut ARIA.\",\n        \"focusable\": \"Rola elementu nie jest prezentacyjna, ponieważ może on przyjmować fokus.\",\n        \"both\": \"Rola elementu nie jest prezentacyjna, ponieważ ma on ogólny atrybut ARIA i może przyjmować fokus.\",\n        \"iframe\": \"Użycie atrybutu \\\"title\\\" na elemencie ${data.nodeName} z rolą prezentacyjną zachowuje się niespójnie pomiędzy czytnikami ekranu.\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"Domyślna semantyka elementu została zastąpiona przez role=\\\"none\\\".\",\n      \"fail\": \"Domyślna semantyka elementu nie została nadpisana przez role=\\\"none\\\".\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Domyślna semantyka elementu została zastąpiona przez role=\\\"presentation\\\".\",\n      \"fail\": \"Domyślna semantyka elementu nie została nadpisana przez role=\\\"presentation\\\".\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"Element ma dziecko, które jest tytułem.\",\n      \"fail\": {\n        \"noTitle\": \"Element nie ma dziecka, które jest tytułem.\",\n        \"emptyTitle\": \"Element title dziecka jest pusty.\"\n      },\n      \"incomplete\": \"Nie można ustalić, czy element ma dziecko, które jest tytułem.\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Pierwszy wiersz tabeli nie jest używany jako podpis.\",\n      \"fail\": \"Pierwszym elementem potomnym (dzieckiem) w tabeli powinien być caption zamiast komórki tabeli.\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Atrybut scope jest używany tylko w elementach nagłówków tabeli (<th>).\",\n      \"fail\": \"W HTML 5, atrybuty scope mogą być używane tylko w elementach nagłówkowych tabeli (<th>).\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Treści atrybutu summary i elementu <caption> nie są powielane.\",\n      \"fail\": \"Treści atrybutu summary i elementu <caption> są identyczne.\",\n      \"incomplete\": \"Nie można określić, czy element <table> ma caption\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Atrybut scope ma poprawną wartość.\",\n      \"fail\": \"Wartością atrybutu scope może być tylko 'row' lub 'col'.\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Wszystkie niepuste komórki danych mają nagłówki tabeli.\",\n      \"fail\": \"Niektóre niepuste komórki danych nie mają nagłówków tabeli.\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Atrybut headers jest używany wyłącznie w odniesieniu do innych komórek w tabeli.\",\n      \"incomplete\": \"Atrybut headers jest pusty.\",\n      \"fail\": \"Atrybut headers nie jest używany wyłącznie w odniesieniu do innych komórek w tabeli.\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Wszystkie komórki nagłówkowe tabeli odnoszą się do komórek danych.\",\n      \"fail\": \"Nie wszystkie komórki nagłówkowe tabeli odnoszą się do komórek danych.\",\n      \"incomplete\": \"Komórki danych tabeli są puste lub nie istnieją.\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Wszystkie treści na stronie zostały przeanalizowane.\",\n      \"fail\": \"Były problemy z analizą treści na tej stronie.\",\n      \"incomplete\": \"Na tej stronie jest ukryta treść, która nie została przeanalizowana. Musisz uruchomić wyświetlanie tej treści, aby ją przeanalizować.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Napraw następujące elementy: {{~it:value}}\\n {{=value.split('\\\\n').join('\\\\n ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Napraw wszystkie poniższe: {{~it:value}}\\n {{=value.split('\\\\n').join('\\\\n ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"AXE nie potrafił określić powodu. Czas użyć inspektora elementów!\"\n}\n"
  },
  {
    "path": "locales/pt_BR.json",
    "content": "{\n  \"lang\": \"pt_BR\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Certifique-se de que cada valor do atributo 'acesskey' é único\",\n      \"help\": \"O valor do atributo 'accesskey' deve ser único\"\n    },\n    \"area-alt\": {\n      \"description\": \"Certifique-se de que elementos <area> de mapas de imagem tenham um texto alternativo\",\n      \"help\": \"Elementos <area> ativos devem ter um texto alternativo\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Certifique-se de que os atributos ARIA são permitidos para a função de um elemento\",\n      \"help\": \"Os elementos devem usar apenas atributos ARIA permitidos\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Certifique-se de que o atributo 'role' tem um valor apropriado para o elemento\",\n      \"help\": \"A função ARIA deve ser apropriada para o elemento\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Certifique-se de que cada botão, link e item de menu ARIA tenha um nome acessível\",\n      \"help\": \"Comandos ARIA devem ter um nome acessível\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Certifique-se de que cada nó de diálogo e diálogo de alerta ARIA tenha um nome acessível\",\n      \"help\": \"Nós de diálogo e diálogo de alerta ARIA devem ter um nome acessível\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Certifique-se de que aria-hidden='true' não está presente no elemento <body>.\",\n      \"help\": \"aria-hidden='true' não deve estar presente no elemento <body>\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Certifique-se de que elementos com aria-hidden não contenham elementos focalizáveis\",\n      \"help\": \"Elementos ocultados através de ARIA não devem conter elementos focalizáveis\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Certifique-se de que cada campo de entrada ARIA tenha um nome acessível\",\n      \"help\": \"Campos de entrada ARIA tenham um nome acessível\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Certifique-se de que cada nó de medição ARIA tenha um nome acessível\",\n      \"help\": \"Nós de medição ARIA devem ter um nome acessível\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Certifique-se de que cada nó de barra de progresso ARIA tenha um nome acessível\",\n      \"help\": \"Nós de barra de progresso ARIA devem ter um nome acessível\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Certifique-se de que elementos com funções ARIA tenham todos os atributos ARIA necessários\",\n      \"help\": \"Os atributos ARIA necessários devem ser fornecidos\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Certifique-se de que elementos com uma função ARIA que requer funções filhas as contenham\",\n      \"help\": \"Certas funções ARIA devem conter filhos específicos\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Certifique-se de que elementos com uma função ARIA que requer funções pais estejam contidas nestas\",\n      \"help\": \"Certas funções ARIA devem estar contidas em pais específicos\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Certifique-se de que 'aria-roledescription' somente é utilizado em elementos com uma função implícita ou explícita\",\n      \"help\": \"Use 'aria-roledescription' em elementos com função semântica\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Certifique-se de que todos os elementos com um atributo 'role' utilizem um valor válido\",\n      \"help\": \"Os ARIA 'roles' utilizados devem ter valores válidos\"\n    },\n    \"aria-text\": {\n      \"description\": \"Certifique-se de que \\\"role=text\\\" é usado em elementos sem descendentes focalizáveis\",\n      \"help\": \"\\\"role=text\\\" não deve ter descendentes focalizáveis\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Certifique-se de que cada botão de alternância ARIA tenha um nome acessível\",\n      \"help\": \"Botões de alternância ARIA tenham um nome acessível\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Certifique-se de que cada nó de dica de tela ARIA tenha um nome acessível\",\n      \"help\": \"Nós de dica de tela ARIA devem ter um nome acessível\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Certifique-se de que cada nó de 'treeitem' ARIA deve ter um nome acessível\",\n      \"help\": \"Nós de 'treeitem' ARIA devem ter um nome acessível\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Certifique-se de que cada atributo ARIA tenha um valor válido\",\n      \"help\": \"Atributos ARIA devem ter valores válidos\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Certifique-se de que atributos que se iniciem com aria- são atributos ARIA válidos\",\n      \"help\": \"Atributos ARIA devem ter nomes válidos\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Certifique-se de que elementos <audio> tenham legendas\",\n      \"help\": \"Elementos <audio> devem ter uma trilha de legenda\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Certifique-se de que o atributo 'autocomplete' esteja correto e adequado para o campo de formulário\",\n      \"help\": \"Atributo 'autocomplete' deve ser usado corretamente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Certifique-se de que o espaçamento de texto definido via atributos de estilo pode ser ajustado com folhas de estilo personalizadas\",\n      \"help\": \"O espaçamento de texto 'inline' deve ser ajustável com folhas de estilo personalizadas\"\n    },\n    \"blink\": {\n      \"description\": \"Certifique-se de que elementos <blink> não são utilizados\",\n      \"help\": \"Elementos <blink> foram descontinuados e não devem ser utilizados\"\n    },\n    \"button-name\": {\n      \"description\": \"Certifique-se de que botões tenham texto discernível\",\n      \"help\": \"Botões devem ter texto discernível\"\n    },\n    \"bypass\": {\n      \"description\": \"Certifique-se de que cada página tenha ao menos um mecanismo para o usuário ignorar a navegação e ir direto ao conteúdo\",\n      \"help\": \"A página deve ter meios para ignorar blocos repetidos\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Certifique-se de que o contraste entre as cores de primeiro plano e de fundo atenda aos limites de relação de contraste WCAG 2 AA\",\n      \"help\": \"Os elementos devem ter contraste de cor suficiente\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Certifique-se de que o contraste entre as cores de primeiro plano e de fundo atenda aos limites de relação de contraste WCAG 2 AAA\",\n      \"help\": \"Os elementos devem ter contraste de cor suficiente\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Certifique-se de que o conteúdo não está bloqueado para nenhuma orientação de tela específica, e que o mesmo seja operável em todas as orientações de tela\",\n      \"help\": \"CSS Media queries não são usadas para bloquear a orientação de tela\"\n    },\n    \"definition-list\": {\n      \"description\": \"Certifique-se de que elementos <dl> estão estruturados corretamente\",\n      \"help\": \"Elementos <dl> devem conter diretamente apenas grupos <dt> e <dd> propriamente ordenados, ou elementos <script> ou <template>\"\n    },\n    \"dlitem\": {\n      \"description\": \"Certifique-se de que elementos <dt> e <dd> estão contidos em um elemento <dl>\",\n      \"help\": \"Elementos <dt> e <dd> devem estar contidos em um elemento <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Certifique-se de que cada documento HTML contenha um elemento <title> não vazio\",\n      \"help\": \"Documentos devem ter um elemento <title> para ajudar na navegação\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Certifique-se de que cada valor do atributo 'id' de elementos ativos seja único\",\n      \"help\": \"IDs de elementos ativos devem ser únicos\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Certifique-se de que cada valor do atributo 'id' usado no ARIA e em rótulos seja único\",\n      \"help\": \"IDs usados no ARIA e em 'labels' devem ser únicos\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Certifique-se de que cada valor do atributo 'id' seja único\",\n      \"help\": \"O valor do atributo 'id' deve ser único\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Certifique-se de que os títulos tenham texto discernível\",\n      \"help\": \"Os títulos não devem ser vazios\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Certifique-se de que cabeçalhos de tabela tenham texto discernível\",\n      \"help\": \"O texto do cabeçalho de tabela não deve ser vazio\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Certifique-se de que os elementos na ordem de foco tenham uma função apropriada\",\n      \"help\": \"Elementos na ordem de foco necessitam de uma função apropriada para conteúdo interativo\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Certifique-se de que campos de formulários não tenham múltiplos elementos 'label'\",\n      \"help\": \"Campos de formulário não devem ter múltiplos elementos 'label'\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Certifique-se de que elementos <frame> e <iframe> com tabindex=-1 não tenham conteúdo focalizável\",\n      \"help\": \"Quadros com tabindex=-1 não devem ter conteúdo focalizável\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Certifique-se de que elementos <iframe> e <frame> contenham o script axe-core\",\n      \"help\": \"Os 'frames' devem ser testados com o axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Certifique-se de que os elementos <iframe> e <frame> contenham um atributo 'title' único\",\n      \"help\": \"Os 'frames' devem ter um atributo 'title' único\"\n    },\n    \"frame-title\": {\n      \"description\": \"Certifique-se de que os elementos <iframe> e <frame> contenham um atributo 'title' não vazio\",\n      \"help\": \"Os 'frames' devem ter um atributo 'title'\"\n    },\n    \"heading-order\": {\n      \"description\": \"Certifique-se de que a hierarquia dos níveis de títulos seja semanticamente correta\",\n      \"help\": \"Níveis dos títulos devem aumentar de um em um\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informar aos usuários sobre conteúdo oculto.\",\n      \"help\": \"O conteúdo oculto na página não pode ser analisado\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Certifique-se de que cada documento HTML tenha um atributo 'lang'\",\n      \"help\": \"O elemento <html> deve ter um atributo 'lang'\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Certifique-se de que o atributo 'lang' do elemento <html> tenha um valor válido\",\n      \"help\": \"O elemento <html> deve ter um valor válido para o atributo 'lang'\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Certifique-se de que elementos HTML com ambos os atributos 'lang' e 'xml:lang' válidos concordem entre si sobre o idioma base da página\",\n      \"help\": \"Elementos HTML com 'lang' e 'xml:lang' devem ter o mesmo idioma base\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Certifique-se de que links com o mesmo nome acessível servem a um propósito similar\",\n      \"help\": \"Links com o mesmo nome devem ter um propósito similar\"\n    },\n    \"image-alt\": {\n      \"description\": \"Certifique-se de que elementos <img> tenham texto alternativo ou um 'role' igual a 'none' ou 'presentation'\",\n      \"help\": \"Imagens devem ter texto alternativo\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Certifique-se de que a alternativa de imagem não seja repetida como texto\",\n      \"help\": \"O texto alternativo de imagens não deve ser repetido como texto\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Certifique-se de que botões tenham um texto discernível\",\n      \"help\": \"Botões de controle devem ter um texto discernível\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Certifique-se de que elementos <input type=\\\"image\\\"> tenham um texto alternativo\",\n      \"help\": \"Botões de imagem devem ter um texto alternativo\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Certifique-se de que elementos nomeados por meio de seu conteúdo tenham seu texto visível como parte de seu nome acessível\",\n      \"help\": \"Elementos devem ter seu texto visível como parte de seu nome acessível\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Certifique-se de que cada elemento de formulário não seja rotulado apenas usando os atributos 'title' ou 'aria-describedby'\",\n      \"help\": \"Elementos de formulário devem ter um rótulo visível\"\n    },\n    \"label\": {\n      \"description\": \"Certifique-se de que cada elemento de formulário tenha um rótulo\",\n      \"help\": \"Elementos de formulário devem ter rótulos\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Certifique-se de que a região 'banner' esteja no nível principal\",\n      \"help\": \"A região 'banner' não deve estar contida em outra região\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Certifique-se de que a região 'complementary' ou 'aside' estejam no nível principal\",\n      \"help\": \"'Aside' não deve estar contido em outra região\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Certifique-se de que a região 'contentinfo' esteja no nível principal\",\n      \"help\": \"A região 'contentinfo' não deve estar contida em outra região\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Certifique-se de que a região 'main' esteja no nível principal\",\n      \"help\": \"A região 'main' não deve estar contida em outra região\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Certifique-se de que o documento tenha no máximo uma região 'banner'\",\n      \"help\": \"O documento não deve ter mais do que uma região 'banner'\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Certifique-se de que o documento tenha no máximo uma região 'contentinfo'\",\n      \"help\": \"O documento não deve ter mais do que uma região 'contentinfo'\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Certifique-se de que o documento tenha, no máximo, uma região 'main'\",\n      \"help\": \"O documento não deve ter mais de uma região 'main'\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Certifique-se de que o documento tenha apenas uma região 'main' e que cada 'iframe' na página tenha no náximo uma região 'main'\",\n      \"help\": \"O documento deve ter uma região 'main'\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"Certifique-se de que as regiões são únicas\",\n      \"description\": \"As regiões devem ter um 'role' ou combinação de 'role'/'label'/'title' (ou seja, um nome acessível) únicos\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Links podem ser distinguidos sem depender da cor\",\n      \"help\": \"Links devem ser distinguidos do texto ao redor de uma maneira que não dependa da cor\"\n    },\n    \"link-name\": {\n      \"description\": \"Certifique-se de que links tenham um texto discernível\",\n      \"help\": \"Links devem ter um texto discernível\"\n    },\n    \"list\": {\n      \"description\": \"Certifique-se de que listas sejam estruturadas corretamente\",\n      \"help\": \"Elementos <ul> e <ol> devem conter diretamente apenas elementos <li>, <script> ou <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Certifique-se de que elementos <li> são usados semanticamente\",\n      \"help\": \"Elementos <li> devem estar contidos em um <ul> ou <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Certifique-se de que elementos <marquee> não são utilizados\",\n      \"help\": \"Elementos <marquee> foram descontinuados e não devem ser utilizados\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Certifique-se de que <meta http-equiv=\\\"refresh\\\"> não é utilizado\",\n      \"help\": \"Atualização temporizada não deve existir\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Certifique-se de que <meta name=\\\"viewport\\\"> permite ampliar de maneira significativa\",\n      \"help\": \"Os usuários devem poder dar zoom e ampliar o texto em até 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Certifique-se de que <meta name=\\\"viewport\\\"> não desabilite a ampliação de texto e o zoom\",\n      \"help\": \"O zoom e a ampliação de texto não devem ser desabilitados\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Controles interativos aninhados não são anunciados por leitores de tela\",\n      \"help\": \"Certifique-se de que controles interativos não estejam aninhados\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Certifique-se de que elementos <video> ou <audio> não reproduzam áudio automaticamente por mais de 3 segundos sem um mecanismo de controle para parar ou silenciar o áudio\",\n      \"help\": \"Elementos <video> ou <audio> não reproduzam áudio automaticamente\"\n    },\n    \"object-alt\": {\n      \"description\": \"Certifique-se de que elementos <object> tenham um texto alternativo\",\n      \"help\": \"Elementos <object> devem ter um texto alternativo\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Certifique-se de que elementos <p> não sejam utilizados para estilizar títulos\",\n      \"help\": \"Negrito, itálico e 'font-size' não sejam utilizados para estilizar elementos <p> como um título\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Certifique-se de que a página, ou pelo menos um dos seus 'frames', contenha um título de primeiro nível\",\n      \"help\": \"A página deve conter um título de primeiro nível\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Sinaliza elementos cuja função é 'none' ou 'presentation', e que causam o acionamento da resolução de conflito de funções.\",\n      \"help\": \"Elementos com função 'none' ou 'presentation' devem ser sinalizados\"\n    },\n    \"region\": {\n      \"description\": \"Certifique-se de todo o conteúdo da página esteja contido em regiões (landmarks)\",\n      \"help\": \"Todo o conteúdo da página deve estar contido em regiões (landmarks)\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Certifique-se de que elementos com [role='img'] tenham texto alternativo\",\n      \"help\": \"Elementos com [role='img'] tenham um texto alternativo\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Certifique-se de que o atributo 'scope' é utilizado corretamente em tabelas\",\n      \"help\": \"O atributo 'scope' deve ser utilizado corretamente\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Elementos que tenham conteúdo rolável devem ser acessíveis via teclado\",\n      \"help\": \"Certificar-se de que regiões roláveis tenham acesso via teclado\"\n    },\n    \"select-name\": {\n      \"description\": \"Certifique-se que o elemento 'select' tenha um nome acessível\",\n      \"help\": \"O elemento 'select' deve ter um nome acessível\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Certifique-se de que mapas de imagem do lado do servidor não são utilizados\",\n      \"help\": \"Mapas de imagem do lado do servidor não devem ser utilizados\"\n    },\n    \"skip-link\": {\n      \"description\": \"Certifique-se de que todos os links de escape tenham um destino focalizável\",\n      \"help\": \"O destino de um link de escape deve existir e ser focalizável\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Certifique-se de que elementos svg com um 'role' de 'img', 'graphics-document' ou 'graphics-symbol' tenham um texto acessível\",\n      \"help\": \"Elementos svg com um 'role' de 'img' tenham um texto alternativo\"\n    },\n    \"tabindex\": {\n      \"description\": \"Certifique-se de que os valores do atributo 'tabindex' não são maiores do que 0\",\n      \"help\": \"Elementos não devem ter um 'tabindex' maior do que zero\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Certifique-se de que tabelas não tenham 'summary' e <caption> iguais\",\n      \"help\": \"O elemento <caption> não deve conter o mesmo texto que o atributo 'summary'\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Certifique-se de que tabelas com uma legenda usem o elemento <caption>.\",\n      \"help\": \"Células de dados ou cabeçalhos não devem ser usadas para exibir uma legenda para uma tabela de dados.\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Certifique-se de que cada célula de dados não vazia em uma tabela grande tenha um ou mais cabeçalhos\",\n      \"help\": \"Todos os elementos 'td' não vazios em uma tabela maior que 3 por 3 devem ter um cabeçalho associado\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Certifique-se de que cada célula em uma tabela usando o atributo 'headers' se refira a outra célula nesta tabela\",\n      \"help\": \"Todas as células em uma tabela que utilizem o atributo 'headers' devem se referir apenas a outras células desta mesma tabela\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Certifique-se de cada cabeçalho em uma tabela de dados se refira a células de dados\",\n      \"help\": \"Todos os elementos 'th' e elementos com role=columnheader/rowheader devem ter células de dados que eles descrevem\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Certifique-se de que os atributos 'lang' tenham valores válidos\",\n      \"help\": \"O atributo 'lang' deve ter um valor válido\"\n    },\n    \"video-caption\": {\n      \"description\": \"Certifique-se de que elementos <video> tenham legendas\",\n      \"help\": \"Elementos <video> devem ter legendas\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"As funções abstratas não são utilizadas\",\n      \"fail\": {\n        \"singular\": \"A função abstrata não pode ser diretamente utilizada: ${data.values}\",\n        \"plural\": \"As funções abstratas não podem ser diretamente utilizadas: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Atributos ARIA são usados corretamente para a função definida\",\n      \"fail\": {\n        \"singular\": \"O atributo ARIA não é permitido: ${data.values}\",\n        \"plural\": \"Os atributos ARIA não são permitidos: ${data.values}\"\n      }\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"O ARIA 'role' é permitido para o elemento dado\",\n      \"fail\": {\n        \"singular\": \"O ARIA 'role' ${data.values} não é permitido para o elemento dado\",\n        \"plural\": \"Os ARIA 'roles' ${data.values} não são permitidos para o elemento dado\"\n      },\n      \"incomplete\": {\n        \"singular\": \"O ARIA 'role' ${data.values} deve ser removido quando o elemento é tornado visível, uma vez que não é permitido para o elemento\",\n        \"plural\": \"Os ARIA 'roles' ${data.values} devem ser removidos quando o elemento é tornado visível, uma vez que não são permitidos para o elemento\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"Usa uma técnica de 'aria-errormessage' suportada\",\n      \"fail\": {\n        \"singular\": \"O valor de 'aria-errormessage' `${data.values}` deve usar uma técnica para anunciar a mensagem (por exemplo, 'aria-live', 'aria-describedby', role=alert, etc.)\",\n        \"plural\": \"Os valores de 'aria-errormessage' `${data.values}` devem usar uma técnica para anunciar a mensagem (por exemplo, 'aria-live', 'aria-describedby', role=alert, etc.)\"\n      },\n      \"incomplete\": {\n        \"singular\": \"certifique-se de que o valor de 'aria-errormessage' `${data.values}` referencia um elemento existente\",\n        \"plural\": \"certifque-se de que os valores de 'aria-errormessage' `${data.values}` referenciem elementos existentes\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Nenhum atributo 'aria-hidden' está presente no elemento <body>\",\n      \"fail\": \"'aria-hidden=true' não deve estar presente no elemento <body>\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"O atributo ARIA é permitido\",\n      \"fail\": \"O atributo ARIA não pode ser utilizado: ${data.values}\",\n      \"incomplete\": \"O atributo ARIA não é bem suportado no elemento e o conteúdo de texto será usado em seu lugar: ${data.values}\"\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Todos os atributos ARIA necessários estão presentes\",\n      \"fail\": {\n        \"singular\": \"Atributo ARIA necessário ausente: ${data.values}\",\n        \"plural\": \"Atributos ARIA necessários ausentes: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Os ARIA filhos necessários estão presentes\"\n      },\n      \"fail\": {\n        \"singular\": \"Função ARIA filha necessária ausente: ${data.values}\",\n        \"plural\": \"Funções ARIA filhas necessárias ausentes: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Esperando função ARIA filha a ser adicionada: ${data.values}\",\n        \"plural\": \"Esperando funções ARIA filhas a serem adicionadas: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"A função ARIA pai necessária está presente\",\n      \"fail\": {\n        \"singular\": \"Função ARIA pai necessária ausente: ${data.values}\",\n        \"plural\": \"Funções ARIA pais necessárias ausentes: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"'aria-roledescription' usado em função semântica suportada\",\n      \"incomplete\": \"Garanta que o 'aria-roledescription' é anunciado pelos leitores de tela suportados\",\n      \"fail\": \"Dê ao elemento uma função que suporte 'aria-roledescription'\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"O atributo ARIA é suportado\",\n      \"fail\": \"O atributo ARIA não é amplamente suportado em leitores de tela e tecnologias assistivas:  ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Os valores dos atributos ARIA são válidos\",\n      \"fail\": {\n        \"singular\": \"Valor de atributo ARIA inválido: ${data.values}\",\n        \"plural\": \"Valores de atributos ARIA inválidos: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"O ID do atributo ARIA do elemento não existe na página: ${data.needsReview}\",\n        \"ariaCurrent\": \"O valor do atributo ARIA é inválido e será tratado como \\\"aria-current=true\\\": ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": {\n        \"singular\": \"Os nomes dos atributos ARIA são válidos\",\n        \"plural\": \"O nome do atributo ARIA é valido\"\n      },\n      \"fail\": {\n        \"singular\": \"Nome de atributo ARIA inválido: ${data.values}\",\n        \"plural\": \"Nomes de atributos ARIA inválidos: ${data.values}\"\n      }\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Apenas um valor de 'role' deve ser utilizado\",\n      \"fail\": \"Use apenas um valor de 'role', uma vez que funções 'fallback' não são suportadas em navegadores mais antigos\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"O elemento tem um atributo ARIA global: ${data.values}\",\n        \"plural\": \"O elemento tem atributos ARIA globais: ${data.values}\"\n      },\n      \"fail\": \"O elemento não tem atributo ARIA global\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Elemento tem um 'role' de 'widget'.\",\n      \"fail\": \"Elemento não tem um 'role' de 'widget'.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"O ARIA 'role' é válido\",\n      \"fail\": {\n        \"singular\": \"O 'role' deve ser um dos ARIA 'roles' válidos: ${data.values}\",\n        \"plural\": \"Os 'roles' devem ser um dos ARIA 'roles' válidos: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"O elemento é focalizável.\",\n      \"fail\": \"O elemento não é focalizável.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Não há nenhuma divergência entre <label> e o nome acessível\",\n      \"incomplete\": \"Verifique se o <label> não precisa fazer parte do nome do campo ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"O ARIA 'role' é suportado\",\n      \"fail\": \"O 'role' usado não é amplamente suportado em leitores de tela e tecnologias assistivas: ${data.values}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"O elemento possui semântica válida para um elemento na ordem de foco.\",\n      \"fail\": \"O elemento possui semântica inválida para um elemento na ordem de foco.\"\n    },\n    \"color-contrast\": {\n      \"pass\": \"O elemento tem contraste suficiente no valor de ${data.contrastRatio}\",\n      \"fail\": \"O elemento tem contraste insuficiente no valor de ${data.contrastRatio} (cor do primeiro plano: ${data.fgColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, normal/negrito: ${data.fontWeight}). Contraste esperado no valor de ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"default\": \"Impossível determinar o contraste\",\n        \"bgImage\": \"A cor de fundo do elemento não pôde ser determinada devido a uma imagem de fundo\",\n        \"bgGradient\": \"A cor de fundo do elemento não pôde ser determinada devido a um gradiente de fundo\",\n        \"imgNode\": \"A cor de fundo do elemento não pôde ser determinada porque o elemento contém um nó de imagem\",\n        \"bgOverlap\": \"A cor de fundo do elemento não pôde ser determinada porque está sobreposta por outro elemento\",\n        \"fgAlpha\": \"A cor do primeiro plano do elemento não pôde ser determinada devido à transparência alfa\",\n        \"elmPartiallyObscured\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente obscurecida por outro elemento\",\n        \"elmPartiallyObscuring\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente sobreposta a outros elementos\",\n        \"outsideViewport\": \"A cor de fundo do elemento não pôde ser determinada porque está fora da 'viewport'\",\n        \"equalRatio\": \"O elemento tem um contraste de 1:1 com plano de fundo\",\n        \"shortTextContent\": \"O conteúdo do elemento é muito curto para determinar se é conteúdo de texto real\",\n        \"nonBmp\": \"O conteúdo do elemento contém apenas caracteres não textuais\",\n        \"pseudoContent\": \"A cor de fundo do elemento não pode ser determinada devido a um pseudo-elemento\"\n      }\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"O elemento tem contraste suficiente no valor de ${data.contrastRatio}\",\n      \"fail\": \"O elemento tem contraste insuficiente no valor de ${data.contrastRatio} (cor do primeiro plano: ${data.fgColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, normal/negrito: ${data.fontWeight}). Contraste esperado no valor de ${data.expectedContrastRatio}\",\n      \"incomplete\": {\n        \"default\": \"Impossível determinar o contraste\",\n        \"bgImage\": \"A cor de fundo do elemento não pôde ser determinada devido a uma imagem de fundo\",\n        \"bgGradient\": \"A cor de fundo do elemento não pôde ser determinada devido a um gradiente de fundo\",\n        \"imgNode\": \"A cor de fundo do elemento não pôde ser determinada porque o elemento contém um nó de imagem\",\n        \"bgOverlap\": \"A cor de fundo do elemento não pôde ser determinada porque está sobreposta por outro elemento\",\n        \"fgAlpha\": \"A cor do primeiro plano do elemento não pôde ser determinada devido à transparência alfa\",\n        \"elmPartiallyObscured\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente obscurecida por outro elemento\",\n        \"elmPartiallyObscuring\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente sobreposta a outros elementos\",\n        \"outsideViewport\": \"A cor de fundo do elemento não pôde ser determinada porque está fora da 'viewport'\",\n        \"equalRatio\": \"O elemento tem um contraste de 1:1 com plano de fundo\",\n        \"shortTextContent\": \"O conteúdo do elemento é muito curto para determinar se é conteúdo de texto real\",\n        \"nonBmp\": \"O conteúdo do elemento contém apenas caracteres não textuais\",\n        \"pseudoContent\": \"A cor de fundo do elemento não pode ser determinada devido a um pseudo-elemento\"\n      }\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Links pode ser distinguidos do texto ao redor de outra maneira além da cor\",\n      \"fail\": \"Links precisam ser distinguidos do texto ao redor de outra maneira além da cor\",\n      \"incomplete\": {\n        \"default\": \"Impossível determinar o contraste\",\n        \"bgContrast\": \"O contraste do elemento não pôde ser determinado. Verifique se há um estilo distinto para 'hover'/'focus'.\",\n        \"bgImage\": \"O contraste do elemento não pôde ser determinado devido a uma imagem de fundo\",\n        \"bgGradient\": \"O contraste do elemento não pôde ser determinado devido a um gradiente de fundo\",\n        \"imgNode\": \"O contraste do elemento não pôde ser determinado porque o elemento contém um nó de imagem\",\n        \"bgOverlap\": \"O contraste do elemento não pôde ser determinado devido a uma sobreposição do elemento\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"o valor 'autocomplete' está em um elemento apropriado\",\n      \"fail\": \"o valor 'autocomplete' está em um elemento inapropriado para este tipo de entrada\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"o atributo 'autocomplete' está corretamente formatado\",\n      \"fail\": \"o atributo 'autocomplete' está incorretamente formatado\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"O valor do atributo 'accesskey' é único\",\n      \"fail\": \"O documento tem múltiplos elementos com a mesma 'accesskey'\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"O elemento contém elementos focalizáveis\",\n      \"fail\": \"O elemento deveria ter conteúdo focalizável\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Nenhum elemento focalizável contido no elemento\",\n      \"fail\": \"Conteúdo focalizável deve ser desabilitado ou removido do DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"O elemento é focalizável\",\n      \"fail\": \"O elemento deveria ser focalizável\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Nenhum elemento focalizável enquanto um modal está aberto\",\n      \"incomplete\": \"Garanta que os elementos focalizáveis não estejam na ordem de tabulação no estado atual\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"O elemento não está na ordem de tabulação ou tem texto acessível\",\n      \"fail\": \"O elemento está na ordem de tabulação e não tem nome acessível\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem um nome acessível\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Nenhum elemento focalizável contido no elemento\",\n      \"fail\": \"Conteúdo focalizável deve ter tabindex='-1' ou ser removido do DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"O elemento não tem descendentes focalizáveis\",\n      \"fail\": \"O elemento tem descendentes focalizáveis\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem descendentes\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"A região ${data.role} está no nível principal.\",\n      \"fail\": \"A região ${data.role} está contida em outra região.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"O elemento não tem descendentes focalizáveis\",\n      \"fail\": \"O elemento tem descendentes focalizáveis\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem descendentes\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"A página tem pelo menos um título de primeiro nível\",\n      \"fail\": \"A página deve ter pelo menos um título de primeiro nível\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"O documento tem pelo menos uma região 'main'\",\n      \"fail\": \"O documento não tem uma região 'main'\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"O documento não tem mais de uma região 'banner'\",\n      \"fail\": \"O documento tem mais de uma região 'banner'\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"O documento não tem mais de uma região 'contentinfo'\",\n      \"fail\": \"O documento tem mais de uma região 'contenteinfo'\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"O documento não tem mais de uma região 'main'\",\n      \"fail\": \"O documento tem mais de uma região 'main'\"\n    },\n    \"tabindex\": {\n      \"pass\": \"O elemento não tem um 'tabindex' maior do que 0\",\n      \"fail\": \"O elemento tem um 'tabindex' maior do que 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"O elemento tem um valor válido para o atributo 'alt'\",\n      \"fail\": \"O elemento tem um atributo 'alt' contendo apenas um caracter de espaço, o que não é ignorado por todos os leitores de tela\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"O elemento não duplica um texto existente no atributo 'alt' do elemento <img>\",\n      \"fail\": \"O elemento contém o elemento <img> com texto alternativo duplicando um texto existente\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"O elemento de formulário tem um <label> explícito\",\n      \"fail\": \"O elemento de formulário não tem um <label> explícito\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem um <label> explícito\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"O texto de ajuda ('title' ou 'aria-describedby') não duplica o texto do rótulo\",\n      \"fail\": \"O texto de ajuda ('title' ou 'aria-describedby') é o igual ao texto do rótulo\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"O elemento de formulário tem um <label> explícito visível\",\n      \"fail\": \"O elemento de formulário tem um <label> explícito que está oculto\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem um <label> explícito que está oculto\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"O elemento de formulário tem um <label> implícito (envolvido)\",\n      \"fail\": \"O elemento de formulário não tem <label> implícito (envolvido)\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem um <label> implícito (envolvido)\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"O elemento contém o texto visível como parte do seu nome acessível\",\n      \"fail\": \"O texto dentro do elemento não está incluído no nome acessível\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"O campo de formulário não tem múltiplos elementos <label>\",\n      \"incomplete\": \"O uso de múltiplos elementos <label> não é amplamente suportado pelas tecnologias assistivas. Certifique-se de que o primeiro <label> contém toda a informação necessária.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Elementos de formulário não usam somente o atributo 'title' como seu rótulo\",\n      \"fail\": \"Apenas 'title' usado para gerar o rótulo do elemento de formulário\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"As regiões devem ter um 'role' ou combinação de 'role'/'label'/'title' (ou seja, nome acessível) únicos\",\n      \"fail\": \"A região deve ter um 'aria-label', 'aria-labelledby', ou 'title' único para tornar as regiões distinguíveis\"\n    },\n    \"has-lang\": {\n      \"pass\": \"O elemento <html> tem um atributo 'lang'\",\n      \"fail\": {\n        \"noXHTML\": \"O atributo 'xml:lang' não é válido em páginas HTML, use o atributo 'lang'.\",\n        \"noLang\": \"O elemento <html> não possui um atributo 'lang'\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"O valor do atributo 'lang' está incluso na lista de idiomas válidos\",\n      \"fail\": \"O valor do atributo 'lang' não está incluso na lista de idiomas válidos\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Os atributos 'lang' e 'xml:lang' têm o mesmo idioma base\",\n      \"fail\": \"Os atributos 'lang' e 'xml:lang' não têm o mesmo idioma base\"\n    },\n    \"dlitem\": {\n      \"pass\": \"O item da lista de descrição tem um elemento pai <dl>\",\n      \"fail\": \"O item da lista de descrição não tem um elemente pai <dl>\"\n    },\n    \"listitem\": {\n      \"pass\": \"O item de lista tem um elemento pai <ul>, <ol> ou role=\\\"list\\\"\",\n      \"fail\": {\n        \"default\": \"O item de lista não tem um elemento pai <ul> ou <ol>\",\n        \"roleNotValid\": \"O item de lista não tem um elemento pai <ul> ou <ol> sem um 'role', ou um role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"O elemento de lista tem somente filhos diretos que são permitidos dentro de elementos <dt> ou <dd>\",\n      \"fail\": \"O elemento de lista tem filhos diretos que não são permitidos dentro de elementos <dt> ou <dd>\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"O elemento de lista tem somente filhos diretos que são permitidos dentro de elementos <li>\",\n      \"fail\": {\n        \"default\": \"O elemento de lista tem filhos diretos que não são permitidos dentro de elementos <li>\",\n        \"roleNotValid\": \"O elemento de lista tem filhos diretos com um 'role' que não é permitido: ${data.roles}\"\n      }\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Quando não vazio, o elemento tem ambos elementos <dt> e <dd>\",\n      \"fail\": \"Quando não vazio, o elemento não tem nem ao menos um elemento <dt> seguindo de pelo menos um elemento <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"O elemento multimídia tem uma trilha de legendas\",\n      \"incomplete\": \"Verifique se legendas estão disponíveis para o elemento\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"O 'iframe' foi testado com o 'axe-core'\",\n      \"fail\": \"O 'iframe' não pôde ser testado com o 'axe-core'\",\n      \"incomplete\": \"O 'iframe' ainda tem que ser testado com o 'axe-core'\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"Elementos <video> ou <audio> não reproduzem áudio por mais do que a duração permitida ou possuem mecanismos de controle\",\n      \"fail\": \"Elementos <video> ou <audio> reproduzem áudio por uma duração maior que a permitida ou não possuem mecanismos de controle\",\n      \"incomplete\": \"Garanta que elementos <video> ou <audio> não reproduzam áudio por mais do que a duração permitida ou disponibilize mecanismos de controle\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"A tela é operável, e o bloqueio da orientação não existe\",\n      \"fail\": \"Bloqueio de orientação CSS aplicado, o que torna a tela inoperável\",\n      \"incomplete\": \"Bloqueio de orientação CSS não pode ser determinado\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"A tag <meta> não impede o zoom significativo em dispositivos móveis\",\n      \"fail\": \"A tag <meta> limita o zoom em dispositivos móveis\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"A tag <meta> não desabilita o zoom em dispositivos móveis\",\n      \"fail\": \"${data} na tag <meta> desabilita o zoom em dispositivos móveis\"\n    },\n    \"header-present\": {\n      \"pass\": \"A página tem um cabeçalho (header)\",\n      \"fail\": \"A página não tem um cabeçalho (header)\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Hierarquia de títulos válida\",\n      \"fail\": \"Hierarquia de títulos inválida\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Não há outros links com o mesmo nome, que direcionem para uma URL diferente\",\n      \"incomplete\": \"Verifique se os links têm o mesmo propósito ou são intencionalmente ambíguos.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Link de escape válido encontrado\",\n      \"fail\": \"Nenhum link de escape válido encontrado\"\n    },\n    \"landmark\": {\n      \"pass\": \"A página tem uma região (landmark)\",\n      \"fail\": \"A página não tem nenhuma região (landmark)\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"A tag <meta> não atualiza a página imediatamente\",\n      \"fail\": \"A tag <meta> força atualizações temporizadas da página\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Elementos <p> não são estilizados como títulos\",\n      \"fail\": \"Elementos de título devem ser utilizados no lugar de elementos <p> estilizados\"\n    },\n    \"region\": {\n      \"pass\": \"Todo o conteúdo da página está contido em regiões (landmarks)\",\n      \"fail\": \"Algum conteúdo da página não está contido em regiões (landmarks)\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Destino do link de escape existe\",\n      \"incomplete\": \"Destino do link de escape deve se tornar visível na ativação\",\n      \"fail\": \"Nenhum destino para o link de escape\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"O atributo 'title' do elemento é único\",\n      \"fail\": \"O atributo 'title' do elemento não é único\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"O documento não tem elementos ativos que compartilham o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem elementos ativos com o mesmo atributo 'id': ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"O documento não tem elementos referenciados com ARIA ou rótulos que compartilham o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem múltiplos elementos referenciados com ARIA com o mesmo atributo 'id': ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"O documento não tem elementos estáticos que compartilham o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem múltiplos elementos estáticos com o mesmo atributo 'id'\"\n    },\n    \"aria-label\": {\n      \"pass\": \"O atributo 'aria-label' existe e não está vazio\",\n      \"fail\": \"O atributo 'aria-label' não existe ou está vazio\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"O atributo 'aria-labelledby' existe e referencia elementos que são visíveis aos leitores de tela\",\n      \"fail\": \"O atributo 'aria-labelledby' não existe, referencia elementos que não existem ou são vazios\",\n      \"incomplete\": \"certifique-se de que 'aria-labelledby' referencie um elemento existente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Nenhum estilo 'inline' com '!important' que afete o espaçamento do texto foi especificado\",\n      \"fail\": {\n        \"singular\": \"Remova '!important' do estilo 'inline' ${data.values}, uma vez que sobrepor isto não é suportado pela maioria dos navegadores\",\n        \"plural\": \"Remova '!important' dos estilos 'inline' ${data.values}, uma vez que sobrepor isto não é suportado pela maioria dos navegadores\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"O elemento tem texto interno que é visível aos leitores de tela\",\n      \"fail\": \"O elemento não tem texto interno que seja visível aos leitores de tela\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem filhos\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"O documento tem um elemento <title> não vazio\",\n      \"fail\": \"O documento não tem um elemento <title> não vazio\"\n    },\n    \"exists\": {\n      \"pass\": \"O elemento não existe\",\n      \"incomplete\": \"O elemento existe\"\n    },\n    \"has-alt\": {\n      \"pass\": \"O elemento tem um atributo 'alt'\",\n      \"fail\": \"O elemento não tem um atributo 'alt'\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"O elemento tem texto que é visível aos leitores de tela\",\n      \"fail\": \"O elemento não tem texto que seja visível aos leitores de tela\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem filhos\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"O elemento não é visível\",\n      \"fail\": \"O elemento é visível\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"O elemento tem um atributo 'alt' não vazio\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'alt'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'alt' vazio\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"O elemento tem um atributo 'value\",\n        \"has-label\": \"O elemento tem um atributo 'value' não vazio\"\n      },\n      \"fail\": \"O elemento tem um atributo 'value' e ele está vazio\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"O elemento tem um atributo 'placeholder'\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'placeholder'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'placeholder' vazio\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"O elemento tem um atributo 'title'\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'title'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'title' vazio\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"O elemento tem um atributo 'value' não vazio\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'value'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'value' vazio\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"A semântica padrão do elemento foi sobrescrita com role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"A semântica padrão do elemento não foi sobrescrita com role=\\\"none\\\" or role=\\\"presentation\\\"\",\n        \"globalAria\": \"A função do elemento não é de apresentação pois ele tem um atributo ARIA global\",\n        \"focusable\": \"A função do elemento não é de apresentação pois ele é focalizável\",\n        \"both\": \"A função do elemento não é de apresentação pois ele tem um atributo ARIA global e é focalizável\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"A semântica padrão do elemento foi sobrescrita com role=\\\"none\\\"\",\n      \"fail\": \"A semântica padrão do elemento não foi sobrescrita com role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"A semântica padrão do elemento foi sobrescrita com role=\\\"presentation\\\"\",\n      \"fail\": \"A semântica padrão do elemento não foi sobrescrita com role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"O elemento tem um filho <title>\",\n      \"fail\": {\n        \"noTitle\": \"O elemento não tem um filho que seja 'title'\",\n        \"emptyTitle\": \"O 'title' filho do elemento está vazio\"\n      },\n      \"incomplete\": \"Não foi possível determinar se o elemento tem um filho que seja um 'title'\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"A primeira linha de uma tabela não é usada como legenda\",\n      \"fail\": \"O primeiro elemento da tabela deve ser um <caption> em vez de uma célula da tabela\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"O atributo 'scope' só é utilizado em elementos de cabeçalho de tabela (<th>)\",\n      \"fail\": \"No HTML 5, os atributos 'scope' só devem ser utilizados em elementos de cabeçalho de tabela (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"O conteúdo do atributo 'summary' e <caption> não estão duplicados\",\n      \"fail\": \"O conteúdo do atributo 'summary' e <caption> são idênticos\"\n    },\n    \"scope-value\": {\n      \"pass\": \"O atributo 'scope' é usado corretamente\",\n      \"fail\": \"O valor do atributo 'scope' pode ser apenas 'row' ou 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Todos as células de dados não vazias possuem cabeçalhos\",\n      \"fail\": \"Algumas células de dados não vazias não possuem cabeçalho\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"O atributo 'headers' é usado exclusivamente para referenciar outras células da tabela\",\n      \"incomplete\": \"O atributo 'headers' está vazio\",\n      \"fail\": \"O atributo 'headers' não é usado exclusivamente para referenciar outras células da tabela\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Todas as células de cabeçalho da tabela se referem a células de dados\",\n      \"fail\": \"Nem todas as células de cabeçalho da tabela se referem a células de dados\",\n      \"incomplete\": \"Células de dados da tabela estão ausentes ou vazias\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Todo o conteúdo da página foi analisado.\",\n      \"fail\": \"Houve problemas ao analisar o conteúdo desta página.\",\n      \"incomplete\": \"Existe conteúdo oculto na página que não foi analisado. Será preciso provocar a exibição deste conteúdo para poder analisá-lo.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Corrija qualquer um dos itens a seguir:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Corrija todos os itens a seguir:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"Corrija todos os itens a seguir:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n}\n"
  },
  {
    "path": "locales/pt_PT.json",
    "content": "{\n  \"lang\": \"pt_PT\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Garantir que cada valor do atributo 'accesskey' seja único\",\n      \"help\": \"O valor do atributo 'accesskey' deve ser único\"\n    },\n    \"area-alt\": {\n      \"description\": \"Garantir que os elementos <area> dos mapas de imagem tenham texto alternativo\",\n      \"help\": \"Os elementos <area> ativos devem ter texto alternativo\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Garantir que a função de um elemento suporte os seus atributos ARIA\",\n      \"help\": \"Os elementos devem usar apenas atributos ARIA suportados\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Garantir que o atributo 'role' tenha um valor apropriado para o elemento\",\n      \"help\": \"A 'role' ARIA deve ser apropriada para o elemento\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Garantir que 'aria-braillelabel' e 'aria-brailleroledescription' tenham um equivalente não braile\",\n      \"help\": \"Os atributos 'aria-braille' devem ter um equivalente não braile\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Garantir que cada botão, link e item de menu ARIA tenha um nome acessível\",\n      \"help\": \"Os comandos ARIA devem ter um nome acessível\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Garantir que os atributos ARIA sejam usados conforme descrito na especificação da função do elemento\",\n      \"help\": \"Os atributos ARIA devem ser usados conforme especificado para a função do elemento\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Garantir que os elementos não usem funções obsoletas\",\n      \"help\": \"Não se devem usar funções ARIA obsoletas\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Garantir que cada diálogo e diálogo de alerta ARIA tenha um nome acessível\",\n      \"help\": \"Os nós de diálogo e diálogo de alerta ARIA devem ter um nome acessível\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Garantir que 'aria-hidden=\\\"true\\\"' não esteja presente no corpo do documento.\",\n      \"help\": \"'aria-hidden=\\\"true\\\"' não deve estar presente no corpo do documento\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Garantir que elementos com 'aria-hidden' não sejam focáveis nem contenham elementos focáveis\",\n      \"help\": \"Um elemento oculto por ARIA não deve ser focável nem conter elementos focáveis\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Garantir que cada campo de entrada ARIA tenha um nome acessível\",\n      \"help\": \"Os campos de entrada ARIA devem ter um nome acessível\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Garantir que cada nó de medidor ARIA tenha um nome acessível\",\n      \"help\": \"Os nós de medidor ARIA devem ter um nome acessível\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Garantir que cada nó de barra de progresso ARIA tenha um nome acessível\",\n      \"help\": \"Os nós de barra de progresso ARIA devem ter um nome acessível\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Garantir que os atributos ARIA não sejam proibidos para a função de um elemento\",\n      \"help\": \"Os elementos devem usar apenas atributos ARIA permitidos\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Garantir que os elementos com funções ARIA tenham todos os atributos ARIA obrigatórios\",\n      \"help\": \"Devem ser fornecidos os atributos ARIA obrigatórios\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Garantir que os elementos com uma função ARIA que requer funções filhas as contenham\",\n      \"help\": \"Certas funções ARIA devem conter determinados elementos filhos\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Garantir que os elementos com uma função ARIA que requer funções parentais estejam contidos por estes\",\n      \"help\": \"Certas funções ARIA devem estar contidas em pais específicos\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Garantir que 'aria-roledescription' seja usado apenas em elementos com uma função implícita ou explícita\",\n      \"help\": \"'aria-roledescription' deve estar em elementos com uma função semântica\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Garantir que todos os elementos com o atributo 'role' usem um valor válido\",\n      \"help\": \"As funções 'roles' ARIA utilizadas devem corresponder a valores válidos\"\n    },\n    \"aria-text\": {\n      \"description\": \"Garantir que 'role=\\\"text\\\"' seja usado em elementos sem descendentes focáveis\",\n      \"help\": \"'\\\"role=text\\\"' não deve ter descendentes focáveis\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Garantir que cada campo de alternância ARIA tenha um nome acessível\",\n      \"help\": \"Os campos de alternância ARIA devem ter um nome acessível\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Garantir que cada nó de 'tooltip' ARIA tenha um nome acessível\",\n      \"help\": \"Os nós de 'tooltip' ARIA devem ter um nome acessível\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Garantir que cada nó de 'treeitem' ARIA tenha um nome acessível\",\n      \"help\": \"Os nós de 'treeitem' ARIA devem ter um nome acessível\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Garantir que todos os atributos ARIA tenham valores válidos\",\n      \"help\": \"Os atributos ARIA devem corresponder a valores válidos\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Garantir que os atributos que começam com 'aria-' sejam atributos ARIA válidos\",\n      \"help\": \"Os atributos ARIA devem corresponder a nomes válidos\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Garantir que os elementos <audio> tenham legendas\",\n      \"help\": \"Os elementos <audio> devem ter uma pista de legendas\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Garantir que o atributo 'autocomplete' esteja correto e seja adequado para o campo de formulário\",\n      \"help\": \"O atributo 'autocomplete' deve ser utilizado corretamente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Garantir que o espaçamento do texto definido através de atributos de estilo possa ser ajustado com folhas de estilo personalizadas\",\n      \"help\": \"O espaçamento do texto 'inline' deve ser ajustável com folhas de estilo personalizadas\"\n    },\n    \"blink\": {\n      \"description\": \"Garantir que os elementos <blink> não sejam usados\",\n      \"help\": \"Os elementos <blink> estão obsoletos e não devem ser usados\"\n    },\n    \"button-name\": {\n      \"description\": \"Garantir que os botões tenham texto discernível\",\n      \"help\": \"Os botões devem ter texto discernível\"\n    },\n    \"bypass\": {\n      \"description\": \"Garantir que cada página tenha pelo menos um mecanismo para o utilizador contornar a navegação e saltar diretamente para o conteúdo\",\n      \"help\": \"A página deve ter meios para contornar blocos repetitivos\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Garantir que o contraste entre as cores de primeiro plano e de fundo cumpra os limites de razão de contraste aprimorada, WCAG 2 AAA\",\n      \"help\": \"Os elementos devem cumprir os limites de razão de contraste aprimorada\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Garantir que o contraste entre as cores de primeiro plano e de fundo cumpra os limites mínimos de razão de contraste, WCAG 2 AA\",\n      \"help\": \"Os elementos devem cumprir os limites mínimos de razão de contraste\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Garantir que o conteúdo não esteja bloqueado a uma orientação de ecrã específica, e que seja operativo em todas as orientações de ecrã\",\n      \"help\": \"As media queries CSS não devem bloquear a orientação do ecrã\"\n    },\n    \"definition-list\": {\n      \"description\": \"Garantir que os elementos <dl> estejam estruturados corretamente\",\n      \"help\": \"Os elementos <dl> devem conter apenas grupos de <dt> e <dd> ordenados corretamente, ou elementos <script>, <template> ou <div>\"\n    },\n    \"dlitem\": {\n      \"description\": \"Garantir que os elementos <dt> e <dd> estejam contidos num <dl>\",\n      \"help\": \"Os elementos <dt> e <dd> devem estar contidos num <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Garantir que cada documento HTML contenha um elemento <title> não vazio\",\n      \"help\": \"Os documentos devem ter um elemento <title> para ajudar na navegação\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Garantir que cada valor do atributo 'id' dos elementos ativos seja único\",\n      \"help\": \"Os IDs dos elementos ativos devem ser únicos\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Garantir que cada valor do atributo 'id' utilizado em ARIA e em etiquetas seja único\",\n      \"help\": \"Os IDs usados em ARIA e etiquetas devem ser únicos\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Garantir que cada valor do atributo 'id' seja único\",\n      \"help\": \"O valor do atributo 'id' deve ser único\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Garantir que os cabeçalhos tenham texto discernível\",\n      \"help\": \"Os cabeçalhos não devem estar vazios\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Garantir que os cabeçalhos de tabelas tenham texto discernível\",\n      \"help\": \"O texto dos cabeçalhos de tabela não deve estar vazio\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Garantir que os elementos na ordem de foco tenham uma função apropriada para conteúdo interativo\",\n      \"help\": \"Os elementos na ordem de foco devem ter uma função apropriada\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Garantir que o campo de formulário não possua múltiplos elementos 'label'\",\n      \"help\": \"O campo de formulário não deve ter múltiplos elementos 'label'\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Garantir que os elementos <frame> e <iframe> com conteúdo focável não tenham tabindex=-1\",\n      \"help\": \"Os frames com conteúdo focável não devem ter tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Garantir que os elementos <iframe> e <frame> contenham o script axe-core\",\n      \"help\": \"Os frames devem ser testados com axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Garantir que os elementos <iframe> e <frame> contenham um atributo 'title' único\",\n      \"help\": \"Os frames devem ter um atributo 'title' único\"\n    },\n    \"frame-title\": {\n      \"description\": \"Garantir que os elementos <iframe> e <frame> tenham um atributo 'title' acessível\",\n      \"help\": \"Os frames devem ter um atributo 'title' acessível\"\n    },\n    \"heading-order\": {\n      \"description\": \"Garantir que a ordem dos cabeçalhos seja semanticamente correta\",\n      \"help\": \"Os níveis de cabeçalho devem aumentar apenas de um em um\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Informa os utilizadores sobre conteúdo oculto.\",\n      \"help\": \"O conteúdo oculto na página deve ser analisado\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Garantir que cada documento HTML tenha um atributo 'lang'\",\n      \"help\": \"O elemento <html> deve ter um atributo 'lang'\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Garantir que o atributo 'lang' do elemento <html> tenha um valor válido\",\n      \"help\": \"O elemento <html> deve ter um valor válido para o atributo 'lang'\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Garantir que os elementos HTML com atributos 'lang' e 'xml:lang' válidos concordem na língua base da página\",\n      \"help\": \"Os elementos HTML com 'lang' e 'xml:lang' devem ter a mesma língua base\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Garantir que links com o mesmo nome acessível tenham um propósito semelhante\",\n      \"help\": \"Links com o mesmo nome devem ter um propósito semelhante\"\n    },\n    \"image-alt\": {\n      \"description\": \"Garantir que os elementos <img> tenham texto alternativo ou um 'role' igual a 'none' ou 'presentation'\",\n      \"help\": \"As imagens devem ter texto alternativo\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Garantir que o texto alternativo da imagem não seja repetido como texto\",\n      \"help\": \"O texto alternativo das imagens não deve ser repetido como texto\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Garantir que os botões de 'input' tenham texto discernível\",\n      \"help\": \"Os botões de 'input' devem ter texto discernível\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Garantir que os elementos <input type=\\\"image\\\"> tenham texto alternativo\",\n      \"help\": \"Os botões de imagem devem ter texto alternativo\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Garantir que os elementos etiquetados através do seu conteúdo incluam o texto visível como parte do seu nome acessível\",\n      \"help\": \"Os elementos devem incluir o seu texto visível como parte do nome acessível\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Garantir que cada elemento de formulário tenha uma etiqueta visível e não seja etiquetado apenas através de etiquetas ocultas, ou dos atributos 'title' ou 'aria-describedby'\",\n      \"help\": \"Os elementos de formulário devem ter uma etiqueta visível\"\n    },\n    \"label\": {\n      \"description\": \"Garantir que cada elemento de formulário tenha uma etiqueta\",\n      \"help\": \"Os elementos de formulário devem ter etiquetas\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Garantir que o marco de 'banner' esteja no nível principal\",\n      \"help\": \"O marco de 'banner' não deve estar contido noutro marco\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Garantir que o marco 'complementary' ou 'aside' estejam no nível principal\",\n      \"help\": \"O 'aside' não deve estar contido noutro marco\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Garantir que o marco 'contentinfo' esteja no nível principal\",\n      \"help\": \"O marco 'contentinfo' não deve estar contido noutro marco\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Garantir que o marco 'main' esteja no nível principal\",\n      \"help\": \"O marco 'main' não deve estar contido noutro marco\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Garantir que o documento tenha, no máximo, um marco de 'banner'\",\n      \"help\": \"O documento não deve ter mais do que um marco de 'banner'\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Garantir que o documento tenha, no máximo, um marco 'contentinfo'\",\n      \"help\": \"O documento não deve ter mais do que um marco 'contentinfo'\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Garantir que o documento tenha, no máximo, um marco 'main'\",\n      \"help\": \"O documento não deve ter mais do que um marco 'main'\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Garantir que o documento tenha um marco 'main'\",\n      \"help\": \"O documento deve ter um marco 'main'\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"Garantir que os marcos sejam únicos\",\n      \"help\": \"Os marcos devem ter uma combinação única de 'role' ou 'role'/'label'/'title' (ou seja, nome acessível)\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Garantir que os links se distingam do texto circundante de forma que não dependa da cor\",\n      \"help\": \"Os links devem ser distinguíveis sem depender da cor\"\n    },\n    \"link-name\": {\n      \"description\": \"Garantir que os links tenham texto discernível\",\n      \"help\": \"Os links devem ter texto discernível\"\n    },\n    \"list\": {\n      \"description\": \"Garantir que as listas estejam estruturadas corretamente\",\n      \"help\": \"Os elementos <ul> e <ol> devem conter diretamente apenas elementos <li>, <script> ou <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Garantir que os elementos <li> sejam usados de forma semântica\",\n      \"help\": \"Os elementos <li> devem estar contidos num <ul> ou <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Garantir que os elementos <marquee> não sejam usados\",\n      \"help\": \"Os elementos <marquee> estão obsoletos e não devem ser usados\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Garantir que <meta http-equiv=\\\"refresh\\\"> não seja usado para atualização com atraso\",\n      \"help\": \"Não se deve usar atualização com atraso\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Garantir que <meta http-equiv=\\\"refresh\\\"> não seja usado para atualização com atraso\",\n      \"help\": \"Não se deve usar atualização com atraso inferior a 20 horas\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Garantir que <meta name=\\\"viewport\\\"> permita uma ampliação significativa\",\n      \"help\": \"Os utilizadores devem ser capazes de ampliar e escalar o texto até 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Garantir que <meta name=\\\"viewport\\\"> não desative a escalabilidade e ampliação do texto\",\n      \"help\": \"A ampliação e escalabilidade não devem ser desativadas\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Garantir que os controles interativos não estejam aninhados, pois nem sempre são anunciados pelos leitores de ecrã ou podem causar problemas de foco para tecnologias assistivas\",\n      \"help\": \"Os controles interativos não devem estar aninhados\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Garantir que os elementos <video> ou <audio> não reproduzam áudio automaticamente por mais de 3 segundos sem um mecanismo de controlo para parar ou silenciar o áudio\",\n      \"help\": \"Os elementos <video> ou <audio> não devem reproduzir automaticamente\"\n    },\n    \"object-alt\": {\n      \"description\": \"Garantir que os elementos <object> tenham texto alternativo\",\n      \"help\": \"Os elementos <object> devem ter texto alternativo\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Garantir que texto em negrito, itálico e tamanho de fonte não sejam usados para estilizar elementos <p> como cabeçalhos\",\n      \"help\": \"Os elementos <p> estilizados não devem ser usados como cabeçalhos\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Garantir que a página, ou pelo menos um dos seus 'frames', contenha um cabeçalho de nível 1\",\n      \"help\": \"A página deve conter um cabeçalho de nível 1\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Os elementos marcados como 'none' ou 'presentation' não devem ter ARIA global ou 'tabindex', para garantir que todos os leitores de ecrã os ignorem\",\n      \"help\": \"Garantir que os elementos marcados como 'none' ou 'presentation' sejam consistentemente ignorados\"\n    },\n    \"region\": {\n      \"description\": \"Garantir que todo o conteúdo da página esteja contido em marcos (landmarks)\",\n      \"help\": \"Todo o conteúdo da página deve estar contido em marcos (landmarks)\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Garantir que os elementos com 'role=\\\"img\\\"' tenham texto alternativo\",\n      \"help\": \"Os elementos com 'role=\\\"img\\\"' devem ter um texto alternativo\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Garantir que o atributo 'scope' seja usado corretamente em tabelas\",\n      \"help\": \"O atributo 'scope' deve ser usado corretamente\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Garantir que os elementos com conteúdo rolável sejam acessíveis por teclado\",\n      \"help\": \"A região rolável deve ser acessível por teclado\"\n    },\n    \"select-name\": {\n      \"description\": \"Garantir que o elemento 'select' tenha um nome acessível\",\n      \"help\": \"O elemento 'select' deve ter um nome acessível\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Garantir que os mapas de imagem do lado do servidor não sejam usados\",\n      \"help\": \"Os mapas de imagem do lado do servidor não devem ser usados\"\n    },\n    \"skip-link\": {\n      \"description\": \"Garantir que todos os links de salto tenham um destino focável\",\n      \"help\": \"O destino do link de salto deve existir e ser focável\"\n    },\n    \"summary-name\": {\n      \"description\": \"Garantir que os elementos 'summary' tenham texto discernível\",\n      \"help\": \"Os elementos 'summary' devem ter texto discernível\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Garantir que os elementos <svg> com uma função 'img', 'graphics-document' ou 'graphics-symbol' tenham um texto acessível\",\n      \"help\": \"Os elementos <svg> com a função 'img' devem ter um texto alternativo\"\n    },\n    \"tabindex\": {\n      \"description\": \"Garantir que os valores do atributo 'tabindex' não sejam superiores a 0\",\n      \"help\": \"Os elementos não devem ter 'tabindex' superior a zero\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Garantir que o elemento <caption> não contenha o mesmo texto que o atributo 'summary'\",\n      \"help\": \"As tabelas não devem ter o mesmo 'summary' e <caption>\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Garantir que as tabelas com caption utilizem o elemento <caption>.\",\n      \"help\": \"Não se devem usar células de dados ou de cabeçalho para dar caption a uma tabela de dados.\"\n    },\n    \"target-size\": {\n      \"description\": \"Garantir que os alvos tácteis tenham tamanho e espaço suficientes\",\n      \"help\": \"Todos os alvos tácteis devem ter 24px de dimensão ou deixar espaço suficiente\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Garantir que cada célula de dados não vazia numa tabela maior do que 3x3 tenha um ou mais cabeçalhos\",\n      \"help\": \"Os elementos <td> não vazios em tabelas maiores devem ter um cabeçalho associado\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Garantir que cada célula numa tabela que usa o atributo 'headers' se refira apenas a outras células nessa tabela\",\n      \"help\": \"As células de tabela que usam o atributo 'headers' devem referir-se apenas a células na mesma tabela\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Garantir que os elementos <th> e os elementos com 'role=columnheader/rowheader' tenham células de dados que descrevem\",\n      \"help\": \"Os cabeçalhos de tabela numa tabela de dados devem referir-se às células de dados\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Garantir que os atributos 'lang' tenham valores válidos\",\n      \"help\": \"O atributo 'lang' deve ter um valor válido\"\n    },\n    \"video-caption\": {\n      \"description\": \"Garantir que os elementos <video> tenham legendas\",\n      \"help\": \"Os elementos <video> devem ter legendas\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Funções abstratas não são utilizadas\",\n      \"fail\": {\n        \"singular\": \"A função abstrata não pode ser usada diretamente: ${data.values}\",\n        \"plural\": \"As funções abstratas não podem ser usadas diretamente: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"Os atributos ARIA são usados corretamente para a função definida\",\n      \"fail\": {\n        \"singular\": \"Atributo ARIA não é permitido: ${data.values}\",\n        \"plural\": \"Atributos ARIA não são permitidos: ${data.values}\"\n      },\n      \"incomplete\": \"Verifique se não há problemas se o atributo ARIA for ignorado neste elemento: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"A função ARIA é permitida para o elemento\",\n      \"fail\": {\n        \"singular\": \"A função ARIA ${data.values} não é permitida para o elemento\",\n        \"plural\": \"As funções ARIA ${data.values} não são permitidas para o elemento\"\n      },\n      \"incomplete\": {\n        \"singular\": \"A função ARIA ${data.values} deve ser removida quando o elemento se tornar visível, pois não é permitida para o elemento\",\n        \"plural\": \"As funções ARIA ${data.values} devem ser removidas quando o elemento se tornar visível, pois não são permitidas para o elemento\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"O elemento tem o atributo 'aria-busy'\",\n      \"fail\": \"O elemento utiliza 'aria-busy=\\\"true\\\"' enquanto mostra um carregador\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"O atributo ARIA é permitido\",\n      \"fail\": {\n        \"checkbox\": \"Remova aria-checked, ou defina-o como '\\\"${data.checkState}\\\"' para corresponder ao estado real da caixa de seleção\",\n        \"rowSingular\": \"Este atributo é suportado em linhas de 'treegrid', mas não em ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"Estes atributos são suportados em linhas de 'treegrid', mas não em ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"'aria-errormessage' existe e referencia elementos visíveis para leitores de ecrã que utilizam uma técnica suportada de 'aria-errormessage'\",\n      \"fail\": {\n        \"singular\": \"O valor de 'aria-errormessage' `${data.values}` deve utilizar uma técnica para anunciar a mensagem (por exemplo, aria-live, aria-describedby, role=alert, etc.)\",\n        \"plural\": \"Os valores de 'aria-errormessage' `${data.values}` devem utilizar uma técnica para anunciar a mensagem (por exemplo, aria-live, aria-describedby, role=alert, etc.)\",\n        \"hidden\": \"O valor de 'aria-errormessage' `${data.values}` não pode referenciar um elemento oculto\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Assegure que o valor de 'aria-errormessage' `${data.values}` referencia um elemento existente\",\n        \"plural\": \"Assegure que os valores de 'aria-errormessage' `${data.values}` referenciem elementos existentes\",\n        \"idrefs\": \"Não foi possível determinar se o elemento 'aria-errormessage' existe na página: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Não há nenhum atributo 'aria-hidden' no corpo do documento\",\n      \"fail\": \"'aria-hidden=true' não deve estar presente no corpo do documento\"\n    },\n    \"aria-level\": {\n      \"pass\": \"Os valores de 'aria-level' são válidos\",\n      \"incomplete\": \"Valores de 'aria-level' superiores a 6 não são suportados em todas as combinações de leitores de ecrã e navegadores\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"O atributo ARIA é permitido\",\n      \"fail\": {\n        \"hasRolePlural\": \"Os atributos ${data.prohibited} não podem ser usados com a função '\\\"${data.role}\\\"'.\",\n        \"hasRoleSingular\": \"O atributo ${data.prohibited} não pode ser usado com a função '\\\"${data.role}\\\"'.\",\n        \"noRolePlural\": \"Os atributos ${data.prohibited} não podem ser usados num ${data.nodeName} sem um atributo 'role' válido.\",\n        \"noRoleSingular\": \"O atributo ${data.prohibited} não pode ser usado num ${data.nodeName} sem um atributo 'role' válido.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"O atributo ${data.prohibited} não é bem suportado com a função '\\\"${data.role}\\\"'.\",\n        \"hasRolePlural\": \"Os atributos ${data.prohibited} não são bem suportados com a função '\\\"${data.role}\\\"'.\",\n        \"noRoleSingular\": \"O atributo ${data.prohibited} não é bem suportado num ${data.nodeName} sem um atributo 'role' válido.\",\n        \"noRolePlural\": \"Os atributos ${data.prohibited} não são bem suportados num ${data.nodeName} sem um atributo 'role' válido.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Todos os atributos ARIA obrigatórios estão presentes\",\n      \"fail\": {\n        \"singular\": \"Atributo ARIA obrigatório não está presente: ${data.values}\",\n        \"plural\": \"Atributos ARIA obrigatórios não estão presentes: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Os elementos filhos ARIA obrigatórios estão presentes\",\n        \"aria-busy\": \"O elemento tem o atributo 'aria-busy', pelo que é permitido omitir os elementos filhos obrigatórios\"\n      },\n      \"fail\": {\n        \"singular\": \"A função ARIA de filho obrigatório não está presente: ${data.values}\",\n        \"plural\": \"A função ARIA de filhos obrigatórios não está presente: ${data.values}\",\n        \"unallowed\": \"O elemento tem filhos que não são permitidos: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Espera-se que seja adicionada a função ARIA de filho: ${data.values}\",\n        \"plural\": \"Espera-se que sejam adicionadas as funções ARIA de filhos: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"A função ARIA parental obrigatória está presente\",\n      \"fail\": {\n        \"singular\": \"A função ARIA parental obrigatória não está presente: ${data.values}\",\n        \"plural\": \"As funções ARIA parentais obrigatórias não estão presentes: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"'aria-roledescription' é utilizado numa função semântica suportada\",\n      \"incomplete\": \"Verifique se o 'aria-roledescription' é anunciado por leitores de ecrã suportados\",\n      \"fail\": \"Atribua ao elemento uma função que suporte 'aria-roledescription'\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"O atributo ARIA é suportado\",\n      \"fail\": \"O atributo ARIA não é amplamente suportado em leitores de ecrã e tecnologias assistivas: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Os valores dos atributos ARIA são válidos\",\n      \"fail\": {\n        \"singular\": \"Valor de atributo ARIA inválido: ${data.values}\",\n        \"plural\": \"Valores de atributo ARIA inválidos: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"O ID do elemento do atributo ARIA não existe na página: ${data.needsReview}\",\n        \"noIdShadow\": \"O ID do elemento do atributo ARIA não existe na página ou é descendente de uma shadow DOM tree diferente: ${data.needsReview}\",\n        \"ariaCurrent\": \"O valor do atributo ARIA é inválido e será tratado como '\\\"aria-current=true\\\"': ${data.needsReview}\",\n        \"idrefs\": \"Não foi possível determinar se o ID do elemento do atributo ARIA existe na página: ${data.needsReview}\",\n        \"empty\": \"O valor do atributo ARIA é ignorado quando está vazio: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"Não foi possível determinar se o ID referenciado por 'aria-controls existe' na página enquanto se utiliza 'aria-haspopup': ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"O nome do atributo ARIA é válido\",\n      \"fail\": {\n        \"singular\": \"Nome de atributo ARIA inválido: ${data.values}\",\n        \"plural\": \"Nomes de atributos ARIA inválidos: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"'aria-braillelabel' é utilizado num elemento com texto acessível\",\n      \"fail\": \"'aria-braillelabel' é utilizado num elemento sem texto acessível\",\n      \"incomplete\": \"Não foi possível calcular o texto acessível\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"'aria-brailleroledescription' é utilizado num elemento com aria-roledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"'aria-brailleroledescription' é utilizado num elemento sem 'aria-roledescription'\",\n        \"emptyRoleDescription\": \"'aria-brailleroledescription' é utilizado num elemento com 'aria-roledescription' vazio\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"A função ARIA não está obsoleta\",\n      \"fail\": \"A função utilizada está obsoleta: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Foi utilizado apenas um valor de 'role'\",\n      \"fail\": \"Utilize apenas um valor de 'role', pois as funções de 'fallback' não são suportadas em navegadores mais antigos\",\n      \"incomplete\": \"Utilize apenas o role 'presentation' ou 'none', pois são sinónimos.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"O elemento tem o atributo ARIA global: ${data.values}\",\n        \"plural\": \"O elemento tem os atributos ARIA globais: ${data.values}\"\n      },\n      \"fail\": \"O elemento não tem atributo ARIA global\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"O elemento tem uma função de 'widget'.\",\n      \"fail\": \"O elemento não tem uma função de 'widget'.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"A função ARIA é válida\",\n      \"fail\": {\n        \"singular\": \"A função deve ser uma das funções ARIA válidas: ${data.values}\",\n        \"plural\": \"As funções devem ser uma das funções ARIA válidas: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"O elemento é focável.\",\n      \"fail\": \"O elemento não é focável.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Não há discrepância entre uma <label> e o nome acessível\",\n      \"incomplete\": \"Verifique se a <label> não precisa fazer parte do nome do campo ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"A função ARIA é suportada\",\n      \"fail\": \"A função utilizada não é amplamente suportada em leitores de ecrã e tecnologias assistivas: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"O elemento tem semântica válida para um elemento na ordem de foco.\",\n      \"fail\": \"O elemento tem semântica inválida para um elemento na ordem de foco.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"O elemento tem um contraste de cor suficiente de ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} (cor de primeiro plano: ${data.fgColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} entre a cor de primeiro plano e a cor da sombra (cor de primeiro plano: ${data.fgColor}, cor do text-shadow: ${data.shadowColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} entre a cor da sombra e a cor de fundo (cor do text-shadow: ${data.shadowColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Não foi possível determinar a razão de contraste\",\n        \"bgImage\": \"A cor de fundo do elemento não pôde ser determinada devido a uma imagem de fundo\",\n        \"bgGradient\": \"A cor de fundo do elemento não pôde ser determinada devido a um gradiente de fundo\",\n        \"imgNode\": \"A cor de fundo do elemento não pôde ser determinada porque o elemento contém um nó de imagem\",\n        \"bgOverlap\": \"A cor de fundo do elemento não pôde ser determinada porque está sobreposto por outro elemento\",\n        \"fgAlpha\": \"A cor de primeiro plano do elemento não pôde ser determinada devido à transparência alfa\",\n        \"elmPartiallyObscured\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente encoberta por outro elemento\",\n        \"elmPartiallyObscuring\": \"A cor de fundo do elemento não pôde ser determinada porque se sobrepõe parcialmente a outros elementos\",\n        \"outsideViewport\": \"A cor de fundo do elemento não pôde ser determinada porque está fora da área visível\",\n        \"equalRatio\": \"O elemento tem uma razão de contraste 1:1 com o fundo\",\n        \"shortTextContent\": \"O conteúdo do elemento é demasiado curto para determinar se é realmente conteúdo textual\",\n        \"nonBmp\": \"O conteúdo do elemento contém apenas caracteres não textuais\",\n        \"pseudoContent\": \"A cor de fundo do elemento não pôde ser determinada devido a um pseudo-elemento\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"O elemento tem um contraste de cor suficiente de ${data.contrastRatio}\",\n        \"hidden\": \"O elemento está oculto\"\n      },\n      \"fail\": {\n        \"default\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} (cor de primeiro plano: ${data.fgColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} entre a cor de primeiro plano e a cor da sombra (cor de primeiro plano: ${data.fgColor}, cor do text-shadow: ${data.shadowColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"O elemento tem um contraste de cor insuficiente de ${data.contrastRatio} entre a cor da sombra e a cor de fundo (cor do text-shadow: ${data.shadowColor}, cor de fundo: ${data.bgColor}, tamanho da fonte: ${data.fontSize}, peso da fonte: ${data.fontWeight}). Esperava-se uma razão de contraste de ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Não foi possível determinar a razão de contraste\",\n        \"bgImage\": \"A cor de fundo do elemento não pôde ser determinada devido a uma imagem de fundo\",\n        \"bgGradient\": \"A cor de fundo do elemento não pôde ser determinada devido a um gradiente de fundo\",\n        \"imgNode\": \"A cor de fundo do elemento não pôde ser determinada porque o elemento contém um nó de imagem\",\n        \"bgOverlap\": \"A cor de fundo do elemento não pôde ser determinada porque está sobreposto por outro elemento\",\n        \"complexTextShadows\": \"Não foi possível determinar o contraste do elemento porque utiliza sombras de texto complexas\",\n        \"fgAlpha\": \"A cor de primeiro plano do elemento não pôde ser determinada devido à transparência alfa\",\n        \"elmPartiallyObscured\": \"A cor de fundo do elemento não pôde ser determinada porque está parcialmente encoberta por outro elemento\",\n        \"elmPartiallyObscuring\": \"A cor de fundo do elemento não pôde ser determinada porque se sobrepõe parcialmente a outros elementos\",\n        \"outsideViewport\": \"A cor de fundo do elemento não pôde ser determinada porque está fora da área visível\",\n        \"equalRatio\": \"O elemento tem uma razão de contraste 1:1 com o fundo\",\n        \"shortTextContent\": \"O conteúdo do elemento é demasiado curto para determinar se é realmente texto\",\n        \"nonBmp\": \"O conteúdo do elemento contém apenas caracteres não textuais\",\n        \"pseudoContent\": \"A cor de fundo do elemento não pôde ser determinada devido a um pseudo-elemento\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Os links podem ser distinguidos do texto circundante através de estilos visuais\",\n      \"incomplete\": {\n        \"default\": \"Verifique se o link necessita de estilos para se distinguir do texto próximo\",\n        \"pseudoContent\": \"Verifique se o pseudo-estilo do link é suficiente para o distinguir do texto circundante\"\n      },\n      \"fail\": \"O link não tem estilos (como sublinhado) para se distinguir do texto circundante\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Os links podem ser distinguidos do texto circundante de alguma forma que não seja pela cor\",\n      \"fail\": {\n        \"fgContrast\": \"O link tem um contraste de cor insuficiente de ${data.contrastRatio}:1 com o texto circundante. (O contraste mínimo é ${data.requiredContrastRatio}:1, cor do link: ${data.nodeColor}, cor do texto circundante: ${data.parentColor})\",\n        \"bgContrast\": \"O fundo do link tem um contraste de cor insuficiente de ${data.contrastRatio} (o contraste mínimo é ${data.requiredContrastRatio}:1, cor de fundo do link: ${data.nodeBackgroundColor}, cor de fundo circundante: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Não foi possível determinar a razão de contraste do primeiro plano do elemento\",\n        \"bgContrast\": \"Não foi possível determinar a razão de contraste do fundo do elemento\",\n        \"bgImage\": \"Não foi possível determinar a razão de contraste do elemento devido a uma imagem de fundo\",\n        \"bgGradient\": \"Não foi possível determinar a razão de contraste do elemento devido a um gradiente de fundo\",\n        \"imgNode\": \"Não foi possível determinar a razão de contraste do elemento porque contém um nó de imagem\",\n        \"bgOverlap\": \"Não foi possível determinar a razão de contraste do elemento devido à sobreposição de elementos\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"O valor de 'autocomplete' está num elemento apropriado\",\n      \"fail\": \"O valor de 'autocomplete' é inapropriado para este tipo de entrada\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"o atributo 'autocomplete' está formatado corretamente\",\n      \"fail\": \"o atributo 'autocomplete' está formatado incorretamente\",\n      \"incomplete\": \"o atributo 'autocomplete' tem um valor não padrão. Verifique se poderia ser utilizado algum valor padrão.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"O valor do atributo 'accesskey' é único\",\n      \"fail\": \"O documento tem múltiplos elementos com o mesmo 'accesskey'\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"O elemento contém elementos focáveis\",\n      \"fail\": \"O elemento deveria ter conteúdo focável\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Não há elementos focáveis contidos no elemento\",\n      \"incomplete\": \"Verifique se os elementos focáveis movem imediatamente o indicador de foco\",\n      \"fail\": \"O conteúdo focável deve ser desativado ou removido do DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"O elemento é focável\",\n      \"fail\": \"O elemento deveria ser focável\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Não há elementos focáveis enquanto um modal está aberto\",\n      \"incomplete\": \"Verifique se os elementos focáveis não são tabuláveis no estado atual\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"O elemento não está na ordem de tabulação ou tem texto acessível\",\n      \"fail\": \"O elemento está na ordem de tabulação e não tem texto acessível\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem um nome acessível\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Não há elementos focáveis contidos no elemento\",\n      \"incomplete\": \"Verifique se os elementos focáveis movem imediatamente o indicador de foco\",\n      \"fail\": \"O conteúdo focável deve ter 'tabindex=\\\"-1\\\"' ou ser removido do DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"O elemento não tem descendentes focáveis\",\n      \"fail\": \"O elemento tem descendentes focáveis\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem descendentes\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"O marco ${data.role} está no nível principal.\",\n      \"fail\": \"O marco ${data.role} está contido noutro marco.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"O elemento não tem descendentes focáveis\",\n      \"fail\": {\n        \"default\": \"O elemento tem descendentes focáveis\",\n        \"notHidden\": \"Utilizar um tabindex negativo num elemento dentro de um controlo interativo não impede que as tecnologias assistivas foquem o elemento (mesmo com aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Não foi possível determinar se o elemento tem descendentes\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"A página tem pelo menos um cabeçalho de nível 1\",\n      \"fail\": \"A página deve ter um cabeçalho de nível 1\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"O documento tem pelo menos um marco 'main'\",\n      \"fail\": \"O documento não tem um marco 'main'\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"O documento não tem mais do que um marco de 'banner'\",\n      \"fail\": \"O documento tem mais do que um marco de 'banner'\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"O documento não tem mais do que um marco 'contentinfo'\",\n      \"fail\": \"O documento tem mais do que um marco 'contentinfo'\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"O documento não tem mais do que um marco 'main'\",\n      \"fail\": \"O documento tem mais do que um marco 'main'\"\n    },\n    \"tabindex\": {\n      \"pass\": \"O elemento não tem um 'tabindex' superior a 0\",\n      \"fail\": \"O elemento tem um 'tabindex' superior a 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"O elemento tem um valor válido para o atributo 'alt'\",\n      \"fail\": \"O elemento tem um atributo 'alt' que contém apenas um espaço, o qual não é ignorado por todos os leitores de ecrã\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"O elemento não duplica o texto existente no atributo 'alt' da <img>\",\n      \"fail\": \"O elemento contém uma <img> com texto 'alt' que duplica texto existente\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"O elemento tem uma <label> explícita\",\n      \"fail\": \"O elemento não tem uma <label> explícita\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem uma <label> explícita\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"O texto de ajuda (title ou aria-describedby) não duplica o texto da etiqueta\",\n      \"fail\": \"O texto de ajuda (title ou aria-describedby) é igual ao texto da etiqueta\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"O elemento de formulário tem uma <label> explícita visível\",\n      \"fail\": \"O elemento de formulário tem uma <label> explícita que está oculta\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem uma <label> explícita oculta\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"O elemento tem uma <label> implícita (envolvida)\",\n      \"fail\": \"O elemento não tem uma <label> implícita (envolvida)\",\n      \"incomplete\": \"Não foi possível determinar se o elemento de formulário tem uma <label> implícita (envolvida)\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"O elemento contém texto visível como parte do seu nome acessível\",\n      \"fail\": \"O texto dentro do elemento não está incluído no nome acessível\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"O campo de formulário não tem múltiplos <label>\",\n      \"incomplete\": \"Múltiplos <label> não são amplamente suportadas em tecnologias assistivas. Assegure que a primeira etiqueta contém toda a informação necessária.\"\n    },\n    \"title-only\": {\n      \"pass\": \"O elemento de formulário não utiliza apenas o atributo 'title' para a sua etiqueta\",\n      \"fail\": \"Apenas o 'title' é usado para gerar a etiqueta do elemento de formulário\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Os marcos devem ter uma combinação única de 'role' ou 'role'/'label'/'title' (ou seja, nome acessível)\",\n      \"fail\": \"O marco deve ter um 'aria-label', 'aria-labelledby' ou 'title' únicos para torná-lo distinguível\"\n    },\n    \"has-lang\": {\n      \"pass\": \"O elemento <html> tem um atributo 'lang'\",\n      \"fail\": {\n        \"noXHTML\": \"O atributo 'xml:lang' não é válido em páginas HTML, use o atributo 'lang'.\",\n        \"noLang\": \"O elemento <html> não tem um atributo 'lang'\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"O valor do atributo 'lang' está incluído na lista de línguas válidas\",\n      \"fail\": \"O valor do atributo 'lang' não está incluído na lista de línguas válidas\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Os atributos 'lang' e 'xml:lang' têm a mesma língua base\",\n      \"fail\": \"Os atributos 'lang' e 'xml:lang' não têm a mesma língua base\"\n    },\n    \"dlitem\": {\n      \"pass\": \"O item de lista de descrições tem um elemento pai <dl>\",\n      \"fail\": \"O item de lista de descrições não tem um elemento pai <dl>\"\n    },\n    \"listitem\": {\n      \"pass\": \"O item de lista tem um elemento pai <ul>, <ol> ou 'role=\\\"list\\\"'\",\n      \"fail\": {\n        \"default\": \"O item de lista não tem um elemento pai <ul> ou <ol>\",\n        \"roleNotValid\": \"O elemento pai do item de lista tem uma role que não é 'role=\\\"list\\\"'\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"O elemento <dl> tem apenas filhos diretos permitidos: <dt>, <dd> ou <div>\",\n      \"fail\": \"O elemento <dl> tem filhos diretos não permitidos: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"O elemento de lista tem apenas filhos diretos permitidos, que devem ser <li>\",\n      \"fail\": \"O elemento de lista tem filhos diretos não permitidos: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Quando não está vazio, o elemento tem tanto <dt> como <dd>\",\n      \"fail\": \"Quando não está vazio, o elemento não tem pelo menos um elemento <dt> seguido de pelo menos um <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"O elemento multimédia tem uma pista de legendas\",\n      \"incomplete\": \"Verifique se as legendas estão disponíveis para o elemento\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"O 'iframe' foi testado com axe-core\",\n      \"fail\": \"O 'iframe' não pôde ser testado com axe-core\",\n      \"incomplete\": \"O 'iframe' ainda tem de ser testado com axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> ou <audio> não reproduzem áudio por mais do que a duração permitida ou têm um mecanismo de controlo\",\n      \"fail\": \"<video> ou <audio> reproduzem áudio por mais do que a duração permitida e não têm um mecanismo de controlo\",\n      \"incomplete\": \"Verifique se o <video> ou <audio> não reproduzem áudio por mais do que a duração permitida ou fornecem um mecanismo de controlo\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"O ecrã é operativo, e não existe bloqueio de orientação\",\n      \"fail\": \"O bloqueio de orientação CSS está aplicado e torna o ecrã inoperável\",\n      \"incomplete\": \"Não é possível determinar o bloqueio de orientação CSS\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"A tag <meta> não impede uma ampliação significativa em dispositivos móveis\",\n      \"fail\": \"A tag <meta> limita a ampliação em dispositivos móveis\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"A tag <meta> não desativa a ampliação em dispositivos móveis\",\n      \"fail\": \"${data} na tag <meta> desativa a ampliação em dispositivos móveis\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"O alvo tem espaço suficiente dos seus vizinhos mais próximos. O espaço clicável seguro tem um diâmetro de ${data.closestOffset}px, que é, no mínimo, ${data.minOffset}px.\",\n        \"large\": \"O alvo excede largamente o tamanho mínimo de ${data.minOffset}px.\"\n      },\n      \"fail\": \"O alvo tem espaço insuficiente em relação aos vizinhos mais próximos. O espaço clicável seguro tem um diâmetro de ${data.closestOffset}px, em vez de, pelo menos, ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"O elemento com tabindex negativo tem espaço insuficiente em relação aos seus vizinhos mais próximos. O espaço clicável seguro tem um diâmetro de ${data.closestOffset}px, em vez de, pelo menos, ${data.minOffset}px. É este um alvo?\",\n        \"nonTabbableNeighbor\": \"O alvo tem espaço insuficiente em relação aos seus vizinhos mais próximos. O espaço clicável seguro tem um diâmetro de ${data.closestOffset}px, em vez de, pelo menos, ${data.minOffset}px. Será que o vizinho é um alvo?\",\n        \"tooManyRects\": \"Não foi possível obter o tamanho do alvo porque há elementos sobrepostos em excesso\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"O controlo tem um tamanho suficiente (${data.width}px por ${data.height}px, devendo ter pelo menos ${data.minSize}px por ${data.minSize}px)\",\n        \"obscured\": \"O controlo é ignorado porque está totalmente encoberto e, por isso, não é clicável\",\n        \"large\": \"O alvo excede largamente o tamanho mínimo de ${data.minSize}px.\"\n      },\n      \"fail\": {\n        \"default\": \"O alvo tem um tamanho insuficiente (${data.width}px por ${data.height}px, devendo ter pelo menos ${data.minSize}px por ${data.minSize}px)\",\n        \"partiallyObscured\": \"O alvo tem um tamanho insuficiente porque está parcialmente encoberto (o menor espaço é de ${data.width}px por ${data.height}px, devendo ser pelo menos ${data.minSize}px por ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"O elemento com 'tabindex' negativo tem um tamanho insuficiente (${data.width}px por ${data.height}px, devendo ser pelo menos ${data.minSize}px por ${data.minSize}px). É este um alvo?\",\n        \"contentOverflow\": \"O tamanho do elemento não pôde ser determinado com precisão devido ao conteúdo com overflow\",\n        \"partiallyObscured\": \"O elemento com 'tabindex' negativo tem um tamanho insuficiente porque está parcialmente encoberto (o menor espaço é de ${data.width}px por ${data.height}px, devendo ser pelo menos ${data.minSize}px por ${data.minSize}px). É este um alvo?\",\n        \"partiallyObscuredNonTabbable\": \"O alvo tem um tamanho insuficiente porque está parcialmente encoberto por um vizinho com 'tabindex' negativo (o menor espaço é de ${data.width}px por ${data.height}px, devendo ser pelo menos ${data.minSize}px por ${data.minSize}px). Será que o vizinho é um alvo?\",\n        \"tooManyRects\": \"Não foi possível obter o tamanho do alvo porque há elementos sobrepostos em excesso\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"A página tem um cabeçalho\",\n      \"fail\": \"A página não tem um cabeçalho\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Ordem dos cabeçalhos válida\",\n      \"fail\": \"Ordem dos cabeçalhos inválida\",\n      \"incomplete\": \"Não foi possível determinar o cabeçalho anterior\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Não existem outros links com o mesmo nome que direcionem para uma URL diferente\",\n      \"incomplete\": \"Verifique se os links têm o mesmo propósito ou se são intencionalmente ambíguos.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Foi encontrado um link de salto válido\",\n      \"fail\": \"Não foi encontrado um link de salto válido\"\n    },\n    \"landmark\": {\n      \"pass\": \"A página tem uma região de marco\",\n      \"fail\": \"A página não tem uma região de marco\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"A tag <meta> não atualiza imediatamente a página\",\n      \"fail\": \"A tag <meta> força a atualização da página com temporização\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"A tag <meta> não atualiza imediatamente a página\",\n      \"fail\": \"A tag <meta> força a atualização da página com temporização (inferior a 20 horas)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Os elementos <p> não estão estilizados como cabeçalhos\",\n      \"fail\": \"Deveriam ser usados elementos de cabeçalho em vez de elementos <p> estilizados\",\n      \"incomplete\": \"Não foi possível determinar se os elementos <p> estão estilizados como cabeçalhos\"\n    },\n    \"region\": {\n      \"pass\": \"Todo o conteúdo da página está contido em marcos\",\n      \"fail\": \"Algum conteúdo da página não está contido em marcos\"\n    },\n    \"skip-link\": {\n      \"pass\": \"O destino do link de salto existe\",\n      \"incomplete\": \"O destino do link de salto deve tornar-se visível na ativação\",\n      \"fail\": \"Não há destino para o link de salto\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"O atributo 'title' do elemento é único\",\n      \"fail\": \"O atributo 'title' do elemento não é único\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"O documento não tem elementos ativos que partilhem o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem elementos ativos com o mesmo atributo 'id': ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"O documento não tem elementos referenciados com ARIA ou etiquetas que partilhem o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem múltiplos elementos referenciados com ARIA com o mesmo atributo 'id': ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"O documento não tem elementos estáticos que partilhem o mesmo atributo 'id'\",\n      \"fail\": \"O documento tem múltiplos elementos estáticos com o mesmo atributo 'id': ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"O atributo 'aria-label' existe e não está vazio\",\n      \"fail\": \"O atributo 'aria-label' não existe ou está vazio\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"O atributo 'aria-labelledby' existe e referencia elementos visíveis para leitores de ecrã\",\n      \"fail\": \"O atributo 'aria-labelledby' não existe, referencia elementos que não existem ou que estão vazios\",\n      \"incomplete\": \"Assegure que 'aria-labelledby' referencia um elemento existente\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Não foram especificados estilos 'inline' com '!important' que afetem o espaçamento do texto\",\n      \"fail\": {\n        \"singular\": \"Remova '!important' do estilo 'inline' ${data.values}, pois a sua sobreposição não é suportada pela maioria dos navegadores\",\n        \"plural\": \"Remova '!important' dos estilos 'inline' ${data.values}, pois a sua sobreposição não é suportada pela maioria dos navegadores\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"O elemento tem texto interno que é visível para leitores de ecrã\",\n      \"fail\": \"O elemento não tem texto interno que seja visível para leitores de ecrã\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem filhos\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"O documento tem um elemento <title> não vazio\",\n      \"fail\": \"O documento não tem um elemento <title> não vazio\"\n    },\n    \"exists\": {\n      \"pass\": \"O elemento não existe\",\n      \"incomplete\": \"O elemento existe\"\n    },\n    \"has-alt\": {\n      \"pass\": \"O elemento tem um atributo 'alt'\",\n      \"fail\": \"O elemento não tem um atributo 'alt'\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"O elemento tem texto que é visível para leitores de ecrã\",\n      \"fail\": \"O elemento não tem texto que seja visível para leitores de ecrã\",\n      \"incomplete\": \"Não foi possível determinar se o elemento tem filhos\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"O espaçamento entre letras no atributo 'style' não está definido como !important, ou cumpre o mínimo\",\n      \"fail\": \"O espaçamento entre letras no atributo 'style' não deve usar !important, ou ter ${data.minValue}em (atualmente ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"A altura da linha no atributo 'style' não está definida como !important, ou cumpre o mínimo\",\n      \"fail\": \"A altura da linha no atributo 'style' não deve usar !important, ou ter ${data.minValue}em (atualmente ${data.value}em)\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"O espaçamento entre palavras no atributo 'style' não está definido como !important, ou cumpre o mínimo\",\n      \"fail\": \"O espaçamento entre palavras no atributo 'style' não deve usar !important, ou ter ${data.minValue}em (atualmente ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"O elemento não está visível\",\n      \"fail\": \"O elemento está visível\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"O elemento tem um atributo 'alt' não vazio\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'alt'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'alt' vazio\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"O elemento não tem um atributo 'value'\",\n        \"has-label\": \"O elemento tem um atributo 'value' não vazio\"\n      },\n      \"fail\": \"O elemento tem um atributo 'value' e este está vazio\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"O elemento tem um atributo 'placeholder'\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'placeholder'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'placeholder' vazio\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"O elemento tem um atributo 'title'\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'title'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'title' vazio\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"O elemento tem um atributo 'value' não vazio\",\n      \"fail\": {\n        \"noAttr\": \"O elemento não tem um atributo 'value'\",\n        \"emptyAttr\": \"O elemento tem um atributo 'value' vazio\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"A semântica por defeito do elemento foi substituída por 'role=\\\"${data.role}\\\"'\",\n      \"fail\": {\n        \"default\": \"A semântica por defeito do elemento não foi substituída por 'role=\\\"none\\\"' ou 'role=\\\"presentation\\\"'\",\n        \"globalAria\": \"A função do elemento não é de apresentação porque tem um atributo ARIA global\",\n        \"focusable\": \"A função do elemento não é de apresentação porque é focável\",\n        \"both\": \"A função do elemento não é de apresentação porque tem um atributo ARIA global e é focável\",\n        \"iframe\": \"Utilizar o atributo '\\\"title\\\"' num elemento ${data.nodeName} com uma função de apresentação comporta-se de forma inconsistente entre os leitores de ecrã\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"A semântica por defeito do elemento foi substituída por 'role=\\\"none\\\"'\",\n      \"fail\": \"A semântica por defeito do elemento não foi substituída por 'role=\\\"none\\\"'\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"A semântica por defeito do elemento foi substituída por 'role=\\\"presentation\\\"'\",\n      \"fail\": \"A semântica por defeito do elemento não foi substituída por 'role=\\\"presentation\\\"'\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"O elemento tem um filho que é um título\",\n      \"fail\": {\n        \"noTitle\": \"O elemento não tem um filho que seja um título\",\n        \"emptyTitle\": \"O título do filho do elemento está vazio\"\n      },\n      \"incomplete\": \"Não foi possível determinar se o elemento tem um filho que seja um título\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"A primeira linha de uma tabela não é utilizada como <caption>\",\n      \"fail\": \"O primeiro filho da tabela deveria ser um caption em vez de uma célula\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"O atributo scope é usado apenas em elementos de cabeçalho de tabela (<th>)\",\n      \"fail\": \"No HTML 5, os atributos scope só podem ser usados em elementos de cabeçalho de tabela (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"O conteúdo do atributo 'summary' e do <caption> não está duplicado\",\n      \"fail\": \"O conteúdo do atributo 'summary' e do elemento <caption> são idênticos\",\n      \"incomplete\": \"Não foi possível determinar se o elemento <table> tem um caption\"\n    },\n    \"scope-value\": {\n      \"pass\": \"O atributo 'scope' é usado corretamente\",\n      \"fail\": \"O valor do atributo 'scope' só pode ser 'row' ou 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Todas as células de dados não vazias têm cabeçalhos de tabela\",\n      \"fail\": \"Algumas células de dados não vazias não têm cabeçalhos de tabela\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"O atributo 'headers' é usado exclusivamente para referir-se a outras células da tabela\",\n      \"incomplete\": \"O atributo 'headers' está vazio\",\n      \"fail\": \"O atributo 'headers' não é usado exclusivamente para referir-se a outras células da tabela\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Todos os cabeçalhos de tabela numa tabela de dados referem-se às células de dados\",\n      \"fail\": \"Nem todas as células de cabeçalho de tabela referem-se a células de dados\",\n      \"incomplete\": \"Faltam células de dados na tabela ou estão vazias\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Todo o conteúdo da página foi analisado.\",\n      \"fail\": \"Houve problemas na análise do conteúdo desta página.\",\n      \"incomplete\": \"Existe conteúdo oculto na página que não foi analisado. Será necessário acionar a sua exibição para o analisar.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Corrija qualquer um dos seguintes:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Corrija todos os seguintes:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"O axe não conseguiu identificar a razão. Hora de recorrer ao inspetor de elementos!\"\n}\n"
  },
  {
    "path": "locales/ru.json",
    "content": "{\n  \"lang\": \"ru\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"Убедитесь, что значение атрибута accesskey является уникальным\",\n      \"help\": \"Значение атрибута accesskey должно быть уникальным\"\n    },\n    \"area-alt\": {\n      \"description\": \"Убедитесь, что элементы <area> карт изображений имеют альтернативный текст\",\n      \"help\": \"Активные элементы <area> должны иметь альтернативный текст\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"Убедитесь, что роль элемента поддерживает его ARIA атрибуты\",\n      \"help\": \"Элементы должны использовать только поддерживаемые ARIA атрибуты\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"Убедитесь, что атрибут role имеет допустимое значение для элемента\",\n      \"help\": \"ARIA роль должна быть подходящей для элемента\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"Убедитесь, что aria-braillelabel и aria-brailleroledescription имеют эквивалентный текст не для шрифта Брайля\",\n      \"help\": \"Атрибуты aria-braille должны иметь эквивалентный текст не для шрифта Брайля\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"Убедитесь, что каждая ARIA кнопка, ссылка и пункт меню имеет доступное имя\",\n      \"help\": \"ARIA команды должны иметь доступное имя\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"Убедитесь, что ARIA атрибуты используются в соответствии со спецификацией роли элемента\",\n      \"help\": \"ARIA атрибуты должны использоваться в соответствии с ролью элемента\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"Убедитесь, что элементы не используют устаревшие роли\",\n      \"help\": \"Устаревшие ARIA роли не должны использоваться\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"Убедитесь, что каждая ARIA диалоговая форма и alertdialog узел имеют доступное имя\",\n      \"help\": \"ARIA диалоговые формы и alertdialog узлы должны иметь доступное имя\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"Убедитесь, что aria-hidden=\\\"true\\\" отсутствует на теле документа.\",\n      \"help\": \"aria-hidden=\\\"true\\\" не должно присутствовать на теле документа\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"Убедитесь, что aria-hidden элементы не могут быть сфокусированы и не содержат фокусируемых элементов\",\n      \"help\": \"ARIA скрытые элементы не должны быть фокусируемыми или содержать фокусируемые элементы\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"Убедитесь, что каждое ARIA поле ввода имеет доступное имя\",\n      \"help\": \"ARIA поля ввода должны иметь доступное имя\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"Убедитесь, что каждый ARIA meter узел имеет доступное имя\",\n      \"help\": \"ARIA meter узлы должны иметь доступное имя\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"Убедитесь, что каждый ARIA progressbar узел имеет доступное имя\",\n      \"help\": \"ARIA progressbar узлы должны иметь доступное имя\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"Убедитесь, что ARIA атрибуты не запрещены для роли элемента\",\n      \"help\": \"Элементы должны использовать только разрешенные ARIA атрибуты\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"Убедитесь, что элементы с ARIA ролями имеют все необходимые ARIA атрибуты\",\n      \"help\": \"Необходимые ARIA атрибуты должны быть предоставлены\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"Убедитесь, что элементы с ARIA ролями, требующими дочерние роли, содержат их\",\n      \"help\": \"Некоторые ARIA роли должны содержать определенные дочерние роли\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"Убедитесь, что элементы с ARIA ролями, требующими родительские роли, содержатся ими\",\n      \"help\": \"Некоторые ARIA роли должны содержаться в определенных родительских ролях\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"Убедитесь, что aria-roledescription используется только на элементах с явной или неявной ролью\",\n      \"help\": \"aria-roledescription должен использоваться на элементах с семантической ролью\"\n    },\n    \"aria-roles\": {\n      \"description\": \"Убедитесь, что все элементы с атрибутом role используют допустимое значение\",\n      \"help\": \"ARIA роли должны соответствовать допустимым значениям\"\n    },\n    \"aria-text\": {\n      \"description\": \"Убедитесь, что роль=\\\"text\\\" используется на элементах без фокусируемых потомков\",\n      \"help\": \"\\\"роль=text\\\" не должна иметь фокусируемых потомков\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"Убедитесь, что каждое ARIA toggle поле имеет доступное имя\",\n      \"help\": \"ARIA toggle поля должны иметь доступное имя\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"Убедитесь, что каждый ARIA tooltip узел имеет доступное имя\",\n      \"help\": \"ARIA tooltip узлы должны иметь доступное имя\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"Убедитесь, что каждый ARIA treeitem узел имеет доступное имя\",\n      \"help\": \"ARIA treeitem узлы должны иметь доступное имя\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"Убедитесь, что все ARIA атрибуты имеют допустимые значения\",\n      \"help\": \"ARIA атрибуты должны соответствовать допустимым значениям\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"Убедитесь, что атрибуты, начинающиеся с aria-, являются допустимыми ARIA атрибутами\",\n      \"help\": \"ARIA атрибуты должны соответствовать допустимым именам\"\n    },\n    \"audio-caption\": {\n      \"description\": \"Убедитесь, что элементы <audio> имеют субтитры\",\n      \"help\": \"Элементы <audio> должны иметь трек субтитров\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"Убедитесь, что атрибут autocomplete правильный и подходит для поля формы\",\n      \"help\": \"Атрибут autocomplete должен использоваться правильно\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"Убедитесь, что расстояние между текстом, установленное через атрибуты стиля, можно настроить с помощью пользовательских таблиц стилей\",\n      \"help\": \"Встроенное текстовое пространство должно быть регулируемым с помощью пользовательских таблиц стилей\"\n    },\n    \"blink\": {\n      \"description\": \"Убедитесь, что элементы <blink> не используются\",\n      \"help\": \"Элементы <blink> устарели и не должны использоваться\"\n    },\n    \"button-name\": {\n      \"description\": \"Убедитесь, что кнопки имеют различимый текст\",\n      \"help\": \"Кнопки должны иметь различимый текст\"\n    },\n    \"bypass\": {\n      \"description\": \"Убедитесь, что на каждой странице есть хотя бы один механизм, позволяющий пользователю обходить навигацию и переходить непосредственно к содержимому\",\n      \"help\": \"На странице должен быть способ обойти повторяющиеся блоки\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"Убедитесь, что контраст между цветами переднего и заднего плана соответствует пороговым значениям улучшенной контрастности WCAG 2 AAA\",\n      \"help\": \"Элементы должны соответствовать пороговым значениям улучшенной контрастности цвета\"\n    },\n    \"color-contrast\": {\n      \"description\": \"Убедитесь, что контраст между цветами переднего и заднего плана соответствует минимальным пороговым значениям контрастности WCAG 2 AA\",\n      \"help\": \"Элементы должны соответствовать минимальным пороговым значениям контрастности цвета\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"Убедитесь, что содержимое не привязано к определенной ориентации дисплея, и оно доступно во всех ориентациях дисплея\",\n      \"help\": \"Медиа-запросы CSS не должны блокировать ориентацию дисплея\"\n    },\n    \"definition-list\": {\n      \"description\": \"Убедитесь, что элементы <dl> структурированы правильно\",\n      \"help\": \"Элементы <dl> должны содержать только группы <dt> и <dd>, <script>, <template> или <div> элементы\"\n    },\n    \"dlitem\": {\n      \"description\": \"Убедитесь, что элементы <dt> и <dd> находятся внутри <dl>\",\n      \"help\": \"Элементы <dt> и <dd> должны быть размещены внутри <dl>\"\n    },\n    \"document-title\": {\n      \"description\": \"Убедитесь, что каждый HTML-документ содержит непустой элемент <title>\",\n      \"help\": \"Документы должны иметь элемент <title> для навигации\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"Убедитесь, что каждое значение атрибута id активных элементов уникально\",\n      \"help\": \"Идентификаторы активных элементов должны быть уникальными\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"Убедитесь, что каждое значение атрибута id, используемого в ARIA и метках, уникально\",\n      \"help\": \"Идентификаторы, используемые в ARIA и метках, должны быть уникальными\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"Убедитесь, что каждое значение атрибута id уникально\",\n      \"help\": \"Значение атрибута id должно быть уникальным\"\n    },\n    \"empty-heading\": {\n      \"description\": \"Убедитесь, что заголовки содержат различимый текст\",\n      \"help\": \"Заголовки не должны быть пустыми\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"Убедитесь, что заголовки таблиц содержат различимый текст\",\n      \"help\": \"Текст заголовка таблицы не должен быть пустым\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"Убедитесь, что элементы в порядке фокуса имеют роль, соответствующую интерактивному содержимому\",\n      \"help\": \"Элементы в порядке фокуса должны иметь соответствующую роль\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"Убедитесь, что поле формы не имеет нескольких элементов label\",\n      \"help\": \"Поле формы не должно иметь несколько элементов label\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"Убедитесь, что элементы <frame> и <iframe> с фокусируемым содержимым не имеют tabindex=-1\",\n      \"help\": \"Фреймы с фокусируемым содержимым не должны иметь tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"Убедитесь, что элементы <iframe> и <frame> содержат скрипт axe-core\",\n      \"help\": \"Фреймы должны быть протестированы с помощью axe-core\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"Убедитесь, что элементы <iframe> и <frame> содержат уникальный атрибут title\",\n      \"help\": \"Фреймы должны иметь уникальный атрибут title\"\n    },\n    \"frame-title\": {\n      \"description\": \"Убедитесь, что элементы <iframe> и <frame> имеют доступное имя\",\n      \"help\": \"Фреймы должны иметь доступное имя\"\n    },\n    \"heading-order\": {\n      \"description\": \"Убедитесь, что порядок заголовков соответствует семантической структуре\",\n      \"help\": \"Уровни заголовков должны увеличиваться на единицу\"\n    },\n    \"hidden-content\": {\n      \"description\": \"Информирует пользователей о скрытом содержимом.\",\n      \"help\": \"Скрытое содержимое на странице должно быть проанализировано\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"Убедитесь, что каждый HTML-документ имеет атрибут lang\",\n      \"help\": \"Элемент <html> должен иметь атрибут lang\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"Убедитесь, что атрибут lang элемента <html> имеет допустимое значение\",\n      \"help\": \"Элемент <html> должен иметь допустимое значение для атрибута lang\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"Убедитесь, что HTML элементы с атрибутами lang и xml:lang согласуются в базовом языке страницы\",\n      \"help\": \"HTML элементы с атрибутами lang и xml:lang должны согласоваться в базовом языке\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"Убедитесь, что ссылки с одинаковым доступным именем выполняют схожую цель\",\n      \"help\": \"Ссылки с одинаковым именем должны выполнять схожую цель\"\n    },\n    \"image-alt\": {\n      \"description\": \"Убедитесь, что элементы <img> имеют альтернативный текст или роль \\\"none\\\" или \\\"presentation\\\"\",\n      \"help\": \"Изображения должны иметь альтернативный текст\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"Убедитесь, что альтернативный текст изображения не повторяется в тексте\",\n      \"help\": \"Альтернативный текст изображения не должен повторяться в тексте\"\n    },\n    \"input-button-name\": {\n      \"description\": \"Убедитесь, что кнопки ввода имеют различимый текст\",\n      \"help\": \"Кнопки ввода должны иметь различимый текст\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"Убедитесь, что элементы <input type=\\\"image\\\"> имеют альтернативный текст\",\n      \"help\": \"Кнопки изображения должны иметь альтернативный текст\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"Убедитесь, что элементы, маркированные через их содержимое, содержат видимый текст как часть доступного имени\",\n      \"help\": \"Элементы должны содержать видимый текст как часть доступного имени\"\n    },\n    \"label-title-only\": {\n      \"description\": \"Убедитесь, что каждый элемент формы имеет видимую метку и не маркируется исключительно скрытыми метками, или атрибутами title или aria-describedby\",\n      \"help\": \"Элементы формы должны иметь видимую метку\"\n    },\n    \"label\": {\n      \"description\": \"Убедитесь, что каждый элемент формы имеет метку\",\n      \"help\": \"Элементы формы должны иметь метки\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"Убедитесь, что область баннера находится на верхнем уровне\",\n      \"help\": \"Область баннера не должна быть вложена в другую область\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"Убедитесь, что дополнительная область или aside находятся на верхнем уровне\",\n      \"help\": \"Aside не должен быть вложен в другую область\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"Убедитесь, что область contentinfo находится на верхнем уровне\",\n      \"help\": \"Область contentinfo не должна быть вложена в другую область\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"Убедитесь, что область main находится на верхнем уровне\",\n      \"help\": \"Область main не должна быть вложена в другую область\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"Убедитесь, что документ содержит не более одного баннера\",\n      \"help\": \"Документ не должен содержать более одного баннера\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"Убедитесь, что документ содержит не более одной области contentinfo\",\n      \"help\": \"Документ не должен содержать более одной области contentinfo\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"Убедитесь, что документ содержит не более одной области main\",\n      \"help\": \"Документ не должен содержать более одной области main\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"Убедитесь, что документ содержит одну основную область\",\n      \"help\": \"Документ должен содержать одну основную область\"\n    },\n    \"landmark-unique\": {\n      \"description\": \"Убедитесь, что области уникальны\",\n      \"help\": \"Области должны иметь уникальную роль или комбинацию роли/метки/названия (т.е. доступное имя)\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"Убедитесь, что ссылки выделяются на фоне окружающего текста, не полагаясь только на цвет\",\n      \"help\": \"Ссылки должны быть различимы без использования цвета\"\n    },\n    \"link-name\": {\n      \"description\": \"Убедитесь, что ссылки содержат различимый текст\",\n      \"help\": \"Ссылки должны содержать различимый текст\"\n    },\n    \"list\": {\n      \"description\": \"Убедитесь, что списки структурированы правильно\",\n      \"help\": \"Элементы <ul> и <ol> должны содержать только элементы <li>, <script> или <template>\"\n    },\n    \"listitem\": {\n      \"description\": \"Убедитесь, что элементы <li> используются семантически правильно\",\n      \"help\": \"Элементы <li> должны быть вложены в <ul> или <ol>\"\n    },\n    \"marquee\": {\n      \"description\": \"Убедитесь, что элементы <marquee> не используются\",\n      \"help\": \"Элементы <marquee> устарели и не должны использоваться\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"Убедитесь, что <meta http-equiv=\\\"refresh\\\"> не используется для отложенного обновления\",\n      \"help\": \"Отложенное обновление не должно использоваться\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"Убедитесь, что <meta http-equiv=\\\"refresh\\\"> не используется для отложенного обновления\",\n      \"help\": \"Отложенное обновление менее 20 часов не должно использоваться\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"Убедитесь, что <meta name=\\\"viewport\\\"> позволяет значительное масштабирование\",\n      \"help\": \"Пользователи должны иметь возможность увеличивать и масштабировать текст до 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"Убедитесь, что <meta name=\\\"viewport\\\"> не отключает масштабирование и увеличение текста\",\n      \"help\": \"Масштабирование и увеличение текста не должны быть отключены\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"Убедитесь, что интерактивные элементы не вложены, так как они могут не озвучиваться экранными считывателями или вызывать проблемы с фокусом для вспомогательных технологий\",\n      \"help\": \"Интерактивные элементы не должны быть вложены\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"Убедитесь, что элементы <video> или <audio> не воспроизводят аудио автоматически более 3 секунд без механизма для его остановки или отключения\",\n      \"help\": \"Элементы <video> или <audio> не должны воспроизводиться автоматически\"\n    },\n    \"object-alt\": {\n      \"description\": \"Убедитесь, что элементы <object> имеют альтернативный текст\",\n      \"help\": \"Элементы <object> должны иметь альтернативный текст\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"Убедитесь, что текст, оформленный как жирный, курсивный или с измененным размером шрифта, не используется для оформления элементов <p> в качестве заголовков\",\n      \"help\": \"Оформленные элементы <p> не должны использоваться как заголовки\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"Убедитесь, что страница или хотя бы один из ее фреймов содержит заголовок первого уровня\",\n      \"help\": \"Страница должна содержать заголовок первого уровня\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"Элементы, отмеченные как презентационные, не должны иметь глобальные ARIA атрибуты или tabindex, чтобы все экранные считыватели игнорировали их\",\n      \"help\": \"Убедитесь, что элементы, отмеченные как презентационные, последовательно игнорируются\"\n    },\n    \"region\": {\n      \"description\": \"Убедитесь, что все содержимое страницы заключено в ориентиры\",\n      \"help\": \"Все содержимое страницы должно быть заключено в ориентиры\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"Убедитесь, что элементы с ролью [role=\\\"img\\\"] имеют альтернативный текст\",\n      \"help\": \"Элементы с ролью [role=\\\"img\\\"] должны иметь альтернативный текст\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"Убедитесь, что атрибут scope используется правильно в таблицах\",\n      \"help\": \"Атрибут scope должен использоваться правильно\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"Убедитесь, что элементы с прокручиваемым содержимым доступны с клавиатуры\",\n      \"help\": \"Прокручиваемая область должна быть доступна с клавиатуры\"\n    },\n    \"select-name\": {\n      \"description\": \"Убедитесь, что элемент select имеет доступное имя\",\n      \"help\": \"Элемент select должен иметь доступное имя\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"Убедитесь, что серверные карты изображений не используются\",\n      \"help\": \"Серверные карты изображений не должны использоваться\"\n    },\n    \"skip-link\": {\n      \"description\": \"Убедитесь, что все ссылки для пропуска имеют фокусируемую цель\",\n      \"help\": \"Цель ссылки для пропуска должна существовать и быть фокусируемой\"\n    },\n    \"summary-name\": {\n      \"description\": \"Убедитесь, что элементы summary содержат различимый текст\",\n      \"help\": \"Элементы summary должны содержать различимый текст\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"Убедитесь, что элементы <svg> с ролью img, graphics-document или graphics-symbol имеют доступный текст\",\n      \"help\": \"Элементы <svg> с ролью img должны иметь альтернативный текст\"\n    },\n    \"tabindex\": {\n      \"description\": \"Убедитесь, что значения атрибута tabindex не превышают 0\",\n      \"help\": \"Элементы не должны иметь tabindex больше нуля\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"Убедитесь, что элемент <caption> не содержит тот же текст, что и атрибут summary\",\n      \"help\": \"Таблицы не должны иметь одинаковый summary и caption\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"Убедитесь, что таблицы с заголовком используют элемент <caption>\",\n      \"help\": \"Данные или заголовочные ячейки не должны использоваться для заголовка таблицы\"\n    },\n    \"target-size\": {\n      \"description\": \"Убедитесь, что целевые элементы для касания имеют достаточный размер и пространство\",\n      \"help\": \"Все целевые элементы для касания должны быть не менее 24px или иметь достаточное пространство\"\n    },\n    \"td-has-header\": {\n      \"description\": \"Убедитесь, что каждая непустая ячейка данных в таблице больше 3 на 3 имеет один или несколько заголовков таблицы\",\n      \"help\": \"Непустые элементы <td> в больших таблицах должны иметь связанные заголовки таблицы\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"Убедитесь, что каждая ячейка в таблице, использующая атрибут headers, ссылается только на другие элементы <th> в этой таблице\",\n      \"help\": \"Атрибуты headers ячеек таблицы должны ссылаться на другие элементы <th> в той же таблице\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"Убедитесь, что элементы <th> и элементы с ролью columnheader/rowheader имеют ячейки данных, которые они описывают\",\n      \"help\": \"Заголовки таблицы в таблице данных должны ссылаться на ячейки данных\"\n    },\n    \"valid-lang\": {\n      \"description\": \"Убедитесь, что атрибуты lang имеют допустимые значения\",\n      \"help\": \"Атрибут lang должен иметь допустимое значение\"\n    },\n    \"video-caption\": {\n      \"description\": \"Убедитесь, что элементы <video> имеют субтитры\",\n      \"help\": \"Элементы <video> должны иметь субтитры\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"Абстрактные роли не используются\",\n      \"fail\": {\n        \"singular\": \"Абстрактная роль не может быть использована напрямую: ${data.values}\",\n        \"plural\": \"Абстрактные роли не могут быть использованы напрямую: ${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA атрибуты используются правильно для заданной роли\",\n      \"fail\": {\n        \"singular\": \"ARIA атрибут не допускается: ${data.values}\",\n        \"plural\": \"ARIA атрибуты не допускаются: ${data.values}\"\n      },\n      \"incomplete\": \"Проверьте, не возникает ли проблема, если атрибут ARIA будет проигнорирован на этом элементе: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"ARIA роль допустима для данного элемента\",\n      \"fail\": {\n        \"singular\": \"ARIA роль ${data.values} не допускается для данного элемента\",\n        \"plural\": \"ARIA роли ${data.values} не допускаются для данного элемента\"\n      },\n      \"incomplete\": {\n        \"singular\": \"ARIA роль ${data.values} должна быть удалена, когда элемент становится видимым, так как она не допускается для элемента\",\n        \"plural\": \"ARIA роли ${data.values} должны быть удалены, когда элемент становится видимым, так как они не допускаются для элемента\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"Элемент имеет атрибут aria-busy\",\n      \"fail\": \"Элемент использует aria-busy=\\\"true\\\", пока отображается индикатор загрузки\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"ARIA атрибут допустим\",\n      \"fail\": {\n        \"checkbox\": \"Удалите aria-checked или установите его значение в \\\"${data.checkState}\\\", чтобы оно соответствовало фактическому состоянию флажка\",\n        \"rowSingular\": \"Этот атрибут поддерживается для строк treegrid, но не для ${data.ownerRole}: ${data.invalidAttrs}\",\n        \"rowPlural\": \"Эти атрибуты поддерживаются для строк treegrid, но не для ${data.ownerRole}: ${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage существует и ссылается на элементы, видимые для экранных считывателей, которые используют поддерживаемую технику aria-errormessage\",\n      \"fail\": {\n        \"singular\": \"Значение aria-errormessage `${data.values}` должно использовать технику для объявления сообщения (например, aria-live, aria-describedby, role=alert и т.д.)\",\n        \"plural\": \"Значения aria-errormessage `${data.values}` должны использовать технику для объявления сообщения (например, aria-live, aria-describedby, role=alert и т.д.)\",\n        \"hidden\": \"Значение aria-errormessage `${data.values}` не может ссылаться на скрытый элемент\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Убедитесь, что значение aria-errormessage `${data.values}` ссылается на существующий элемент\",\n        \"plural\": \"Убедитесь, что значения aria-errormessage `${data.values}` ссылаются на существующие элементы\",\n        \"idrefs\": \"Невозможно определить, существует ли элемент aria-errormessage на странице: ${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"Атрибут aria-hidden отсутствует на теле документа\",\n      \"fail\": \"aria-hidden=true не должно присутствовать на теле документа\"\n    },\n    \"aria-level\": {\n      \"pass\": \"Значения aria-level допустимы\",\n      \"incomplete\": \"Значения aria-level больше 6 не поддерживаются всеми комбинациями экранных считывателей и браузеров\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"ARIA атрибут допустим\",\n      \"fail\": {\n        \"hasRolePlural\": \"Атрибуты ${data.prohibited} не могут использоваться с ролью \\\"${data.role}\\\".\",\n        \"hasRoleSingular\": \"Атрибут ${data.prohibited} не может использоваться с ролью \\\"${data.role}\\\".\",\n        \"noRolePlural\": \"Атрибуты ${data.prohibited} не могут использоваться на ${data.nodeName} без допустимого атрибута role.\",\n        \"noRoleSingular\": \"Атрибут ${data.prohibited} не может использоваться на ${data.nodeName} без допустимого атрибута role.\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"Атрибут ${data.prohibited} не поддерживается с ролью \\\"${data.role}\\\".\",\n        \"hasRolePlural\": \"Атрибуты ${data.prohibited} не поддерживаются с ролью \\\"${data.role}\\\".\",\n        \"noRoleSingular\": \"Атрибут ${data.prohibited} не поддерживается на ${data.nodeName} без допустимого атрибута role.\",\n        \"noRolePlural\": \"Атрибуты ${data.prohibited} не поддерживаются на ${data.nodeName} без допустимого атрибута role.\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"Все необходимые ARIA атрибуты присутствуют\",\n      \"fail\": {\n        \"singular\": \"Необходимый ARIA атрибут отсутствует: ${data.values}\",\n        \"plural\": \"Необходимые ARIA атрибуты отсутствуют: ${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"Необходимые ARIA дочерние элементы присутствуют\",\n        \"aria-busy\": \"Элемент имеет атрибут aria-busy, поэтому разрешено пропускать необходимые дочерние элементы\"\n      },\n      \"fail\": {\n        \"singular\": \"Необходимая ARIA дочерняя роль отсутствует: ${data.values}\",\n        \"plural\": \"Необходимые ARIA дочерние роли отсутствуют: ${data.values}\",\n        \"unallowed\": \"Элемент имеет недопустимые дочерние элементы: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"Ожидается добавление ARIA дочерней роли: ${data.values}\",\n        \"plural\": \"Ожидается добавление ARIA дочерних ролей: ${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"Необходимая ARIA родительская роль присутствует\",\n      \"fail\": {\n        \"singular\": \"Необходимая ARIA родительская роль отсутствует: ${data.values}\",\n        \"plural\": \"Необходимые ARIA родительские роли отсутствуют: ${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"aria-roledescription используется на поддерживаемой семантической роли\",\n      \"incomplete\": \"Проверьте, объявляется ли aria-roledescription поддерживаемыми экранными считывателями\",\n      \"fail\": \"Назначьте элементу роль, поддерживающую aria-roledescription\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA атрибут поддерживается\",\n      \"fail\": \"ARIA атрибут не поддерживается в большинстве экранных считывателей и вспомогательных технологий: ${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"Значения ARIA атрибутов допустимы\",\n      \"fail\": {\n        \"singular\": \"Недопустимое значение ARIA атрибута: ${data.values}\",\n        \"plural\": \"Недопустимые значения ARIA атрибутов: ${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"Элемент ID атрибута ARIA не существует на странице: ${data.needsReview}\",\n        \"noIdShadow\": \"Элемент ID атрибута ARIA не существует на странице или является потомком другого дерева теневых DOM: ${data.needsReview}\",\n        \"ariaCurrent\": \"Значение атрибута ARIA недопустимо и будет трактоваться как \\\"aria-current=true\\\": ${data.needsReview}\",\n        \"idrefs\": \"Невозможно определить, существует ли элемент ID атрибута ARIA на странице: ${data.needsReview}\",\n        \"empty\": \"Значение атрибута ARIA игнорируется, если оно пустое: ${data.needsReview}\",\n        \"controlsWithinPopup\": \"Невозможно определить, существует ли ссылка на ID атрибута aria-controls на странице при использовании aria-haspopup: ${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"Имя ARIA атрибута допустимо\",\n      \"fail\": {\n        \"singular\": \"Недопустимое имя ARIA атрибута: ${data.values}\",\n        \"plural\": \"Недопустимые имена ARIA атрибутов: ${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabel используется на элементе с доступным текстом\",\n      \"fail\": \"aria-braillelabel используется на элементе без доступного текста\",\n      \"incomplete\": \"Не удалось вычислить доступный текст\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescription используется на элементе с aria-roledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription используется на элементе без aria-roledescription\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription используется на элементе с пустым aria-roledescription\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA роль не устарела\",\n      \"fail\": \"Используемая роль устарела: ${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"Используется только одно значение роли\",\n      \"fail\": \"Используйте только одно значение роли, так как резервные роли не поддерживаются в старых браузерах\",\n      \"incomplete\": \"Используйте только роль 'presentation' или 'none', так как они являются синонимами.\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"Элемент имеет глобальный ARIA атрибут: ${data.values}\",\n        \"plural\": \"Элемент имеет глобальные ARIA атрибуты: ${data.values}\"\n      },\n      \"fail\": \"Элемент не имеет глобального ARIA атрибута\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"Элемент имеет роль виджета.\",\n      \"fail\": \"Элемент не имеет роли виджета.\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA роль допустима\",\n      \"fail\": {\n        \"singular\": \"Роль должна быть одной из допустимых ARIA ролей: ${data.values}\",\n        \"plural\": \"Роли должны быть одной из допустимых ARIA ролей: ${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"Элемент может быть сфокусирован.\",\n      \"fail\": \"Элемент не может быть сфокусирован.\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"Нет несоответствия между <label> и доступным именем\",\n      \"incomplete\": \"Проверьте, нужно ли, чтобы <label> был частью имени поля ARIA ${data}\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA роль поддерживается\",\n      \"fail\": \"Используемая роль не поддерживается большинством экранных считывателей и вспомогательных технологий: ${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"Элемент имеет допустимую семантику для элемента в порядке фокусировки.\",\n      \"fail\": \"Элемент имеет недопустимую семантику для элемента в порядке фокусировки.\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"Элемент имеет достаточный цветовой контраст ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} (цвет переднего плана: ${data.fgColor}, цвет фона: ${data.bgColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} между цветом переднего плана и тенью (цвет переднего плана: ${data.fgColor}, цвет текстовой тени: ${data.shadowColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} между цветом тени и фоном (цвет текстовой тени: ${data.shadowColor}, цвет фона: ${data.bgColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Невозможно определить коэффициент контрастности\",\n        \"bgImage\": \"Невозможно определить цвет фона элемента из-за изображения на фоне\",\n        \"bgGradient\": \"Невозможно определить цвет фона элемента из-за градиента на фоне\",\n        \"imgNode\": \"Невозможно определить цвет фона элемента, так как он содержит изображение\",\n        \"bgOverlap\": \"Невозможно определить цвет фона элемента, так как он перекрыт другим элементом\",\n        \"fgAlpha\": \"Невозможно определить цвет переднего плана элемента из-за прозрачности альфа-канала\",\n        \"elmPartiallyObscured\": \"Невозможно определить цвет фона элемента, так как он частично перекрыт другим элементом\",\n        \"elmPartiallyObscuring\": \"Невозможно определить цвет фона элемента, так как он частично перекрывает другие элементы\",\n        \"outsideViewport\": \"Невозможно определить цвет фона элемента, так как он находится за пределами видимой области\",\n        \"equalRatio\": \"Элемент имеет коэффициент контрастности 1:1 с фоном\",\n        \"shortTextContent\": \"Содержимое элемента слишком короткое, чтобы определить, является ли оно текстом\",\n        \"nonBmp\": \"Содержимое элемента содержит только не текстовые символы\",\n        \"pseudoContent\": \"Невозможно определить цвет фона элемента из-за псевдоэлемента\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"Элемент имеет достаточный цветовой контраст ${data.contrastRatio}\",\n        \"hidden\": \"Элемент скрыт\"\n      },\n      \"fail\": {\n        \"default\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} (цвет переднего плана: ${data.fgColor}, цвет фона: ${data.bgColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} между цветом переднего плана и тенью (цвет переднего плана: ${data.fgColor}, цвет текстовой тени: ${data.shadowColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"Элемент имеет недостаточный цветовой контраст ${data.contrastRatio} между цветом тени и фоном (цвет текстовой тени: ${data.shadowColor}, цвет фона: ${data.bgColor}, размер шрифта: ${data.fontSize}, толщина шрифта: ${data.fontWeight}). Ожидаемый коэффициент контрастности ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"Невозможно определить коэффициент контрастности\",\n        \"bgImage\": \"Невозможно определить цвет фона элемента из-за изображения на фоне\",\n        \"bgGradient\": \"Невозможно определить цвет фона элемента из-за градиента на фоне\",\n        \"imgNode\": \"Невозможно определить цвет фона элемента, так как он содержит изображение\",\n        \"bgOverlap\": \"Невозможно определить цвет фона элемента, так как он перекрыт другим элементом\",\n        \"complexTextShadows\": \"Невозможно определить контрастность элемента из-за сложных теней текста\",\n        \"fgAlpha\": \"Невозможно определить цвет переднего плана элемента из-за прозрачности альфа-канала\",\n        \"elmPartiallyObscured\": \"Невозможно определить цвет фона элемента, так как он частично перекрыт другим элементом\",\n        \"elmPartiallyObscuring\": \"Невозможно определить цвет фона элемента, так как он частично перекрывает другие элементы\",\n        \"outsideViewport\": \"Невозможно определить цвет фона элемента, так как он находится за пределами видимой области\",\n        \"equalRatio\": \"Элемент имеет коэффициент контрастности 1:1 с фоном\",\n        \"shortTextContent\": \"Содержимое элемента слишком короткое, чтобы определить, является ли оно текстом\",\n        \"nonBmp\": \"Содержимое элемента содержит только не текстовые символы\",\n        \"pseudoContent\": \"Невозможно определить цвет фона элемента из-за псевдоэлемента\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"Ссылки могут быть выделены среди окружающего текста с помощью визуального стиля\",\n      \"incomplete\": {\n        \"default\": \"Проверьте, нужно ли оформлять ссылку, чтобы отличить её от окружающего текста\",\n        \"pseudoContent\": \"Проверьте, достаточно ли псевдо-стиля ссылки, чтобы отличить её от окружающего текста\"\n      },\n      \"fail\": \"Ссылка не имеет стиля (например, подчеркивания), чтобы отличить её от окружающего текста\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"Ссылки можно отличить от окружающего текста каким-либо способом, кроме цвета\",\n      \"fail\": {\n        \"fgContrast\": \"У ссылки недостаточный цветовой контраст ${data.contrastRatio}:1 с окружающим текстом. (Минимальный контраст ${data.requiredContrastRatio}:1, текст ссылки: ${data.nodeColor}, окружающий текст: ${data.parentColor})\",\n        \"bgContrast\": \"Фон ссылки имеет недостаточный цветовой контраст ${data.contrastRatio} (Минимальный контраст ${data.requiredContrastRatio}:1, цвет фона ссылки: ${data.nodeBackgroundColor}, цвет фона окружающего текста: ${data.parentBackgroundColor})\"\n      },\n      \"incomplete\": {\n        \"default\": \"Невозможно определить контрастность переднего плана элемента\",\n        \"bgContrast\": \"Невозможно определить контрастность фона элемента\",\n        \"bgImage\": \"Невозможно определить контрастность элемента из-за изображения на фоне\",\n        \"bgGradient\": \"Невозможно определить контрастность элемента из-за градиента на фоне\",\n        \"imgNode\": \"Невозможно определить контрастность элемента, так как он содержит изображение\",\n        \"bgOverlap\": \"Невозможно определить контрастность элемента, так как он перекрыт другим элементом\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"Значение autocomplete подходит для данного элемента\",\n      \"fail\": \"Значение autocomplete не подходит для этого типа ввода\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"Атрибут autocomplete правильно отформатирован\",\n      \"fail\": \"Атрибут autocomplete неправильно отформатирован\",\n      \"incomplete\": \"Атрибут autocomplete имеет нестандартное значение. Проверьте, можно ли использовать вместо него какое-либо стандартное значение.\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"Значение атрибута accesskey уникально\",\n      \"fail\": \"Документ содержит несколько элементов с одинаковым значением accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"Элемент содержит фокусируемые элементы\",\n      \"fail\": \"Элемент должен содержать фокусируемое содержимое\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"Внутри элемента нет фокусируемых элементов\",\n      \"incomplete\": \"Проверьте, немедленно ли индикатор фокуса перемещается на фокусируемые элементы\",\n      \"fail\": \"Фокусируемое содержимое должно быть отключено или удалено из DOM\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"Элемент является фокусируемым\",\n      \"fail\": \"Элемент должен быть фокусируемым\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"Нет фокусируемых элементов при открытии модального окна\",\n      \"incomplete\": \"Проверьте, что фокусируемые элементы не являются доступными в текущем состоянии\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"Элемент не находится в таб-ордре или имеет доступный текст\",\n      \"fail\": \"Элемент находится в таб-ордре и не имеет доступного текста\",\n      \"incomplete\": \"Невозможно определить, есть ли у элемента доступное имя\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"Внутри элемента нет фокусируемых элементов\",\n      \"incomplete\": \"Проверьте, немедленно ли индикатор фокуса перемещается на фокусируемые элементы\",\n      \"fail\": \"Фокусируемое содержимое должно иметь tabindex=\\\"-1\\\" или быть удалено из DOM\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"Элемент не имеет фокусируемых потомков\",\n      \"fail\": \"Элемент имеет фокусируемых потомков\",\n      \"incomplete\": \"Невозможно определить, есть ли у элемента потомки\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} область находится на верхнем уровне.\",\n      \"fail\": \"${data.role} область содержится в другой области.\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"Элемент не имеет фокусируемых потомков\",\n      \"fail\": {\n        \"default\": \"Элемент имеет фокусируемых потомков\",\n        \"notHidden\": \"Использование отрицательного tabindex на элементе внутри интерактивного управления не предотвращает фокусировку элемента вспомогательными технологиями (даже с aria-hidden=\\\"true\\\")\"\n      },\n      \"incomplete\": \"Невозможно определить, есть ли у элемента потомки\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"На странице есть хотя бы один заголовок первого уровня\",\n      \"fail\": \"На странице должен быть заголовок первого уровня\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"В документе есть хотя бы одна основная область\",\n      \"fail\": \"В документе нет основной области\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"В документе нет более одного баннера\",\n      \"fail\": \"В документе более одного баннера\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"В документе нет более одной области contentinfo\",\n      \"fail\": \"В документе более одной области contentinfo\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"В документе нет более одной основной области\",\n      \"fail\": \"В документе более одной основной области\"\n    },\n    \"tabindex\": {\n      \"pass\": \"Элемент не имеет tabindex больше 0\",\n      \"fail\": \"Элемент имеет tabindex больше 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"Элемент имеет допустимое значение атрибута alt\",\n      \"fail\": \"Элемент имеет атрибут alt, содержащий только пробельный символ, который не игнорируется всеми экранными считывателями\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"Элемент не дублирует существующий текст в тексте атрибута alt <img>\",\n      \"fail\": \"Элемент содержит <img> с текстом атрибута alt, дублирующим существующий текст\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"Элемент формы имеет явную <label>\",\n      \"fail\": \"Элемент формы не имеет явной <label>\",\n      \"incomplete\": \"Невозможно определить, имеет ли элемент формы явную <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"Текст помощи (title или aria-describedby) не дублирует текст метки\",\n      \"fail\": \"Текст помощи (title или aria-describedby) совпадает с текстом метки\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"Элемент формы имеет видимую явную <label>\",\n      \"fail\": \"Элемент формы имеет явную <label>, которая скрыта\",\n      \"incomplete\": \"Невозможно определить, имеет ли элемент формы явную <label>, которая скрыта\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"Элемент формы имеет неявную (обернутую) <label>\",\n      \"fail\": \"Элемент формы не имеет неявной (обернутой) <label>\",\n      \"incomplete\": \"Невозможно определить, имеет ли элемент формы неявную (обернутую) <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"Элемент содержит видимый текст как часть его доступного имени\",\n      \"fail\": \"Текст внутри элемента не включен в доступное имя\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"Поле формы не имеет нескольких элементов label\",\n      \"incomplete\": \"Использование нескольких элементов label не поддерживается большинством вспомогательных технологий. Убедитесь, что первый элемент label содержит всю необходимую информацию.\"\n    },\n    \"title-only\": {\n      \"pass\": \"Элемент формы не использует только атрибут title для своей метки\",\n      \"fail\": \"Для создания метки элемента формы используется только атрибут title\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"Ориентиры должны иметь уникальную роль или комбинацию роли/метки/названия (т.е. доступное имя)\",\n      \"fail\": \"Ориентир должен иметь уникальный aria-label, aria-labelledby или title, чтобы сделать ориентиры различимыми\"\n    },\n    \"has-lang\": {\n      \"pass\": \"Элемент <html> имеет атрибут lang\",\n      \"fail\": {\n        \"noXHTML\": \"Атрибут xml:lang недействителен на HTML страницах, используйте атрибут lang.\",\n        \"noLang\": \"Элемент <html> не имеет атрибута lang\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"Значение атрибута lang включено в список допустимых языков\",\n      \"fail\": \"Значение атрибута lang не включено в список допустимых языков\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Атрибуты lang и xml:lang имеют один и тот же базовый язык\",\n      \"fail\": \"Атрибуты lang и xml:lang не имеют один и тот же базовый язык\"\n    },\n    \"dlitem\": {\n      \"pass\": \"Элемент описания списка имеет родительский элемент <dl>\",\n      \"fail\": \"Элемент описания списка не имеет родительского элемента <dl>\"\n    },\n    \"listitem\": {\n      \"pass\": \"Элемент списка имеет родительский элемент <ul>, <ol> или роль=\\\"list\\\"\",\n      \"fail\": {\n        \"default\": \"Элемент списка не имеет родительского элемента <ul>, <ol>\",\n        \"roleNotValid\": \"Родительский элемент списка имеет роль, которая не является ролью=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"Элемент dl содержит только допустимые дочерние элементы; <dt>, <dd> или <div> элементы\",\n      \"fail\": \"Элемент dl содержит недопустимые дочерние элементы: ${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"Элемент списка содержит только допустимые дочерние элементы <li>\",\n      \"fail\": \"Элемент списка содержит недопустимые дочерние элементы: ${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"Когда элемент не пустой, он имеет как минимум один элемент <dt> и один элемент <dd>\",\n      \"fail\": \"Когда элемент не пустой, он не содержит по крайней мере одного элемента <dt>, за которым следует по крайней мере один элемент <dd>\"\n    },\n    \"caption\": {\n      \"pass\": \"Мультимедийный элемент имеет дорожку субтитров\",\n      \"incomplete\": \"Проверьте, доступны ли субтитры для элемента\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"Элемент iframe был протестирован с помощью axe-core\",\n      \"fail\": \"Не удалось протестировать элемент iframe с помощью axe-core\",\n      \"incomplete\": \"Элемент iframe все еще должен быть протестирован с помощью axe-core\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> или <audio> не воспроизводит аудио более допустимой продолжительности или имеет механизмы управления\",\n      \"fail\": \"<video> или <audio> воспроизводит аудио более допустимой продолжительности и не имеет механизмов управления\",\n      \"incomplete\": \"Проверьте, не воспроизводит ли <video> или <audio> аудио более допустимой продолжительности или предоставляет ли механизмы управления\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"Дисплей работает, блокировка ориентации отсутствует\",\n      \"fail\": \"CSS блокировка ориентации применяется и делает дисплей неработоспособным\",\n      \"incomplete\": \"Невозможно определить, применяется ли CSS блокировка ориентации\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"Тег <meta> не предотвращает значительное масштабирование на мобильных устройствах\",\n      \"fail\": \"Тег <meta> ограничивает масштабирование на мобильных устройствах\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"Тег <meta> не отключает масштабирование на мобильных устройствах\",\n      \"fail\": \"${data} на теге <meta> отключает масштабирование на мобильных устройствах\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"Целевой элемент имеет достаточное пространство от ближайших соседей. Безопасное кликабельное пространство имеет диаметр ${data.closestOffset}px, что не менее ${data.minOffset}px.\",\n        \"large\": \"Целевой элемент значительно превышает минимальный размер в ${data.minOffset}px.\"\n      },\n      \"fail\": \"Целевой элемент имеет недостаточное пространство до ближайших соседей. Безопасное кликабельное пространство имеет диаметр ${data.closestOffset}px вместо как минимум ${data.minOffset}px.\",\n      \"incomplete\": {\n        \"default\": \"Элемент с отрицательным tabindex имеет недостаточное пространство до ближайших соседей. Безопасное кликабельное пространство имеет диаметр ${data.closestOffset}px вместо как минимум ${data.minOffset}px. Является ли это целевым элементом?\",\n        \"nonTabbableNeighbor\": \"Целевой элемент имеет недостаточное пространство до ближайших соседей. Безопасное кликабельное пространство имеет диаметр ${data.closestOffset}px вместо как минимум ${data.minOffset}px. Является ли сосед целевым элементом?\",\n        \"tooManyRects\": \"Не удалось определить размер целевого элемента из-за слишком большого количества перекрывающихся элементов\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"Контрол имеет достаточный размер (${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px)\",\n        \"obscured\": \"Контрол игнорируется, так как он полностью скрыт и, следовательно, не кликабелен\",\n        \"large\": \"Целевой элемент значительно превышает минимальный размер в ${data.minSize}px.\"\n      },\n      \"fail\": {\n        \"default\": \"Целевой элемент имеет недостаточный размер (${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px)\",\n        \"partiallyObscured\": \"Целевой элемент имеет недостаточный размер, так как он частично скрыт (наименьшее пространство ${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px)\"\n      },\n      \"incomplete\": {\n        \"default\": \"Элемент с отрицательным tabindex имеет недостаточный размер (${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px). Является ли это целевым элементом?\",\n        \"contentOverflow\": \"Размер элемента не может быть точно определен из-за переполненного содержимого\",\n        \"partiallyObscured\": \"Элемент с отрицательным tabindex имеет недостаточный размер, так как он частично скрыт (наименьшее пространство ${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px). Является ли это целевым элементом?\",\n        \"partiallyObscuredNonTabbable\": \"Целевой элемент имеет недостаточный размер, так как он частично скрыт соседом с отрицательным tabindex (наименьшее пространство ${data.width}px на ${data.height}px, должно быть как минимум ${data.minSize}px на ${data.minSize}px). Является ли сосед целевым элементом?\",\n        \"tooManyRects\": \"Не удалось определить размер целевого элемента из-за слишком большого количества перекрывающихся элементов\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"На странице есть заголовок\",\n      \"fail\": \"На странице нет заголовка\"\n    },\n    \"heading-order\": {\n      \"pass\": \"Порядок заголовков корректен\",\n      \"fail\": \"Порядок заголовков некорректен\",\n      \"incomplete\": \"Невозможно определить предыдущий заголовок\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"Нет других ссылок с тем же именем, которые ведут на другой URL\",\n      \"incomplete\": \"Проверьте, имеют ли ссылки одинаковую цель, или они намеренно неоднозначны.\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"Найдена допустимая ссылка для пропуска\",\n      \"fail\": \"Допустимая ссылка для пропуска не найдена\"\n    },\n    \"landmark\": {\n      \"pass\": \"На странице есть область-ориентир\",\n      \"fail\": \"На странице нет области-ориентира\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"Тег <meta> не вызывает немедленное обновление страницы\",\n      \"fail\": \"Тег <meta> вызывает обновление страницы по таймеру\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"Тег <meta> не вызывает немедленное обновление страницы\",\n      \"fail\": \"Тег <meta> вызывает обновление страницы по таймеру (менее 20 часов)\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"Элементы <p> не оформлены как заголовки\",\n      \"fail\": \"Для заголовков должны использоваться элементы <h1> - <h6>, а не стилизованные <p>\",\n      \"incomplete\": \"Невозможно определить, оформлены ли элементы <p> как заголовки\"\n    },\n    \"region\": {\n      \"pass\": \"Все содержимое страницы заключено в ориентиры\",\n      \"fail\": \"Некоторое содержимое страницы не заключено в ориентиры\"\n    },\n    \"skip-link\": {\n      \"pass\": \"Целевая ссылка для пропуска существует\",\n      \"incomplete\": \"Целевая ссылка для пропуска должна стать видимой при активации\",\n      \"fail\": \"Целевая ссылка для пропуска не найдена\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"Атрибут title элемента является уникальным\",\n      \"fail\": \"Атрибут title элемента не является уникальным\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"Документ не содержит активных элементов с одинаковым значением id атрибута\",\n      \"fail\": \"Документ содержит активные элементы с одинаковым значением id атрибута: ${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"Документ не содержит элементов, ссылающихся на ARIA или метки с одинаковым значением id атрибута\",\n      \"fail\": \"Документ содержит несколько элементов, ссылающихся на ARIA, с одинаковым значением id атрибута: ${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"Документ не содержит статических элементов с одинаковым значением id атрибута\",\n      \"fail\": \"Документ содержит несколько статических элементов с одинаковым значением id атрибута: ${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"Атрибут aria-label существует и не пуст\",\n      \"fail\": \"Атрибут aria-label не существует или пуст\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"Атрибут aria-labelledby существует и ссылается на элементы, видимые для экранных считывателей\",\n      \"fail\": \"Атрибут aria-labelledby не существует, ссылается на несуществующие элементы или ссылается на пустые элементы\",\n      \"incomplete\": \"Убедитесь, что aria-labelledby ссылается на существующий элемент\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"Внутренние стили с '!important', которые влияют на интервал текста, не указаны\",\n      \"fail\": {\n        \"singular\": \"Удалите '!important' из внутреннего стиля ${data.values}, так как переопределение этого стиля не поддерживается большинством браузеров\",\n        \"plural\": \"Удалите '!important' из внутренних стилей ${data.values}, так как переопределение этих стилей не поддерживается большинством браузеров\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"Элемент содержит внутренний текст, видимый для экранных считывателей\",\n      \"fail\": \"Элемент не содержит внутренний текст, видимый для экранных считывателей\",\n      \"incomplete\": \"Невозможно определить, есть ли у элемента дочерние элементы\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"Документ содержит непустой элемент <title>\",\n      \"fail\": \"Документ не содержит непустой элемент <title>\"\n    },\n    \"exists\": {\n      \"pass\": \"Элемент не существует\",\n      \"incomplete\": \"Элемент существует\"\n    },\n    \"has-alt\": {\n      \"pass\": \"Элемент имеет атрибут alt\",\n      \"fail\": \"Элемент не имеет атрибута alt\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"Элемент содержит текст, видимый для экранных считывателей\",\n      \"fail\": \"Элемент не содержит текст, видимый для экранных считывателей\",\n      \"incomplete\": \"Невозможно определить, есть ли у элемента дочерние элементы\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"Letter-spacing в атрибуте стиля не установлен на !important, либо соответствует минимальному значению\",\n      \"fail\": \"Letter-spacing в атрибуте стиля не должен использовать !important, или должен быть не менее ${data.minValue}em (текущее значение ${data.value}em)\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"Line-height в атрибуте стиля не установлен на !important, либо соответствует минимальному значению\",\n      \"fail\": \"Line-height в атрибуте стиля не должен использовать !important, или должен быть не менее ${data.minValue}em (текущее значение ${data.value}em)\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"Word-spacing в атрибуте стиля не установлен на !important, либо соответствует минимальному значению\",\n      \"fail\": \"Word-spacing в атрибуте стиля не должен использовать !important, или должен быть не менее ${data.minValue}em (текущее значение ${data.value}em)\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"Элемент не виден\",\n      \"fail\": \"Элемент виден\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"Элемент имеет непустой атрибут alt\",\n      \"fail\": {\n        \"noAttr\": \"Элемент не имеет атрибута alt\",\n        \"emptyAttr\": \"Элемент имеет пустой атрибут alt\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"Элемент не имеет атрибута value\",\n        \"has-label\": \"Элемент имеет непустой атрибут value\"\n      },\n      \"fail\": \"Элемент имеет атрибут value, и этот атрибут пуст\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"Элемент имеет атрибут placeholder\",\n      \"fail\": {\n        \"noAttr\": \"Элемент не имеет атрибута placeholder\",\n        \"emptyAttr\": \"Элемент имеет пустой атрибут placeholder\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"Элемент имеет атрибут title\",\n      \"fail\": {\n        \"noAttr\": \"Элемент не имеет атрибута title\",\n        \"emptyAttr\": \"Элемент имеет пустой атрибут title\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"Элемент имеет непустой атрибут value\",\n      \"fail\": {\n        \"noAttr\": \"Элемент не имеет атрибута value\",\n        \"emptyAttr\": \"Элемент имеет пустой атрибут value\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"Стандартная семантика элемента была переопределена с помощью role=\\\"${data.role}\\\"\",\n      \"fail\": {\n        \"default\": \"Стандартная семантика элемента не была переопределена с помощью role=\\\"none\\\" или role=\\\"presentation\\\"\",\n        \"globalAria\": \"Роль элемента не является презентационной, так как у него есть глобальный ARIA атрибут\",\n        \"focusable\": \"Роль элемента не является презентационной, так как он может быть сфокусирован\",\n        \"both\": \"Роль элемента не является презентационной, так как у него есть глобальный ARIA атрибут и он может быть сфокусирован\",\n        \"iframe\": \"Использование атрибута \\\"title\\\" на элементе ${data.nodeName} с презентационной ролью ведет себя непоследовательно между экранными считывателями\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"Стандартная семантика элемента была переопределена с помощью role=\\\"none\\\"\",\n      \"fail\": \"Стандартная семантика элемента не была переопределена с помощью role=\\\"none\\\"\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"Стандартная семантика элемента была переопределена с помощью role=\\\"presentation\\\"\",\n      \"fail\": \"Стандартная семантика элемента не была переопределена с помощью role=\\\"presentation\\\"\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"Элемент имеет дочерний элемент title\",\n      \"fail\": {\n        \"noTitle\": \"Элемент не имеет дочернего элемента title\",\n        \"emptyTitle\": \"Дочерний элемент title пуст\"\n      },\n      \"incomplete\": \"Невозможно определить, имеет ли элемент дочерний элемент title\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"Первая строка таблицы не используется в качестве заголовка\",\n      \"fail\": \"Первый дочерний элемент таблицы должен быть заголовком, а не ячейкой таблицы\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"Атрибут scope используется только для элементов заголовков таблицы (<th>)\",\n      \"fail\": \"В HTML 5 атрибуты scope могут использоваться только для элементов заголовков таблицы (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"Содержимое атрибута summary и элемента <caption> не дублируется\",\n      \"fail\": \"Содержимое атрибута summary и элемента <caption> идентично\",\n      \"incomplete\": \"Невозможно определить, есть ли у элемента <table> заголовок\"\n    },\n    \"scope-value\": {\n      \"pass\": \"Атрибут scope используется правильно\",\n      \"fail\": \"Значение атрибута scope может быть только 'row' или 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"Все непустые ячейки данных имеют заголовки таблицы\",\n      \"fail\": \"Некоторые непустые ячейки данных не имеют заголовков таблицы\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"Атрибут headers используется исключительно для ссылки на другие ячейки таблицы\",\n      \"incomplete\": \"Атрибут headers пуст\",\n      \"fail\": {\n        \"cell-header-not-in-table\": \"Атрибут headers не используется исключительно для ссылки на другие заголовочные ячейки в таблице\",\n        \"cell-header-not-th\": \"Атрибут headers должен ссылаться на заголовочные ячейки, а не на ячейки с данными\",\n        \"header-refs-self\": \"Элемент с атрибутом headers ссылается на самого себя\"\n      }\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"Все ячейки заголовков таблицы ссылаются на ячейки данных\",\n      \"fail\": \"Не все ячейки заголовков таблицы ссылаются на ячейки данных\",\n      \"incomplete\": \"Ячейки данных таблицы отсутствуют или пусты\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"Все содержимое страницы было проанализировано.\",\n      \"fail\": \"Возникли проблемы с анализом содержимого на этой странице.\",\n      \"incomplete\": \"На странице есть скрытое содержимое, которое не было проанализировано. Вам нужно будет включить отображение этого содержимого, чтобы его проанализировать.\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"Исправьте любое из следующего:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"Исправьте все из следующего:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe не смог определить причину. Время использовать инспектор элементов!\"\n}\n"
  },
  {
    "path": "locales/zh_CN.json",
    "content": "{\n  \"lang\": \"zh_CN\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"确保每个 accesskey 属性值都是唯一的\",\n      \"help\": \"accesskey 属性值应当唯一\"\n    },\n    \"area-alt\": {\n      \"description\": \"确保图像映射中的 <area> 元素有替代文本\",\n      \"help\": \"<area> 元素必须有替代文本\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"确保元素的角色支持其 ARIA 属性\",\n      \"help\": \"元素仅能使用受支持的 ARIA 属性\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"确保 role 属性值符合所属元素\",\n      \"help\": \"ARIA 角色应该符合元素\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"确保 aria-braillelabel 和 aria-brailleroledescription 有对应的非盲文的标签和描述\",\n      \"help\": \"aria-braille 相关属性必须有非盲文的对应属性\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"确保每个 ARIA 按钮、链接和菜单项都有可访问名称\",\n      \"help\": \"ARIA 指令必须有一个可访问名称\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"确保 ARIA 属性的使用符合所属元素角色的特定规范\",\n      \"help\": \"ARIA 属性的使用必须符合元素的特定角色\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"确保页面元素不使用已弃用的角色\",\n      \"help\": \"不得使用已弃用的 ARIA 角色\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"确保每个 ARIA 对话框和警示框节点都有可访问的名称\",\n      \"help\": \"ARIA 对话框和警示框节点应当有可访问的名称\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"确保 <body> 不存在 aria-hidden=\\\"true\\\"\",\n      \"help\": \"<body> 上不得存在 aria-hidden=\\\"true\\\"\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"确保设置为 ARIA 隐藏的元素不可聚焦，且不包含可聚焦的元素\",\n      \"help\": \"被设定为 ARIA 隐藏的元素不可聚焦，且不包含可聚焦的元素\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"确保每个 ARIA 输入字段都有可访问的名称\",\n      \"help\": \"ARIA 输入字段必须有可访问的名称\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"确保每个 ARIA 计量器节点都有可访问的名称\",\n      \"help\": \"ARIA 计量器节点必须有可访问的名称\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"确保每个 ARIA 进度条节点都有可访问的名称\",\n      \"help\": \"ARIA 进度条节点必须有可访问的名称\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"确保 ARIA 属性没有被禁止用于某元素的角色\",\n      \"help\": \"元素只能使用允许的 ARIA 属性\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"确保拥有 ARIA 角色的元素包含了所有必需的 ARIA 属性\",\n      \"help\": \"必须提供必需的 ARIA 属性\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"如果元素的ARIA角色需要子角色，确保该元素包含这些子元素\",\n      \"help\": \"某些 ARIA 角色必须包含特定的子元素\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"如果元素的ARIA角色需要父角色，确保该元素被这些父元素包含\",\n      \"help\": \"某些 ARIA 角色必须被特定的父元素包含\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"确保 aria-roledescription 仅用于有隐式或显式角色的元素\",\n      \"help\": \"aria-roledescription 必须用于有语义角色的元素\"\n    },\n    \"aria-roles\": {\n      \"description\": \"确保所有元素包含的 role 属性值都是有效的\",\n      \"help\": \"使用的 ARIA 角色属性值必须有效\"\n    },\n    \"aria-text\": {\n      \"description\": \"确保使用 role=\\\"text\\\" 的元素没有任何可聚焦后代\",\n      \"help\": \"\\\"role=text\\\" 应该没有任何可聚焦的后代\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"确保每个 ARIA 切换字段都有可访问的名称\",\n      \"help\": \"ARIA 切换字段必须有可访问名称\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"确保每个 ARIA 工具提示节点都有一个可访问名称\",\n      \"help\": \"ARIA 工具提示节点必须有一个可访问名称\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"确保每个 ARIA 树项目节点都有一个可访问名称\",\n      \"help\": \"ARIA 树项目节点应有一个可访问名称\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"确保所有 ARIA 属性值都是有效的\",\n      \"help\": \"ARIA 属性值必须有效\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"确保以 aria- 开头的属性是有效的 ARIA 属性\",\n      \"help\": \"ARIA 属性名称必须有效\"\n    },\n    \"audio-caption\": {\n      \"description\": \"确保 <audio> 元素有字幕\",\n      \"help\": \"<audio> 元素必须有字幕轨道\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"确保 autocomplete 属性正确且适合表单字段\",\n      \"help\": \"autocomplete 属性必须正确使用\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"确保通过样式属性设置的文本间距可以通过自定义样式表进行调整\",\n      \"help\": \"内联文本间距必须能够通过自定义样式表调整\"\n    },\n    \"blink\": {\n      \"description\": \"确保不使用 <blink> 元素\",\n      \"help\": \"<blink> 元素已被弃用，不得使用\"\n    },\n    \"button-name\": {\n      \"description\": \"确保按钮有可辨识的文本\",\n      \"help\": \"按钮必须有可辨识的文本\"\n    },\n    \"bypass\": {\n      \"description\": \"确保每个页面至少有一种机制，使用户能够绕过导航直接跳转到内容\",\n      \"help\": \"页面必须有方法绕过重复的部分\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"确保前景色与背景色之间的对比度满足 WCAG 2 AAA 增强对比度比率阈值\",\n      \"help\": \"元素必须满足增强的颜色对比度比率阈值\"\n    },\n    \"color-contrast\": {\n      \"description\": \"确保前景色与背景色之间的对比度满足 WCAG 2 AA 最小对比度比率阈值\",\n      \"help\": \"元素必须满足最小颜色对比度比率阈值\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"确保内容不锁定于任何特定显示方向，并且在所有显示方向下都可操作\",\n      \"help\": \"CSS 媒体查询不得锁定显示方向\"\n    },\n    \"definition-list\": {\n      \"description\": \"确保 <dl> 元素结构正确\",\n      \"help\": \"<dl> 元素必须只直接包含按正确顺序排列的 <dt> 和 <dd> 组、<script>、<template> 或 <div> 元素\"\n    },\n    \"dlitem\": {\n      \"description\": \"确保 <dt> 和 <dd> 元素被 <dl> 包含\",\n      \"help\": \"<dt> 和 <dd> 元素必须被 <dl> 包含\"\n    },\n    \"document-title\": {\n      \"description\": \"确保每个 HTML 页面包含一个非空的 <title> 元素\",\n      \"help\": \"页面必须有 <title> 元素以帮助导航\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"确保激活元素的每个 id 属性值是唯一的\",\n      \"help\": \"激活元素的 ID 必须唯一\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"确保在 ARIA 和标签中使用的每个 id 属性值是唯一的\",\n      \"help\": \"在 ARIA 和标签中使用的 ID 必须唯一\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"确保每个 id 属性值是唯一的\",\n      \"help\": \"id 属性值必须是唯一的\"\n    },\n    \"empty-heading\": {\n      \"description\": \"确保标题有可辨识的文本\",\n      \"help\": \"标题不应该为空\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"确保表格标题有可辨识的文本\",\n      \"help\": \"表格标题文本不应该为空\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"确保焦点顺序中的元素有适合交互内容的角色\",\n      \"help\": \"焦点顺序中的元素应该有适当的角色\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"确保表单字段没有多个标签元素\",\n      \"help\": \"表单字段不得有多个标签元素\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"确保有可聚焦内容的 <frame> 和 <iframe> 元素没有设置 tabindex=-1\",\n      \"help\": \"有可聚焦内容的框架不得设置 tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"确保 <iframe> 和 <frame> 元素包含 axe-core 脚本\",\n      \"help\": \"框架应该使用 axe-core 进行测试\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"确保 <iframe> 和 <frame> 元素包含唯一的 title 属性\",\n      \"help\": \"框架必须有一个唯一的 title 属性\"\n    },\n    \"frame-title\": {\n      \"description\": \"确保 <iframe> 和 <frame> 元素有可访问名称\",\n      \"help\": \"框架必须有可访问名称\"\n    },\n    \"heading-order\": {\n      \"description\": \"确保标题的顺序在语义上是正确的\",\n      \"help\": \"标题级别只应递增一\"\n    },\n    \"hidden-content\": {\n      \"description\": \"向用户通报隐藏内容\",\n      \"help\": \"页面上的隐藏内容应该被分析\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"确保每个 HTML 页面有 lang 属性\",\n      \"help\": \"<html> 元素必须有一个 lang 属性\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"确保 <html> 元素的 lang 属性包含有效值\",\n      \"help\": \"<html> 元素的 lang 属性必须有一个有效值\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"确保包含有效的 lang 和 xml:lang 属性的 HTML 元素在页面的基础语言上一致\",\n      \"help\": \"有 lang 和 xml:lang 属性的 HTML 元素必须有相同的基础语言\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"确保有相同可访问名称的链接服务于类似的目的\",\n      \"help\": \"有相同名称的链接必须有相似的目的\"\n    },\n    \"image-alt\": {\n      \"description\": \"确保 <img> 元素有替代文本或 role 属性为 \\\"none\\\" 或 \\\"presentation\\\"\",\n      \"help\": \"图像必须有替代文本\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"确保图像的替代文本不会作为文本重复\",\n      \"help\": \"图像的替代文本不应作为文本重复\"\n    },\n    \"input-button-name\": {\n      \"description\": \"确保输入按钮有可辨识的文本\",\n      \"help\": \"输入按钮必须有可辨识的文本\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"确保 <input type=\\\"image\\\"> 元素有替代文本\",\n      \"help\": \"图像按钮必须有替代文本\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"确保通过其内容标记的元素必须将其可见文本作为其可访问名称的一部分\",\n      \"help\": \"元素必须将其可见文本作为其可访问名称的一部分\"\n    },\n    \"label-title-only\": {\n      \"description\": \"确保每个表单元素都有一个可见的标签，并且不仅仅是使用隐藏的标签或 title 或 aria-describedby 属性来标记\",\n      \"help\": \"表单元素应该有一个可见的标签\"\n    },\n    \"label\": {\n      \"description\": \"确保每个表单元素都有标签\",\n      \"help\": \"表单元素必须有标签\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"确保 banner landmark 位于顶层\",\n      \"help\": \"Banner landmark 不应该包含在另一个landmark中\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"确保 complementary landmark 或 aside 位于顶层\",\n      \"help\": \"Aside 不应该包含在另一个 landmark 中\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"确保 contentinfo landmark 位于顶层\",\n      \"help\": \"Contentinfo landmark 不应该包含在另一个 landmark 中\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"确保主要 landmark 位于顶层\",\n      \"help\": \"主要 landmark 不应该包含在另一个 landmark 中\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"确保页面最多只有一个 banner landmark\",\n      \"help\": \"页面不应有多于一个 banner landmark\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"确保页面最多有一个 contentinfo landmark\",\n      \"help\": \"页面不应有多于一个 contentinfo landmark\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"确保页面最多有一个主要 landmark\",\n      \"help\": \"页面不应有多于一个主要 landmark\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"确保页面有一个主要 landmark\",\n      \"help\": \"页面应有一个主要 landmark\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"确保 landmark 是唯一的\",\n      \"description\": \"landmark 应有唯一的角色或角色/标签/标题（即可访问名称）组合\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"确保链接以不依赖颜色的方式与周围文本区分开\",\n      \"help\": \"链接必须能够不依赖颜色而被区分\"\n    },\n    \"link-name\": {\n      \"description\": \"确保链接有可辨识的文本\",\n      \"help\": \"链接必须有可辨识的文本\"\n    },\n    \"list\": {\n      \"description\": \"确保列表结构正确\",\n      \"help\": \"<ul> 和 <ol> 只能直接包含 <li>、<script> 或 <template> 元素\"\n    },\n    \"listitem\": {\n      \"description\": \"确保 <li> 元素的使用有语义性\",\n      \"help\": \"<li> 元素必须包含在 <ul> 或 <ol> 中\"\n    },\n    \"marquee\": {\n      \"description\": \"确保不使用 <marquee> 元素\",\n      \"help\": \"<marquee> 元素已被弃用，不得使用\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"确保不使用 <meta http-equiv=\\\"refresh\\\"> 进行延迟刷新\",\n      \"help\": \"不得使用延迟刷新\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"确保不使用 <meta http-equiv=\\\"refresh\\\"> 进行少于20小时的延迟刷新\",\n      \"help\": \"不得使用少于20小时的延迟刷新\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"确保 <meta name=\\\"viewport\\\"> 可以足够缩放\",\n      \"help\": \"用户应能够将文本缩放至 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"确保 <meta name=\\\"viewport\\\"> 未禁用文本缩放\",\n      \"help\": \"不得禁用缩放\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"确保交互控件不被嵌套，因为它们不总是被屏幕阅读器宣布，或可能导致辅助技术的焦点问题\",\n      \"help\": \"交互控件不得嵌套\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"确保 <video> 或 <audio> 元素不在没有停止或静音控制机制的情况下自动播放超过 3 秒的音频\",\n      \"help\": \"<video> 或 <audio> 元素不得自动播放\"\n    },\n    \"object-alt\": {\n      \"description\": \"确保 <object> 元素有替代文本\",\n      \"help\": \"<object> 元素必须有替代文本\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"确保不使用加粗、斜体文本和字体大小来将 <p> 元素样式化为标题\",\n      \"help\": \"样式化的 <p> 元素不得作为标题\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"确保页面或至少其中一个框架包含一个一级标题\",\n      \"help\": \"页面应包含一个一级标题\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"标记为展示目的的元素不应有全局 ARIA 或 tabindex 属性，以确保所有屏幕阅读器忽略它们\",\n      \"help\": \"确保标记为展示目的的元素始终被忽略\"\n    },\n    \"region\": {\n      \"description\": \"确保所有页面内容都包含在 landmark 中\",\n      \"help\": \"所有页面内容应包含在 landmark 中\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"确保 [role=\\\"img\\\"] 元素有替代文本\",\n      \"help\": \"[role=\\\"img\\\"] 元素必须有一个替代文本\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"确保表格中正确使用 scope 属性\",\n      \"help\": \"scope 属性应正确使用\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"确保有滚动内容的元素可以通过键盘访问\",\n      \"help\": \"可滚动区域必须可以键盘访问\"\n    },\n    \"select-name\": {\n      \"description\": \"确保 select 元素有一个可访问的名称\",\n      \"help\": \"select 元素必须有一个可访问的名称\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"确保不使用服务器端图像映射\",\n      \"help\": \"不得使用服务器端图像映射\"\n    },\n    \"skip-link\": {\n      \"description\": \"确保所有跳过链接有一个可聚焦的目标\",\n      \"help\": \"跳过链接的目标应存在且可聚焦\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"确保有 img、graphics-document 或 graphics-symbol 角色的 <svg> 元素有可访问文本\",\n      \"help\": \"有 img 角色的 <svg> 元素必须有一个替代文本\"\n    },\n    \"tabindex\": {\n      \"description\": \"确保 tabindex 属性值不大于0\",\n      \"help\": \"元素的 tabindex 不应大于零\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"确保 <caption> 元素不包含与 summary 属性相同的文本\",\n      \"help\": \"表格的 summary 和 caption 不应相同\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"确保带有标题的表格使用 <caption> 元素\",\n      \"help\": \"数据单元格或标题单元格不得用于为数据表提供标题\"\n    },\n    \"target-size\": {\n      \"description\": \"确保触摸目标有足够的大小和空间\",\n      \"help\": \"所有触摸目标的大小必须为 24px，或留出足够的空间\"\n    },\n    \"td-has-header\": {\n      \"description\": \"确保大于 3 x 3 的 <table> 中每个非空数据单元格都有一个或多个表头\",\n      \"help\": \"较大 <table> 中的非空 <td> 元素必须有关联的表头\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"确保使用 headers 属性的表格中的每个单元格仅引用该表中的其他单元格\",\n      \"help\": \"使用 headers 属性的表格单元格必须仅引用同一表中的单元格\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"确保 <th> 元素和有 role=columnheader/rowheader 角色的元素描述了它们所描述的数据单元格\",\n      \"help\": \"数据表中的表头必须引用数据单元格\"\n    },\n    \"valid-lang\": {\n      \"description\": \"确保 lang 属性值有效\",\n      \"help\": \"lang 属性值必须有效\"\n    },\n    \"video-caption\": {\n      \"description\": \"确保 <video> 元素有字幕\",\n      \"help\": \"<video> 元素必须有字幕\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"未使用抽象角色\",\n      \"fail\": {\n        \"singular\": \"抽象角色不能直接使用：${data.values}\",\n        \"plural\": \"抽象角色不能直接使用：${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"所定义的角色使用的 ARIA 属性都是正确的\",\n      \"fail\": {\n        \"singular\": \"不允许使用 ARIA 属性: ${data.values}\",\n        \"plural\": \"不允许使用 ARIA 属性: ${data.values}\"\n      },\n      \"incomplete\": \"检查如果忽略该元素的 ARIA 属性，会不会出现问题: ${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"给定元素允许 ARIA 角色\",\n      \"fail\": {\n        \"singular\": \"给定元素不允许 ARIA 角色 ${data.values}\",\n        \"plural\": \"给定元素不允许 ARIA 角色 ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"当元素可见时，必须删除 ARIA 角色 ${data.values}，因为该元素不允许该角色\",\n        \"plural\": \"当元素可见时，必须删除 ARIA 角色 ${data.values}，因为该元素不允许这些角色\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"元素有 aria-busy 属性\",\n      \"fail\": \"元素在显示加载时使用 aria-busy=\\\"true\\\"\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"允许使用 ARIA 属性\",\n      \"fail\": {\n        \"checkbox\": \"删除 aria-checked 属性，或将其设置为\\\"${data.checkState}\\\"以匹配真实复选框状态\",\n        \"rowSingular\": \"树形表格的行 treegrid rows 支持此属性，但 ${data.ownerRole} 不支持：${data.invalidAttrs}\",\n        \"rowPlural\": \"树形表格的行 treegrid rows 支持此属性，但 ${data.ownerRole} 不支持：${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage 存在，并引用了对于使用支持的 aria-errormessage 技术的屏幕阅读器可见的元素\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage 值 `${data.values}` 必须使用一种方法来通知消息（例如：aria-live、aria-describedby、role=alert 等）\",\n        \"plural\": \"aria-errormessage 值 `${data.values}` 必须使用一种方法来通知消息（例如：aria-live、aria-describedby、role=alert 等）\",\n        \"hidden\": \"aria-errormessage 值 `${data.values}` 无法引用隐藏元素\"\n      },\n      \"incomplete\": {\n        \"singular\": \"确保 aria-errormessage 值 `${data.values}` 引用了现有元素\",\n        \"plural\": \"确保 aria-errormessage 值 `${data.values}` 引用了现有元素\",\n        \"idrefs\": \"无法确定页面上是否存在 aria-errormessage 元素：${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"<body> 不存在 aria-hidden 属性\",\n      \"fail\": \"<body> 不应该存在 aria-hidden=true\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria-level 值是有效的\",\n      \"incomplete\": \"部分屏幕阅读器和浏览器组合不支持大于 6 的 aria-level 值\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"允许使用 ARIA 属性\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} 属性不能与角色 \\\"${data.role}\\\" 一起使用\",\n        \"hasRoleSingular\": \"${data.prohibited} 属性不能与角色\\\"${data.role}\\\" 一起使用\",\n        \"noRolePlural\": \"${data.prohibited} 属性不能在没有有效 role 属性的 ${data.nodeName} 上使用\",\n        \"noRoleSingular\": \"${data.prohibited 属性不能在没有有效 role 属性的 ${data.nodeName} 上使用\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"角色 \\\"${data.role}\\\" 无法良好支持 ${data.prohibited} 属性\",\n        \"hasRolePlural\": \"角色 \\\"${data.role}\\\" 无法良好支持 ${data.prohibited} 属性\",\n        \"noRoleSingular\": \"没有有效 role 属性的 ${data.nodeName} 无法良好支持 ${data.prohibited} 属性\",\n        \"noRolePlural\": \"没有有效 role 属性的 ${data.nodeName} 无法良好支持 ${data.prohibited} 属性\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"所有必需的 ARIA 属性都已存在\",\n      \"fail\": {\n        \"singular\": \"缺少必需的 ARIA 属性：${data.values}\",\n        \"plural\": \"缺少必需的 ARIA 属性：${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": {\n        \"default\": \"子元素需要 ARIA 角色\",\n        \"aria-busy\": \"元素有 aria-busy 属性，因此可以省略必需的子元素\"\n      },\n      \"fail\": {\n        \"singular\": \"子元素缺少必需的 ARIA 角色：${data.values}\",\n        \"plural\": \"子元素缺少必需的 ARIA 角色：${data.values}\",\n        \"unallowed\": \"元素有不允许的子元素：${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"子元素期望添加 ARIA 角色：${data.values}\",\n        \"plural\": \"子元素期望添加 ARIA 角色：${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"父元素需要存在 ARIA 角色\",\n      \"fail\": {\n        \"singular\": \"父元素缺少必需的 ARIA 角色：${data.values}\",\n        \"plural\": \"父元素缺少必需的 ARIA 角色：${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"在支持的语义角色上使用了 aria-roledescription\",\n      \"incomplete\": \"检查 aria-roledescription 是否被支持的屏幕阅读器宣布\",\n      \"fail\": \"给元素赋予一个支持 'aria-roledescription' 的角色\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"ARIA 属性受支持\",\n      \"fail\": \"ARIA 属性在屏幕阅读器和辅助技术中不受广泛支持：${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA 属性值有效\",\n      \"fail\": {\n        \"singular\": \"无效的 ARIA 属性值：${data.values}\",\n        \"plural\": \"无效的 ARIA 属性值：${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"页面上不存在 ARIA 属性元素 ID：${data.needsReview}\",\n        \"noIdShadow\": \"ARIA 属性元素 ID 在页面上不存在或属于不同影子 DOM 树的后代：${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA 属性值无效，将被视为 \\\"aria-current=true\\\"：${data.needsReview}\",\n        \"idrefs\": \"无法确定页面上是否存在 ARIA 属性元素 ID：${data.needsReview}\",\n        \"empty\": \"ARIA 属性值为空时被忽略：${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"ARIA 属性名称有效\",\n      \"fail\": {\n        \"singular\": \"无效的 ARIA 属性名称：${data.values}\",\n        \"plural\": \"无效的 ARIA 属性名称：${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"在有可访问文本的元素上使用了 aria-braillelabel\",\n      \"fail\": \"在没有可访问文本的元素上使用了 aria-braillelabel\",\n      \"incomplete\": \"无法计算可访问文本\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"在有 aria-roledescription 的元素上使用了 aria-brailleroledescription\",\n      \"fail\": {\n        \"noRoleDescription\": \"在没有 aria-roledescription 的元素上使用了 aria-brailleroledescription\",\n        \"emptyRoleDescription\": \"在有空 aria-roledescription 的元素上使用了 aria-brailleroledescription\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA 角色未弃用\",\n      \"fail\": \"使用的角色已弃用：${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"只使用一个角色值\",\n      \"fail\": \"只使用一个角色值，因为旧版浏览器不支持回退角色\",\n      \"incomplete\": \"只使用角色 'presentation' 或 'none'，因为它们是同义词\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"元素有全局 ARIA 属性：${data.values}\",\n        \"plural\": \"元素有全局 ARIA 属性：${data.values}\"\n      },\n      \"fail\": \"元素没有全局 ARIA 属性\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"元素有小部件角色\",\n      \"fail\": \"元素没有小部件角色\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA 角色有效\",\n      \"fail\": {\n        \"singular\": \"角色必须是有效的 ARIA 角色之一：${data.values}\",\n        \"plural\": \"角色必须是有效的 ARIA 角色之一：${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"元素可聚焦\",\n      \"fail\": \"元素不可聚焦\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"<label> 和可访问名称之间没有不匹配\",\n      \"incomplete\": \"检查 <label> 有没有必要成为 ARIA ${data} 字段名称的一部分\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"ARIA 角色受支持\",\n      \"fail\": \"所使用的角色在屏幕阅读器和辅助技术中不受广泛支持：${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"元素对于焦点顺序中的元素包含有效的语义\",\n      \"fail\": \"元素对于焦点顺序中的元素包含无效的语义\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"元素有足够的颜色对比度 ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"元素的颜色对比度 ${data.contrastRatio} 不足（前景色：${data.fgColor}，背景色：${data.bgColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"元素在前景色和阴影色之间的对比度 ${data.contrastRatio} 不足（前景色：${data.fgColor}，文本阴影颜色：${data.shadowColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"元素在阴影颜色和背景色之间的对比度 ${data.contrastRatio} 不足（文本阴影颜色：${data.shadowColor}，背景色：${data.bgColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"无法确定对比度\",\n        \"bgImage\": \"由于背景是图像，因此无法确定元素的背景颜色\",\n        \"bgGradient\": \"由于背景是渐变，因此无法确定元素的背景颜色\",\n        \"imgNode\": \"由于元素包含图像节点，因此无法确定元素的背景颜色\",\n        \"bgOverlap\": \"由于元素被另一个元素覆盖，因此无法确定元素的背景颜色\",\n        \"fgAlpha\": \"由于存在 Alpha 透明度，因此无法确定元素的前景色\",\n        \"elmPartiallyObscured\": \"由于部分被另一个元素遮挡，因此无法确定元素的背景颜色\",\n        \"elmPartiallyObscuring\": \"由于部分重叠其他元素，因此无法确定元素的背景颜色\",\n        \"outsideViewport\": \"由于超出视窗，因此无法确定元素的背景颜色\",\n        \"equalRatio\": \"元素与背景的对比度为 1:1\",\n        \"shortTextContent\": \"元素内容过短，无法确定其是否为实际文本内容\",\n        \"nonBmp\": \"元素内容仅包含非文本字符\",\n        \"pseudoContent\": \"由于存在伪元素，因此无法确定元素的背景颜色\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"元素有足够的颜色对比度 ${data.contrastRatio}\",\n        \"hidden\": \"元素被隐藏\"\n      },\n      \"fail\": {\n        \"default\": \"元素的颜色对比度 ${data.contrastRatio} 不足（前景色：${data.fgColor}，背景色：${data.bgColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"元素在前景色和阴影色之间的对比度 ${data.contrastRatio} 不足（前景色：${data.fgColor}，文本阴影颜色：${data.shadowColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"元素在阴影颜色和背景色之间的对比度 ${data.contrastRatio} 不足（文本阴影颜色：${data.shadowColor}，背景色：${data.bgColor}，字体大小：${data.fontSize}，字体粗细：${data.fontWeight}）。预期的对比度为 ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"无法确定对比度\",\n        \"bgImage\": \"由于背景是图像，因此无法确定元素的背景颜色\",\n        \"bgGradient\": \"由于背景是渐变，因此无法确定元素的背景颜色\",\n        \"imgNode\": \"由于元素包含图像节点，因此无法确定元素的背景颜色\",\n        \"bgOverlap\": \"由于元素被另一个元素覆盖，因此无法确定元素的背景颜色\",\n        \"complexTextShadows\": \"由于元素使用了复杂的文本阴影，因此无法确定元素的对比度\",\n        \"fgAlpha\": \"由于 Alpha 透明度，因此无法确定元素的前景色\",\n        \"elmPartiallyObscured\": \"由于部分被另一个元素遮挡，因此无法确定元素的背景颜色\",\n        \"elmPartiallyObscuring\": \"由于部分重叠其他元素，因此无法确定元素的背景颜色\",\n        \"outsideViewport\": \"由于超出视窗，因此无法确定元素的背景颜色\",\n        \"equalRatio\": \"元素与背景的对比度为 1:1\",\n        \"shortTextContent\": \"元素内容过短，因此无法确定其是否为实际文本内容\",\n        \"nonBmp\": \"元素内容仅包含非文本字符\",\n        \"pseudoContent\": \"由于存在伪元素，因此无法确定元素的背景颜色\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"可以通过视觉样式将链接与周围文本区分开\",\n      \"incomplete\": {\n        \"default\": \"检查链接是否需要样式以将其与附近的文本区分开\",\n        \"pseudoContent\": \"检查链接的伪样式是否足以将其与周围的文本区分开\"\n      },\n      \"fail\": \"该链接没有样式（如下划线）来将其与周围的文本区分开\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"链接可以通过颜色以外的其他方式与周围文本区分开\",\n      \"fail\": {\n        \"fgContrast\": \"链接与周围文本的对比度 ${data.contrastRatio}:1 不足（最小对比度为${data.requiredContrastRatio}:1，链接文本颜色：${data.nodeColor}，周围文本颜色：${data.parentColor}）\",\n        \"bgContrast\": \"链接背景与周围背景的对比度 ${data.contrastRatio} 不足（最小对比度为${data.requiredContrastRatio}:1，链接背景颜色：${data.nodeBackgroundColor}，周围背景颜色：${data.parentBackgroundColor}）\"\n      },\n      \"incomplete\": {\n        \"default\": \"无法确定元素的前景对比度\",\n        \"bgContrast\": \"无法确定元素的背景对比度\",\n        \"bgImage\": \"由于背景是图像，因此无法确定元素的对比度\",\n        \"bgGradient\": \"由于背景是渐变，因此无法确定元素的对比度\",\n        \"imgNode\": \"由于元素包含图像节点，因此无法确定元素的对比度\",\n        \"bgOverlap\": \"由于元素重叠，因此无法确定元素的对比度\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"autocomplete 值适用于适当的元素\",\n      \"fail\": \"autocomplete 值不适用于此类输入\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"autocomplete 属性格式正确\",\n      \"fail\": \"autocomplete 属性格式不正确\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"accesskey 属性值是唯一的\",\n      \"fail\": \"页面有多个元素有相同的 accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"元素包含可聚焦元素\",\n      \"fail\": \"元素应包含可聚焦内容\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"元素内没有可聚焦的元素\",\n      \"incomplete\": \"检查可聚焦的元素是否立即移动焦点指示器\",\n      \"fail\": \"可聚焦内容应该被禁用或从 DOM 中移除\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"元素可以聚焦\",\n      \"fail\": \"元素应该是可聚焦的\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"在对话窗打开时没有可聚焦的元素\",\n      \"incomplete\": \"检查在当前状态下可聚焦的元素是否可以通过 tab 键访问\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"元素不在 tab 顺序中或没有可访问的文本\",\n      \"fail\": \"元素在 tab 顺序中且没有可访问的文本\",\n      \"incomplete\": \"无法确定元素是否有可访问的名称\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"元素内没有可聚焦的元素\",\n      \"incomplete\": \"检查可聚焦的元素是否立即移动焦点指示器\",\n      \"fail\": \"可聚焦内容应该有 tabindex=\\\"-1\\\" 或从 DOM 中移除\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"元素没有可聚焦的后代元素\",\n      \"fail\": \"元素有可聚焦的后代元素\",\n      \"incomplete\": \"无法确定元素是否有后代元素\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} landmark 位于顶层\",\n      \"fail\": \"${data.role} landmark 包含在另一个 landmark 中\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"元素没有可聚焦的后代元素\",\n      \"fail\": {\n        \"default\": \"元素有可聚焦的后代元素\",\n        \"notHidden\": \"在交互控件内部使用负 tabindex 不能阻止辅助技术将焦点放在元素上（即使使用 aria-hidden=\\\"true\\\"）\"\n      },\n      \"incomplete\": \"无法确定元素是否有后代元素\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"页面至少有一个一级标题\",\n      \"fail\": \"页面必须有一级标题\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"页面至少有一个主要的 landmark\",\n      \"fail\": \"页面没有主要的 landmark\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"页面没有多个 banner landmark\",\n      \"fail\": \"页面有多个 banner landmark\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"页面没有多个 contentinfo landmark\",\n      \"fail\": \"页面有多个 contentinfo landmark\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"页面没有多个主要 landmark\",\n      \"fail\": \"页面有多个主要 landmark\"\n    },\n    \"tabindex\": {\n      \"pass\": \"元素的 tabindex 不大于 0\",\n      \"fail\": \"元素的 tabindex 大于 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"元素包含有效的 alt 属性值\",\n      \"fail\": \"元素的 alt 属性只包含空格字符，所有屏幕阅读器不会忽略该属性\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"元素内的文本没有和元素的 <img> alt 文本重复\",\n      \"fail\": \"元素内的文本和元素的 <img> alt 文本重复\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"表单元素有显式的标签文本 <label>\",\n      \"fail\": \"表单元素没有显式的标签文本 <label>\",\n      \"incomplete\": \"无法确定表单元素是否有显式的标签文本 <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"说明文本（title 或 aria-describedby）不重复标签文本\",\n      \"fail\": \"说明文本（title 或 aria-describedby）与标签文本相同\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"表单元素有可见的显式标签文本 <label>\",\n      \"fail\": \"表单元素有隐藏的显式标签文本 <label>\",\n      \"incomplete\": \"无法确定表单元素是否有隐藏的显式标签文本 <label>\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"表单元素有隐式标签文本 <label>\",\n      \"fail\": \"表单元素没有隐式标签文本 <label>\",\n      \"incomplete\": \"无法确定表单元素是否有隐式标签文本 <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"元素包含可见文本作为其可访问名称的一部分\",\n      \"fail\": \"元素内的文本未包含在可访问名称中\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"表单字段没有多个标签元素\",\n      \"incomplete\": \"辅助软件并未广泛支持多个标签元素。确保第一个标签包含所有必要的信息\"\n    },\n    \"title-only\": {\n      \"pass\": \"表单元素不仅使用 title 属性作为其标签\",\n      \"fail\": \"仅使用 title 生成表单元素的标签\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"landmark 必须有唯一的角色或角色/标签/标题（即可访问名称）组合\",\n      \"fail\": \"landmark 必须有唯一的 aria-label、aria-labelledby 或 title 用于方便区分 landmark\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> 元素有 lang 属性\",\n      \"fail\": {\n        \"noXHTML\": \"xml:lang 属性在 HTML 页面上无效，请使用 lang 属性\",\n        \"noLang\": \"<html> 元素没有 lang 属性\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"lang 属性值包含在有效语言列表中\",\n      \"fail\": \"lang 属性值未包含在有效语言列表中\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"lang 和 xml:lang 属性有相同的基本语言\",\n      \"fail\": \"lang 和 xml:lang 属性没有相同的基本语言\"\n    },\n    \"dlitem\": {\n      \"pass\": \"描述列表项有 <dl> 父元素\",\n      \"fail\": \"描述列表项没有 <dl> 父元素\"\n    },\n    \"listitem\": {\n      \"pass\": \"列表项有 <ul>、<ol> 或 role=\\\"list\\\" 父元素\",\n      \"fail\": {\n        \"default\": \"列表项的父元素不是 <ul> 或 <ol>\",\n        \"roleNotValid\": \"列表项的父元素不是 <ul>、<ol> 或包含 role=\\\"list\\\" 属性\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"dl 元素仅包含允许的直接子元素：<dt>、<dd> 或 <div> 元素\",\n      \"fail\": \"dl 元素包含不允许的直接子元素：${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"列表元素仅允许在 <li> 元素内由直接子元素\",\n      \"fail\": \"列表元素由不允许的直接子元素：${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"当不为空时，元素同时有 <dt> 和 <dd> 元素\",\n      \"fail\": \"当不为空时，元素没有至少一个 <dt> 元素后跟至少一个 <dd> 元素\"\n    },\n    \"caption\": {\n      \"pass\": \"多媒体元素有字幕轨道\",\n      \"incomplete\": \"检查元素是否提供字幕\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"iframe 使用 axe-core 进行了测试\",\n      \"fail\": \"iframe 无法使用 axe-core 进行测试\",\n      \"incomplete\": \"iframe 仍需使用 axe-core 进行测试\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> 或 <audio> 不会播放超过允许时长的音频或有控制机制\",\n      \"fail\": \"<video> 或 <audio> 播放超过允许时长的音频，且没有控制机制\",\n      \"incomplete\": \"检查 <video> 或 <audio> 是否播放超过允许时长的音频或提供了控制机制\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"显示可操作，且不存在方向锁定\",\n      \"fail\": \"CSS 方向锁定已应用，导致显示无法操作\",\n      \"incomplete\": \"无法确定 CSS 方向锁定是否已应用\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta> 标签不会阻止在移动设备上的重要缩放\",\n      \"fail\": \"<meta> 标签限制了移动设备上的缩放\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta> 标签不会在移动设备上禁用缩放\",\n      \"fail\": \"在 <meta> 标签上的 ${data} 禁用了移动设备上的缩放\"\n    },\n    \"target-offset\": {\n      \"pass\": {\n        \"default\": \"目标与最近的邻元素有足够的空间。安全可点击空间距离为 ${data.closestOffset}px，至少为 ${data.minOffset}px\",\n        \"large\": \"目标远远超过最小尺寸 ${data.minOffset}px\"\n      },\n      \"fail\": \"目标与最近的邻元素之间的空间不足。安全可点击空间距离为 ${data.closestOffset}px，而不是至少 ${data.minOffset}px\",\n      \"incomplete\": {\n        \"default\": \"tabindex 为负的元素与最近的邻元素之间的空间不足。安全可点击空间距离为 ${data.closestOffset}px，而不是至少 ${data.minOffset}px。这是一个目标吗？\",\n        \"nonTabbableNeighbor\": \"目标与最近的邻元素之间的空间不足。安全可点击空间距离为 ${data.closestOffset}px，而不是至少 ${data.minOffset}px。邻元素是目标吗？\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"控件尺寸足够（${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）\",\n        \"obscured\": \"由于完全被遮挡而忽略控件，因此无法点击\",\n        \"large\": \"目标远远超过最小尺寸 ${data.minSize}px\"\n      },\n      \"fail\": {\n        \"default\": \"目标尺寸不足（${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）\",\n        \"partiallyObscured\": \"目标尺寸不足，因为部分被遮挡（最小尺寸为 ${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）\"\n      },\n      \"incomplete\": {\n        \"default\": \"tabindex 为负元的素尺寸不足（${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）。这是一个目标吗？\",\n        \"contentOverflow\": \"由于内容溢出，无法准确确定元素的大小\",\n        \"partiallyObscured\": \"tabindex 为负的元素尺寸不足，因为部分被遮挡（最小尺寸为 ${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）。这是一个目标吗？\",\n        \"partiallyObscuredNonTabbable\": \"目标尺寸不足，因为部分被 tabindex 为负的邻元素遮挡（最小尺寸为 ${data.width}px x ${data.height}px，应至少为 ${data.minSize}px x ${data.minSize}px）。邻元素是目标吗？\",\n        \"tooManyRects\": \"无法获取目标尺寸，因为有太多重叠的元素\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"页面有标题\",\n      \"fail\": \"页面没有标题\"\n    },\n    \"heading-order\": {\n      \"pass\": \"标题顺序有效\",\n      \"fail\": \"标题顺序无效\",\n      \"incomplete\": \"无法确定上一个标题\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"没有其他有相同名称的链接跳转到不同的 URL\",\n      \"incomplete\": \"检查链接是否有相同的目的，或者是否有意模糊不清\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"找到有效的跳转链接\",\n      \"fail\": \"找不到有效的跳转链接\"\n    },\n    \"landmark\": {\n      \"pass\": \"页面有 landmark 区域\",\n      \"fail\": \"页面没有 landmark 区域\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"<meta> 标签不会立即刷新页面\",\n      \"fail\": \"<meta> 标签强制定时刷新页面\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta> 标签不会立即刷新页面\",\n      \"fail\": \"<meta> 标签强制定时刷新页面（少于20小时）\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> 元素的样式不为标题\",\n      \"fail\": \"应该使用标题元素而不是样式化的 <p> 元素\",\n      \"incomplete\": \"无法确定 <p> 元素是否被样式化为标题\"\n    },\n    \"region\": {\n      \"pass\": \"所有页面内容都包含在 landmark 中\",\n      \"fail\": \"某些页面内容不包含在 landmark 中\"\n    },\n    \"skip-link\": {\n      \"pass\": \"跳转链接目标存在\",\n      \"incomplete\": \"跳转链接目标应在激活时变得可见\",\n      \"fail\": \"没有跳转链接目标\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"元素的 title 属性是唯一的\",\n      \"fail\": \"元素的 title 属性不唯一\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"页面中没有相同 id 属性的激活元素\",\n      \"fail\": \"页面中有相同 id 属性的激活元素：${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"页面中没有通过 ARIA 引用的元素或有相同的 id 属性的标签\",\n      \"fail\": \"页面中有通过 ARIA 引用的多个有相同 id 属性的元素：${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"页面中没有相同 id 属性的静态元素\",\n      \"fail\": \"页面中有多个相同 id 属性的静态元素：${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label 属性存在且不为空\",\n      \"fail\": \"aria-label 属性不存在或为空\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby 属性存在并引用对屏幕阅读器可见的元素\",\n      \"fail\": \"aria-labelledby 属性不存在，引用不存在的元素或引用空元素\",\n      \"incomplete\": \"确保 aria-labelledby 引用了一个现有的元素\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"没有使用 '!important' 影响文本间距的内联样式\",\n      \"fail\": {\n        \"singular\": \"从内联样式 ${data.values} 中移除 '!important'，因为大多数浏览器不支持覆盖此样式\",\n        \"plural\": \"从内联样式 ${data.values} 中移除 '!important'，因为大多数浏览器不支持覆盖此样式\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"元素有对屏幕阅读器可见的内部文本\",\n      \"fail\": \"元素没有对屏幕阅读器可见的内部文本\",\n      \"incomplete\": \"无法确定元素是否有子元素\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"页面有一个非空的 <title> 元素\",\n      \"fail\": \"页面没有一个非空的 <title> 元素\"\n    },\n    \"exists\": {\n      \"pass\": \"元素不存在\",\n      \"incomplete\": \"元素存在\"\n    },\n    \"has-alt\": {\n      \"pass\": \"元素有 alt 属性\",\n      \"fail\": \"元素没有 alt 属性\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"元素有对屏幕阅读器可见的文本\",\n      \"fail\": \"元素没有对屏幕阅读器可见的文本\",\n      \"incomplete\": \"无法确定元素是否有子元素\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"style 属性中的 letter-spacing 未设置为 !important，或满足最小值\",\n      \"fail\": \"style 属性中的 letter-spacing 一定不能使用 !important，也不能是 ${data.minValue}em（当前 ${data.value}em）\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"style 属性中的 line-height 未设置为 !important，或满足最小值\",\n      \"fail\": \"style 属性中的 line-height 一定不能使用 !important，也不能是 ${data.minValue}em（当前 ${data.value}em）\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"style 属性中的 word-spacing 未设置为 !important，或满足最小值\",\n      \"fail\": \"style 属性中的 word-spacing 一定不能使用 !important，也不能是 ${data.minValue}em（当前 ${data.value}em）\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"元素不可见\",\n      \"fail\": \"元素可见\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"元素有不为空的 alt 属性\",\n      \"fail\": {\n        \"noAttr\": \"元素没有 alt 属性\",\n        \"emptyAttr\": \"元素的 alt 属性为空\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"元素没有 value 属性\",\n        \"has-label\": \"元素有不为空的 value 属性\"\n      },\n      \"fail\": \"元素有 value 属性且 value 属性为空\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"元素有 placeholder 属性\",\n      \"fail\": {\n        \"noAttr\": \"元素没有 placeholder 属性\",\n        \"emptyAttr\": \"元素的 placeholder 属性为空\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"元素有 title 属性\",\n      \"fail\": {\n        \"noAttr\": \"元素没有 title 属性\",\n        \"emptyAttr\": \"元素的 title 属性为空\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"元素有不为空的 value 属性\",\n      \"fail\": {\n        \"noAttr\": \"元素没有 value 属性\",\n        \"emptyAttr\": \"元素有空的 value 属性\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"元素的默认语义被 role=\\\"${data.role}\\\" 覆盖\",\n      \"fail\": {\n        \"default\": \"元素的默认语义没有被 role=\\\"none\\\" 或 role=\\\"presentation\\\" 覆盖\",\n        \"globalAria\": \"元素的角色不是表现性的，因为它有全局 ARIA 属性\",\n        \"focusable\": \"元素的角色不是表现性的，因为它是可聚焦的\",\n        \"both\": \"元素的角色不是表现性的，因为它有全局 ARIA 属性并且是可聚焦的\",\n        \"iframe\": \"在有表现性角色的 ${data.nodeName} 元素上使用 \\\"title\\\" 属性在屏幕阅读器之间表现不一致\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"元素的默认语义被 role=\\\"none\\\" 覆盖\",\n      \"fail\": \"元素的默认语义没有被 role=\\\"none\\\" 覆盖\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"元素的默认语义被 role=\\\"presentation\\\" 覆盖\",\n      \"fail\": \"元素的默认语义未被 role=\\\"presentation\\\" 覆盖\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"元素有一个标题子元素\",\n      \"fail\": {\n        \"noTitle\": \"元素没有标题子元素\",\n        \"emptyTitle\": \"元素的标题子元素为空\"\n      },\n      \"incomplete\": \"无法确定元素是否有标题子元素\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"表格的第一行不用作标题\",\n      \"fail\": \"表格的第一个子元素应该是标题而不是表格单元格\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"scope 属性只用于表头元素 (<th>)\",\n      \"fail\": \"在 HTML 5 中，scope 属性只能用于表头元素 (<th>)\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"summary 属性的内容和 <caption> 不重复\",\n      \"fail\": \"summary 属性的内容和 <caption> 元素的内容相同\",\n      \"incomplete\": \"无法确定 <table> 元素是否有标题\"\n    },\n    \"scope-value\": {\n      \"pass\": \"scope 属性的使用是正确的\",\n      \"fail\": \"scope 属性的值只能是 'row' 或 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"所有非空数据单元格都有表头\",\n      \"fail\": \"一些非空数据单元格没有表头\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"headers 属性专门用于引用表格中的其他单元格\",\n      \"incomplete\": \"headers 属性为空\",\n      \"fail\": \"headers 属性不专门用于引用表格中的其他单元格\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"所有表头单元格都引用数据单元格\",\n      \"fail\": \"并非所有表头单元格都引用数据单元格\",\n      \"incomplete\": \"表格数据单元格缺失或为空\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"页面上的所有内容都已经分析过\",\n      \"fail\": \"分析页面内容时出现问题\",\n      \"incomplete\": \"页面上存在未分析的隐藏内容。您需要触发显示此内容以便进行分析\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"修复以下任一问题：{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"修复以下所有问题：{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe 无法确定原因。请打开元素检查器！\"\n}\n"
  },
  {
    "path": "locales/zh_TW.json",
    "content": "{\n  \"lang\": \"zh_TW\",\n  \"rules\": {\n    \"accesskeys\": {\n      \"description\": \"確保每個 accesskey 屬性值都是唯一的\",\n      \"help\": \"accesskey 屬性值都要是唯一的\"\n    },\n    \"area-alt\": {\n      \"description\": \"確保圖像映射的 <area> 元素具有替代文字\",\n      \"help\": \"<area> 元素必須具有替代文字\"\n    },\n    \"aria-allowed-attr\": {\n      \"description\": \"確保元素的角色支援其 ARIA 屬性\",\n      \"help\": \"元素只能使用受支援的 ARIA 屬性\"\n    },\n    \"aria-allowed-role\": {\n      \"description\": \"確保角色屬性具有適合該元素的值\",\n      \"help\": \"ARIA 角色應該適合該元素\"\n    },\n    \"aria-braille-equivalent\": {\n      \"description\": \"確保 aria-braillelabel 和 aria-brailleroledescription 具有非點字等效項\",\n      \"help\": \"aria-braille 屬性必須具有非點字等效項\"\n    },\n    \"aria-command-name\": {\n      \"description\": \"確保每個 ARIA 按鈕、連結和選單項目都有一個無障礙的名稱\",\n      \"help\": \"ARIA 指令必須具有無障礙的名稱\"\n    },\n    \"aria-conditional-attr\": {\n      \"description\": \"確保按照元素角色規範中的描述使用 ARIA 屬性\",\n      \"help\": \"必須按照元素角色指定的方式使用 ARIA 屬性\"\n    },\n    \"aria-deprecated-role\": {\n      \"description\": \"確保元素不使用已棄用的角色\",\n      \"help\": \"不得使用已棄用的 ARIA 角色\"\n    },\n    \"aria-dialog-name\": {\n      \"description\": \"確保每個 ARIA 對話框和警報對話框節點都有一個無障礙的名稱\",\n      \"help\": \"ARIA 對話框和警報對話框節點應該有一個無障礙的名稱\"\n    },\n    \"aria-hidden-body\": {\n      \"description\": \"確保 <body> 沒有 aria-hidden=\\\"true\\\"\",\n      \"help\": \"aria-hidden=\\\"true\\\" 不得出現在 <body>\"\n    },\n    \"aria-hidden-focus\": {\n      \"description\": \"確保 aria 隱藏元素不可對焦也不包含可聚焦元素\",\n      \"help\": \"ARIA 隱藏元素不得為可聚焦元素或包含可聚焦元素\"\n    },\n    \"aria-input-field-name\": {\n      \"description\": \"確保每個 ARIA 輸入欄位都有一個無障礙的名稱\",\n      \"help\": \"ARIA 輸入欄位必須具有無障礙的名稱\"\n    },\n    \"aria-meter-name\": {\n      \"description\": \"確保每個 ARIA 計量節點都有一個無障礙的名稱\",\n      \"help\": \"ARIA 計量節點必須具有無障礙的名稱\"\n    },\n    \"aria-progressbar-name\": {\n      \"description\": \"確保每個 ARIA 進度條節點都有一個無障礙的名稱\",\n      \"help\": \"ARIA 進度條節點必須具有無障礙的名稱\"\n    },\n    \"aria-prohibited-attr\": {\n      \"description\": \"確保元素角色不禁止 ARIA 屬性\",\n      \"help\": \"元素只能使用允許的 ARIA 屬性\"\n    },\n    \"aria-required-attr\": {\n      \"description\": \"確保具有 ARIA 角色的元素具有所有必需的 ARIA 屬性\",\n      \"help\": \"必須提供必需的 ARIA 屬性\"\n    },\n    \"aria-required-children\": {\n      \"description\": \"確保具有需要子角色的 ARIA 角色的元素包含它們\",\n      \"help\": \"某些 ARIA 角色必須包含特定的子角色\"\n    },\n    \"aria-required-parent\": {\n      \"description\": \"確保具有需要父角色的 ARIA 角色的元素包含在其中\",\n      \"help\": \"某些 ARIA 角色必須被特定的父級包含\"\n    },\n    \"aria-roledescription\": {\n      \"description\": \"確保 aria-roledescription 僅用於具有隱式或顯式角色的元素\",\n      \"help\": \"aria-roledescription 必須位於具有語意角色的元素上\"\n    },\n    \"aria-roles\": {\n      \"description\": \"確保具有角色屬性的所有元素都使用有效值\",\n      \"help\": \"使用的 ARIA 角色必須符合有效值\"\n    },\n    \"aria-text\": {\n      \"description\": \"確保在沒有可聚焦後代的元素上使用 role=\\\"text\\\"\",\n      \"help\": \"\\\"role=text\\\"不應有可聚焦的後代\"\n    },\n    \"aria-toggle-field-name\": {\n      \"description\": \"確保每個 ARIA 切換欄位都有一個無障礙的名稱\",\n      \"help\": \"ARIA 切換欄位必須具有無障礙的名稱\"\n    },\n    \"aria-tooltip-name\": {\n      \"description\": \"確保每個 ARIA 工具提示節點都有一個無障礙的名稱\",\n      \"help\": \"ARIA 工具提示節點必須具有無障礙的名稱\"\n    },\n    \"aria-treeitem-name\": {\n      \"description\": \"確保每個 ARIA 樹項節點都有一個無障礙的名稱\",\n      \"help\": \"ARIA 樹項節點應該有一個無障礙的名稱\"\n    },\n    \"aria-valid-attr-value\": {\n      \"description\": \"確保所有 ARIA 屬性都具有有效值\",\n      \"help\": \"ARIA 屬性必須符合有效值\"\n    },\n    \"aria-valid-attr\": {\n      \"description\": \"確保以 aria- 開頭的屬性是有效的 ARIA 屬性\",\n      \"help\": \"ARIA 屬性必須符合有效名稱\"\n    },\n    \"audio-caption\": {\n      \"description\": \"確保 <audio> 元素有標題\",\n      \"help\": \"<audio> 元素必須有字幕軌道\"\n    },\n    \"autocomplete-valid\": {\n      \"description\": \"確保 autocomplete 屬性正確且適合表單字段\",\n      \"help\": \"必須正確使用 autocomplete 屬性\"\n    },\n    \"avoid-inline-spacing\": {\n      \"description\": \"確保可以使用自訂樣式表調整透過樣式屬性設定的文字間距\",\n      \"help\": \"內嵌文字間距必須可以使用自訂樣式表進行調整\"\n    },\n    \"blink\": {\n      \"description\": \"確保不使用 <blink> 元素\",\n      \"help\": \"<blink> 元素已棄用，不得使用\"\n    },\n    \"button-name\": {\n      \"description\": \"確保按鈕具有可識別的文字\",\n      \"help\": \"按鈕必須有可辨識的文字\"\n    },\n    \"bypass\": {\n      \"description\": \"確保每個頁面至少有一個機制供用戶繞過導航並直接跳到內容\",\n      \"help\": \"頁面必須具有繞過重複區塊的方法\"\n    },\n    \"color-contrast-enhanced\": {\n      \"description\": \"確保前景色和背景色之間的對比度符合 WCAG 2 AAA 增強對比度閾值\",\n      \"help\": \"元素必須滿足增強的顏色對比閾值\"\n    },\n    \"color-contrast\": {\n      \"description\": \"確保前景色和背景色之間的對比度符合 WCAG 2 AA 最小對比度閾值\",\n      \"help\": \"元素必須滿足最低顏色對比閾值\"\n    },\n    \"css-orientation-lock\": {\n      \"description\": \"確保內容不會鎖定到任何特定的顯示方向，並且內容可在所有顯示方向上操作\",\n      \"help\": \"CSS 媒體查詢不得鎖定顯示方向\"\n    },\n    \"definition-list\": {\n      \"description\": \"確保 <dl> 元素結構正確\",\n      \"help\": \"<dl> 元素只能直接包含正確排序的 <dt> 和 <dd> 群組、<script>、<template> 或 <div> 元素\"\n    },\n    \"dlitem\": {\n      \"description\": \"確保 <dt> 和 <dd> 元素包含在 <dl> 中\",\n      \"help\": \"<dt> 和 <dd> 元素必須包含在 <dl> 中\"\n    },\n    \"document-title\": {\n      \"description\": \"確保每個 HTML 頁面包含非空 <title> 元素\",\n      \"help\": \"頁面必須具有 <title> 元素以幫助導航\"\n    },\n    \"duplicate-id-active\": {\n      \"description\": \"確保活動元素的每個 id 屬性值都是唯一的\",\n      \"help\": \"活動元素的 ID 必須是唯一的\"\n    },\n    \"duplicate-id-aria\": {\n      \"description\": \"確保 ARIA 和標籤中使用的每個 id 屬性值都是唯一的\",\n      \"help\": \"ARIA 和標籤中使用的 ID 必須是唯一的\"\n    },\n    \"duplicate-id\": {\n      \"description\": \"確保每個 id 屬性值都是唯一的\",\n      \"help\": \"id 屬性值必須是唯一的\"\n    },\n    \"empty-heading\": {\n      \"description\": \"確保標題具有可識別的文字\",\n      \"help\": \"標題不能為空\"\n    },\n    \"empty-table-header\": {\n      \"description\": \"確保表格標題具有可識別的文字\",\n      \"help\": \"表格標題文字不應為空\"\n    },\n    \"focus-order-semantics\": {\n      \"description\": \"確保焦點次序中的元素具有適合互動式內容的角色\",\n      \"help\": \"焦點次序中的元素應具有適當的角色\"\n    },\n    \"form-field-multiple-labels\": {\n      \"description\": \"確保表單欄位沒有多個標籤元素\",\n      \"help\": \"表單欄位不得有多個標籤元素\"\n    },\n    \"frame-focusable-content\": {\n      \"description\": \"確保具有可聚焦內容的 <frame> 和 <iframe> 元素沒有 tabindex=-1\",\n      \"help\": \"具有可聚焦內容的框架不得具有 tabindex=-1\"\n    },\n    \"frame-tested\": {\n      \"description\": \"確保 <iframe> 和 <frame> 元素包含 axe-core 腳本\",\n      \"help\": \"框架應使用 axe-core 進行測試\"\n    },\n    \"frame-title-unique\": {\n      \"description\": \"確保 <iframe> 和 <frame> 元素包含唯一的標題屬性\",\n      \"help\": \"框架必須具有唯一的標題屬性\"\n    },\n    \"frame-title\": {\n      \"description\": \"確保 <iframe> 和 <frame> 元素具有無障礙的名稱\",\n      \"help\": \"框架必須有一個易於訪問的名稱\"\n    },\n    \"heading-order\": {\n      \"description\": \"確保標題順序在語意上正確\",\n      \"help\": \"標題等級只能增加一級\"\n    },\n    \"hidden-content\": {\n      \"description\": \"告知使用者隱藏內容\",\n      \"help\": \"應分析頁面上的隱藏內容\"\n    },\n    \"html-has-lang\": {\n      \"description\": \"確保每個 HTML 頁面都有一個 lang 屬性\",\n      \"help\": \"<html> 元素必須有 lang 屬性\"\n    },\n    \"html-lang-valid\": {\n      \"description\": \"確保 <html> 元素的 lang 屬性具有有效值\",\n      \"help\": \"<html> 元素必須具有有效的 lang 屬性值\"\n    },\n    \"html-xml-lang-mismatch\": {\n      \"description\": \"確保具有有效 lang 和 xml:lang 屬性的 HTML 元素與頁面的基本語言一致\",\n      \"help\": \"帶有 lang 和 xml:lang 的 HTML 元素必須具有相同的基本語言\"\n    },\n    \"identical-links-same-purpose\": {\n      \"description\": \"確保具有相同無障礙名稱的連結具有相似的目的\",\n      \"help\": \"具有相同名稱的連結必須具有相似的目的\"\n    },\n    \"image-alt\": {\n      \"description\": \"確保 <img> 元素具有替代文字或 \\\"none\\\" 或 \\\"presentation\\\" 角色\",\n      \"help\": \"圖片必須有替代文字\"\n    },\n    \"image-redundant-alt\": {\n      \"description\": \"確保替代圖像不會重複為文字\",\n      \"help\": \"圖像的替代文字不應作為文字重複\"\n    },\n    \"input-button-name\": {\n      \"description\": \"確保輸入按鈕具有可識別的文字\",\n      \"help\": \"輸入按鈕必須有可辨識的文字\"\n    },\n    \"input-image-alt\": {\n      \"description\": \"確保 <input type=\\\"image\\\"> 元素具有替代文字\",\n      \"help\": \"圖像按鈕必須有替代文字\"\n    },\n    \"label-content-name-mismatch\": {\n      \"description\": \"確保透過其內容標記的元素必須將其可見文字作為其無障礙名稱的一部分\",\n      \"help\": \"元素的可見文字必須作為其無障礙名稱的一部分\"\n    },\n    \"label-title-only\": {\n      \"description\": \"確保每個表單都有一個可見標籤，並且不僅僅使用隱藏標籤或標題或 aria-describedby 屬性\",\n      \"help\": \"表單應該有一個可見的標籤\"\n    },\n    \"label\": {\n      \"description\": \"確保每個表單都有一個標籤\",\n      \"help\": \"表單必須有標籤\"\n    },\n    \"landmark-banner-is-top-level\": {\n      \"description\": \"確保橫幅 landmark 位於頂層\",\n      \"help\": \"橫幅 landmark 不應包含在另一個 landmark 中\"\n    },\n    \"landmark-complementary-is-top-level\": {\n      \"description\": \"確保互補的 landmark 或旁位於頂層\",\n      \"help\": \"Aside 不應包含在另一個 landmark 中\"\n    },\n    \"landmark-contentinfo-is-top-level\": {\n      \"description\": \"確保 contentinfo landmark 位於頂層\",\n      \"help\": \"Contentinfo landmark 不應包含在另一個 landmark 中\"\n    },\n    \"landmark-main-is-top-level\": {\n      \"description\": \"確保主要 landmark 位於頂層\",\n      \"help\": \"主要 landmark 不應包含在另一個 landmark 中\"\n    },\n    \"landmark-no-duplicate-banner\": {\n      \"description\": \"確保頁面最多有一個橫幅 landmark\",\n      \"help\": \"頁面不應有超過一個橫幅 landmark\"\n    },\n    \"landmark-no-duplicate-contentinfo\": {\n      \"description\": \"確保頁面最多有一個 contentinfo landmark\",\n      \"help\": \"頁面不應包含多個內容資訊 landmark\"\n    },\n    \"landmark-no-duplicate-main\": {\n      \"description\": \"確保頁面最多有一個主要 landmark\",\n      \"help\": \"頁面不應有超過一個主要 landmark\"\n    },\n    \"landmark-one-main\": {\n      \"description\": \"確保頁面有一個主要 landmark\",\n      \"help\": \"頁面應該有一個主要 landmark\"\n    },\n    \"landmark-unique\": {\n      \"help\": \"確保 landmark 的獨特性\",\n      \"description\": \"landmark 應具有獨特的角色或角色/標籤/標題（即無障礙的名稱） 組合成唯一\"\n    },\n    \"link-in-text-block\": {\n      \"description\": \"確保連結以不依賴顏色的方式與周圍的文字區分開來\",\n      \"help\": \"連結必須能夠在不依賴顏色的情況下區分\"\n    },\n    \"link-name\": {\n      \"description\": \"確保連結具有可識別的文字\",\n      \"help\": \"連結必須有可識別的文字\"\n    },\n    \"list\": {\n      \"description\": \"確保列表結構正確\",\n      \"help\": \"<ul> 和 <ol> 只能直接包含 <li>、<script> 或 <template> 元素\"\n    },\n    \"listitem\": {\n      \"description\": \"確保 <li> 元素按語義使用\",\n      \"help\": \"<li> 元素必須包含在 <ul> 或 <ol> 中\"\n    },\n    \"marquee\": {\n      \"description\": \"確保不使用 <marquee> 元素\",\n      \"help\": \"<marquee> 元素已棄用，不得使用\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"description\": \"確保 <meta http-equiv=\\\"refresh\\\"> 不用於延遲刷新\",\n      \"help\": \"不得使用延遲刷新\"\n    },\n    \"meta-refresh\": {\n      \"description\": \"確保 <meta http-equiv=\\\"refresh\\\"> 不用於延遲刷新\",\n      \"help\": \"不得使用20小時以下的延遲刷新\"\n    },\n    \"meta-viewport-large\": {\n      \"description\": \"確保 <meta name=\\\"viewport\\\"> 可以足夠縮放量\",\n      \"help\": \"用戶應該能夠將文字縮放至 500%\"\n    },\n    \"meta-viewport\": {\n      \"description\": \"確保 <meta name=\\\"viewport\\\"> 不會停用文字縮放\",\n      \"help\": \"不得禁用縮放\"\n    },\n    \"nested-interactive\": {\n      \"description\": \"確保互動式控制不嵌套，因為螢幕閱讀器並不總是宣布它們，或者可能導致輔助軟體出現焦點問題\",\n      \"help\": \"互動控件不得嵌套\"\n    },\n    \"no-autoplay-audio\": {\n      \"description\": \"確保 <video> 或 <audio> 元素在沒有控制機制停止或靜音音訊的情況下自動播放音訊不會超過 3 秒\",\n      \"help\": \"<video> 或 <audio> 元素不得自動播放\"\n    },\n    \"object-alt\": {\n      \"description\": \"確保 <object> 元素具有替代文字\",\n      \"help\": \"<object> 元素必須有替代文字\"\n    },\n    \"p-as-heading\": {\n      \"description\": \"確保粗體、斜體文字和字體大小不用於將 <p> 元素設定為標題\",\n      \"help\": \"樣式化的 <p> 元素不得作為標題\"\n    },\n    \"page-has-heading-one\": {\n      \"description\": \"確保頁面或至少其中一個框架包含一級標題\",\n      \"help\": \"頁面應包含一級標題\"\n    },\n    \"presentation-role-conflict\": {\n      \"description\": \"標記為展示性的元素不應具有全域 ARIA 或 tabindex 以確保所有螢幕閱讀器忽略它們\",\n      \"help\": \"確保標記為展示性的元素始終被忽略\"\n    },\n    \"region\": {\n      \"description\": \"確保所有頁面內容都包含在 landmark 中\",\n      \"help\": \"所有頁面內容應包含在 landmark 中\"\n    },\n    \"role-img-alt\": {\n      \"description\": \"確保 [role=\\\"img\\\"] 元素具有替代文字\",\n      \"help\": \"[role=\\\"img\\\"] 元素必須有替代文字\"\n    },\n    \"scope-attr-valid\": {\n      \"description\": \"確保在表上正確使用範圍屬性\",\n      \"help\": \"應正確使用範圍屬性\"\n    },\n    \"scrollable-region-focusable\": {\n      \"description\": \"確保具有可滾動內容的元素可以透過鍵盤訪問\",\n      \"help\": \"可滾動區域必須具有鍵盤存取權限\"\n    },\n    \"select-name\": {\n      \"description\": \"確保選擇元素具有無障礙的名稱\",\n      \"help\": \"選擇元素必須具有無障礙的名稱\"\n    },\n    \"server-side-image-map\": {\n      \"description\": \"確保不使用伺服器端圖像映射\",\n      \"help\": \"不得使用伺服器端圖像映射\"\n    },\n    \"skip-link\": {\n      \"description\": \"確保所有跳過連結都有可聚焦的目標\",\n      \"help\": \"跳過連結目標應該存在並且可聚焦\"\n    },\n    \"svg-img-alt\": {\n      \"description\": \"確保具有 img、圖形頁面或圖形符號角色的 <svg> 元素具有無障礙的文字\",\n      \"help\": \"具有 img 角色的 <svg> 元素必須有替代文字\"\n    },\n    \"tabindex\": {\n      \"description\": \"確保 tabindex 屬性值不大於 0\",\n      \"help\": \"元素的 tabindex 不應大於零\"\n    },\n    \"table-duplicate-name\": {\n      \"description\": \"確保 <caption> 元素不包含與摘要屬性相同的文字\",\n      \"help\": \"表格不應具有相同的摘要和標題\"\n    },\n    \"table-fake-caption\": {\n      \"description\": \"確保帶有標題的表格使用 <caption> 元素\",\n      \"help\": \"資料或標題儲存格不得用於為資料表提供標題\"\n    },\n    \"target-size\": {\n      \"description\": \"確保觸摸目標有足夠的尺寸和空間\",\n      \"help\": \"所有觸摸目標必須為 24px 大，或留出足夠的空間\"\n    },\n    \"td-has-header\": {\n      \"description\": \"確保大於 3 x 3 的 <table> 中的每個非空資料單元格都具有一個或多個表標題\",\n      \"help\": \"較大 <table> 中的非空 <td> 元素必須具有關聯的表頭\"\n    },\n    \"td-headers-attr\": {\n      \"description\": \"確保表中使用 headers 屬性的每個單元格僅引用該表中的其他單元格\",\n      \"help\": \"使用 headers 屬性的表格儲存格只能引用同一個表格中的儲存格\"\n    },\n    \"th-has-data-cells\": {\n      \"description\": \"確保 <th> 元素和具有 role=columnheader/rowheader 的元素具有它們所描述的資料單元\",\n      \"help\": \"資料表中的表標題必須引用資料儲存格\"\n    },\n    \"valid-lang\": {\n      \"description\": \"確保 lang 屬性具有有效值\",\n      \"help\": \"lang 屬性必須具有有效值\"\n    },\n    \"video-caption\": {\n      \"description\": \"確保 <video> 元素有標題\",\n      \"help\": \"<video> 元素必須有標題\"\n    }\n  },\n  \"checks\": {\n    \"abstractrole\": {\n      \"pass\": \"不使用抽象角色\",\n      \"fail\": {\n        \"singular\": \"抽象角色不能直接使用：${data.values}\",\n        \"plural\": \"抽象角色不能直接使用：${data.values}\"\n      }\n    },\n    \"aria-allowed-attr\": {\n      \"pass\": \"ARIA 屬性正確用於定義的角色\",\n      \"fail\": {\n        \"singular\": \"不允許使用 ARIA 屬性：${data.values}\",\n        \"plural\": \"不允許使用 ARIA 屬性：${data.values}\"\n      },\n      \"incomplete\": \"檢查是否在此元素上忽略 ARIA 屬性沒有問題：${data.values}\"\n    },\n    \"aria-allowed-role\": {\n      \"pass\": \"給定元素允許 ARIA 角色\",\n      \"fail\": {\n        \"singular\": \"給定元素不允許 ARIA 角色 ${data.values}\",\n        \"plural\": \"給定元素不允許 ARIA 角色 ${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"當元素可見時，必須刪除 ARIA 角色 ${data.values}，因為該元素不允許這樣做\",\n        \"plural\": \"當元素可見時，必須刪除 ARIA 角色 ${data.values}，因為元素不允許使用它們\"\n      }\n    },\n    \"aria-busy\": {\n      \"pass\": \"元素具有 aria-busy 屬性\",\n      \"fail\": \"元素在顯示載入程式時使用 aria-busy=\\\"true\\\"\"\n    },\n    \"aria-conditional-attr\": {\n      \"pass\": \"允許使用 ARIA 屬性\",\n      \"fail\": {\n        \"checkbox\": \"刪除 aria-checked，或將其設為\\\"${data.checkState}\\\"以符合真實的複選框狀態\",\n        \"rowSingular\": \"樹狀網格行支援此屬性，但 ${data.ownerRole} 不支援：${data.invalidAttrs}\",\n        \"rowPlural\": \"樹網格行支援這些屬性，但 ${data.ownerRole} 不支援：${data.invalidAttrs}\"\n      }\n    },\n    \"aria-errormessage\": {\n      \"pass\": \"aria-errormessage 存在，並引用使用受支援的 aria-errormessage 技術的螢幕閱讀器可見的元素\",\n      \"fail\": {\n        \"singular\": \"aria-errormessage 值 `${data.values}` 必須使用某種技巧來通告訊息（例如：aria-live、aria-describedby、role=alert 等）\",\n        \"plural\": \"aria-errormessage 值 `${data.values}` 必須使用某種技巧來通告訊息（例如：aria-live、aria-describedby、role=alert 等）\",\n        \"hidden\": \"aria-errormessage 值 `${data.values}` 無法引用隱藏元素\"\n      },\n      \"incomplete\": {\n        \"singular\": \"確保 aria-errormessage 值 `${data.values}` 引用現有元素\",\n        \"plural\": \"確保 aria-errormessage 值 `${data.values}` 引用現有元素\",\n        \"idrefs\": \"無法確定頁面上是否存在 aria-errormessage 元素：${data.values}\"\n      }\n    },\n    \"aria-hidden-body\": {\n      \"pass\": \"<body> 不存在 aria-hidden 屬性\",\n      \"fail\": \"aria-hidden=true 不應出現在 <body>\"\n    },\n    \"aria-level\": {\n      \"pass\": \"aria 等級值有效\",\n      \"incomplete\": \"並非所有螢幕閱讀器和瀏覽器組合都支援大於 6 的 aria-level 值\"\n    },\n    \"aria-prohibited-attr\": {\n      \"pass\": \"允許使用 ARIA 屬性\",\n      \"fail\": {\n        \"hasRolePlural\": \"${data.prohibited} 屬性不能與角色「${data.role}」一起使用\",\n        \"hasRoleSingular\": \"${data.prohibited} 屬性不能與角色「${data.role}」一起使用\",\n        \"noRolePlural\": \"${data.prohibited} 屬性不能在沒有有效角色屬性的 ${data.nodeName} 上使用\",\n        \"noRoleSingular\": \"${data.prohibited} 屬性不能在沒有有效角色屬性的 ${data.nodeName} 上使用\"\n      },\n      \"incomplete\": {\n        \"hasRoleSingular\": \"角色「${data.role}」較不支援 ${data.prohibited} 屬性\",\n        \"hasRolePlural\": \"角色「${data.role}」不能很好地支援 ${data.prohibited} 屬性\",\n        \"noRoleSingular\": \"沒有有效角色屬性的 ${data.nodeName} 不能很好地支援 ${data.prohibited} 屬性\",\n        \"noRolePlural\": \"沒有有效角色屬性的 ${data.nodeName} 不能很好地支援 ${data.prohibited} 屬性\"\n      }\n    },\n    \"aria-required-attr\": {\n      \"pass\": \"所有必需的 ARIA 屬性均已存在\",\n      \"fail\": {\n        \"singular\": \"所需的 ARIA 屬性不存在：${data.values}\",\n        \"plural\": \"所需的 ARIA 屬性不存在：${data.values}\"\n      }\n    },\n    \"aria-required-children\": {\n      \"pass\": \"子元素需要 ARIA 角色\",\n      \"fail\": {\n        \"singular\": \"子元素所需的 ARIA 角色不存在：${data.values}\",\n        \"plural\": \"子元素所需的 ARIA 角色不存在：${data.values}\",\n        \"unallowed\": \"元素具有不允許的子元素：${data.values}\"\n      },\n      \"incomplete\": {\n        \"singular\": \"子元素期望加入 ARIA 角色：${data.values}\",\n        \"plural\": \"子元素期望加入 ARIA 角色：${data.values}\"\n      }\n    },\n    \"aria-required-parent\": {\n      \"pass\": \"父元素需要存在 ARIA 角色\",\n      \"fail\": {\n        \"singular\": \"父元素所需的 ARIA 角色不存在：${data.values}\",\n        \"plural\": \"父元素所需的 ARIA 角色不存在：${data.values}\"\n      }\n    },\n    \"aria-roledescription\": {\n      \"pass\": \"用於支援的語意角色的 aria-roledescription\",\n      \"incomplete\": \"檢查 aria-roledescription 是否由受支援的螢幕閱讀器宣布\",\n      \"fail\": \"賦予元素一個支持 'aria-roledescription' 的角色\"\n    },\n    \"aria-unsupported-attr\": {\n      \"pass\": \"支援 ARIA 屬性\",\n      \"fail\": \"ARIA 屬性在螢幕閱讀器和輔助軟體並未被廣泛支援：${data.values}\"\n    },\n    \"aria-valid-attr-value\": {\n      \"pass\": \"ARIA 屬性值有效\",\n      \"fail\": {\n        \"singular\": \"無效的 ARIA 屬性值：${data.values}\",\n        \"plural\": \"無效的 ARIA 屬性值：${data.values}\"\n      },\n      \"incomplete\": {\n        \"noId\": \"頁面上不存在 ARIA 屬性元素 ID：${data.needsReview}\",\n        \"noIdShadow\": \"ARIA 屬性元素 ID 在頁面上不存在或不同影子 DOM 樹的後代：${data.needsReview}\",\n        \"ariaCurrent\": \"ARIA 屬性值無效，將被視為「aria-current=true」：${data.needsReview}\",\n        \"idrefs\": \"無法確定頁面上是否存在 ARIA 屬性元素 ID：${data.needsReview}\",\n        \"empty\": \"ARIA 屬性值在空時被忽略：${data.needsReview}\"\n      }\n    },\n    \"aria-valid-attr\": {\n      \"pass\": \"ARIA 屬性名稱有效\",\n      \"fail\": {\n        \"singular\": \"無效的 ARIA 屬性名稱：${data.values}\",\n        \"plural\": \"無效的 ARIA 屬性名稱：${data.values}\"\n      }\n    },\n    \"braille-label-equivalent\": {\n      \"pass\": \"aria-braillelabel 用於具有無障礙文字的元素\",\n      \"fail\": \"aria-braillelabel 用於沒有無障礙文字的元素\",\n      \"incomplete\": \"無法計算無障礙的文字\"\n    },\n    \"braille-roledescription-equivalent\": {\n      \"pass\": \"aria-brailleroledescription 用於具有 aria-roledescription 的元素\",\n      \"fail\": {\n        \"noRoleDescription\": \"aria-brailleroledescription 用於沒有 aria-roledescription 的元素\",\n        \"emptyRoleDescription\": \"aria-brailleroledescription 用於具有空 aria-roledescription 的元素\"\n      }\n    },\n    \"deprecatedrole\": {\n      \"pass\": \"ARIA 角色並未棄用\",\n      \"fail\": \"使用的角色已棄用：${data}\"\n    },\n    \"fallbackrole\": {\n      \"pass\": \"僅使用一個角色值\",\n      \"fail\": \"僅使用一個角色值，因為舊版瀏覽器不支援後備角色\",\n      \"incomplete\": \"僅使用角色 'presentation' 或 'none'，因為它們是同義詞\"\n    },\n    \"has-global-aria-attribute\": {\n      \"pass\": {\n        \"singular\": \"元素具有全域 ARIA 屬性：${data.values}\",\n        \"plural\": \"元素具有全域 ARIA 屬性：${data.values}\"\n      },\n      \"fail\": \"元素沒有全域 ARIA 屬性\"\n    },\n    \"has-widget-role\": {\n      \"pass\": \"元素具有小部件角色\",\n      \"fail\": \"元素沒有小部件角色\"\n    },\n    \"invalidrole\": {\n      \"pass\": \"ARIA 角色有效\",\n      \"fail\": {\n        \"singular\": \"角色必須是有效的 ARIA 角色之一：${data.values}\",\n        \"plural\": \"角色必須是有效的 ARIA 角色之一：${data.values}\"\n      }\n    },\n    \"is-element-focusable\": {\n      \"pass\": \"元素是可聚焦的\",\n      \"fail\": \"元素不可聚焦\"\n    },\n    \"no-implicit-explicit-label\": {\n      \"pass\": \"<label> 和無障礙名稱之間沒有不匹配\",\n      \"incomplete\": \"檢查 <label> 不需要是 ARIA ${data} 欄位的名稱\"\n    },\n    \"unsupportedrole\": {\n      \"pass\": \"支援 ARIA 角色\",\n      \"fail\": \"所使用的角色在螢幕閱讀器和輔助軟體並未被廣泛支持：${data}\"\n    },\n    \"valid-scrollable-semantics\": {\n      \"pass\": \"元素對於焦點次序中的元素具有有效語意\",\n      \"fail\": \"對於焦點次序中的元素，元素具有無效語意\"\n    },\n    \"color-contrast-enhanced\": {\n      \"pass\": \"元素具有足夠的顏色對比 ${data.contrastRatio}\",\n      \"fail\": {\n        \"default\": \"元素的 ${data.contrastRatio} 顏色對比不足（前景色：${data.fgColor}，背景色：${data.bgColor}，字體大小：${data.fontSize}，字體粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"元素的前景色與陰影顏色之間的 ${data.contrastRatio} 顏色對比不足（前景色：${data.fgColor}，文字陰影顏色：${data.shadowColor}，字體大小：${data.fontSize} ，字型粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"元素的陰影顏色和背景顏色之間的 ${data.contrastRatio} 顏色對比不足（文字陰影顏色：${data.shadowColor}，背景顏色：${data.bgColor}，字體大小：${data.fontSize } ，字型粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"無法確定對比度\",\n        \"bgImage\": \"由於背景圖像而無法確定元素的背景顏色\",\n        \"bgGradient\": \"由於背景漸變，無法確定元素的背景顏色\",\n        \"imgNode\": \"無法確定元素的背景顏色，因為元素包含影像節點\",\n        \"bgOverlap\": \"無法確定元素的背景顏色，因為它與另一個元素重疊\",\n        \"fgAlpha\": \"由於 Alpha 透明度，無法確定元素的前景色\",\n        \"elmPartiallyObscured\": \"無法確定元素的背景顏色，因為它被另一個元素部分遮擋\",\n        \"elmPartiallyObscuring\": \"無法確定元素的背景顏色，因為它與其他元素部分重疊\",\n        \"outsideViewport\": \"無法確定元素的背景顏色，因為它位於視窗之外\",\n        \"equalRatio\": \"元素與背景的對比為 1:1\",\n        \"shortTextContent\": \"元素內容太短，無法判斷是否為實際文字內容\",\n        \"nonBmp\": \"元素內容僅包含非文字字符\",\n        \"pseudoContent\": \"由於偽元素，無法確定元素的背景顏色\"\n      }\n    },\n    \"color-contrast\": {\n      \"pass\": {\n        \"default\": \"元素具有足夠的顏色對比 ${data.contrastRatio}\",\n        \"hidden\": \"元素被隱藏\"\n      },\n      \"fail\": {\n        \"default\": \"元素的 ${data.contrastRatio} 顏色對比不足（前景色：${data.fgColor}，背景色：${data.bgColor}，字體大小：${data.fontSize}，字體粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\",\n        \"fgOnShadowColor\": \"元素的前景色與陰影顏色之間的 ${data.contrastRatio} 顏色對比不足（前景色：${data.fgColor}，文字陰影顏色：${data.shadowColor}，字體大小：${data.fontSize} ，字型粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\",\n        \"shadowOnBgColor\": \"元素的陰影顏色和背景顏色之間的 ${data.contrastRatio} 顏色對比不足（文字陰影顏色：${data.shadowColor}，背景顏色：${data.bgColor}，字體大小：${data.fontSize} ，字型粗細：${data.fontWeight}），預期對比 ${data.expectedContrastRatio}\"\n      },\n      \"incomplete\": {\n        \"default\": \"無法確定對比度\",\n        \"bgImage\": \"由於背景圖像而無法確定元素的背景顏色\",\n        \"bgGradient\": \"由於背景漸變，無法確定元素的背景顏色\",\n        \"imgNode\": \"無法確定元素的背景顏色，因為元素包含影像節點\",\n        \"bgOverlap\": \"無法確定元素的背景顏色，因為它與另一個元素重疊\",\n        \"complexTextShadows\": \"無法確定元素的對比度，因為它使用複雜的文字陰影\",\n        \"fgAlpha\": \"由於 Alpha 透明度，無法確定元素的前景色\",\n        \"elmPartiallyObscured\": \"無法確定元素的背景顏色，因為它被另一個元素部分遮擋\",\n        \"elmPartiallyObscuring\": \"無法確定元素的背景顏色，因為它與其他元素部分重疊\",\n        \"outsideViewport\": \"無法確定元素的背景顏色，因為它位於視窗之外\",\n        \"equalRatio\": \"元素與背景的對比為 1:1\",\n        \"shortTextContent\": \"元素內容太短，無法判斷是否為實際文字內容\",\n        \"nonBmp\": \"元素內容僅包含非文字字符\",\n        \"pseudoContent\": \"由於偽元素，無法確定元素的背景顏色\"\n      }\n    },\n    \"link-in-text-block-style\": {\n      \"pass\": \"可以透過視覺樣式將連結與周圍的文字區分開來\",\n      \"incomplete\": {\n        \"default\": \"檢查連結是否需要樣式以將其與附近的文字區分開\",\n        \"pseudoContent\": \"檢查連結的偽樣式是否足以將其與周圍的文字區分開來\"\n      },\n      \"fail\": \"該連結沒有樣式（例如下劃線）來將其與周圍的文字區分開來\"\n    },\n    \"link-in-text-block\": {\n      \"pass\": \"連結可以透過顏色以外的其他方式與周圍的文字區分開來\",\n      \"fail\": {\n        \"fgContrast\": \"此連結與周圍文字的顏色對比 ${data.contrastRatio}:1 不足，(最小對比為 ${data.requiredContrastRatio}:1，連結文字：${data.nodeColor}，周圍文字：${data.parentColor}）\",\n        \"bgContrast\": \"連結背景的顏色對比 ${data.contrastRatio} 不足（最小對比為 ${data.requiredContrastRatio}:1，連結背景顏色：${data.nodeBackgroundColor}，周圍背景顏色：${data.parentBackgroundColor}）\"\n      },\n      \"incomplete\": {\n        \"default\": \"無法確定元素的前景對比度\",\n        \"bgContrast\": \"無法確定元素的背景對比度\",\n        \"bgImage\": \"由於背景影像而無法確定元素的對比度\",\n        \"bgGradient\": \"由於背景漸變，無法確定元素的對比\",\n        \"imgNode\": \"無法確定元素的對比度，因為元素包含影像節點\",\n        \"bgOverlap\": \"由於元素重疊，無法確定元素的對比\"\n      }\n    },\n    \"autocomplete-appropriate\": {\n      \"pass\": \"autocomplete 值位於適當的元素上\",\n      \"fail\": \"autocomplete 值不適合此類輸入\"\n    },\n    \"autocomplete-valid\": {\n      \"pass\": \"autocomplete 屬性的格式正確\",\n      \"fail\": \"autocomplete 屬性的格式不正確\"\n    },\n    \"accesskeys\": {\n      \"pass\": \"accesskey 屬性值是唯一的\",\n      \"fail\": \"頁面有多個元素具有相同的 accesskey\"\n    },\n    \"focusable-content\": {\n      \"pass\": \"元素包含可聚焦元素\",\n      \"fail\": \"元素應該具有可聚焦的內容\"\n    },\n    \"focusable-disabled\": {\n      \"pass\": \"元素內不包含可聚焦元素\",\n      \"incomplete\": \"檢查可聚焦元素是否立即移動焦點指示器\",\n      \"fail\": \"應停用可聚焦內容或從 DOM 中刪除可聚焦內容\"\n    },\n    \"focusable-element\": {\n      \"pass\": \"元素可聚焦\",\n      \"fail\": \"元素應該是可聚焦的\"\n    },\n    \"focusable-modal-open\": {\n      \"pass\": \"模式開啟時沒有可聚焦的元素\",\n      \"incomplete\": \"檢查可聚焦元素在目前狀態下是否無法選項\"\n    },\n    \"focusable-no-name\": {\n      \"pass\": \"元素未按 Tab 鍵順序或具有無障礙的文字\",\n      \"fail\": \"元素按 Tab 鍵順序排列，並且沒有可訪問的文字\",\n      \"incomplete\": \"無法確定元素是否具有無障礙的名稱\"\n    },\n    \"focusable-not-tabbable\": {\n      \"pass\": \"元素內不包含可聚焦元素\",\n      \"incomplete\": \"檢查可聚焦元素是否立即移動焦點指示器\",\n      \"fail\": \"可聚焦內容應具有 tabindex=\\\"-1\\\" 或從 DOM 中刪除\"\n    },\n    \"frame-focusable-content\": {\n      \"pass\": \"元素沒有可聚焦的後代\",\n      \"fail\": \"元素具有可聚焦的後代\",\n      \"incomplete\": \"無法確定元素是否有後代\"\n    },\n    \"landmark-is-top-level\": {\n      \"pass\": \"${data.role} landmark 位於頂層\",\n      \"fail\": \"${data.role} landmark 包含在另一個 landmark\"\n    },\n    \"no-focusable-content\": {\n      \"pass\": \"元素沒有可聚焦的後代\",\n      \"fail\": {\n        \"default\": \"元素具有可聚焦的後代\",\n        \"notHidden\": \"在互動式控制項內的元素上使用負 tabindex 不會阻止輔助軟體聚焦該元素（即使使用 aria-hidden=\\\"true\\\"）\"\n      },\n      \"incomplete\": \"無法確定元素是否有後代\"\n    },\n    \"page-has-heading-one\": {\n      \"pass\": \"頁面至少有一個一級標題\",\n      \"fail\": \"頁面必須有一級標題\"\n    },\n    \"page-has-main\": {\n      \"pass\": \"頁面至少有一個主要 landmark\",\n      \"fail\": \"頁面沒有主要 landmark\"\n    },\n    \"page-no-duplicate-banner\": {\n      \"pass\": \"頁面不包含超過一個橫幅 landmark\",\n      \"fail\": \"頁面具有多個橫幅 landmark\"\n    },\n    \"page-no-duplicate-contentinfo\": {\n      \"pass\": \"頁面不包含多個 contentinfo landmark\",\n      \"fail\": \"頁面具有多個 contentinfo landmark\"\n    },\n    \"page-no-duplicate-main\": {\n      \"pass\": \"頁面沒有超過一個主要 landmark\",\n      \"fail\": \"頁面有多個主要 landmark\"\n    },\n    \"tabindex\": {\n      \"pass\": \"元素的 tabindex 不大於 0\",\n      \"fail\": \"元素的 tabindex 大於 0\"\n    },\n    \"alt-space-value\": {\n      \"pass\": \"元素具有有效的 alt 屬性值\",\n      \"fail\": \"元素具有僅包含空格字元的 alt 屬性，所有螢幕閱讀器都不會忽略該屬性\"\n    },\n    \"duplicate-img-label\": {\n      \"pass\": \"元素內的文字沒有和元素內的 <img> 替代文字重複\",\n      \"fail\": \"元素內的文字和元素內的 <img> 替代文字重複\"\n    },\n    \"explicit-label\": {\n      \"pass\": \"表單有一個明確的標籤文字 <label>\",\n      \"fail\": \"表單沒有明確的標籤文字 <label>\",\n      \"incomplete\": \"無法確定表單是否具有明確的標籤文字 <label>\"\n    },\n    \"help-same-as-label\": {\n      \"pass\": \"說明文字（title 或 aria-describedby） 不重複標籤文字\",\n      \"fail\": \"說明文字（title 或 aria-describedby） 文字與標籤文字相同\"\n    },\n    \"hidden-explicit-label\": {\n      \"pass\": \"表單有一個可見且明確的標籤文字 <label>\",\n      \"fail\": \"表單具有隱藏且明確的標籤文字 <label>\",\n      \"incomplete\": \"無法確定表單是否具有隱藏且明確的標籤文字 <label>\"\n    },\n    \"implicit-label\": {\n      \"pass\": \"表單有隱藏標籤文字 <label>\",\n      \"fail\": \"表單沒有隱藏標籤文字 <label>\",\n      \"incomplete\": \"無法確定表單是否具有隱藏標籤文字 <label>\"\n    },\n    \"label-content-name-mismatch\": {\n      \"pass\": \"元素包含可見文字作為其無障礙名稱的一部分\",\n      \"fail\": \"元素內的文字不包含在無障礙名稱中\"\n    },\n    \"multiple-label\": {\n      \"pass\": \"表單欄位沒有多個標籤元素\",\n      \"incomplete\": \"輔助軟體並未廣泛支援多個標籤元素，確保第一個標籤包含所有必要的資訊\"\n    },\n    \"title-only\": {\n      \"pass\": \"表單不僅僅使用 title 屬性作為其標籤\",\n      \"fail\": \"僅使用標題來產生表單的標籤\"\n    },\n    \"landmark-is-unique\": {\n      \"pass\": \"landmark 必須具有唯一的角色或由角色/標籤/標題（即無障礙的名稱）組合成唯一\",\n      \"fail\": \"landmark 必須具有唯一的 aria-label、aria-labelledby 或標題，以使 landmark 易於區分\"\n    },\n    \"has-lang\": {\n      \"pass\": \"<html> 元素有一個 lang 屬性\",\n      \"fail\": {\n        \"noXHTML\": \"xml:lang 屬性在 HTML 頁面上無效，請使用 lang 屬性\",\n        \"noLang\": \"<html> 元素沒有 lang 屬性\"\n      }\n    },\n    \"valid-lang\": {\n      \"pass\": \"lang 屬性的值包含在有效語言清單中\",\n      \"fail\": \"lang 屬性的值未包含在有效語言清單中\"\n    },\n    \"xml-lang-mismatch\": {\n      \"pass\": \"Lang 和 xml:lang 屬性具有相同的基本語言\",\n      \"fail\": \"Lang 和 xml:lang 屬性沒有相同的基本語言\"\n    },\n    \"dlitem\": {\n      \"pass\": \"描述列表項目有一個 <dl> 父元素\",\n      \"fail\": \"說明清單項目沒有 <dl> 父元素\"\n    },\n    \"listitem\": {\n      \"pass\": \"列表項目具有 <ul>、<ol> 或 role=\\\"list\\\" 父元素\",\n      \"fail\": {\n        \"default\": \"列表項目的父元素不是 <ul> 或 <ol>\",\n        \"roleNotValid\": \"列表項目的父元素不是 <ul> 或 <ol> ，且列表項目的父元素沒有屬性 role=\\\"list\\\"\"\n      }\n    },\n    \"only-dlitems\": {\n      \"pass\": \"dl 元素僅具有允許在內部的直接子元素； <dt>、<dd> 或 <div> 元素\",\n      \"fail\": \"dl 元素有不允許的直接子元素：${data.values}\"\n    },\n    \"only-listitems\": {\n      \"pass\": \"列表元素僅允許在 <li> 元素內具有直接子元素\",\n      \"fail\": \"列表元素具有不允許的直接子元素：${data.values}\"\n    },\n    \"structured-dlitems\": {\n      \"pass\": \"當不為空時，元素同時具有 <dt> 和 <dd> 元素\",\n      \"fail\": \"當不為空時，元素沒有至少一個 <dt> 元素後面跟著至少一個 <dd> 元素\"\n    },\n    \"caption\": {\n      \"pass\": \"多媒體元素有一個字幕軌道\",\n      \"incomplete\": \"檢查標題是否適用於該元素\"\n    },\n    \"frame-tested\": {\n      \"pass\": \"iframe 使用 axe-core 進行了測試\",\n      \"fail\": \"iframe 無法使用 axe-core 進行測試\",\n      \"incomplete\": \"iframe 仍需使用 axe-core 進行測試\"\n    },\n    \"no-autoplay-audio\": {\n      \"pass\": \"<video> 或 <audio> 輸出音訊的時間不會超過允許的持續時間或具有控制機制\",\n      \"fail\": \"<video> 或 <audio> 輸出音訊的時間超過允許的持續時間，且沒有控制機制\",\n      \"incomplete\": \"檢查 <video> 或 <audio> 輸出音訊的時間是否超過允許的持續時間或提供控制機制\"\n    },\n    \"css-orientation-lock\": {\n      \"pass\": \"顯示可操作，方向鎖定不存在\",\n      \"fail\": \"CSS 方向鎖定已套用，導致顯示無法操作\",\n      \"incomplete\": \"CSS 方向鎖定無法確定\"\n    },\n    \"meta-viewport-large\": {\n      \"pass\": \"<meta> 標籤不會阻止行動裝置上的顯著縮放\",\n      \"fail\": \"<meta> 標籤限制行動裝置上的縮放\"\n    },\n    \"meta-viewport\": {\n      \"pass\": \"<meta> 標籤不會停用行動裝置上的縮放\",\n      \"fail\": \"<meta> 標記上的 ${data} 停用行動裝置上的縮放\"\n    },\n    \"target-offset\": {\n      \"pass\": \"目標與其最近的鄰近的元素有足夠的空間，安全可點選空間的距離為 ${data.closestOffset}px，至少為 ${data.minOffset}px\",\n      \"fail\": \"目標與其最近鄰近的元素之間的空間不足，安全可點擊空間的距離為 ${data.closestOffset}px，而不是至少 ${data.minOffset}px\",\n      \"incomplete\": {\n        \"default\": \"tabindex 為負的元素與其最近鄰近的元素的空間不足，安全可點擊空間的距離為 ${data.closestOffset}px，而不是至少 ${data.minOffset}px，這是一個目標嗎？\",\n        \"nonTabbableNeighbor\": \"目標與其最近鄰近的元素之間的空間不足，安全可點擊空間的距離為 ${data.closestOffset}px，而不是至少 ${data.minOffset}px，鄰近的元素是目標嗎？\"\n      }\n    },\n    \"target-size\": {\n      \"pass\": {\n        \"default\": \"控制的內容具有足夠的大小（${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data.minSize}px）\",\n        \"obscured\": \"控制的內容被忽略，因為它被完全遮擋，因此不可點擊\"\n      },\n      \"fail\": {\n        \"default\": \"目標大小不足（${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data.minSize}px）\",\n        \"partiallyObscured\": \"目標尺寸不足，因為它被部分遮蔽（最小空間為 ${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data.minSize}px）\"\n      },\n      \"incomplete\": {\n        \"default\": \"tabindex 為負的元素大小不足（${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data.minSize}px），這是一個目標嗎？\",\n        \"contentOverflow\": \"由於內容溢出，無法準確確定元素大小\",\n        \"partiallyObscured\": \"tabindex 為負的元素大小不足，因為它被部分遮蔽（最小空間為 ${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data.minSize}像素），這是一個目標嗎？\",\n        \"partiallyObscuredNonTabbable\": \"目標大小不足，因為它被負 tabindex 的鄰近的元素部分遮擋（最小空間為 ${data.width}px x ${data.height}px，應至少為 ${data.minSize}px x ${data .minSize }px），相鄰的元素是目標嗎？\"\n      }\n    },\n    \"header-present\": {\n      \"pass\": \"頁面有標題\",\n      \"fail\": \"頁面沒有標題\"\n    },\n    \"heading-order\": {\n      \"pass\": \"標題順序有效\",\n      \"fail\": \"標題順序無效\",\n      \"incomplete\": \"無法確定前一個標題\"\n    },\n    \"identical-links-same-purpose\": {\n      \"pass\": \"沒有其他具有相同名稱且轉到不同 URL 連結\",\n      \"incomplete\": \"檢查連結是否有相同的目的，或是否故意含糊不清\"\n    },\n    \"internal-link-present\": {\n      \"pass\": \"找到有效的跳過連結\",\n      \"fail\": \"找不到有效的跳過連結\"\n    },\n    \"landmark\": {\n      \"pass\": \"頁面有一個 landmark 區域\",\n      \"fail\": \"頁面沒有 landmark 區域\"\n    },\n    \"meta-refresh-no-exceptions\": {\n      \"pass\": \"<meta> 標籤不會立即重新整理頁面\",\n      \"fail\": \"<meta> 標籤強制頁面定時刷新\"\n    },\n    \"meta-refresh\": {\n      \"pass\": \"<meta> 標籤不會立即重新整理頁面\",\n      \"fail\": \"<meta> 標籤強制頁面定時刷新（小於20小時）\"\n    },\n    \"p-as-heading\": {\n      \"pass\": \"<p> 元素的樣式不為標題\",\n      \"fail\": \"應使用標題元素而不是樣式化的 <p> 元素\",\n      \"incomplete\": \"無法確定 <p> 元素是否設定為標題樣式\"\n    },\n    \"region\": {\n      \"pass\": \"所有頁面內容均包含在 landmark 中\",\n      \"fail\": \"某些頁面內容不包含在 landmark 中\"\n    },\n    \"skip-link\": {\n      \"pass\": \"跳過連結目標存在\",\n      \"incomplete\": \"跳過連結目標在啟動時應該變得可見\",\n      \"fail\": \"無跳過連結目標\"\n    },\n    \"unique-frame-title\": {\n      \"pass\": \"元素的標題屬性是唯一的\",\n      \"fail\": \"元素的 title 屬性不唯一\"\n    },\n    \"duplicate-id-active\": {\n      \"pass\": \"頁面沒有共用相同 id 屬性的活動元素\",\n      \"fail\": \"頁面具有相同 id 屬性的活動元素：${data}\"\n    },\n    \"duplicate-id-aria\": {\n      \"pass\": \"頁面沒有使用 ARIA 引用的元素或共享相同 id 屬性的標籤\",\n      \"fail\": \"頁面具有使用 ARIA 引用的多個具有相同 id 屬性的元素：${data}\"\n    },\n    \"duplicate-id\": {\n      \"pass\": \"頁面沒有共享相同 id 屬性的靜態元素\",\n      \"fail\": \"頁面有多個具有相同 id 屬性的靜態元素：${data}\"\n    },\n    \"aria-label\": {\n      \"pass\": \"aria-label 屬性存在且不為空\",\n      \"fail\": \"aria-label 屬性不存在或為空\"\n    },\n    \"aria-labelledby\": {\n      \"pass\": \"aria-labelledby 屬性存在並引用螢幕閱讀器可見的元素\",\n      \"fail\": \"aria-labelledby 屬性不存在、引用不存在的元素或引用為空的元素\",\n      \"incomplete\": \"確保 aria-labelledby 引用現有元素\"\n    },\n    \"avoid-inline-spacing\": {\n      \"pass\": \"未指定影響文字間距的帶有「!important」的內聯樣式\",\n      \"fail\": {\n        \"singular\": \"從內聯樣式 ${data.values} 中刪除 '!important'，因為大多數瀏覽器不支援覆寫它\",\n        \"plural\": \"從內聯樣式 ${data.values} 中刪除 '!important'，因為大多數瀏覽器不支援覆寫它\"\n      }\n    },\n    \"button-has-visible-text\": {\n      \"pass\": \"元素具有螢幕閱讀器可見的內部文字\",\n      \"fail\": \"元素沒有螢幕閱讀器可見的內部文字\",\n      \"incomplete\": \"無法確定元素是否有子元素\"\n    },\n    \"doc-has-title\": {\n      \"pass\": \"文檔有一個非空的 <title> 元素\",\n      \"fail\": \"頁面沒有非空 <title> 元素\"\n    },\n    \"exists\": {\n      \"pass\": \"元素不存在\",\n      \"incomplete\": \"元素存在\"\n    },\n    \"has-alt\": {\n      \"pass\": \"元素具有 alt 屬性\",\n      \"fail\": \"元素沒有 alt 屬性\"\n    },\n    \"has-visible-text\": {\n      \"pass\": \"元素具有螢幕閱讀器可見的文字\",\n      \"fail\": \"元素沒有螢幕閱讀器可見的文字\",\n      \"incomplete\": \"無法確定元素是否有子元素\"\n    },\n    \"important-letter-spacing\": {\n      \"pass\": \"style 屬性中的字母間距未設定為 !important，或滿足最小值\",\n      \"fail\": \"樣式屬性中的字母間距不得使用 !important，或位於 ${data.minValue}em（目前 ${data.value}em）\"\n    },\n    \"important-line-height\": {\n      \"pass\": \"style 屬性中的line-height沒有設定為!important，或滿足最小值\",\n      \"fail\": \"style 屬性中的 line-height 不得使用 !important，或位於 ${data.minValue}em（目前 ${data.value}em）\"\n    },\n    \"important-word-spacing\": {\n      \"pass\": \"style 屬性中的 word-spacing 未設定為 !important，或滿足最小值\",\n      \"fail\": \"style 屬性中的字間距不得使用 !important，或位於 ${data.minValue}em（目前 ${data.value}em）\"\n    },\n    \"is-on-screen\": {\n      \"pass\": \"元素不可見\",\n      \"fail\": \"元素可見\"\n    },\n    \"non-empty-alt\": {\n      \"pass\": \"元素具有非空 alt 屬性\",\n      \"fail\": {\n        \"noAttr\": \"元素沒有 alt 屬性\",\n        \"emptyAttr\": \"元素具有空的 alt 屬性\"\n      }\n    },\n    \"non-empty-if-present\": {\n      \"pass\": {\n        \"default\": \"元素沒有 value 屬性\",\n        \"has-label\": \"元素具有非空值屬性\"\n      },\n      \"fail\": \"元素有 value 屬性且 value 屬性為空\"\n    },\n    \"non-empty-placeholder\": {\n      \"pass\": \"元素具有佔位符屬性\",\n      \"fail\": {\n        \"noAttr\": \"元素沒有佔位符屬性\",\n        \"emptyAttr\": \"元素具有空佔位符屬性\"\n      }\n    },\n    \"non-empty-title\": {\n      \"pass\": \"元素有一個標題屬性\",\n      \"fail\": {\n        \"noAttr\": \"元素沒有 title 屬性\",\n        \"emptyAttr\": \"元素的標題屬性為空\"\n      }\n    },\n    \"non-empty-value\": {\n      \"pass\": \"元素具有非空值屬性\",\n      \"fail\": {\n        \"noAttr\": \"元素沒有 value 屬性\",\n        \"emptyAttr\": \"元素具有空值屬性\"\n      }\n    },\n    \"presentational-role\": {\n      \"pass\": \"元素的預設語意被 role=\\\"${data.role}\\\" 覆蓋\",\n      \"fail\": {\n        \"default\": \"元素的預設語意未被 role=\\\"none\\\" 或 role=\\\"presentation\\\" 覆蓋\",\n        \"globalAria\": \"元素的角色不是表現性的，因為它具有全域 ARIA 屬性\",\n        \"focusable\": \"元素的作用不是表現性的，因為它是可聚焦的\",\n        \"both\": \"元素的角色不是表現性的，因為它具有全局 ARIA 屬性並且可聚焦\",\n        \"iframe\": \"在具有演示角色的 ${data.nodeName} 元素上使用「title」屬性在螢幕閱讀器之間的行為不一致\"\n      }\n    },\n    \"role-none\": {\n      \"pass\": \"元素的預設語意被 role=\\\"none\\\" 覆蓋\",\n      \"fail\": \"元素的預設語意未被 role=\\\"none\\\" 覆蓋\"\n    },\n    \"role-presentation\": {\n      \"pass\": \"元素的預設語意被 role=\\\"presentation\\\" 覆蓋\",\n      \"fail\": \"元素的預設語意未被 role=\\\"presentation\\\" 覆蓋\"\n    },\n    \"svg-non-empty-title\": {\n      \"pass\": \"元素有一個子元素，它是標題\",\n      \"fail\": {\n        \"noTitle\": \"元素沒有作為標題的子元素\",\n        \"emptyTitle\": \"元素子標題為空\"\n      },\n      \"incomplete\": \"無法確定元素的子元素是標題\"\n    },\n    \"caption-faked\": {\n      \"pass\": \"表格的第一行不作為標題\",\n      \"fail\": \"表格的第一個子項目應該是標題而不是表格儲存格\"\n    },\n    \"html5-scope\": {\n      \"pass\": \"範圍屬性僅用於表頭元素（<th>）\",\n      \"fail\": \"在 HTML 5 中，範圍屬性只能用在表頭元素（<th>） 上\"\n    },\n    \"same-caption-summary\": {\n      \"pass\": \"摘要屬性和 <caption> 的內容不重複\",\n      \"fail\": \"摘要屬性和 <caption> 元素的內容相同\",\n      \"incomplete\": \"無法確定 <table> 元素是否有標題\"\n    },\n    \"scope-value\": {\n      \"pass\": \"範圍屬性使用正確\",\n      \"fail\": \"scope 屬性的值只能是 'row' 或 'col'\"\n    },\n    \"td-has-header\": {\n      \"pass\": \"所有非空資料儲存格都有表格標題\",\n      \"fail\": \"某些非空資料儲存格沒有表頭\"\n    },\n    \"td-headers-attr\": {\n      \"pass\": \"headers 屬性專門用於引用表格中的其他儲存格\",\n      \"incomplete\": \"headers 屬性為空\",\n      \"fail\": \"headers 屬性不僅僅用於引用表中的其他單元格\"\n    },\n    \"th-has-data-cells\": {\n      \"pass\": \"所有表格標題儲存格均引用資料儲存格\",\n      \"fail\": \"並非所有表格標題儲存格都引用資料儲存格\",\n      \"incomplete\": \"表格資料單元格缺失或為空\"\n    },\n    \"hidden-content\": {\n      \"pass\": \"頁面上的所有內容都已分析\",\n      \"fail\": \"分析此頁面上的內容時出現問題\",\n      \"incomplete\": \"頁面上存在未分析的隱藏內容，您將需要觸發此內容的顯示才能對其進行分析\"\n    }\n  },\n  \"failureSummaries\": {\n    \"any\": {\n      \"failureMessage\": \"修正以下任一問題：{{~it:value}}\\n {{=value.split('\\\\n').join('\\\\n ')}}{{~}}\"\n    },\n    \"none\": {\n      \"failureMessage\": \"修正以下所有問題：{{~it:value}}\\n {{=value.split('\\\\n').join('\\\\n ')}}{{~}}\"\n    }\n  },\n  \"incompleteFallbackMessage\": \"axe 也說不出原因，該使用元素檢查器了！\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"axe-core\",\n  \"description\": \"Accessibility engine for automated Web UI testing\",\n  \"version\": \"4.11.1\",\n  \"license\": \"MPL-2.0\",\n  \"engines\": {\n    \"node\": \">=4\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"David Sturley\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Dylan Barrell\",\n      \"email\": \"dylan@barrell.com\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Wilco Fiers\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Dian Fay\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Marcy Sutton\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    },\n    {\n      \"name\": \"Ava Gaiety Wroten\",\n      \"organization\": \"Deque Systems, Inc.\",\n      \"url\": \"http://deque.com/\"\n    }\n  ],\n  \"homepage\": \"https://www.deque.com/axe/\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/dequelabs/axe-core.git\"\n  },\n  \"keywords\": [\n    \"Accessibility\",\n    \"a11y\",\n    \"testing\",\n    \"unit\",\n    \"tdd\",\n    \"bdd\",\n    \"axe\"\n  ],\n  \"main\": \"axe.js\",\n  \"typings\": \"axe.d.ts\",\n  \"files\": [\n    \"axe.js\",\n    \"axe.min.js\",\n    \"axe.d.ts\",\n    \"sri-history.json\",\n    \"locales/\",\n    \"LICENSE-3RD-PARTY.txt\"\n  ],\n  \"standard-version\": {\n    \"scripts\": {\n      \"postbump\": \"npm ci && npm run sri-update && git add doc/rule-descriptions.md\"\n    },\n    \"skip\": {\n      \"tag\": true\n    }\n  },\n  \"scripts\": {\n    \"start\": \"http-server -a \\\"\\\" -p 9876 --silent\",\n    \"develop\": \"grunt dev --force\",\n    \"api-docs\": \"jsdoc --configure .jsdoc.json\",\n    \"build\": \"grunt\",\n    \"patch\": \"npx patch-package\",\n    \"unpatch\": \"npx patch-package --reverse\",\n    \"eslint\": \"eslint --color --format stylish '{lib,test,build,doc}/**/*.js' 'Gruntfile.js' '.github/bin/*.mjs'\",\n    \"test\": \"npm run test:tsc && run-s \\\"test:unit:* -- {@}\\\" --\",\n    \"test:tsc\": \"tsc\",\n    \"test:unit\": \"karma start test/karma.conf.js\",\n    \"test:debug\": \"npm run test:unit -- --no-single-run --browsers=ChromeDebugging\",\n    \"test:unit:core\": \"npm run test:unit -- testDirs=core\",\n    \"test:unit:commons\": \"npm run test:unit -- testDirs=commons\",\n    \"test:unit:rule-matches\": \"npm run test:unit -- testDirs=rule-matches\",\n    \"test:unit:checks\": \"npm run test:unit -- testDirs=checks\",\n    \"test:unit:api\": \"npm run test:unit -- testDirs=api\",\n    \"test:unit:integration\": \"npm run test:unit -- testDirs=integration\",\n    \"test:unit:virtual-rules\": \"npm run test:unit -- testDirs=virtual-rules\",\n    \"integration\": \"node test/integration/full/test-webdriver.js\",\n    \"integration:apg\": \"mocha --fail-zero test/aria-practices/*.spec.js\",\n    \"integration:chrome\": \"npm run integration -- browser=Chrome\",\n    \"integration:firefox\": \"npm run integration -- browser=Firefox\",\n    \"test:integration\": \"npm run test:integration:chrome\",\n    \"test:integration:chrome\": \"start-server-and-test 9876 integration:chrome\",\n    \"test:integration:firefox\": \"start-server-and-test 9876 integration:firefox\",\n    \"test:examples\": \"node ./doc/examples/test-examples\",\n    \"test:act\": \"mocha --fail-zero test/act-rules/*.spec.js\",\n    \"test:apg\": \"start-server-and-test 9876 integration:apg\",\n    \"test:locales\": \"mocha test/test-locales.js\",\n    \"test:virtual-rules\": \"mocha test/test-virtual-rules.js\",\n    \"test:rule-help-version\": \"mocha test/test-rule-help-version.js\",\n    \"test:node\": \"node test/node/node.js\",\n    \"test:jsdom\": \"mocha test/node/jsdom.js\",\n    \"version\": \"echo \\\"use 'npm run release' to bump axe-core version\\\" && exit 1\",\n    \"release\": \"git fetch origin --tags --force && standard-version -a\",\n    \"rule-gen\": \"node build/rule-generator\",\n    \"sri-update\": \"grunt build && node build/sri-update && git add sri-history.json\",\n    \"sri-validate\": \"node build/sri-update --validate\",\n    \"fmt\": \"prettier --write .\",\n    \"fmt:check\": \"prettier --check .\",\n    \"prepare\": \"husky && npm run patch\",\n    \"prebuild\": \"node ./build/check-node-version.js\",\n    \"pretest\": \"node ./build/check-node-version.js\",\n    \"postbuild\": \"prettier --write ./locales/_template.json ./doc/rule-descriptions.md\"\n  },\n  \"devDependencies\": {\n    \"@axe-core/webdriverjs\": \"^4.10.2\",\n    \"@babel/core\": \"^7.20.12\",\n    \"@babel/plugin-proposal-object-rest-spread\": \"^7.20.7\",\n    \"@babel/preset-env\": \"^7.20.2\",\n    \"@babel/runtime-corejs3\": \"^7.20.7\",\n    \"@deque/dot\": \"^1.1.5\",\n    \"@types/node\": \"^4.9.5\",\n    \"aria-practices\": \"github:w3c/aria-practices#ce0336bd82d7d3651abcbde86af644197ddbc629\",\n    \"aria-query\": \"^5.1.3\",\n    \"chai\": \"^4.3.7\",\n    \"chalk\": \"^4.x\",\n    \"chromedriver\": \"latest\",\n    \"clean-jsdoc-theme\": \"^4.2.17\",\n    \"clone\": \"^2.1.2\",\n    \"colorjs.io\": \"0.4.3\",\n    \"conventional-commits-parser\": \"^5.0.0\",\n    \"core-js\": \"^3.27.1\",\n    \"css-selector-parser\": \"^1.4.1\",\n    \"emoji-regex\": \"^10.2.1\",\n    \"es6-promise\": \"^4.2.8\",\n    \"esbuild\": \"^0.10.x\",\n    \"eslint\": \"^9.2.0\",\n    \"eslint-config-prettier\": \"^10.0.1\",\n    \"eslint-plugin-mocha-no-only\": \"^1.2.0\",\n    \"execa\": \"5.x\",\n    \"glob\": \"^10.3.10\",\n    \"globals\": \"^17.1.0\",\n    \"grunt\": \"^1.5.3\",\n    \"grunt-babel\": \"^8.0.0\",\n    \"grunt-bytesize\": \"^0.2.0\",\n    \"grunt-contrib-clean\": \"^2.0.1\",\n    \"grunt-contrib-concat\": \"^2.1.0\",\n    \"grunt-contrib-uglify\": \"^5.2.2\",\n    \"grunt-contrib-watch\": \"^1.1.0\",\n    \"html-entities\": \"^2.4.0\",\n    \"http-server\": \"^14.1.1\",\n    \"husky\": \"^9.0.7\",\n    \"inquirer\": \"^8.2.5\",\n    \"jquery\": \"^4.0.0\",\n    \"jsdoc\": \"^4.0.2\",\n    \"jsdom\": \"^28.1.0\",\n    \"karma\": \"^6.4.1\",\n    \"karma-chai\": \"^0.1.0\",\n    \"karma-chrome-launcher\": \"^3.1.1\",\n    \"karma-firefox-launcher\": \"^2.1.2\",\n    \"karma-ie-launcher\": \"^1.0.0\",\n    \"karma-mocha\": \"^2.0.1\",\n    \"karma-sinon\": \"^1.0.5\",\n    \"karma-spec-reporter\": \"^0.0.36\",\n    \"lint-staged\": \"^16.1.0\",\n    \"memoizee\": \"^0.4.15\",\n    \"mocha\": \"^11.1.0\",\n    \"node-notifier\": \"^10.0.1\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"outdent\": \"^0.8.0\",\n    \"patch-package\": \"^8.0.0\",\n    \"prettier\": \"^3.0.3\",\n    \"revalidator\": \"^0.3.1\",\n    \"selenium-webdriver\": \"^4.7.1\",\n    \"serve-handler\": \"^6.1.5\",\n    \"sinon\": \"^21.0.0\",\n    \"sri-toolbox\": \"^0.2.0\",\n    \"standard-version\": \"^9.5.0\",\n    \"start-server-and-test\": \"^2.0.1\",\n    \"typedarray\": \"^0.0.7\",\n    \"typescript\": \"^5.2.2\",\n    \"uglify-js\": \"^3.17.4\",\n    \"wcag-act-rules\": \"github:w3c/wcag-act-rules#5adb55d19eb2cd7fb23213b2d1acedf9004dc063\",\n    \"weakmap-polyfill\": \"^2.0.4\"\n  },\n  \"lint-staged\": {\n    \"*.{md,json,ts,html}\": [\n      \"prettier --write\"\n    ],\n    \"*.{js,mjs}\": [\n      \"prettier --write\",\n      \"eslint --fix\"\n    ]\n  }\n}\n"
  },
  {
    "path": "patches/colorjs.io+0.4.3.patch",
    "content": "diff --git a/node_modules/colorjs.io/dist/color.js b/node_modules/colorjs.io/dist/color.js\nindex fa7a099..b3ccb68 100644\n--- a/node_modules/colorjs.io/dist/color.js\n+++ b/node_modules/colorjs.io/dist/color.js\n@@ -1967,7 +1967,7 @@ var P3 = new RGBColorSpace({\n // Default space for CSS output. Code in Color.js makes this wider if there's a DOM available\n defaults.display_space = sRGB;\n \n-if (typeof CSS !== \"undefined\" && CSS.supports) {\n+if (typeof CSS !== \"undefined\" && CSS?.supports) {\n \t// Find widest supported color space for CSS\n \tfor (let space of [lab, REC2020, P3]) {\n \t\tlet coords = space.getMinCoords();\n@@ -1996,7 +1996,7 @@ if (typeof CSS !== \"undefined\" && CSS.supports) {\n function display (color, {space = defaults.display_space, ...options} = {}) {\n \tlet ret = serialize(color, options);\n \n-\tif (typeof CSS === \"undefined\" || CSS.supports(\"color\", ret) || !defaults.display_space) {\n+\tif (typeof CSS === \"undefined\" || CSS?.supports(\"color\", ret) || !defaults.display_space) {\n \t\tret = new String(ret);\n \t\tret.color = color;\n \t}\n"
  },
  {
    "path": "sri-history.json",
    "content": "{\n  \"1.0.1\": {\n    \"axe.js\": \"sha256-F14wfpbaL6+ZafS2ufmz74/R6CD1L777gCOJVuLn5Ao=\",\n    \"axe.min.js\": \"sha256-GfbQgd6aKJKQT21HaQfVUuGEbMUfSKhi7RsWrrih7SM=\"\n  },\n  \"1.1.0\": {\n    \"axe.js\": \"sha256-nG9+LLksY/HS6yG4x0iS08U7RAmTucy50uXQS5ndQnM=\",\n    \"axe.min.js\": \"sha256-5Lfk3/s+ieHttrDTOCVuepGjDgH2D1neTDDtXnJdIT0=\"\n  },\n  \"1.1.1\": {\n    \"axe.js\": \"sha256-K0MLUClls79s14CT01F82LPc+ZubTBwt6fZDdN5iyFw=\",\n    \"axe.min.js\": \"sha256-UWXq259l36QypXwd2e4K2V1Lli2qHeSPmKfDXy+7uIM=\"\n  },\n  \"2.0.5\": {\n    \"axe.js\": \"sha256-KVA5sj4tNmFLneuHPPbg4iEp3MBzsHvn3+s9CxfMrmc=\",\n    \"axe.min.js\": \"sha256-lt5eNq/L7IBUaoSUSAbQ7MEO02DThMGdVm/oxeA88gk=\"\n  },\n  \"2.0.6\": {\n    \"axe.js\": \"sha256-cLV1ABoE5NjfwLaRAJYIstAJCmciDXZ55/TrQS5rR/M=\",\n    \"axe.min.js\": \"sha256-fnbwW70tA7/ya+5q5Oimc8wCRsdiv3WrU0MElp/euvY=\"\n  },\n  \"2.0.7\": {\n    \"axe.js\": \"sha256-HjHe1xKQqP6i3eGpDlARb5HkFsZxvAslvr1JJhUlp60=\",\n    \"axe.min.js\": \"sha256-vo7Qs4YxFxzFEW5DG9u05JdAUCsFxx7dBIoBP5rzmKA=\"\n  },\n  \"2.1.7\": {\n    \"axe.js\": \"sha256-kXUowSb3HQy6AChF4LYR4cvNxKEHCWqCcuiSxvY6E1s=\",\n    \"axe.min.js\": \"sha256-xJPQwkKDFmgwYDLqrt/esIHlc2HLUB2ogf9c1uS1BXA=\"\n  },\n  \"2.2.0\": {\n    \"axe.js\": \"sha256-0iX4Q9QT3uHAo+oxS5NWsrU4DNZKB30JRf7m+jjZcX0=\",\n    \"axe.min.js\": \"sha256-3UsQJdfWbhtVd0QzcYi2VAJEfs8DMzxrRvr3h9WbMiE=\"\n  },\n  \"2.2.1\": {\n    \"axe.js\": \"sha256-1kuZOWtoYszbvrtG7TYfl/FuO48uwZeFpN4aNnebABk=\",\n    \"axe.min.js\": \"sha256-WSHVQ1/IJaG9ZOydoFg+QXHLjOq8x/mhYkRAyN1yAdI=\"\n  },\n  \"2.2.2\": {\n    \"axe.js\": \"sha256-jTirRblNTKinTlmCK3FqDG6POtZVbP/bJNdrXZhCuqk=\",\n    \"axe.min.js\": \"sha256-zpmYSLTgHx09UyYIvYrS5K6uj4VuBbspk54kHk5hPqo=\"\n  },\n  \"2.2.3\": {\n    \"axe.js\": \"sha256-dV8RkE0hyyzj7qgValVkoP7Rtu8789BmbyeHZmidvqA=\",\n    \"axe.min.js\": \"sha256-BrNWjOcDL265Me8eKQosGWerJ6ju2g+G9+RvMWiBGOA=\"\n  },\n  \"2.3.0\": {\n    \"axe.js\": \"sha256-rkyHB2lHjs+tissQLBUxuxIvWlzRbS4f4cdaH+TjQvo=\",\n    \"axe.min.js\": \"sha256-MGWkallV18uw6bSq6w8cjbGsf9v4rJtXP+NDtMEbO14=\"\n  },\n  \"2.3.1\": {\n    \"axe.js\": \"sha256-63oq1xHBiOhX9jlvn5sJmoL8TwJ8JaLYIB91gyb74RI=\",\n    \"axe.min.js\": \"sha256-BGAllCBTUJjJXw3yOPMVai2Bj+1PVaEhK2na699nI/o=\"\n  },\n  \"2.4.1\": {\n    \"axe.js\": \"sha256-nR7Ix22wBzWJJw7bNukb3n8Bw16bvHdpGKW86+G8dU4=\",\n    \"axe.min.js\": \"sha256-xVpSddrzxbQVFp90jaOUOQGp3fwCwK4sYxYmkIU3N2c=\"\n  },\n  \"2.4.2\": {\n    \"axe.js\": \"sha256-x1Yy3nmIKx4qWNKjPy+jtpXSozwbuFsLez1iHliFgEQ=\",\n    \"axe.min.js\": \"sha256-v8V4N5vGmwEq7XbSIyUkGN9TrjhGBHjM1ZuAAIhgp6o=\"\n  },\n  \"2.5.0\": {\n    \"axe.js\": \"sha256-NYUXSdma9KjPfzmpt7jw/hlbmeAha8K3zEA2UOW+eWw=\",\n    \"axe.min.js\": \"sha256-7MV3BvKtgHeecwFjYOBYJbmOhvh2wdTGU7odxgpcrG0=\"\n  },\n  \"2.6.0\": {\n    \"axe.js\": \"sha256-zK6kpREBDqi1wucJ1GoH9UafxT0E0+XUnrSzg03wdmw=\",\n    \"axe.min.js\": \"sha256-vqKSLjjqbp9J3seCuphn1h/3OC6o5jntS8LtlIftvdY=\"\n  },\n  \"2.6.1\": {\n    \"axe.js\": \"sha256-fFHgLOnvH7mY6LNF58cvts9CXZCsyUgd++hhHeZpowo=\",\n    \"axe.min.js\": \"sha256-arpGpcEwKuAfd4XQmHqNdmXP/nUvm4eMonLh+L0s6cc=\"\n  },\n  \"3.0.0-alpha.7\": {\n    \"axe.js\": \"sha256-10kIurI2DW5bjegHOgc/MMSHiiXK2CAVWCQfoN6h0fs=\",\n    \"axe.min.js\": \"sha256-cDpGlEEbGsAh0MI82ZFwvX+oOpD5j+uo+kjKH/gJ5t4=\"\n  },\n  \"3.0.0-alpha.8\": {\n    \"axe.js\": \"sha256-1ZYjd7ajlao8zM/E8GJLh7u0H/uoMgO5K1fayVUOW5Q=\",\n    \"axe.min.js\": \"sha256-hqbtoVFN2hNjaNhgH2anN1itc31jUDUnOX2cj/6SR2M=\"\n  },\n  \"3.0.0-alpha.9\": {\n    \"axe.js\": \"sha256-9eUbLQe6YDrwxRsdNnAeTGoBV3lTIrzokBmP2gaStBI=\",\n    \"axe.min.js\": \"sha256-2nIufAEb6J4t/f/mnSa3D16vrdcBd0fPjEFy81F+N7k=\"\n  },\n  \"3.0.0-beta.1\": {\n    \"axe.js\": \"sha256-F9UktrEmuvDZZ3uYq5WVR9pyfgS+IP+RmRMq8WY6NzU=\",\n    \"axe.min.js\": \"sha256-K0dHFoOFTxCCdRuVrey4KVRGQx6Oj5afJyp3YVpZNz0=\"\n  },\n  \"3.0.0-beta.2\": {\n    \"axe.js\": \"sha256-cMk6LIQ0mYRm54wqfZ6O2U/6BT6XpHPjqIPVyhMMHYU=\",\n    \"axe.min.js\": \"sha256-66fXkU5+wmx5X9sURMglHTsWKO8R+LoOIcBwB8N9dPI=\"\n  },\n  \"3.0.0-beta.3\": {\n    \"axe.js\": \"sha256-9le58CGdN3pVlavrlAiyCmo276kXpnTKIIgFRlUhauI=\",\n    \"axe.min.js\": \"sha256-yzD9M6lgRosBZtC6x3acx5XaiLgR2jAWe1dDMqutmbs=\"\n  },\n  \"3.0.0\": {\n    \"axe.js\": \"sha256-1wR8RsA0/NYF2caKkmvgOqcCoyZXOZK2q7/EfZ4Y8Fo=\",\n    \"axe.min.js\": \"sha256-6SQ3NZobDnpEoUWc8h2u+4G3H2/yzt53VX3nv9PZ8g8=\"\n  },\n  \"3.0.1\": {\n    \"axe.js\": \"sha256-Vf/arxSrHppK2X5x6VgBZLJnCy8yK6P6uH99WwzQ30s=\",\n    \"axe.min.js\": \"sha256-vMPyo7vifw5RTaVEAlnfwGFa9VyHymsNqanCsHh3Q8c=\"\n  },\n  \"3.0.2\": {\n    \"axe.js\": \"sha256-D24i3Yy35gMxOZNTNZyQLAyL3W3wVvW1wUYakK5v1VI=\",\n    \"axe.min.js\": \"sha256-Hsc1oDUNhkVBP4gVUaC9jNm9t0qmLpTJzXW4uzx10bo=\"\n  },\n  \"3.0.3\": {\n    \"axe.js\": \"sha256-QhYA1btt0EGa6HUDloEPZ5p6ZArWsL8J9C7PDoqi320=\",\n    \"axe.min.js\": \"sha256-Xlzi+kk/RJRo2x/+w04BppEgcrO/Cy9xTSbuIp6m/CU=\"\n  },\n  \"3.0.4\": {\n    \"axe.js\": \"sha256-l8dI6H61gah8+nZRMFdcZkCU08rxvMgpUaRx0Hs/lL8=\",\n    \"axe.min.js\": \"sha256-cqgH55deHQaFac6ItvRSV9eRcUbf4dlqjc3Nwp1jUUI=\"\n  },\n  \"3.1.0\": {\n    \"axe.js\": \"sha256-qUMvWIpNXGal2tqBIDJsrXde0clrbTcATxlteQSyA4M=\",\n    \"axe.min.js\": \"sha256-q1K/bHXBTINVndnMyDdntNSJHTAei0YhN/kNqeaGD5A=\"\n  },\n  \"3.1.1\": {\n    \"axe.js\": \"sha256-54k0QW1jMWPC2Lq/pTSX2xu8ky7QSd39uEqcy3Yk35o=\",\n    \"axe.min.js\": \"sha256-Zs6maznrnIa0ko83hAWIKlhPPeBLmKmRmfphyuqBJbA=\"\n  },\n  \"3.1.2\": {\n    \"axe.js\": \"sha256-goUY5vBRPoITOCdg7HK/vfHRs50+RdQRQQkNCU3lnMs=\",\n    \"axe.min.js\": \"sha256-wIvlzfT77n6fOnSL6/oLbzB873rY7QHTW/e0Z0mOoYs=\"\n  },\n  \"3.1.3\": {\n    \"axe.js\": \"sha256-X/FaOBNaYhVis9NrSYGNPLyjzkmTD1OqSc8o7A9y2RA=\",\n    \"axe.min.js\": \"sha256-KpiyZmcHc5wnCsMY7WLhvQEuB3wz1mjV7UA//ifbCK4=\"\n  },\n  \"3.2.0\": {\n    \"axe.js\": \"sha256-wdCvSfDepBhwcvTqB0gm586ywpla6Yi81qLCHjegILM=\",\n    \"axe.min.js\": \"sha256-Tvf4toyAt1NFmmkuLsgStXW+4pcG5GG9ugopPoOvOwg=\"\n  },\n  \"3.2.1\": {\n    \"axe.js\": \"sha256-VPX1ngRx0XWqC5BP9XdHoDd8YlbgXGK39cz14GBL9bg=\",\n    \"axe.min.js\": \"sha256-ynN9JxvxoI9VpL7IbdTVCUCLhy3N7Ygrnit2r7a2vK8=\"\n  },\n  \"3.2.2\": {\n    \"axe.js\": \"sha256-ekZaZcmreeOq0/Hm0MYYpjK2k/HfLIBchbdzynX2s1A=\",\n    \"axe.min.js\": \"sha256-WUsm7HQhfTPm48UUbRuQCZ6mXZXpO+trPivOXYelKM0=\"\n  },\n  \"3.2.3\": {\n    \"axe.js\": \"sha256-l36pxPggxG0w7W9WFK21lyw9uaObTj3M5gS1Fl7ZABo=\",\n    \"axe.min.js\": \"sha256-c+LGvM2VcJVUXwaqzmDeKLDBMJSZgKUhnW9GK3JkZ5o=\"\n  },\n  \"3.2.4\": {\n    \"axe.js\": \"sha256-EBY9p4epOAuqOfHhHDmA03TuKUqarplY3FA97bGDwyY=\",\n    \"axe.min.js\": \"sha256-heYXBvx3dOPu5dGnEKhOr2FyynWFxWht0bslELqiUAo=\"\n  },\n  \"3.3.0\": {\n    \"axe.js\": \"sha256-9qKFcrJohPVSRdWg+nYMf1cnxUz00rojnrcDbRpofP4=\",\n    \"axe.min.js\": \"sha256-t2QDE4+vMfgX52JfOztdvXHLxDJLc9Gc1O583kc4l/s=\"\n  },\n  \"3.3.1\": {\n    \"axe.js\": \"sha256-MdbXT1qQi6W6AGWVUzUROzk8KhsTy6ePEpa3pBhO1S4=\",\n    \"axe.min.js\": \"sha256-x2j+s50F+GHZZWsTK2GWiJtcHo4MDvc5zpuIFTwXioY=\"\n  },\n  \"3.3.2\": {\n    \"axe.js\": \"sha256-0gx9+aOF0I9F91zdACXlV8C0eZRP0bjKORUPDFuGOkw=\",\n    \"axe.min.js\": \"sha256-eMkj7GARritK91GFkbOxZind0uesDKEOXS/AggeFJ4E=\"\n  },\n  \"3.3.3\": {\n    \"axe.js\": \"sha256-cWikFVL1B7T3kafsX1ESurvIh/+2bFrhX8CKJJUNnQ0=\",\n    \"axe.min.js\": \"sha256-L/faUr+k2zA3fuo4l2T7mIpMxL8vPB3CWsSujoLyKLk=\"\n  },\n  \"3.3.4\": {\n    \"axe.js\": \"sha256-hcKoJVuQBu5iOURV1//SJrAFn4Gx8rooN/SmKOr5o9g=\",\n    \"axe.min.js\": \"sha256-P/yLNTZPLmpl/OLh4pfocIWG8J6HDuv9oXNhFIRvIOc=\"\n  },\n  \"3.4.0\": {\n    \"axe.js\": \"sha256-J7v+KmmfiDeImDENld25aBnvcJyhg2Z/PayxJyJmL3A=\",\n    \"axe.min.js\": \"sha256-3uvAwJcMTsYRtepeqr32rrpy5zZ/fMYjiKEKPZM6Rtc=\"\n  },\n  \"3.4.1\": {\n    \"axe.js\": \"sha256-2mBsmz6UhYUk3gYnRiMkzuJTiHd86XCEA/Rxf3tN7l8=\",\n    \"axe.min.js\": \"sha256-z1eK+dbxVx44jv/XqW4xYJlyA9T9zXuxMlJbNJfTvOg=\"\n  },\n  \"3.4.2\": {\n    \"axe.js\": \"sha256-1O2tV4TBC/8pYjxbIwj192fUX1SJvsjn8KQH3PdJUuU=\",\n    \"axe.min.js\": \"sha256-XXgQWSZNsMKcqEQnEU2BDkIfCzV6gCjOjrwnrzioYm4=\"\n  },\n  \"3.4.3\": {\n    \"axe.js\": \"sha256-SaFKkccPxaPG6ZNeF2DOwDpKSQlZ/AIrfLJq3R7IDQ4=\",\n    \"axe.min.js\": \"sha256-riQur+AZD7BQyyNzplymxhdhwsEBQHiaJwM5ozy3ISw=\"\n  },\n  \"3.5.0\": {\n    \"axe.js\": \"sha256-w/zYk9v7UUawn15gNmZHGrh8WdzCI1bSxmRM2N7Uk4o=\",\n    \"axe.min.js\": \"sha256-p3SZwFLtGBl/V92T6V9wJvUEkr5GvLswqkPy8rt49S0=\"\n  },\n  \"3.5.1\": {\n    \"axe.js\": \"sha256-J5+J6tFJyKx7Jyn9xK3rJhAyJCGAPR6kOwzXjpdFbEM=\",\n    \"axe.min.js\": \"sha256-wo0QYtgoayRMifoYIHc8Spw5NmeFN4ojGLSqmrUuhX8=\"\n  },\n  \"3.5.2\": {\n    \"axe.js\": \"sha256-wf7AYQ0YxwohIAqhYIfQ6y1VDQmP2zbgGhL6mb8a+Eg=\",\n    \"axe.min.js\": \"sha256-suutTbfowm19n2znRYpsLOcHOkwv9g+ximjS8TLJ6/8=\"\n  },\n  \"3.5.3\": {\n    \"axe.js\": \"sha256-lO/Zp+gRgYruJbjyvHOfqWqqroJ2ogTLJ64DwunyUz4=\",\n    \"axe.min.js\": \"sha256-Oh5/ClgfAAKQWyCvlU3F+Ud6QDsS0myLJr866QFt67w=\"\n  },\n  \"3.5.4\": {\n    \"axe.js\": \"sha256-4f1ZZbAr3xgoAputB3oZK5ASoazrNIw6SU2M1Nnb4bg=\",\n    \"axe.min.js\": \"sha256-XDhCakYtcQtOpujvhE876/a4fUyZjiKtNn8xniP03Ek=\"\n  },\n  \"3.5.5\": {\n    \"axe.js\": \"sha256-u+Xkb1EmxyRof8YJD9nPMbq9e3VXBj5cO99aEtr2uEs=\",\n    \"axe.min.js\": \"sha256-ODhTB+pG6Fxp1+zEO2uNekC30gj1q8gQ4iCZZpMQhuE=\"\n  },\n  \"3.5.6\": {\n    \"axe.js\": \"sha256-kEgieHQSxGEe1pEXRrbCXuzUtQe3riQHWP3b9GCB2vc=\",\n    \"axe.min.js\": \"sha256-0fQXgcsl+WahejLCUoWzSNSPChr65aPlw5P7jsCpoVc=\"\n  },\n  \"4.0.0\": {\n    \"axe.js\": \"sha256-F408iSv2h/vUtSWZ0hzzKvfoV6TVr25kzqD4CZE3gnk=\",\n    \"axe.min.js\": \"sha256-TtQRMYXl4FFwO5r1PgEpv9qfeYUQTUKv4w6M0Mu3wnE=\"\n  },\n  \"4.0.1\": {\n    \"axe.js\": \"sha256-qt/1RCmnUExiG4pAxxvS14Rv5WG2mUl3D/9ZG5hIOoo=\",\n    \"axe.min.js\": \"sha256-UX2nqe7C94kL3oes720eLjMxFcJgNYjwEYgAeIVpdMM=\"\n  },\n  \"4.0.2\": {\n    \"axe.js\": \"sha256-J5lSA5Tjmd8TJgXJw1nplpRsm+0oZdxZhzh3svJCRTA=\",\n    \"axe.min.js\": \"sha256-Lalcd7gfejE06N+Tl7ZMgGdmyHp77fT8Jlu4mQLH7Nc=\"\n  },\n  \"4.0.3\": {\n    \"axe.js\": \"sha256-VQi0wRLTfYCI6xi/IuxJw54g36VbqYkmR61gKkop0f8=\",\n    \"axe.min.js\": \"sha256-lQoBfvmYSxUMKoH5XANiG3ubapXmQ7v+iYjlJ9UngWA=\"\n  },\n  \"4.1.0\": {\n    \"axe.js\": \"sha256-CpsJouSBpr1ARLhTtIKjZlS1eMWKAdb6k2USgtNGHQI=\",\n    \"axe.min.js\": \"sha256-Slv1vTH024BG87L3ztHvVdlKpeQXG+wN5BcNMcHp1qk=\"\n  },\n  \"4.1.1\": {\n    \"axe.js\": \"sha256-Tz6yShAcKxP8ce//YhbRkOLinUj+htEK2MjIi5yjY58=\",\n    \"axe.min.js\": \"sha256-43geecotYZGPnRt6o9Uu7Pjl3GthxnDacXrzCGv11go=\"\n  },\n  \"4.1.2\": {\n    \"axe.js\": \"sha256-wnrhK+P+FXp6U/oYQrYVu6/6PrpO0chRAkik/2ENuS0=\",\n    \"axe.min.js\": \"sha256-TzERMakAkktf5sw0quZYuHVmniguowRhWRIVYpjbLbA=\"\n  },\n  \"4.1.3\": {\n    \"axe.js\": \"sha256-T1FukaTlhjibBqAm8SBwYmo8GLAHdn45r52XRllMdv8=\",\n    \"axe.min.js\": \"sha256-2Z3/OrInIQjAzZ5kMvTBZ2CDUiAgUG/2J5ca1XpNfYw=\"\n  },\n  \"4.1.4\": {\n    \"axe.js\": \"sha256-MURsYMNnII8EGBcbmd0FGY36x9dICBq6IDOz+hnaJns=\",\n    \"axe.min.js\": \"sha256-dYHtyLO/5lpm5JgQFWzcf3DFg4BvMTf9MDfZG7nxMEM=\"\n  },\n  \"4.2.0\": {\n    \"axe.js\": \"sha256-RWY2EhLV83s/DmYMCNZM1ZNSVeVWyYMIXosbCvbSvUs=\",\n    \"axe.min.js\": \"sha256-DGi+8Cpa4CzwJcmtJI/xUnpEDLfhn6mgyescUhP8W8g=\"\n  },\n  \"4.2.1\": {\n    \"axe.js\": \"sha256-DK0go8gPzyLAfXpA0r1wKNnpIYqSAtKJx2XkUxw3cp8=\",\n    \"axe.min.js\": \"sha256-GY7NT5JqpkYWliaJ+xXDHf0t5o30jVvEHb5KRVXykXA=\"\n  },\n  \"4.2.2\": {\n    \"axe.js\": \"sha256-K3oF47ZvKGUvc3E1qiF7l3pyLK9WoxuUobm1EuWJFiQ=\",\n    \"axe.min.js\": \"sha256-uUSPcE2E0gEuXZYQaEoMQAn1Q34H7JSfjUzDwvVaB8A=\"\n  },\n  \"4.2.3\": {\n    \"axe.js\": \"sha256-YpHE4EYgzaX6R5zc08TtcjCwyDoQf2Dczv+W2vhkdEg=\",\n    \"axe.min.js\": \"sha256-7kmyHEX64EqrQmlKiBetbDbidBg++T1OK7pJ0Ch0tVE=\"\n  },\n  \"4.2.4\": {\n    \"axe.js\": \"sha256-5COa2/D9CE8AshTHVGhvT0A1FZXaTZy+Kh0x177VAww=\",\n    \"axe.min.js\": \"sha256-rsECe0URvW9YH4nley2GHQLI7bC9Ucan8zY9AaZdyrY=\"\n  },\n  \"4.3.0\": {\n    \"axe.js\": \"sha256-V36WfTm/iDPLDQHIKkUfj1lPRPloIKZxS+7syZp7gzw=\",\n    \"axe.min.js\": \"sha256-zG5ql3CjWawSvMuVGJI9C8OvXR5MZAXK1IhBt83psrA=\"\n  },\n  \"4.3.1\": {\n    \"axe.js\": \"sha256-59tFDkuadP3UQaLuRg51nCnT6M22O3+I1lBIuiTer8U=\",\n    \"axe.min.js\": \"sha256-QJtI5W/X35A3qw39uNE/Ho4iei3arUL4zrWillwv2oA=\"\n  },\n  \"4.3.2\": {\n    \"axe.js\": \"sha256-q0wpLH5m3+m2qIpuhiAfNx2BMxC6E8O5BLBQ52P6AvM=\",\n    \"axe.min.js\": \"sha256-eGWJWadXBOoWLwGBXmtbrpqclaIxMoIRVIN9mJ5/idw=\"\n  },\n  \"4.3.3\": {\n    \"axe.js\": \"sha256-Zvxy3UIxivqa1hGYQez9LNFxm6JBX826VrkGlH4x6hk=\",\n    \"axe.min.js\": \"sha256-KqKNp9dAHi8QV4BZv/lB28ZJt3ROsN8Y7vVRbxewSJ8=\"\n  },\n  \"4.3.4\": {\n    \"axe.js\": \"sha256-C62RaRaF4cx65nFcFNMq5qhRPs0K3syB/x32hxSnDFM=\",\n    \"axe.min.js\": \"sha256-Qw1EkY/ShOt08vZ7tZXSskZI3bQ6HGw9t9F6Rq05MAo=\"\n  },\n  \"4.3.5\": {\n    \"axe.js\": \"sha256-pBzsgJIL4mZHcw50O3sj5f1ZYkMYLG659lHq6Qrkuzc=\",\n    \"axe.min.js\": \"sha256-PVZfyAKgcOWwy7tmB01H7BVjKLjtFW5QO94AIXxWkcw=\"\n  },\n  \"4.4.0\": {\n    \"axe.js\": \"sha256-gXqUZOaNO3O6o8WlFgwgeyPBiVS+CKGohTW7gC1vreE=\",\n    \"axe.min.js\": \"sha256-0+zQT47ZDlw7BjxlDClFCx2/NA0XcRmpXSn33IL/ua4=\"\n  },\n  \"4.4.1\": {\n    \"axe.js\": \"sha256-3JaN+ADNfrGA15wszgRmfF5HiotL+1zjZznNmgzCYj4=\",\n    \"axe.min.js\": \"sha256-hGavf/+WwgIndgE1q4LjwtSlyHJjEdkQq1iilUUafC8=\"\n  },\n  \"4.4.2\": {\n    \"axe.js\": \"sha256-s3Ip/Rm7jkD6tDMBfUvDdj1Okyjm+wMBKf60/39jaug=\",\n    \"axe.min.js\": \"sha256-LPnPAiGlol21Bn7pq9yM1TkCvztX37pp59UhYeSwShM=\"\n  },\n  \"4.4.3\": {\n    \"axe.js\": \"sha256-R+pmfA40BsLNJsCD6FrO4KRXoV2mV3NKNUZuxFSJk6g=\",\n    \"axe.min.js\": \"sha256-aX6mx+3/D+KJt7vs8f97kBUkm8g0tAmjSM0GDfgK66A=\"\n  },\n  \"4.5.0\": {\n    \"axe.js\": \"sha256-AYLBIQscniY92RkIlsESUAIu3cOplhUmJ3Y4/DKR4SE=\",\n    \"axe.min.js\": \"sha256-cdG6H1F9kSiSIpyWgjGYXkbV3mcfc6/SvE7Zbq2km/A=\"\n  },\n  \"4.5.1\": {\n    \"axe.js\": \"sha256-IkK+029qUdULyO88zkDU2TeO5kQB8AjncJvjmFKqBHQ=\",\n    \"axe.min.js\": \"sha256-Ost3Bp9ymk0FCZFL9pOU2g/ph8u06GuVCm8go6W+THw=\"\n  },\n  \"4.5.2\": {\n    \"axe.js\": \"sha256-GY6QNA/wTZYR80KdSFkH5BevH8QfsZGGbJ9qFzCkneY=\",\n    \"axe.min.js\": \"sha256-4Jw5rCVL57KrRYPfrx9AC87f3txwWIpgTM5n/LbSPTQ=\"\n  },\n  \"4.6.0\": {\n    \"axe.js\": \"sha256-THkPfmokl4bJu3OupcpOt0ndrVCtSuKHPAbmah9KOfI=\",\n    \"axe.min.js\": \"sha256-14jwyUq9PSLhUTnee2RUaKm1i5z93fVEhUTgPCLzGO0=\"\n  },\n  \"4.6.1\": {\n    \"axe.js\": \"sha256-5YoRtLPmXJjZaeVXOVTTPf6DtevHdYBfD4oxaCBDCXw=\",\n    \"axe.min.js\": \"sha256-8CV1yJR7ZBS/j8HkUf1OwOEib+WUH+QovSg8xOh9MVI=\"\n  },\n  \"4.6.2\": {\n    \"axe.js\": \"sha256-6BCLF6TcLdHG7Ok3yyV7HOKF/Se1yJQgzgAiz3AnzX0=\",\n    \"axe.min.js\": \"sha256-US+MWxPC7r+aFzKfSCl+effrzN0Qoz4tS2wxlIpmsCM=\"\n  },\n  \"4.6.3\": {\n    \"axe.js\": \"sha256-N8N7PwDRFNyzsL80UoXAE5ibkfu/KJ71Ixw0zWYX+yM=\",\n    \"axe.min.js\": \"sha256-vOhlk9sgryrpUZOv6801a9uYhuG6Z/NBJmOJb0aAQQQ=\"\n  },\n  \"4.7.0\": {\n    \"axe.js\": \"sha256-JxLtVRh3EvdwPhr6ipXNnoN2ugUYvpwIAx4usr5jKcU=\",\n    \"axe.min.js\": \"sha256-q5YvHv5paIlrWyys5xKDb79XtmXYpiAHFVZcg61Qick=\"\n  },\n  \"4.7.1\": {\n    \"axe.js\": \"sha256-1ruJfhOP+fO+uuQiIs1U9P4ILYOxnFc5MCH7LzaH+Lc=\",\n    \"axe.min.js\": \"sha256-ReDVBpTYnDCOjYC3eTfhCwsWtAcleTtuoekVW8eoU38=\"\n  },\n  \"4.7.2\": {\n    \"axe.js\": \"sha256-EE+7F84cDAG4yvmWb/Cbb3WmQaNAGj4FwU4cKxJZqjg=\",\n    \"axe.min.js\": \"sha256-VTWaX4yE65/ofnyeohwMLLKvU1GEYDoWOCbKlcqFMs8=\"\n  },\n  \"4.8.0\": {\n    \"axe.js\": \"sha256-Rd1U88z7mahhAvxYhchoCfwWxV8R/H+BcSoX6XTQ0gY=\",\n    \"axe.min.js\": \"sha256-1oZiCfWnUt2rwqLWzpkoxlcEaOH53/QFYke0jo+KecA=\"\n  },\n  \"4.8.1\": {\n    \"axe.js\": \"sha256-xAiDRfIT8BFIcORsehvJYCROe4K+U0Ak5MVJMaCvn9k=\",\n    \"axe.min.js\": \"sha256-EkEySwRAuCQljOYWJ29XQp4s80KPPlsmKwFtKFn6Vr0=\"\n  },\n  \"4.8.2\": {\n    \"axe.js\": \"sha256-VZuuruUDDRwfzCo4ZDDzXiVefuy4pSW6BlGx+D/ucC0=\",\n    \"axe.min.js\": \"sha256-O9U055OcfxyKV61g3Qn7N9mvpJNht0RCPcFw+QjWTG4=\"\n  },\n  \"4.8.3\": {\n    \"axe.js\": \"sha256-YWpAAdIo7fwKmLq8nJx1f6pwt7HAXwWm15RSGJKbxhw=\",\n    \"axe.min.js\": \"sha256-/mct+I/4SJnZ30Ce+j9T7ll9zPwzbJVrjdKpbKIP+NA=\"\n  },\n  \"4.8.4\": {\n    \"axe.js\": \"sha256-RRn+EjX3fX893zHeLzMQebvK4/HR3yZpVFNxsV3Pbm0=\",\n    \"axe.min.js\": \"sha256-HXl1GEx0+LwVB27fLmwgdXCmeTM2beVwwFosWvFzLmo=\"\n  },\n  \"4.9.0\": {\n    \"axe.js\": \"sha256-76H9rp5bFFzCQrHiNlIEZniM30DftZzZ5pUJd/ZG4Qg=\",\n    \"axe.min.js\": \"sha256-leYBst5Y3dlncZ2YVBpe5s//BhrMC7XbK9yhUCAyPNA=\"\n  },\n  \"4.9.1\": {\n    \"axe.js\": \"sha256-t7u8FiO2U54AN0dyGu2hAMcYdgpuXEkM6n+Eup6lMyQ=\",\n    \"axe.min.js\": \"sha256-GCpA3F2CB+YmwJhhrWUCfUXoXjpW0BBF0Gji6I7kMuo=\"\n  },\n  \"4.10.0\": {\n    \"axe.js\": \"sha256-n/KN+TQhojZXnh49uUEY3/Df5WszWcCEothu0P58qDY=\",\n    \"axe.min.js\": \"sha256-SDpP7Usv5Wz9lKWFZQIMhjmD+EFeKCfzSB3ONecO/7U=\"\n  },\n  \"4.10.1\": {\n    \"axe.js\": \"sha256-KQDnBck/AyiC+OE3x0AJ/EQYykYTnH+1z7punr4Xc0c=\",\n    \"axe.min.js\": \"sha256-OBXf33U9n+qCj0mI9MLkiVyC1nCzabDnmopjyuv1gZ4=\"\n  },\n  \"4.10.2\": {\n    \"axe.js\": \"sha256-+gZL/deLqt1La/rK9St/UzNzWa4co71yGT5I07FuZuE=\",\n    \"axe.min.js\": \"sha256-tRHNnewBx29LKtFyO2a22zfUwutO0ZkHbhgp2e57deM=\"\n  },\n  \"4.10.3\": {\n    \"axe.js\": \"sha256-vurbjUBDRYSCqEMt0ievkm0brzeP+81jiI6R7AyoU10=\",\n    \"axe.min.js\": \"sha256-iAlwwIFwc2DmTzTOol/5GJL1vJVnWwd2kluXCd2KaLs=\"\n  },\n  \"4.11.0\": {\n    \"axe.js\": \"sha256-RKE4K0g6QAePcUi+Tcnr567YKVEwnoToqe+Qd7w1mzo=\",\n    \"axe.min.js\": \"sha256-6eWGPDOodPCbwBrNkjS348hxR59e74gC+lglREZebQE=\"\n  },\n  \"4.11.1\": {\n    \"axe.js\": \"sha256-fJzJf10MFoA2ixvyHh+fGvPuVq6JXm+IdhMVfaKAnYY=\",\n    \"axe.min.js\": \"sha256-fb+r38YGKTbXnIc927X4EaEhn8o5KL2Myd2B8eZfRyA=\"\n  }\n}\n"
  },
  {
    "path": "test/act-rules/README.md",
    "content": "# ACT Rules\n\nEach ACT rule that axe-core is consistent with has a test file in this directory named [after the ACT rule](https://github.com/act-rules/act-rules.github.io/tree/develop/_rules). These tests use the `act-runner.js` script, which loads the test cases from `node_modules/wcag-act-rules`. The ACT runner accepts a `id` and `title` from the ACT rule, and an array of `axeRules` that map to this ACT rule.\n\nTo run all tests, use `npm run test:act`. To test individual files, you can use Mocha's --grep argument: `npm run test:act -- --grep=afw4f7`.\n\nThe spec file object allows the following properties:\n\n- `id` - (required) The `ruleId` of the ACT rule, found in the [testcases.json file](https://github.com/w3c/wcag-act-rules/blob/main/content-assets/wcag-act-rules/testcases.json).\n- `title` - (required) The `ruleName` of the ACT rule, found in the [testcases.json file](https://github.com/w3c/wcag-act-rules/blob/main/content-assets/wcag-act-rules/testcases.json).\n- `axeRules` - (required) A list of axe-core rule ids to run. A single ACT rule can require multiple axe-core rules in order to cover all the ACT examples (e.g. the ACT rule `button-non-empty` requires both `button-name` and `aria-command-name` to run).\n- `skipTests` - (optional) A list of ACT `testcaseId` to skip. Please be sure to add a comment as to why the test should be skipped. If applicable, please open an issue in axe-core and link to the issue in the code so that we can track the issue and know when we can run the test again.\n"
  },
  {
    "path": "test/act-rules/act-runner.js",
    "content": "const path = require('path');\nconst fs = require('fs');\nconst http = require('http');\nconst handler = require('serve-handler');\nconst { AxeBuilder } = require('@axe-core/webdriverjs');\nconst { getWebdriver } = require('../get-webdriver');\nconst { assert } = require('chai');\n\nconst serverPort = 9898;\nconst axePath = require.resolve('../../axe.js');\nconst axeSource = fs.readFileSync(axePath, 'utf8');\nconst actPath = path.resolve(__dirname, '../../node_modules/wcag-act-rules/');\nconst testCaseJsonPath = path.resolve(\n  actPath,\n  './content-assets/wcag-act-rules/testcases.json'\n);\n\nconst addr = `http://localhost:${serverPort}/WAI/content-assets/wcag-act-rules/`;\nconst testCaseJson = require(testCaseJsonPath);\n\nmodule.exports = ({ id, title, axeRules, skipTests = [] }) => {\n  describe(`${title} (${id})`, function () {\n    let driver, server;\n    const testcases = testCaseJson.testcases.filter(\n      ({ ruleId }) => ruleId === id\n    );\n\n    this.timeout(50000);\n    this.retries(3);\n\n    before(done => {\n      driver = getWebdriver();\n      server = http.createServer((request, response) => {\n        return handler(request, response, {\n          cleanUrls: false,\n          rewrites: [\n            {\n              source:\n                '/WAI/content-assets/wcag-act-rules/:dir?/:subDir?/:file?',\n              destination:\n                '/node_modules/wcag-act-rules/content-assets/wcag-act-rules/:dir?/:subDir?/:file?'\n            }\n          ]\n        });\n      });\n      server.listen(serverPort, done);\n    });\n\n    after(async () => {\n      await driver.quit();\n      await new Promise(r => server.close(r));\n    });\n\n    testcases.forEach(testcase => {\n      const shouldRun =\n        testcase.relativePath.match(/\\.(xhtml|html?)$/) &&\n        !skipTests.includes(testcase.testcaseId);\n\n      (shouldRun ? it : xit)(testcase.testcaseTitle, async () => {\n        await driver.get(`${addr}/${testcase.relativePath}`);\n\n        const builder = new AxeBuilder(driver, axeSource);\n        builder.withRules(axeRules);\n        const results = await builder.analyze();\n\n        if (testcase.expected !== 'failed') {\n          assert.lengthOf(\n            results.violations,\n            0,\n            `Expected 0 violations for testcase ${testcase.testcaseId}`\n          );\n        } else {\n          var issues = results.violations[0] || results.incomplete[0];\n          assert.isDefined(\n            issues,\n            `Expected violations or incomplete for testcase ${testcase.testcaseId}`\n          );\n          assert.isAtLeast(issues.nodes.length, 1);\n        }\n      });\n    });\n  });\n};\n"
  },
  {
    "path": "test/act-rules/aria-attr-defined-5f99a7.spec.js",
    "content": "require('./act-runner.js')({\n  id: '5f99a7',\n  title: 'ARIA attribute is defined in WAI-ARIA',\n  axeRules: ['aria-valid-attr']\n});\n"
  },
  {
    "path": "test/act-rules/aria-hidden-no-focusable-content-6cfa84.spec.js",
    "content": "require('./act-runner.js')({\n  id: '6cfa84',\n  title:\n    'Element with aria-hidden has no content in sequential focus navigation',\n  axeRules: ['aria-hidden-focus']\n});\n"
  },
  {
    "path": "test/act-rules/aria-required-context-ff89c9.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'ff89c9',\n  title: 'ARIA required context role',\n  axeRules: ['aria-required-parent', 'aria-required-children'],\n  // See https://github.com/act-rules/act-rules.github.io/pull/1973\n  skipTests: ['c18579dc18aaebf7eeaa4e24e4bc199d77c432bc']\n});\n"
  },
  {
    "path": "test/act-rules/aria-required-id-references-in6db8.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'in6db8',\n  title: 'ARIA required ID references exist',\n  axeRules: ['aria-valid-attr-value'],\n  // See: https://github.com/dequelabs/axe-core/issues/4202\n  skipTests: ['97bd98302238b32e9131d042174502a83db2a4b2']\n});\n"
  },
  {
    "path": "test/act-rules/aria-required-owned-element-bc4a75.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'bc4a75',\n  title: 'ARIA required owned elements',\n  axeRules: ['list', 'aria-required-children']\n});\n"
  },
  {
    "path": "test/act-rules/aria-state-or-property-permitted-5c01ea.spec.js",
    "content": "require('./act-runner.js')({\n  id: '5c01ea',\n  title: 'ARIA state or property is permitted',\n  axeRules: ['aria-allowed-attr', 'aria-prohibited-attr']\n});\n"
  },
  {
    "path": "test/act-rules/autocomplete-valid-value-73f2c2.spec.js",
    "content": "require('./act-runner.js')({\n  id: '73f2c2',\n  title: 'autocomplete attribute has valid value',\n  axeRules: ['autocomplete-valid']\n});\n"
  },
  {
    "path": "test/act-rules/button-non-empty-accessible-name-97a4e1.spec.js",
    "content": "require('./act-runner.js')({\n  id: '97a4e1',\n  title: 'Button has non-empty accessible name',\n  axeRules: ['button-name', 'aria-command-name']\n});\n"
  },
  {
    "path": "test/act-rules/css-restrict-orientation-b33eff.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'b33eff',\n  title:\n    'Orientation of the page is not restricted using CSS transform property',\n  axeRules: ['css-orientation-lock']\n});\n"
  },
  {
    "path": "test/act-rules/element-lang-valid-de46e4.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'de46e4',\n  title: 'Element with lang attribute has valid language tag',\n  axeRules: ['valid-lang']\n});\n"
  },
  {
    "path": "test/act-rules/element-marked-decorative-is-not-exposed-46ca7f.spec.js",
    "content": "require('./act-runner.js')({\n  id: '46ca7f',\n  title: 'Element marked as decorative is not exposed',\n  axeRules: ['presentation-role-conflict']\n});\n"
  },
  {
    "path": "test/act-rules/explicit-SVG-image-non-empty-accessible-name-7d6734.spec.js",
    "content": "require('./act-runner.js')({\n  id: '7d6734',\n  title: 'svg element with explicit role has non-empty accessible name',\n  axeRules: ['svg-img-alt']\n});\n"
  },
  {
    "path": "test/act-rules/form-field-non-empty-accessible-name-e086e5.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'e086e5',\n  title: 'Form field has non-empty accessible name',\n  axeRules: [\n    'label',\n    'select-name',\n    'aria-input-field-name',\n    'aria-toggle-field-name'\n  ],\n  // See: https://github.com/act-rules/act-rules.github.io/pull/1974\n  skipTests: ['b8e68dccf37727dd39af9fca76a8371a8ec5a81f']\n});\n"
  },
  {
    "path": "test/act-rules/heading-non-empty-accessible-name-ffd0e9.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'ffd0e9',\n  title: 'Heading has non-empty accessible name',\n  axeRules: ['empty-heading']\n});\n"
  },
  {
    "path": "test/act-rules/html-page-lang-b5c3f8.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'b5c3f8',\n  title: 'HTML page has lang attribute',\n  axeRules: ['html-has-lang']\n});\n"
  },
  {
    "path": "test/act-rules/html-page-lang-valid-bf051a.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'bf051a',\n  title: 'HTML page lang attribute has valid language tag',\n  axeRules: ['html-lang-valid']\n});\n"
  },
  {
    "path": "test/act-rules/html-page-lang-xml-lang-match-5b7ae0.spec.js",
    "content": "require('./act-runner.js')({\n  id: '5b7ae0',\n  title: 'HTML page lang and xml:lang attributes have matching values',\n  axeRules: ['html-xml-lang-mismatch']\n});\n"
  },
  {
    "path": "test/act-rules/html-page-non-empty-title-2779a5.spec.js",
    "content": "require('./act-runner.js')({\n  id: '2779a5',\n  title: 'HTML page has non-empty title',\n  axeRules: ['document-title']\n});\n"
  },
  {
    "path": "test/act-rules/id-value-unique-3ea0c8.spec.js",
    "content": "require('./act-runner.js')({\n  id: '3ea0c8',\n  title: 'id attribute value is unique',\n  axeRules: ['duplicate-id', 'duplicate-id-aria', 'duplicate-id-active']\n});\n"
  },
  {
    "path": "test/act-rules/iframe-non-empty-accessible-name-cae760.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'cae760',\n  title: 'Iframe element has non-empty accessible name',\n  axeRules: ['frame-title']\n});\n"
  },
  {
    "path": "test/act-rules/image-button-non-empty-accessible-name-59796f.spec.js",
    "content": "require('./act-runner.js')({\n  id: '59796f',\n  title: 'Image button has non-empty accessible name',\n  axeRules: ['input-image-alt']\n});\n"
  },
  {
    "path": "test/act-rules/image-non-empty-accessible-name-23a2a8.spec.js",
    "content": "require('./act-runner.js')({\n  id: '23a2a8',\n  title: 'Image has non-empty accessible name',\n  axeRules: ['image-alt', 'role-img-alt']\n});\n"
  },
  {
    "path": "test/act-rules/letter-spacing-not-important-24afc2.spec.js",
    "content": "require('./act-runner.js')({\n  id: '24afc2',\n  title: 'Letter spacing in style attributes is not !important',\n  axeRules: ['avoid-inline-spacing'],\n  // See: https://github.com/dequelabs/axe-core/issues/4232\n  skipTests: [\n    '9af5662e9957191c22c558a1a8511bae709a2b36',\n    'd6d5bf7c081939e64d10022dd29f5e31d2153d50'\n  ]\n});\n"
  },
  {
    "path": "test/act-rules/line-height-not-important-78fd32.spec.js",
    "content": "require('./act-runner.js')({\n  id: '78fd32',\n  title: 'Line height in style attributes is not !important',\n  axeRules: ['avoid-inline-spacing']\n});\n"
  },
  {
    "path": "test/act-rules/link-non-empty-accessible-name-c487ae.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'c487ae',\n  title: 'Link has non-empty accessible name',\n  axeRules: ['link-name', 'area-alt']\n});\n"
  },
  {
    "path": "test/act-rules/links-identical-name-equivalent-purpose-b20e66.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'b20e66',\n  title: 'Links with identical accessible names have equivalent purpose',\n  axeRules: ['identical-links-same-purpose']\n});\n"
  },
  {
    "path": "test/act-rules/links-with-identical-names-and-context-serve-equivalent-purpose-fd3a94.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'fd3a94',\n  title:\n    'Links with identical accessible names and context serve equivalent purpose',\n  axeRules: ['identical-links-same-purpose']\n});\n"
  },
  {
    "path": "test/act-rules/menuitem-non-empty-name-m6b1q3.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'm6b1q3',\n  title: 'Menuitem has non-empty accessible name',\n  axeRules: ['aria-command-name', 'button-name', 'link-name']\n});\n"
  },
  {
    "path": "test/act-rules/meta-viewport-b4f0c3.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'b4f0c3',\n  title: 'meta viewport allows for zoom',\n  axeRules: ['meta-viewport'],\n  // See: https://github.com/dequelabs/axe-core/issues/4231\n  skipTests: [\n    '9f288c284df9ade53aa33e50ec50c879d5aba4ef',\n    'c94a59f8c3b17d722781af36da3556ff4b418776'\n  ]\n});\n"
  },
  {
    "path": "test/act-rules/object-has-acessible-name-8fc3b6.spec.js",
    "content": "require('./act-runner.js')({\n  id: '8fc3b6',\n  title:\n    'Object element rendering non-text content has non-empty accessible name',\n  axeRules: ['object-alt']\n});\n"
  },
  {
    "path": "test/act-rules/presentational-children-no-focusable-content-307n5z.spec.js",
    "content": "require('./act-runner.js')({\n  id: '307n5z',\n  title: 'Element with presentational children has no focusable content',\n  axeRules: ['nested-interactive']\n});\n"
  },
  {
    "path": "test/act-rules/role-required-states-and-properties-4e8ab6.spec.js",
    "content": "require('./act-runner.js')({\n  id: '4e8ab6',\n  title: 'Element with role attribute has required states and properties',\n  axeRules: ['aria-required-attr']\n});\n"
  },
  {
    "path": "test/act-rules/scrollable-element-keyboard-accessible-0ssw9k.spec.js",
    "content": "require('./act-runner.js')({\n  id: '0ssw9k',\n  title: 'Scrollable element is keyboard accessible',\n  axeRules: ['scrollable-region-focusable']\n});\n"
  },
  {
    "path": "test/act-rules/table-headers-attribute-refer-to-data-cells-a25f45.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'a25f45',\n  title:\n    'Headers attribute specified on a cell refers to cells in the same table element',\n  axeRules: ['td-headers-attr']\n});\n"
  },
  {
    "path": "test/act-rules/text-contrast-afw4f7.spec.js",
    "content": "require('./act-runner.js')({\n  id: 'afw4f7',\n  title: 'Text has minimum contrast',\n  axeRules: ['color-contrast']\n});\n"
  },
  {
    "path": "test/act-rules/text-contrast-enhanced-09o5cg.spec.js",
    "content": "require('./act-runner.js')({\n  id: '09o5cg',\n  title: 'Text has enhanced contrast',\n  axeRules: ['color-contrast', 'color-contrast-enhanced']\n});\n"
  },
  {
    "path": "test/act-rules/visible-label-in-accessible-name-2ee8b8.spec.js",
    "content": "require('./act-runner.js')({\n  id: '2ee8b8',\n  title: 'Visible label is part of accessible name',\n  axeRules: ['label-content-name-mismatch'],\n  // See: https://github.com/dequelabs/axe-core/issues/4311\n  skipTests: ['e9bbdbec137223e2973c6d2896050770c84c26e5']\n});\n"
  },
  {
    "path": "test/act-rules/word-spacing-not-important-9e45ec.spec.js",
    "content": "require('./act-runner.js')({\n  id: '9e45ec',\n  title: 'Word spacing in style attributes is not !important',\n  axeRules: ['avoid-inline-spacing'],\n  // See: https://github.com/dequelabs/axe-core/issues/4232\n  skipTests: [\n    '15905a239d6755102be6a60aa152ad963d5b1dbb',\n    '8d2baed183149375922c23a9a5f42b52b627d713',\n    'fdd3c30f28464b32eb8a1397f70a41dfd3b2cb1c'\n  ]\n});\n"
  },
  {
    "path": "test/aria-practices/README.md",
    "content": "# ARIA Practices\n\nRuns axe-core on the examples provided in the [ARIA Practices](https://www.w3.org/WAI/ARIA/apg/). To run the tests, run `npm run test:apg`.\n\nIf certain axe-core rules should not be run on certain pages, you can disable those rules using the `disabledRules` object in [apg.spec.js](./apg.spec.js). Please be sure to add a comment as to why the rule should not be run. If applicable, please open an issue in either axe-core or [aria-practices](https://github.com/w3c/aria-practices) and link to the issue in the code so that we can track the issue and know when we can run the rule again.\n"
  },
  {
    "path": "test/aria-practices/apg.spec.js",
    "content": "const path = require('path');\nconst fs = require('fs');\nconst { AxeBuilder } = require('@axe-core/webdriverjs');\nconst { getWebdriver } = require('../get-webdriver');\nconst { assert } = require('chai');\nconst { globSync } = require('glob');\n\ndescribe('aria-practices', function () {\n  // Use path.resolve rather than require.resolve because APG package.json main file does not exist\n  const apgPath = path.resolve(__dirname, '../../node_modules/aria-practices/');\n  const filePaths = globSync(\n    `${apgPath}/content/patterns/*/**/examples/*.html`,\n    { posix: true }\n  );\n  const testFiles = filePaths.map(\n    fileName => fileName.split('/aria-practices/content/patterns/')[1]\n  );\n\n  const addr = `http://localhost:9876/node_modules/aria-practices/`;\n  let driver, axeSource;\n  this.timeout(50000);\n  this.retries(3);\n\n  before(async () => {\n    const axePath = require.resolve('../../axe.js');\n    axeSource = fs.readFileSync(axePath, 'utf8');\n    driver = getWebdriver();\n  });\n\n  after(async () => {\n    await driver.quit();\n  });\n\n  const disabledRules = {\n    '*': [\n      'color-contrast',\n      'target-size',\n      'heading-order', // w3c/aria-practices#2119\n      'scrollable-region-focusable' // w3c/aria-practices#2114\n    ]\n  };\n\n  const skippedPages = [\n    'toolbar/examples/help.html', // Embedded into another page\n    'tabs/examples/tabs-actions.html' // dequelabs/axe-core#4584\n  ];\n\n  it('finds examples', () => {\n    assert.isTrue(testFiles.length > 0);\n  });\n\n  testFiles\n    .filter(filePath => !skippedPages.includes(filePath))\n    .forEach(filePath => {\n      it(`finds no issue in \"${filePath}\"`, async () => {\n        await driver.get(`${addr}content/patterns/${filePath}`);\n\n        const builder = new AxeBuilder(driver, axeSource)\n          // Support table has no title and has duplicate ids\n          .exclude('#at-support')\n          .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])\n          .disableRules([\n            ...disabledRules['*'],\n            ...(disabledRules[filePath] || [])\n          ]);\n\n        const { violations } = await builder.analyze();\n        const issues = violations.map(({ id, nodes }) => ({\n          id,\n          issues: nodes.length\n        }));\n        assert.lengthOf(issues, 0, issues.map(({ id }) => id).join(', '));\n      });\n    });\n});\n"
  },
  {
    "path": "test/assets/webfont-attribution-license.md",
    "content": "# Webfont Attribution and Licenses\n\n## FiraCode-Regular:\n\nhttps://github.com/tonsky/FiraCode\nLicense: SIL Open Font License 1.1\n\n## Ligature Symbols\n\nhttps://github.com/kudakurage/LigatureSymbols\nLicense: SIL Open Font License 1.1\n\n## Material Icons\n\nhttps://google.github.io/material-design-icons\nLicense: Apache License Version 2.0\n\n## Roboto\n\nhttps://fonts.google.com/specimen/Roboto\nLicense: Apache License, Version 2.0\n"
  },
  {
    "path": "test/checks/aria/aria-allowed-attr.js",
    "content": "describe('aria-allowed-attr', () => {\n  'use strict';\n\n  const queryFixture = axe.testUtils.queryFixture;\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should detect incorrectly used attributes', () => {\n    const vNode = queryFixture(\n      '<div role=\"link\" id=\"target\" tabindex=\"1\" aria-selected=\"true\"></div>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-selected=\"true\"']);\n  });\n\n  it('should not report on required attributes', () => {\n    const vNode = queryFixture(\n      '<div role=\"checkbox\" id=\"target\" tabindex=\"1\" aria-checked=\"true\"></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should detect incorrectly used attributes - implicit role', () => {\n    const vNode = queryFixture(\n      '<a href=\"#\" id=\"target\" tabindex=\"1\" aria-selected=\"true\"></a>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-selected=\"true\"']);\n  });\n\n  it('should return true for global attributes if there is no role', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" tabindex=\"1\" aria-busy=\"true\" aria-owns=\"foo\"></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false for non-global attributes if there is no role', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" tabindex=\"1\" aria-selected=\"true\" aria-owns=\"foo\"></div>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-selected=\"true\"']);\n  });\n\n  it('should not report on invalid attributes', () => {\n    const vNode = queryFixture(\n      '<div role=\"dialog\" id=\"target\" tabindex=\"1\" aria-cats=\"true\"></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should not report on allowed attributes', () => {\n    const vNode = queryFixture(\n      '<div role=\"radio\" id=\"target\" tabindex=\"1\" aria-required=\"true\" aria-checked=\"true\"></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should not report on aria-required=false', () => {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-required=\"false\"></button>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false for unallowed aria-required=true', () => {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-required=\"true\"></button>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-required=\"true\"']);\n  });\n\n  it('should not report on aria-multiline=false with contenteditable', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"combobox\" aria-multiline=\"false\" contenteditable></div>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false for unallowed aria-multiline=true and contenteditable', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"combobox\" aria-multiline=\"true\" contenteditable></div>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-multiline=\"true\"']);\n  });\n\n  it('should return false for unallowed aria-multiline=false', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"combobox\" aria-multiline=\"false\"></div>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-multiline=\"false\"']);\n  });\n\n  it('should return false for unallowed aria-multiline=true', () => {\n    const vNode = queryFixture('<div id=\"target\" aria-multiline=\"true\"></div>');\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-multiline=\"true\"']);\n  });\n\n  it('should return undefined for custom element that has no role and is not focusable', () => {\n    const vNode = queryFixture(\n      '<my-custom-element id=\"target\" aria-expanded=\"true\"></my-custom-element>'\n    );\n\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNotNull(checkContext._data);\n  });\n\n  it(\"should return false for custom element that has a role which doesn't allow the attribute\", () => {\n    const vNode = queryFixture(\n      '<my-custom-element role=\"insertion\" id=\"target\" aria-expanded=\"true\"></my-custom-element>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNotNull(checkContext._data);\n  });\n\n  it('should return false for custom element that is focusable', () => {\n    const vNode = queryFixture(\n      '<my-custom-element tabindex=\"1\" id=\"target\" aria-expanded=\"true\"></my-custom-element>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNotNull(checkContext._data);\n  });\n\n  describe('options', () => {\n    it('should allow provided attribute names for a role', () => {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            mccheddarton: {\n              allowedAttrs: ['aria-checked']\n            }\n          }\n        }\n      });\n\n      const vNode = queryFixture(\n        '<div role=\"mccheddarton\" id=\"target\" aria-checked=\"true\" aria-selected=\"true\"></div>'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-allowed-attr')\n          .call(checkContext, null, null, vNode)\n      );\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('aria-allowed-attr').call(\n          checkContext,\n          null,\n          {\n            mccheddarton: ['aria-checked', 'aria-selected']\n          },\n          vNode\n        )\n      );\n    });\n\n    it('should handle multiple roles provided in options', () => {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            mcheddarton: {\n              allowedAttrs: ['aria-checked']\n            },\n            bagley: {\n              allowedAttrs: ['aria-checked']\n            }\n          }\n        }\n      });\n\n      const vNode = queryFixture(\n        '<div role=\"bagley\" id=\"target\" aria-selected=\"true\"></div>'\n      );\n      const options = {\n        mccheddarton: ['aria-selected'],\n        bagley: ['aria-selected']\n      };\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-allowed-attr')\n          .call(checkContext, null, null, vNode)\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('aria-allowed-attr')\n          .call(checkContext, null, options, vNode)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-allowed-role.js",
    "content": "describe('aria-allowed-role', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('returns true if given element is an ignoredTag in options', function () {\n    var vNode = queryFixture(\n      '<article id=\"target\" role=\"presentation\"></article>'\n    );\n    var options = {\n      ignoredTags: ['article']\n    };\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, options, vNode);\n    var expected = true;\n    assert.equal(actual, expected);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false with implicit role of row for TR when allowImplicit is set to false via options', function () {\n    var vNode = queryFixture(\n      '<table role=\"grid\"><tr id=\"target\" role=\"row\"></tr></table>'\n    );\n    var options = {\n      allowImplicit: false\n    };\n    var outcome = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, options, vNode);\n\n    assert.isFalse(outcome);\n    assert.deepEqual(checkContext._data, ['row']);\n  });\n\n  it('returns undefined (needs review) when element is hidden and has unallowed role', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" type=\"button\" aria-hidden=\"true\"' +\n        'role=\"presentation\"></button>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, null, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) when element is with in hidden parent and has unallowed role', function () {\n    var vNode = queryFixture(\n      '<div style=\"display:none\">' +\n        '<button id=\"target\" class=\"mm-tabstart\" type=\"button\"' +\n        'role=\"presentation\"></button>' +\n        '</div>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, null, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns true when BUTTON has type menu and role as menuitem', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" type=\"menu\" role=\"menuitem\"></button>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when img has no alt and role=\"presentation\"', function () {\n    var vNode = queryFixture('<img id=\"target\" role=\"presentation\"/>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, null);\n  });\n\n  it('returns true when img has no alt and role=\"none\"', function () {\n    var vNode = queryFixture('<img id=\"target\" role=\"none\"/>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, null);\n  });\n\n  it('returns true when img has empty alt and role=\"presentation\"', function () {\n    var vNode = queryFixture('<img id=\"target\" alt=\"\" role=\"presentation\"/>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, null);\n  });\n\n  it('returns true when img has empty alt and role=\"none\"', function () {\n    var vNode = queryFixture('<img id=\"target\" alt=\"\" role=\"none\"/>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, null);\n  });\n\n  it('returns false when img has alt and role=\"presentation\"', function () {\n    var vNode = queryFixture(\n      '<img id=\"target\" alt=\"not empty\" role=\"presentation\"/>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['presentation']);\n  });\n\n  it('returns false when img has alt and role=\"none\"', function () {\n    var vNode = queryFixture('<img id=\"target\" alt=\"not empty\" role=\"none\"/>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['none']);\n  });\n\n  it('returns true when img has aria-label and a valid role, role=\"button\"', function () {\n    var vNode = queryFixture(\n      '<img id=\"target\" aria-label=\"foo\" role=\"button\"/>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false when img has aria-label and a invalid role, role=\"alert\"', function () {\n    var vNode = queryFixture(\n      '<img id=\"target\" aria-label=\"foo\" role=\"alert\"/>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['alert']);\n  });\n\n  it('returns true when img has aria-labelledby and a valid role, role=\"menuitem\"', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" aria-labelledby=\"foo\" role=\"menuitem\"/>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false when img has aria-labelledby and a invalid role, role=\"rowgroup\"', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" aria-labelledby=\"foo\" role=\"rowgroup\"/>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['rowgroup']);\n  });\n\n  it('returns true when img has title and a valid role, role=\"link\"', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" title=\"foo\" role=\"link\"/>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false when img has title and a invalid role, role=\"radiogroup\"', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" title=\"foo\" role=\"radiogroup\"/>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['radiogroup']);\n  });\n\n  it('returns true when input of type image and no role', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"image\"/>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns true when INPUT type is checkbox and has aria-pressed attribute', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"checkbox\" aria-pressed=\"\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is text with role combobox', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"text\" role=\"combobox\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is tel with role combobox', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"tel\" role=\"combobox\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is url with role combobox', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"url\" role=\"combobox\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is search with role combobox', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"search\" role=\"combobox\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is email with role combobox', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"email\" role=\"combobox\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is text with role spinbutton', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"text\" role=\"spinbutton\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is number with role spinbutton', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"number\" role=\"spinbutton\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is tel with role spinbutton', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"tel\" role=\"spinbutton\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true when INPUT type is text with role searchbox', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"text\" role=\"searchbox\">'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns false when a role is set on an element that does not allow any role', function () {\n    var vNode = queryFixture('<dd id=\"target\" role=\"link\">');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['link']);\n  });\n\n  it('returns true when a role is set on an element that can have any role', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"link\"></dd>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true an <a> without a href to have any role', function () {\n    var vNode = queryFixture('<a id=\"target\" role=\"presentation\"></a>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true <a> with a empty href to have any valid role', function () {\n    var vNode = queryFixture('<a id=\"target\" role=\"link\" href=\"\"></a>');\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true <img> with a non-empty alt', function () {\n    var vNode = queryFixture('<img id=\"target\" role=\"button\" alt=\"some text\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should allow <select> without a multiple and size attribute to have a menu role', function () {\n    var vNode = queryFixture('<select id=\"target\" role=\"menu\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns true custom element <my-navbar> with a role of navigation', function () {\n    var vNode = queryFixture('<my-navbar id=\"target\" role=\"navigation\">');\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-allowed-role')\n      .call(checkContext, null, null, vNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false if a dpub role’s type is not the element’s implicit role', function () {\n    var vNode = queryFixture('<article id=\"target\" role=\"doc-biblioref\">');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns true if a dpub role’s type is the element’s implicit role', function () {\n    var vNode = queryFixture('<a id=\"target\" href=\"foo\" role=\"doc-biblioref\">');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-allowed-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-busy.js",
    "content": "describe('aria-busy', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('aria-busy');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return false if no aria-busy tag on element', function () {\n    var params = checkSetup('<div id=\"target\" role=\"list\"></div>');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if aria-busy is set to false', function () {\n    var params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-busy=\"false\"></div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if aria-busy is set to true', function () {\n    var params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-busy=\"true\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-conditional-attr.js",
    "content": "describe('aria-conditional-attr', () => {\n  const { checkSetup, getCheckEvaluate } = axe.testUtils;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const ariaConditionalCheck = getCheckEvaluate('aria-conditional-attr');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('is true for non-conditional roles', () => {\n    const roles = ['main', 'button', 'radiogroup', 'tree', 'none'];\n    for (const role of roles) {\n      const params = checkSetup(`<div id=\"target\" role=\"${role}\"></div>`);\n      assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n    }\n  });\n\n  describe('ariaConditionalRoleAttr', () => {\n    const treeGridRowProps = [\n      'aria-posinset=\"1\"',\n      'aria-setsize=\"1\"',\n      'aria-expanded=\"true\"',\n      'aria-level=\"1\"'\n    ];\n\n    it('returns true when valid ARIA props are used on table', () => {\n      const params = checkSetup(\n        `<div role=\"treegrid\">\n          <div id=\"target\" role=\"row\" aria-rowindex=\"1\" aria-label=\"hello world\"></div>\n        </div>`\n      );\n      assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns true when treegrid row props are used on a treegrid row', () => {\n      const params = checkSetup(\n        `<div role=\"treegrid\">\n          <div id=\"target\" role=\"row\" ${treeGridRowProps.join(' ')}></div>\n        </div>`\n      );\n      assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns true when the row is not in a table, grid, or treegrid', () => {\n      const params = checkSetup(\n        `<div id=\"target\" role=\"row\" ${treeGridRowProps.join(' ')}></div>`\n      );\n      assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns false when treegrid row props are used on an ARIA table row', () => {\n      for (const prop of treeGridRowProps) {\n        const params = checkSetup(\n          `<div role=\"table\">\n            <div id=\"target\" role=\"row\" ${prop}></div>\n          </div>`\n        );\n        assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n        assert.deepEqual(checkContext._data, {\n          messageKey: 'rowSingular',\n          invalidAttrs: [prop.split('=')[0]],\n          ownerRole: 'table'\n        });\n      }\n    });\n\n    it('returns false when treegrid row props are used on a grid row', () => {\n      for (const prop of treeGridRowProps) {\n        const params = checkSetup(\n          `<div role=\"grid\">\n            <div id=\"target\" role=\"row\" ${prop}></div>\n          </div>`\n        );\n        assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n        assert.deepEqual(checkContext._data, {\n          messageKey: 'rowSingular',\n          invalidAttrs: [prop.split('=')[0]],\n          ownerRole: 'grid'\n        });\n      }\n    });\n\n    it('returns false when treegrid row props are used on a native table row', () => {\n      for (const prop of treeGridRowProps) {\n        const params = checkSetup(\n          `<table> <tr id=\"target\" ${prop}> <td></td> </tr> </table>`\n        );\n        assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n        assert.deepEqual(checkContext._data, {\n          messageKey: 'rowSingular',\n          invalidAttrs: [prop.split('=')[0]],\n          ownerRole: 'table'\n        });\n      }\n    });\n\n    it('sets messageKey to rowPlural with multiple bad attributes', () => {\n      const params = checkSetup(\n        `<div role=\"table\">\n          <div id=\"target\" role=\"row\" aria-expanded=\"false\" aria-level=\"1\"></div>\n        </div>`\n      );\n      assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'rowPlural',\n        invalidAttrs: ['aria-expanded', 'aria-level'],\n        ownerRole: 'table'\n      });\n    });\n\n    describe('options.invalidTableRowAttrs', function () {\n      it('returns false for removed attribute', () => {\n        const options = { invalidTableRowAttrs: ['aria-rowindex'] };\n        const params = checkSetup(\n          `<table> <tr id=\"target\" aria-rowindex=\"1\"> <td></td> </tr> </table>`,\n          options\n        );\n        assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n      });\n\n      it('returns true for additional attribute', () => {\n        const options = { invalidTableRowAttrs: ['aria-level'] };\n        const params = checkSetup(\n          `<table>\n            <tr id=\"target\" aria-expanded=\"true\" aria-setsize=\"1\" aria-posinset=\"1\"> <td></td> </tr>\n          </table>`,\n          options\n        );\n        assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n      });\n    });\n  });\n\n  describe('ariaConditionalCheckboxAttr', () => {\n    it('returns true for non-native checkbox', () => {\n      const params = checkSetup(\n        `<div id=\"target\" role=\"checkbox\" aria-checked=\"true\"></div>`\n      );\n      assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns true for checkbox without aria-checked value', () => {\n      for (const prop of ['', 'aria-checked', 'aria-checked=\"\"']) {\n        const params = checkSetup(\n          `<input id=\"target\" type=\"checkbox\" ${prop}>`\n        );\n        assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n        assert.isNull(checkContext._data);\n      }\n    });\n\n    describe('checked state', () => {\n      const fixture = document.querySelector('#fixture');\n\n      it('returns true for aria-checked=\"true\" on a [checked] checkbox', () => {\n        fixture.innerHTML = `<input type=\"checkbox\" aria-checked=\"true\" checked>`;\n        const root = axe.setup(fixture);\n        const vNode = axe.utils.querySelectorAll(root, 'input')[0];\n\n        assert.isTrue(\n          ariaConditionalCheck.call(checkContext, null, null, vNode)\n        );\n        assert.isNull(checkContext._data);\n      });\n\n      it('returns true for aria-checked=\"true\" on a clicked checkbox', () => {\n        fixture.innerHTML = `<input type=\"checkbox\" aria-checked=\"true\">`;\n        fixture.firstChild.click(); // set checked state\n        const root = axe.setup(fixture);\n        const vNode = axe.utils.querySelectorAll(root, 'input')[0];\n\n        assert.isTrue(\n          ariaConditionalCheck.call(checkContext, null, null, vNode)\n        );\n        assert.isNull(checkContext._data);\n      });\n\n      it('returns false for other aria-checked values', () => {\n        for (const prop of ['  ', 'false', 'mixed', 'incorrect', '  true  ']) {\n          const params = checkSetup(\n            `<input type=\"checkbox\" aria-checked=\"${prop}\" checked id=\"target\">`\n          );\n          assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'checkbox',\n            checkState: 'true'\n          });\n        }\n      });\n    });\n\n    describe('unchecked state', () => {\n      it('returns true for aria-checked=\"false\"', () => {\n        const params = checkSetup(\n          `<input id=\"target\" type=\"checkbox\" aria-checked=\"false\">`\n        );\n        assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n        assert.isNull(checkContext._data);\n      });\n\n      it('returns true for aria-checked with an invalid value', () => {\n        for (const prop of ['  ', 'invalid', 'FALSE', 'nope']) {\n          const params = checkSetup(\n            `<input type=\"checkbox\" aria-checked=\"${prop}\" id=\"target\">`\n          );\n          assert.isTrue(ariaConditionalCheck.apply(checkContext, params));\n          assert.isNull(checkContext._data);\n        }\n      });\n\n      it('returns false for other aria-checked values', () => {\n        for (const prop of ['true', 'TRUE', 'mixed', 'MiXeD']) {\n          const params = checkSetup(\n            `<input type=\"checkbox\" aria-checked=\"${prop}\" id=\"target\">`\n          );\n          assert.isFalse(ariaConditionalCheck.apply(checkContext, params));\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'checkbox',\n            checkState: 'false'\n          });\n        }\n      });\n    });\n\n    describe('indeterminate state', () => {\n      function asIndeterminateVirtualNode(html) {\n        const fixture = document.querySelector('#fixture');\n        fixture.innerHTML = html;\n        fixture.querySelector('input').indeterminate = true;\n        const root = axe.setup(fixture);\n        return axe.utils.querySelectorAll(root, 'input')[0];\n      }\n\n      it('returns true for aria-checked=\"mixed\"', () => {\n        const vNode = asIndeterminateVirtualNode(\n          `<input type=\"checkbox\" aria-checked=\"mixed\">`\n        );\n        assert.isTrue(\n          ariaConditionalCheck.call(checkContext, null, null, vNode)\n        );\n      });\n\n      it('returns false for other aria-checked values', () => {\n        for (const prop of ['true', 'TRUE', 'false', 'invalid']) {\n          const vNode = asIndeterminateVirtualNode(\n            `<input type=\"checkbox\" aria-checked=\"${prop}\" id=\"target\">`\n          );\n          assert.isFalse(\n            ariaConditionalCheck.call(checkContext, null, null, vNode)\n          );\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'checkbox',\n            checkState: 'mixed'\n          });\n          axe.teardown(); // Reset for the next iteration\n        }\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-hidden-body.js",
    "content": "describe('aria-hidden', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var body = document.body;\n  afterEach(function () {\n    checkContext.reset();\n    body.removeAttribute('aria-hidden');\n  });\n\n  it('should not be present on document.body', function () {\n    var tree = axe.testUtils.flatTreeSetup(body);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-hidden-body')\n        .call(checkContext, null, {}, tree[0])\n    );\n  });\n\n  it('fails appropriately if aria-hidden=true on document.body', function () {\n    body.setAttribute('aria-hidden', true);\n    var tree = axe.testUtils.flatTreeSetup(body);\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-hidden-body')\n        .call(checkContext, null, {}, tree[0])\n    );\n  });\n\n  it('passes if aria-hidden=false on document.body', function () {\n    body.setAttribute('aria-hidden', 'false');\n    var tree = axe.testUtils.flatTreeSetup(body);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-hidden-body')\n        .call(checkContext, null, {}, tree[0])\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-level.js",
    "content": "describe('aria-prohibited-attr', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('aria-level');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if aria-level is less than 6', function () {\n    var params = checkSetup('<div id=\"target\" aria-level=\"2\">Contents</div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if aria-level is 6', function () {\n    var params = checkSetup('<div id=\"target\" aria-level=\"6\">Contents</div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if aria-level is negative', function () {\n    var params = checkSetup('<div id=\"target\" aria-level=\"-2\">Contents</div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if there is no aria-level', function () {\n    var params = checkSetup('<div id=\"target\">Contents</div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return undefined if aria-level is greater than 6', function () {\n    var params = checkSetup('<div id=\"target\" aria-level=\"8\">Contents</div>');\n    assert.isUndefined(checkEvaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-prohibited-attr.js",
    "content": "describe('aria-prohibited-attr', () => {\n  'use strict';\n\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('aria-prohibited-attr');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return true for prohibited attributes and no content', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-hidden=\"false\" aria-label=\"foo\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: 'code',\n      messageKey: 'hasRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  it('should return undefined for prohibited attributes and content', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-hidden=\"false\" aria-label=\"foo\">Contents</div>'\n    );\n    assert.isUndefined(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: 'code',\n      messageKey: 'hasRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  it('should return true for multiple prohibited attributes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-hidden=\"false\"  aria-label=\"foo\" aria-labelledby=\"foo\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: 'code',\n      messageKey: 'hasRolePlural',\n      // attribute order not important\n      prohibited: ['aria-label', 'aria-labelledby']\n    });\n  });\n\n  it('should return undefined if element has no role and has text content (singular)', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-label=\"foo\">Contents</div>'\n    );\n    assert.isUndefined(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  it('should return undefined if element has no role and has text content (plural)', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-label=\"foo\" aria-labelledby=\"foo\">Contents</div>'\n    );\n    assert.isUndefined(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRolePlural',\n      prohibited: ['aria-label', 'aria-labelledby']\n    });\n  });\n\n  it('should return true if element has no role and no text content (singular)', () => {\n    const params = checkSetup('<div id=\"target\" aria-label=\"foo\"></div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  it('should return true if element has no role and no text content (plural)', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-label=\"foo\" aria-labelledby=\"foo\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRolePlural',\n      prohibited: ['aria-label', 'aria-labelledby']\n    });\n  });\n\n  it('should return false if all attributes are allowed', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"button\" aria-label=\"foo\" aria-labelledby=\"foo\">Contents</div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if no prohibited attributes are used', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-selected=\"true\">Contents</div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if prohibited attributes have no value', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-label=\"  \" aria-labelledby=\"  \">Contents</div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should allow `elementsAllowedAriaLabel` nodes to have aria-label', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-label=\"hello world\"></div>',\n      { elementsAllowedAriaLabel: ['div'] }\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should not allow `elementsAllowedAriaLabel` nodes with a prohibited role', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"code\" aria-label=\"hello world\"></div>',\n      { elementsAllowedAriaLabel: ['div'] }\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should allow elements that have an implicit role in chromium', () => {\n    const params = checkSetup(\n      '<svg id=\"target\" aria-label=\"hello world\"></svg>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should not allow aria-label on divs that have an invalid role', function () {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"foo\" aria-label=\"foo\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  it('should allow aria-label on divs with a valid fallback role', function () {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"foo dialog\" aria-label=\"foo\"></div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should not allow aria-label on divs with no valid fallback roles', function () {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"foo bar\" aria-label=\"foo\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, {\n      nodeName: 'div',\n      role: null,\n      messageKey: 'noRoleSingular',\n      prohibited: ['aria-label']\n    });\n  });\n\n  describe('widget ancestor', () => {\n    it('should allow aria-label', () => {\n      const params = checkSetup(`\n        <button>\n          <span>\n            <span id=\"target\" aria-label=\"hello world\"></span>\n          </span>\n        </button>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should allow aria-labelledby', () => {\n      const params = checkSetup(`\n        <div id=\"foo\">hello world</div>\n        <button>\n          <span>\n            <span id=\"target\" aria-labelledby=\"foo\"></span>\n          </span>\n        </button>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should skip \"role=none\" roles in between ancestor', () => {\n      const params = checkSetup(`\n        <button>\n          <h1 role=\"none\">\n            <span id=\"target\" aria-label=\"hello world\"></span>\n          </h1>\n        </button>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should skip \"role=presentation\" roles in between ancestor', () => {\n      const params = checkSetup(`\n        <a href=\"#\">\n          <h1 role=\"presentation\">\n            <span id=\"target\" aria-label=\"hello world\"></span>\n          </h1>\n        </a>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should not allow aria-label on descendant of non-widget', () => {\n      const params = checkSetup(`\n        <div role=\"grid\">\n          <span>\n            <span id=\"target\" aria-label=\"foo\"></span>\n          </span>\n        </div>\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should not allow aria-labelledby on descendant of non-widget', () => {\n      const params = checkSetup(`\n        <div id=\"foo\">hello world</div>\n        <div role=\"grid\">\n          <span>\n            <span id=\"target\" aria-labelledby=\"foo\"></span>\n          </span>\n        </div>\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should use closet non-presentational ancestor', () => {\n      const params = checkSetup(`\n        <button>\n          <span role=\"grid\">\n            <span id=\"target\" aria-label=\"foo\"></span>\n          </span>\n        </button>\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('should use closet chromium role', () => {\n      const params = checkSetup(`\n        <button>\n          <label>\n            <span id=\"target\" aria-label=\"foo\"></span>\n          </label>\n        </button>\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-required-attr.js",
    "content": "describe('aria-required-attr', () => {\n  const { queryFixture, checkSetup } = axe.testUtils;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const requiredAttrCheck =\n    axe.testUtils.getCheckEvaluate('aria-required-attr');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns true for valid attributes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"switch\" tabindex=\"1\" aria-checked=\"false\">'\n    );\n    assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns false for missing attributes', () => {\n    const params = checkSetup('<div id=\"target\" role=\"switch\" tabindex=\"1\">');\n    assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['aria-checked']);\n  });\n\n  it('returns false for null attributes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"switch\" tabindex=\"1\" aria-checked>'\n    );\n    assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['aria-checked']);\n  });\n\n  it('returns false for empty attributes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"switch\" tabindex=\"1\" aria-checked=\"\">'\n    );\n    assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['aria-checked']);\n  });\n\n  it('returns true if there is no role', () => {\n    const params = checkSetup('<div id=\"target\"></div>');\n    assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  it('passes aria-valuenow if element has value property', () => {\n    const params = checkSetup('<input id=\"target\" type=\"range\" role=\"slider\">');\n    assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n  });\n\n  it('passes aria-valuenow if element has aria-valuetext', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"slider\" aria-valuetext=\"foo\"></div>'\n    );\n    assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n  });\n\n  it('passes aria-checkbox if element has checked property', () => {\n    const params = checkSetup(\n      '<input id=\"target\" type=\"checkbox\" role=\"switch\">'\n    );\n    assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n  });\n\n  describe('separator', () => {\n    it('fails a focusable separator', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"separator\" tabindex=\"0\"></div>'\n      );\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('passes a non-focusable separator', () => {\n      const params = checkSetup('<div id=\"target\" role=\"separator\"></div>');\n      assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n    });\n  });\n\n  describe('combobox', () => {\n    it('passes comboboxes that have aria-expanded=\"false\"', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"false\"></div>'\n      );\n      assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('fails comboboxes without aria-controls and with an invalid aria-expanded', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"invalid-value\"></div>'\n      );\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('fails comboboxes that has aria-owns without aria-controls', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"true\" aria-owns=\"ownedchild\"></div>'\n      );\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('passes comboboxes that have aria-controls and aria-expanded', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"true\" aria-controls=\"test\"></div>'\n      );\n\n      assert.isTrue(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('fails comboboxes that have no required attributes', () => {\n      const params = checkSetup('<div id=\"target\" role=\"combobox\"></div>');\n\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('fails comboboxes that have aria-expanded only', () => {\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"true\"></div>'\n      );\n\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n    });\n\n    it('reports missing of multiple attributes correctly', () => {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            combobox: {\n              requiredAttrs: ['aria-expanded', 'aria-label', 'aria-controls']\n            }\n          }\n        }\n      });\n\n      const params = checkSetup(\n        '<div id=\"target\" role=\"combobox\" aria-expanded=\"true\"></div>'\n      );\n      assert.isFalse(requiredAttrCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, ['aria-label', 'aria-controls']);\n    });\n  });\n\n  describe('options', () => {\n    it('requires provided attribute names for a role', () => {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            mccheddarton: {\n              requiredAttrs: ['aria-valuemax']\n            }\n          }\n        }\n      });\n\n      const vNode = queryFixture('<div role=\"mccheddarton\" id=\"target\"></div>');\n      const options = {\n        mccheddarton: ['aria-snuggles']\n      };\n      assert.isFalse(\n        requiredAttrCheck.call(checkContext, vNode.actualNode, options, vNode)\n      );\n      assert.deepEqual(checkContext._data, ['aria-snuggles', 'aria-valuemax']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/aria-roledescription.js",
    "content": "describe('aria-roledescription', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('returns true for elements with an implicit supported role', function () {\n    var vNode = queryFixture(\n      '<button aria-roledescription=\"Awesome Button\" id=\"target\">Click</button>'\n    );\n    var actual = axe.testUtils.getCheckEvaluate('aria-roledescription').call(\n      checkContext,\n      null,\n      {\n        supportedRoles: ['button']\n      },\n      vNode\n    );\n    assert.equal(actual, true);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns true for elements with an explicit supported role', function () {\n    var vNode = queryFixture(\n      '<div role=\"radio\" aria-roledescription=\"Awesome Radio\" id=\"target\">Click</div>'\n    );\n    var actual = axe.testUtils.getCheckEvaluate('aria-roledescription').call(\n      checkContext,\n      null,\n      {\n        supportedRoles: ['radio']\n      },\n      vNode\n    );\n    assert.equal(actual, true);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns undefined for elements with an unsupported role', function () {\n    var vNode = queryFixture(\n      '<div role=\"main\" aria-roledescription=\"Awesome Main\" id=\"target\">The main element</div>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-roledescription')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(actual, undefined);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false for elements without role', function () {\n    var vNode = queryFixture(\n      '<div aria-roledescription=\"Awesome Main\" id=\"target\">The main element</div>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-roledescription')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(actual, false);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false for elements with role=presentation', function () {\n    var vNode = queryFixture(\n      '<div role=\"presentation\" aria-roledescription=\"Awesome Main\" id=\"target\">The main element</div>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-roledescription')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(actual, false);\n    assert.isNull(checkContext._data, null);\n  });\n\n  it('returns false for elements with role=none', function () {\n    var vNode = queryFixture(\n      '<div role=\"none\" aria-roledescription=\"Awesome Main\" id=\"target\">The main element</div>'\n    );\n\n    var actual = axe.testUtils\n      .getCheckEvaluate('aria-roledescription')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(actual, false);\n    assert.isNull(checkContext._data, null);\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/braille-label-equivalent.js",
    "content": "describe('braille-label-equivalent tests', () => {\n  const { checkSetup, getCheckEvaluate } = axe.testUtils;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkEvaluate = getCheckEvaluate('braille-label-equivalent');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns true without aria-braillelabel', () => {\n    const params = checkSetup('<img id=\"target\" alt=\"\" />');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true when aria-braillelabel is empty', () => {\n    const params = checkSetup(\n      '<img id=\"target\" alt=\"\" aria-braillelabel=\"\" />'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true when aria-braillelabel is whitespace-only', () => {\n    const params = checkSetup(\n      '<img id=\"target\" alt=\"\" aria-braillelabel=\" \\r\\t\\n \" />'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  describe('when aria-braillelabel has text', () => {\n    it('returns false when the accessible name is empty', () => {\n      const params = checkSetup(`\n        <img id=\"target\" alt=\"\" aria-braillelabel=\"foo\" />\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('returns false when the accessible name has only whitespace', () => {\n      const params = checkSetup(`\n        <img id=\"target\" alt=\" \\r\\t\\n \" aria-braillelabel=\"foo\" />\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n    });\n\n    it('returns true when the accessible name is not empty', () => {\n      const params = checkSetup(`\n        <img id=\"target\" alt=\"foo\" aria-braillelabel=\"foo\" />\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/braille-roledescription-equivalent.js",
    "content": "describe('braille-roledescription-equivalent tests', () => {\n  const { checkSetup, getCheckEvaluate } = axe.testUtils;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkEvaluate = getCheckEvaluate('braille-roledescription-equivalent');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns true without aria-brailleroledescription', () => {\n    const params = checkSetup('<div id=\"target\"></div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true when aria-brailleroledecription is empty', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-brailleroledescription=\"\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true when aria-brailleroledecription is whitespace-only', () => {\n    const params = checkSetup(\n      '<div id=\"target\" aria-brailleroledescription=\" \\r\\t\\n \"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  describe('when aria-brailleroledescription has text', () => {\n    it('returns false without aria-roledescription', () => {\n      const params = checkSetup(`\n        <div\n          id=\"target\"\n          aria-brailleroledescription=\"foo\"\n        ></div>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, { messageKey: 'noRoleDescription' });\n    });\n\n    it('returns false when aria-roledescription is empty', () => {\n      const params = checkSetup(`\n        <div\n          id=\"target\"\n          aria-roledescription=\"\"\n          aria-brailleroledescription=\"foo\"\n        ></div>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'emptyRoleDescription'\n      });\n    });\n\n    it('returns false when aria-roledescription has only whitespace', () => {\n      const params = checkSetup(`\n        <div\n          id=\"target\"\n          aria-roledescription=\" \\r\\t\\n \"\n          aria-brailleroledescription=\"foo\"\n        ></div>\n      `);\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'emptyRoleDescription'\n      });\n    });\n\n    it('returns true when aria-roledescription is not empty', () => {\n      const params = checkSetup(`\n        <div\n          id=\"target\"\n          aria-roledescription=\"foo\"\n          aria-brailleroledescription=\"foo\"\n        ></div>\n      `);\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/deprecatedrole.js",
    "content": "describe('deprecatedrole', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('deprecatedrole');\n  afterEach(function () {\n    checkContext.reset();\n    axe.reset();\n  });\n\n  it('returns true if applied to a deprecated role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          melon: {\n            type: 'widget',\n            deprecated: true\n          }\n        }\n      }\n    });\n    var params = checkSetup('<div id=\"target\" role=\"melon\">Contents</div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, 'melon');\n  });\n\n  it('returns true if applied to a deprecated DPUB role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          'doc-fizzbuzz': {\n            type: 'widget',\n            deprecated: true\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<div id=\"target\" role=\"doc-fizzbuzz\">Contents</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, 'doc-fizzbuzz');\n  });\n\n  it('returns false if applied to a non-deprecated role', function () {\n    var params = checkSetup('<div id=\"target\" role=\"button\">Contents</div>');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n\n    var params = checkSetup('<button id=\"target\">Contents</button>');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns false if applied to an invalid role', function () {\n    var params = checkSetup('<input id=\"target\" role=\"foo\">');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  describe('with fallback roles', function () {\n    it('returns true if the deprecated role is the first valid role', function () {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            melon: {\n              type: 'widget',\n              deprecated: true\n            }\n          }\n        }\n      });\n      var params = checkSetup(\n        '<div id=\"target\" role=\"foo widget melon button\">Contents</div>'\n      );\n      assert.isTrue(checkEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, 'melon');\n    });\n\n    it('returns false if the deprecated role is not the first valid role', function () {\n      axe.configure({\n        standards: {\n          ariaRoles: {\n            melon: {\n              type: 'widget',\n              deprecated: true\n            }\n          }\n        }\n      });\n      var params = checkSetup(\n        '<div id=\"target\" role=\"button melon widget\">Contents</div>'\n      );\n      assert.isFalse(checkEvaluate.apply(checkContext, params));\n      assert.isNull(checkContext._data);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/errormessage.js",
    "content": "describe('aria-errormessage', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return false if aria-errormessage value is invalid', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"plain\" aria-invalid=\"true\">' +\n        '<div id=\"plain\"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return undefined if aria-errormessage references an element that does not exist', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"plain\" aria-invalid=\"true\">' +\n        '<div></div>'\n    );\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true if aria-errormessage id is alert', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"alert\" aria-invalid=\"true\">' +\n        '<div id=\"alert\" role=\"alert\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true if aria-errormessage id is aria-live=assertive', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"live\" aria-invalid=\"true\">' +\n        '<div id=\"live\" aria-live=\"assertive\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true if aria-errormessage id is aria-describedby', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"plain\" aria-describedby=\"plain\" aria-invalid=\"true\">' +\n        '<div id=\"plain\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false if aria-errormessage has multiple ids (unsupported)', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" aria-invalid=\"true\" aria-describedby=\"error1 error2\" aria-errormessage=\"error1 error2\">' +\n        '<div id=\"error1\">Error 1</div>' +\n        '<div id=\"error2\">Error 2</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unsupported',\n      values: ['error1', 'error2']\n    });\n  });\n\n  it('should return false if aria-errormessage has multiple ids even when one is in aria-describedby', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" aria-invalid=\"true\" aria-describedby=\"error1\" aria-errormessage=\"error1 error2\">' +\n        '<div id=\"error1\">Error 1</div>' +\n        '<div id=\"error2\">Error 2</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unsupported',\n      values: ['error1', 'error2']\n    });\n  });\n\n  it('should return false if aria-errormessage has multiple ids even when none are in aria-describedby', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" aria-invalid=\"true\" aria-describedby=\"other\" aria-errormessage=\"error1 error2\">' +\n        '<div id=\"other\">Other</div>' +\n        '<div id=\"error1\">Error 1</div>' +\n        '<div id=\"error2\">Error 2</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unsupported',\n      values: ['error1', 'error2']\n    });\n  });\n\n  it('sets an unsupported message when aria-errormessage contains multiple ids', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\" foo  bar \\tbaz  \" aria-invalid=\"true\">' +\n        '<div id=\"plain\"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unsupported',\n      values: ['foo', 'bar', 'baz']\n    });\n  });\n\n  it('returns true when aria-errormessage is empty, if that is allowed', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-errormessage': {\n            allowEmpty: true\n          }\n        }\n      }\n    });\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\" \" aria-invalid=\"true\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true when aria-invalid is not set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"plain\">' + '<div id=\"plain\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true when aria-invalid=false', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\"plain\" aria-invalid=\"false\">' +\n        '<div id=\"plain\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('returns false when aria-errormessage is empty, if that is not allowed', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-errormessage': {\n            allowEmpty: false\n          }\n        }\n      }\n    });\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-errormessage=\" \" aria-invalid=\"true\"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false when hidden attribute is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" hidden>Error message 1</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'hidden',\n      values: ['id-message-1']\n    });\n  });\n\n  it('should return false when display: \"none\" is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" style=\"display: none\">Error message 1</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'hidden',\n      values: ['id-message-1']\n    });\n  });\n\n  it('should return false when visibility: \"hidden\" is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" style=\"visibility: hidden\">Error message 1</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'hidden',\n      values: ['id-message-1']\n    });\n  });\n\n  it('should return false when aria-hidden=true is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" aria-hidden=\"true\">Error message 1</div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'hidden',\n      values: ['id-message-1']\n    });\n  });\n\n  it('should return true when aria-hidden=false is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" aria-live=\"assertive\" aria-hidden=\"false\">Error message 1</div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true when no hidden functionality is used', function () {\n    var vNode = queryFixture(\n      '<input type=\"text\" id=\"target\" aria-invalid=\"true\" aria-errormessage=\"id-message-1\">' +\n        '<div id=\"id-message-1\" aria-live=\"assertive\">Error message 1</div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-errormessage')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return undefined if aria-errormessage value crosses shadow boundary',\n    function () {\n      var params = shadowCheckSetup(\n        '<div id=\"target\" aria-errormessage=\"live\" aria-invalid=\"true\"></div>',\n        '<div id=\"live\" aria-live=\"assertive\"></div>'\n      );\n      assert.isUndefined(\n        axe.testUtils\n          .getCheckEvaluate('aria-errormessage')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false if aria-errormessage and invalid reference are both inside shadow dom',\n    function () {\n      var params = shadowCheckSetup(\n        '<div></div>',\n        '<div id=\"target\" aria-errormessage=\"live\" aria-invalid=\"true\"></div>' +\n          '<div id=\"live\"></div>'\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-errormessage')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return true if aria-errormessage and valid reference are both inside shadow dom',\n    function () {\n      var params = shadowCheckSetup(\n        '<div></div>',\n        '<div id=\"target\" aria-errormessage=\"live\" aria-invalid=\"true\"></div>' +\n          '<div id=\"live\" aria-live=\"assertive\"></div>'\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('aria-errormessage')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  describe('SerialVirtualNode', function () {\n    it('should return undefined', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-invalid': 'true',\n          'aria-errormessage': 'test'\n        }\n      });\n      assert.isUndefined(\n        axe.testUtils\n          .getCheckEvaluate('aria-errormessage')\n          .call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'idrefs',\n        values: ['test']\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/fallbackrole.js",
    "content": "describe('fallbackrole', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if fallback role is used', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"button foobar\">Foo</div>'\n    );\n    assert.isTrue(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return false if fallback role is not used', function () {\n    var virtualNode = queryFixture('<div id=\"target\" role=\"button\">Foo</div>');\n    assert.isFalse(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return false if applied to an invalid role', function () {\n    var virtualNode = queryFixture('<div id=\"target\" role=\"foobar\">Foo</div>');\n    assert.isFalse(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return false if applied to an invalid role', function () {\n    var virtualNode = queryFixture('<div id=\"target\" role=\"foobar\">Foo</div>');\n    assert.isFalse(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return undefined/needs review if an element with no implicit role uses both none and presentation', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"none presentation\">Foo</div>'\n    );\n    assert.isUndefined(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return undefined/needs review if an element with no implicit role uses both presentation and none', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"presentation none\">Foo</div>'\n    );\n    assert.isUndefined(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n\n  it('should return true if an element with an implicit role uses both presentation and none', function () {\n    var virtualNode = queryFixture(\n      '<input type=\"text\" id=\"target\" role=\"presentation none\"/>'\n    );\n    assert.isTrue(\n      checks.fallbackrole.evaluate(virtualNode.actualNode, null, virtualNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/has-global-aria-attribute.js",
    "content": "describe('has-global-aria-attribute', function () {\n  'use strict';\n\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var hasGlobalAriaAttribute = axe.testUtils.getCheckEvaluate(\n    'has-global-aria-attribute'\n  );\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if any global ARIA attributes are found', function () {\n    var params = checkSetup('<div aria-label=\"hello\" id=\"target\"></div>');\n    assert.isTrue(hasGlobalAriaAttribute.apply(checkContext, params));\n  });\n\n  it('should return false if no valid ARIA attributes are found', function () {\n    var params = checkSetup('<div aria-random=\"hello\" id=\"target\"></div>');\n    assert.isFalse(hasGlobalAriaAttribute.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/has-widget-role.js",
    "content": "describe('has-widget-role', () => {\n  const queryFixture = axe.testUtils.queryFixture;\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext._data = null;\n  });\n\n  it('should return false for elements with no role', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for elements with nonsensical roles', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"buttonbuttonbutton\"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  // Widget roles\n  it('should return true for role=button', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"button\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=checkbox', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"checkbox\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=gridcell', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"gridcell\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=link', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"link\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=menuitem', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"menuitem\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=menuitemcheckbox', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"menuitemcheckbox\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=menuitemradio', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"menuitemradio\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=option', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"option\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=progressbar', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"progressbar\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=radio', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"radio\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=scrollbar', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"scrollbar\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=searchbox', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"searchbox\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=slider', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"slider\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=spinbutton', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"spinbutton\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=switch', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"switch\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=tab', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"tab\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=textbox', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"textbox\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=treeitem', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"treeitem\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  // Composite widget roles\n  it('should return true for role=combobox', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"combobox\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=grid', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"grid\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=listbox', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"listbox\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=menu', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"menu\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=menubar', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"menubar\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=radiogroup', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"radiogroup\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=tablist', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"tablist\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=tree', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"tree\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true for role=treegrid', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"treegrid\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=application', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"application\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=article', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"article\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=cell', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"cell\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=columnheader', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"columnheader\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=definition', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"definition\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=directory', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"directory\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=document', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"document\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=feed', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"feed\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=figure', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"figure\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=group', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"group\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=heading', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"heading\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=img', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"img\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=list', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"list\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=listitem', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"listitem\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=math', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"math\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=none', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"none\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=note', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"note\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=presentation', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"presentation\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=row', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"row\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=rowgroup', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"rowgroup\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=rowheader', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"rowheader\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=table', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"table\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=tabpanel', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"tabpanel\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=term', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"term\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=toolbar', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"toolbar\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  // Landmark Roles\n  it('should return false for role=banner', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"banner\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=complementary', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"complementary\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=contentinfo', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"contentinfo\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=form', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"form\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=main', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"main\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=navigation', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"navigation\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=region', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"region\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return false for role=search', () => {\n    const vNode = queryFixture('<div id=\"target\" role=\"search\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-widget-role')\n        .call(checkContext, null, null, vNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/is-element-focusable.js",
    "content": "describe('is-element-focusable', function () {\n  'use strict';\n\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var isFocusable = axe.testUtils.getCheckEvaluate('is-element-focusable');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true for div with a tabindex', function () {\n    var params = checkSetup('<div tabIndex=\"1\" id=\"target\"></div>');\n    assert.isTrue(isFocusable.apply(checkContext, params));\n  });\n\n  it('should return false for natively unfocusable element', function () {\n    var params = checkSetup('<span role=\"link\" href=\"#\" id=\"target\"></span>');\n    assert.isFalse(isFocusable.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/no-implicit-explicit-label.js",
    "content": "describe('no-implicit-explicit-label', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var check = checks['no-implicit-explicit-label'];\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('returns false when there is no label text or accessible text', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" role=\"searchbox\" contenteditable=\"true\"></div>'\n    );\n    var actual = check.evaluate.call(checkContext, null, {}, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns undefined when there is no accessible text', function () {\n    var vNode = queryFixture(\n      '<label for=\"target\">Choose currency:</label><div id=\"target\" role=\"searchbox\" contenteditable=\"true\"></div>'\n    );\n    var actual = check.evaluate.call(checkContext, null, {}, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when accessible text does not contain label text', function () {\n    var vNode = queryFixture(\n      '<label for=\"target\">Choose country:</label><div id=\"target\" aria-label=\"country\" role=\"combobox\">England</div>'\n    );\n    var actual = check.evaluate.call(checkContext, null, {}, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns false when accessible text contains label text', function () {\n    var vNode = queryFixture(\n      '<label for=\"target\">Country</label><div id=\"target\" aria-label=\"Choose country\" role=\"combobox\">England</div>'\n    );\n    var actual = check.evaluate.call(checkContext, null, {}, vNode);\n    assert.isFalse(actual);\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return false if there is no parent', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          role: 'combobox',\n          'aria-label': 'woohoo'\n        }\n      });\n      serialNode.parent = null;\n\n      var actual = check.evaluate.call(checkContext, null, {}, serialNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return undefined if incomplete tree', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          role: 'combobox',\n          'aria-label': 'woohoo'\n        }\n      });\n\n      var actual = check.evaluate.call(checkContext, null, {}, serialNode);\n      assert.isUndefined(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/required-children.js",
    "content": "describe('aria-required-children', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkSetup = axe.testUtils.checkSetup;\n  const requiredChildrenCheck = axe.testUtils.getCheckEvaluate(\n    'aria-required-children'\n  );\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should detect missing sole required child', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\"><p>Nothing here.</p></div>'\n    );\n\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['listitem']);\n  });\n\n  (shadowSupported ? it : xit)(\n    'should detect missing sole required child in shadow tree',\n    () => {\n      fixture.innerHTML = '<div id=\"target\" role=\"list\"></div>';\n\n      const target = document.querySelector('#target');\n      const shadowRoot = target.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<p>Nothing here.</p>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const virtualTarget = axe.utils.getNodeFromTree(target);\n\n      const params = [target, undefined, virtualTarget];\n      assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, ['listitem']);\n    }\n  );\n\n  it('should detect multiple missing required children when one required', () => {\n    const params = checkSetup(\n      '<div role=\"grid\" id=\"target\"><p>Nothing here.</p></div>'\n    );\n\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['rowgroup', 'row']);\n  });\n\n  (shadowSupported ? it : xit)(\n    'should detect missing multiple required children in shadow tree when one required',\n    () => {\n      fixture.innerHTML = '<div role=\"grid\" id=\"target\"></div>';\n\n      const target = document.querySelector('#target');\n      const shadowRoot = target.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<p>Nothing here.</p>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const virtualTarget = axe.utils.getNodeFromTree(target);\n\n      const params = [target, undefined, virtualTarget];\n      assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, ['rowgroup', 'row']);\n    }\n  );\n\n  it('should pass all existing required children when all required', () => {\n    const params = checkSetup(\n      `<div id=\"target\" role=\"menu\">\n        <li role=\"none\"></li>\n        <li role=\"menuitem\">Item 1</li>\n        <div role=\"menuitemradio\">Item 2</div>\n        <div role=\"menuitemcheckbox\">Item 3</div>\n      </div>`\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should return undefined when element is empty and is in reviewEmpty options', () => {\n    const params = checkSetup('<div role=\"list\" id=\"target\"></div>', {\n      reviewEmpty: ['list']\n    });\n    assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should return false when children do not have correct role and is in reviewEmpty options', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\"><div role=\"menuitem\"></div></div>',\n      { reviewEmpty: ['list'] }\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should return false when owned children do not have correct role and is in reviewEmpty options', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\" aria-owns=\"ownedchild\"></div><div id=\"ownedchild\" role=\"menuitem\"></div>',\n      { reviewEmpty: ['list'] }\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when list does not have required children listitem', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\"><span>Item 1</span></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.deepEqual(checkContext._data, ['listitem']);\n  });\n\n  it('should pass when missing required children but aria-busy', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-busy=\"true\"><span>Item 1</span></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.deepEqual(checkContext._data, { messageKey: 'aria-busy' });\n  });\n\n  it('should treat aria-busy=\"false\" as not aria-busy', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-busy=\"false\"><span>Item 1</span></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should treat valueless aria-busy as not aria-busy', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-busy><span>Item 1</span></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail list with an unallowed child', () => {\n    const params = checkSetup(`\n      <div id=\"target\" role=\"list\"><div role=\"tabpanel\"></div></div>\n    `);\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unallowed',\n      values: '[role=tabpanel]'\n    });\n  });\n\n  it('should fail list with an unallowed child but show a custom message, even if aria-busy=\"true\"', () => {\n    const params = checkSetup(`\n      <div id=\"target\" role=\"list\" aria-busy=\"true\"><div role=\"tabpanel\"></div></div>\n    `);\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'aria-busy-fail',\n      values: '[role=tabpanel]'\n    });\n  });\n\n  it('should fail when list has intermediate child with role that is not a required role', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\"><div role=\"tabpanel\"><div role=\"listitem\">List item 1</div></div></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    const unallowed = axe.utils.querySelectorAll(\n      axe._tree,\n      '[role=\"tabpanel\"]'\n    )[0];\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unallowed',\n      values: '[role=tabpanel]'\n    });\n    assert.deepEqual(checkContext._relatedNodes, [unallowed]);\n  });\n\n  it('should fail when list has child with global aria attribute but no role', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\"><div aria-live=\"polite\"><div role=\"listitem\">List item 1</div></div></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    const unallowed = axe.utils.querySelectorAll(\n      axe._tree,\n      '[aria-live=\"polite\"]'\n    )[0];\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unallowed',\n      values: 'div[aria-live]'\n    });\n    assert.deepEqual(checkContext._relatedNodes, [unallowed]);\n  });\n\n  it('should fail when list has child with tabindex but no role', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\"><div tabindex=\"0\"><div role=\"listitem\">List item 1</div></div></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    const unallowed = axe.utils.querySelectorAll(\n      axe._tree,\n      '[tabindex=\"0\"]'\n    )[0];\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unallowed',\n      values: 'div[tabindex]'\n    });\n    assert.deepEqual(checkContext._relatedNodes, [unallowed]);\n  });\n\n  it('should remove duplicate unallowed selectors', () => {\n    const params = checkSetup(`\n      <div id=\"target\" role=\"list\">\n        <div role=\"tabpanel\"></div>\n        <div role=\"listitem\">List item 1</div>\n        <div role=\"tabpanel\"></div>\n      </div>\n    `);\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'unallowed',\n      values: '[role=tabpanel]'\n    });\n  });\n\n  it('should pass when list has child with aria-hidden', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\">' +\n        '<div aria-hidden=\"true\">Ignore item</div>' +\n        '<div role=\"listitem\">List item 1</div>' +\n        '</div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass when list has child with aria-hidden and is focusable', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\">' +\n        '<div aria-hidden=\"true\" tabindex=\"0\">Ignore item</div>' +\n        '<div role=\"listitem\">List item 1</div>' +\n        '</div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when nested child with role row does not have required child role cell', () => {\n    const params = checkSetup(\n      '<div  role=\"grid\"><div role=\"row\" id=\"target\"><span>Item 1</span></div></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n    assert.includeMembers(checkContext._data, ['cell']);\n  });\n\n  it('should pass one indirectly aria-owned child when one required', () => {\n    const params = checkSetup(\n      '<div role=\"grid\" id=\"target\" aria-owns=\"r\"></div><div id=\"r\"><div role=\"row\">Nothing here.</div></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should not break if aria-owns points to non-existent node', () => {\n    const params = checkSetup(\n      '<div role=\"row\" id=\"target\" aria-owns=\"nonexistent\"></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass one existing aria-owned child when one required', () => {\n    const params = checkSetup(\n      '<div role=\"grid\" id=\"target\" aria-owns=\"r\"></div><p id=\"r\" role=\"row\">Nothing here.</p>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail one existing aria-owned child when an intermediate child with role that is not a required role exists', () => {\n    const params = checkSetup(\n      '<div id=\"target\" role=\"list\" aria-owns=\"list\"></div><div id=\"list\"><div role=\"tabpanel\"><div role=\"listitem\"></div></div></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass one existing required child when one required (has explicit role of tab)', () => {\n    const params = checkSetup(\n      '<ul id=\"target\" role=\"tablist\"><li role=\"tab\">Tab 1</li><li role=\"tab\">Tab 2</li></ul>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass required child roles (grid contains row, which contains cell)', () => {\n    const params = checkSetup(\n      '<table id=\"target\" role=\"grid\"><tr role=\"row\"><td role=\"cell\">Item 1</td></tr></table>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass one existing required child when one required', () => {\n    const params = checkSetup(\n      '<div role=\"grid\" id=\"target\"><p role=\"row\">Nothing here.</p></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass one existing required child when one required because of implicit role', () => {\n    const params = checkSetup(\n      '<table id=\"target\"><p role=\"row\">Nothing here.</p></table>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass when a child with an implicit role is present', () => {\n    const params = checkSetup(\n      '<table role=\"grid\" id=\"target\"><tr><td>Nothing here.</td></tr></table>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass direct existing required children', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\"><p role=\"listitem\">Nothing here.</p></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass indirect required children', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\"><p>Just a regular ol p that contains a... <p role=\"listitem\">Nothing here.</p></p></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should return true when a role has no required owned', () => {\n    const params = checkSetup(\n      '<div role=\"listitem\" id=\"target\"><p>Nothing here.</p></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass when role allows group and group has required child', () => {\n    const params = checkSetup(\n      '<div role=\"menu\" id=\"target\"><ul role=\"group\"><li role=\"menuitem\">Menuitem</li></ul></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should ignore hidden children inside the group', () => {\n    const params = checkSetup(`\n      <div role=\"menu\" id=\"target\">\n        <ul role=\"group\">\n          <li style=\"display: none\">hidden</li>\n          <li aria-hidden=\"true\">hidden</li>\n          <li style=\"visibility: hidden\" aria-hidden=\"true\">hidden</li>\n          <li role=\"menuitem\">Menuitem</li>\n        </ul>\n      </div>\n    `);\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when role allows group and group does not have required child', () => {\n    const params = checkSetup(\n      '<div role=\"menu\" id=\"target\"><ul role=\"group\"><li>Menuitem</li></ul></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when role does not allow group', () => {\n    const params = checkSetup(\n      '<div role=\"list\" id=\"target\"><ul role=\"group\"><li role=\"listitem\">Item</li></ul></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should pass when role allows rowgroup and rowgroup has required child', () => {\n    const params = checkSetup(\n      '<div role=\"table\" id=\"target\"><ul role=\"rowgroup\"><li role=\"row\">Row</li></ul></div>'\n    );\n    assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when role allows rowgroup and rowgroup does not have required child', () => {\n    const params = checkSetup(\n      '<div role=\"table\" id=\"target\"><ul role=\"rowgroup\"><li>Row</li></ul></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  it('should fail when role does not allow rowgroup', () => {\n    const params = checkSetup(\n      '<div role=\"listbox\" id=\"target\"><ul role=\"rowgroup\"><li role=\"option\">Option</li></ul></div>'\n    );\n    assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n  });\n\n  describe('options', () => {\n    it('should not throw when options is incorrect', () => {\n      const params = checkSetup('<div role=\"row\" id=\"target\"></div>');\n\n      // Options: (incorrect)\n      params[1] = ['menu'];\n      assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n      // Options: (incorrect)\n      params[1] = null;\n      assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n      // Options: (incorrect)\n      params[1] = 'menu';\n      assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n    });\n\n    describe('reviewEmpty', () => {\n      it('should return undefined instead of false when the role is in options.reviewEmpty', () => {\n        const params = checkSetup('<div role=\"grid\" id=\"target\"></div>', {\n          reviewEmpty: []\n        });\n        assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n\n        // Options:\n        params[1] = {\n          reviewEmpty: ['grid']\n        };\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return true if aria-busy preempts a reviewEmpty case', () => {\n        const params = checkSetup(\n          '<div role=\"grid\" id=\"target\" aria-busy=\"true\"></div>',\n          { reviewEmpty: ['grid'] }\n        );\n        assert.isTrue(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when the element has empty children', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"><div></div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return false when the element has empty child with role', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"><div role=\"grid\"></div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when there is a empty text node', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"> &nbsp; <!-- empty --> \\n\\t </div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return false when there is a non-empty text node', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\">  hello  </div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when the element has empty child with role=presentation', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"><div role=\"presentation\"></div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return false when role=none child has visible content', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"><div role=\"none\">hello</div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isFalse(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when role=none child has hidden content', () => {\n        const params = checkSetup(\n          `<div role=\"listbox\" id=\"target\">\n            <div role=\"none\">\n              <h1 style=\"display:none\">hello</h1>\n              <h1 aria-hidden=\"true\">hello</h1>\n              <h1 style=\"visibility:hidden\" aria-hidden=\"true\">hello</h1>\n            </div>\n          </div>`,\n          { reviewEmpty: ['listbox'] }\n        );\n\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when the element has empty child with role=none', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\"><div role=\"none\"></div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when the element has hidden children', () => {\n        const params = checkSetup(\n          `<div role=\"menu\" id=\"target\">\n            <div role=\"menuitem\" hidden></div>\n            <div role=\"none\" hidden></div>\n            <div role=\"list\" hidden></div>\n          </div>`,\n          { reviewEmpty: ['menu'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n\n      it('should return undefined when the element has empty child and aria-label', () => {\n        const params = checkSetup(\n          '<div role=\"listbox\" id=\"target\" aria-label=\"listbox\"><div></div></div>',\n          { reviewEmpty: ['listbox'] }\n        );\n        assert.isUndefined(requiredChildrenCheck.apply(checkContext, params));\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/required-parent.js",
    "content": "describe('aria-required-parent', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('should detect missing required parent', function () {\n    var params = checkSetup(\n      '<div><p role=\"listitem\" id=\"target\">Nothing here.</p></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n    assert.deepEqual(checkContext._data, ['list']);\n  });\n\n  (shadowSupported ? it : xit)(\n    'should detect missing required parent across shadow boundary',\n    function () {\n      fixture.innerHTML = '<div id=\"target\"></div>';\n\n      var shadowRoot = document\n        .querySelector('#target')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<p role=\"listitem\" id=\"target\">Nothing here.</p>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var shadowContent = shadowRoot.querySelector('#target');\n      var virtualTarget = axe.utils.getNodeFromTree(shadowContent);\n\n      var params = [shadowContent, undefined, virtualTarget];\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n      assert.deepEqual(checkContext._data, ['list']);\n    }\n  );\n\n  it('should pass when required parent is present in an ancestral aria-owns context', function () {\n    var snippet =\n      '<div role=\"list\"><div aria-owns=\"parent\"></div></div>' +\n      '<div id=\"parent\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div>';\n    var params = checkSetup(snippet);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should fail when wrong role is present in an aria-owns context', function () {\n    var params = checkSetup(\n      '<div role=\"menu\"><div aria-owns=\"target\"></div></div>' +\n        '<div><p role=\"listitem\" id=\"target\">Nothing here.</p></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n    assert.deepEqual(checkContext._data, ['list']);\n  });\n\n  it('should pass when required parent is present in an aria-owns context', function () {\n    var params = checkSetup(\n      '<div role=\"list\" aria-owns=\"target\"></div><div><p role=\"listitem\" id=\"target\">Nothing here.</p></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass when at least one required parent of multiple is present', function () {\n    var params = checkSetup(\n      '<div role=\"grid\"><p role=\"row\" id=\"target\">Nothing here.</p></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass when required parent is present', function () {\n    var params = checkSetup(\n      '<div role=\"list\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should fail when there is an intermediate role between the child and parent', function () {\n    var params = checkSetup(\n      '<div role=\"list\"><div role=\"tabpanel\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass when intermediate node is role=presentation', function () {\n    var params = checkSetup(\n      '<div role=\"list\"><div role=\"presentation\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass when intermediate node is role=group and required parent is present', function () {\n    var params = checkSetup(\n      '<ul role=\"menu\"><li role=\"group\"><button role=\"menuitem\" id=\"target\">Nothing here.</button></li></ul>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should fail when intermediate node is role=group but required parent is missing', function () {\n    var params = checkSetup(\n      '<ul role=\"none\"><li role=\"group\"><button role=\"menuitem\" id=\"target\">Nothing here.</button></li></ul>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n    assert.deepEqual(checkContext._data, ['menu', 'menubar']);\n  });\n\n  it('should fail when intermediate node is role=group but this not an allowed context', function () {\n    var params = checkSetup(\n      '<div role=\"menu\"><div role=\"group\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  describe('group with ownGroupRoles', function () {\n    it('should pass when the role and grand parent role is in ownGroupRoles', function () {\n      var params = checkSetup(\n        '<div role=\"tree\">' +\n          '<div role=\"treeitem\">' +\n          '<div role=\"group\">' +\n          '<div role=\"treeitem\" id=\"target\">' +\n          '</div></div></div></div>',\n        {\n          ownGroupRoles: ['treeitem']\n        }\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('should fail when the role and grand parent role is in ownGroupRoles', function () {\n      var params = checkSetup(\n        '<div role=\"menu\">' +\n          '<div role=\"menuitem\">' +\n          '<div role=\"group\">' +\n          '<div role=\"menuitem\" id=\"target\">' +\n          '</div></div></div></div>',\n        {\n          ownGroupRoles: ['listitem']\n        }\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('should fail when the role is not in a group', function () {\n      var params = checkSetup(\n        '<div role=\"list\">' +\n          '<div role=\"listitem\">' +\n          '<div role=\"none\">' +\n          '<div role=\"listitem\" id=\"target\">' +\n          '</div></div></div></div>',\n        {\n          ownGroupRoles: ['listitem']\n        }\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n    });\n  });\n\n  it('should pass when intermediate node is role=none', function () {\n    var params = checkSetup(\n      '<div role=\"list\"><div role=\"none\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass when intermediate node is not owned by parent', function () {\n    var params = checkSetup(\n      '<div role=\"list\" aria-owns=\"target\"><div role=\"navigation\"><p role=\"listitem\" id=\"target\">Nothing here.</p></div></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass for multiple group and presentational roles', function () {\n    var params = checkSetup(\n      '<div role=\"tree\"><div role=\"none\"><div role=\"group\"><div role=\"none\"><div role=\"group\"><div role=\"treeitem\" id=\"target\">Nothing here.</div></div></div></div></div></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-required-parent')\n        .apply(checkContext, params)\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should pass when required parent is present across shadow boundary',\n    function () {\n      fixture.innerHTML = '<div role=\"list\" id=\"parent\"></div>';\n\n      var shadowRoot = document\n        .querySelector('#parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<p role=\"listitem\" id=\"target\">Nothing here.</p>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var shadowContent = shadowRoot.querySelector('#target');\n      var virtualTarget = axe.utils.getNodeFromTree(shadowContent);\n\n      var params = [shadowContent, undefined, virtualTarget];\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should fail when aria-owns context crosses shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"parent\"><div role=\"list\" aria-owns=\"target\"></div></div>';\n\n      var shadowRoot = document\n        .querySelector('#parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<p role=\"listitem\" id=\"target\">Nothing here.</p>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var shadowContent = shadowRoot.querySelector('#target');\n      var virtualTarget = axe.utils.getNodeFromTree(shadowContent);\n\n      var params = [shadowContent, undefined, virtualTarget];\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('aria-required-parent')\n          .apply(checkContext, params)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/aria/unsupportedattr.js",
    "content": "describe('unsupportedattr', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var check = checks['aria-unsupported-attr'];\n\n  afterEach(function () {\n    checkContext.reset();\n    axe.reset();\n  });\n\n  it('should return true if applied to an unsupported attribute', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var params = checkSetup(\n      '<div id=\"target\" aria-mccheddarton=\"true\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return true with multiple unsupported and supported attributes', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: true\n          },\n          'aria-bagleypants': {\n            unsupported: true\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<div id=\"target\" aria-mccheddarton=\"true\" aria-bagleypants=\"false\" aria-label=\"Nope\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, [\n      'aria-mccheddarton',\n      'aria-bagleypants'\n    ]);\n  });\n\n  it('should return false if applied to a supported attribute', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-label=\"This is fine\">Contents</div>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return false if all ARIA attributes are supported', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-label=\"This is fine\" aria-haspopup=\"true\">Contents</div>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return false if applied to an element that matches the unsupported \"exceptions\" list', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: {\n              exceptions: ['button']\n            }\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<button id=\"target\" aria-mccheddarton=\"true\">Contents</button>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return false if applied to an element that matches the unsupported \"exceptions\" list using complex conditions', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: {\n              exceptions: [\n                {\n                  nodeName: 'input',\n                  properties: {\n                    type: 'checkbox'\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<input type=\"checkbox\" id=\"target\" aria-mccheddarton=\"true\">'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return true if applied to an element that does not match the unsupported \"exceptions\" list', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: {\n              exceptions: ['button']\n            }\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<div id=\"target\" aria-mccheddarton=\"true\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return true if applied to an element that does not match the unsupported \"exceptions\" list using complex conditions', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: {\n              exceptions: [\n                {\n                  nodeName: 'input',\n                  properties: {\n                    type: 'checkbox'\n                  }\n                }\n              ]\n            }\n          }\n        }\n      }\n    });\n    var params = checkSetup(\n      '<input type=\"radio\" id=\"target\" aria-mccheddarton=\"true\">'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/unsupportedrole.js",
    "content": "describe('unsupportedrole', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var check = checks.unsupportedrole;\n  afterEach(function () {\n    checkContext.reset();\n    axe.reset();\n  });\n\n  it('should return true if applied to an unsupported role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          mccheddarton: {\n            type: 'widget',\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var params = checkSetup(\n      '<div id=\"target\" role=\"mccheddarton\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, 'mccheddarton');\n  });\n\n  it('should return false if applied to a supported role', function () {\n    var params = checkSetup('<div id=\"target\" role=\"alert\">Contents</div>');\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n\n    var params = checkSetup('<button id=\"target\">Contents</button>');\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if applied to an invalid role', function () {\n    var params = checkSetup('<input id=\"target\" role=\"foo\">');\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true if applied to an unsupported dpub role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          'doc-abstract': {\n            type: 'section',\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var params = checkSetup(\n      '<div id=\"target\" role=\"doc-abstract\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, 'doc-abstract');\n  });\n\n  it('should return true if applied to an unsupported fallback role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          alert: {\n            type: 'widget',\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var params = checkSetup(\n      '<div id=\"target\" role=\"unsupported alert\">Contents</div>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, 'alert');\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/valid-attr-value.js",
    "content": "describe('aria-valid-attr-value', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var validAttrValueCheck = axe.testUtils.getCheckEvaluate(\n    'aria-valid-attr-value'\n  );\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should not check the validity of attribute names', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-cats=\"true\" aria-selected=\"true\"></div>'\n    );\n\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true if all values are valid', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-selected=\"true\" aria-checked=\"true\" aria-relevant=\"additions removals\"></div>'\n    );\n\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true if idref(s) values are valid', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-owns=\"test_tgt1 test_tgt2\" aria-activedescendant=\"test_tgt1\"></div>' +\n        '<div id=\"test_tgt1\"></div>' +\n        '<div id=\"test_tgt2\"></div>'\n    );\n\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if any values are invalid', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-live=\"polite\" aria-selected=\"0\"></div>'\n    );\n\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data, ['aria-selected=\"0\"']);\n  });\n\n  it('should allow empty strings rather than idref', function () {\n    var tree = fixtureSetup(\n      '<button aria-controls=\"\">Button</button>' +\n        '<div aria-activedescendant=\"\"></div>'\n    );\n    var passing1 = tree.children[0];\n    var passing2 = tree.children[1];\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, passing1));\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, passing2));\n  });\n\n  it('should allow empty strings rather than idrefs', function () {\n    var tree = fixtureSetup(\n      '<button aria-labelledby=\"\">Button</button>' + '<div aria-owns=\"\"></div>'\n    );\n    var passing1 = tree.children[0];\n    var passing2 = tree.children[1];\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, passing1));\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, passing2));\n  });\n\n  it('should pass on aria-controls and aria-expanded=false when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\" aria-expanded=\"false\">Button</button>'\n    );\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should pass on aria-controls and aria-selected=false when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\" aria-selected=\"false\">Button</button>'\n    );\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-controls and aria-expanded=true when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\" aria-expanded=\"true\">Button</button>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-controls and aria-selected=true when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\" aria-selected=\"true\">Button</button>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-controls when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\">Button</button>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should return undefined on aria-controls with aria-haspopup as we cannot determine if it is in the DOM later', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-controls=\"test\" aria-haspopup=\"true\">Button</button>'\n    );\n    assert.isUndefined(\n      validAttrValueCheck.call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'controlsWithinPopup',\n      needsReview: 'aria-controls=\"test\"'\n    });\n  });\n\n  it('should pass on aria-owns and aria-expanded=false when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-owns=\"test\" aria-expanded=\"false\">Button</button>'\n    );\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-owns and aria-expanded=true when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-owns=\"test\" aria-expanded=\"true\">Button</button>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-owns when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-owns=\"test\">Button</button>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should fail on aria-level when the value is less than 1', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" role=\"heading\" aria-level=\"0\">Heading</div>'\n    );\n    assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should return undefined on aria-describedby when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-describedby=\"test\">Button</button>'\n    );\n    assert.isUndefined(\n      validAttrValueCheck.call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'noId',\n      needsReview: 'aria-describedby=\"test\"'\n    });\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return undefined on aria-describedby when the element is in a different shadow tree',\n    function () {\n      var params = shadowCheckSetup(\n        '<div></div>',\n        '<button id=\"target\" aria-describedby=\"test\">Button</button>'\n      );\n      assert.isUndefined(validAttrValueCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'noIdShadow',\n        needsReview: 'aria-describedby=\"test\"'\n      });\n    }\n  );\n\n  it('should return undefined on aria-labelledby when the element is not in the DOM', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-labelledby=\"test\">Button</button>'\n    );\n    assert.isUndefined(\n      validAttrValueCheck.call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'noId',\n      needsReview: 'aria-labelledby=\"test\"'\n    });\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return undefined on aria-labelledby when the element is in a different shadow tree',\n    function () {\n      var params = shadowCheckSetup(\n        '<div></div>',\n        '<button id=\"target\" aria-labelledby=\"test\">Button</button>'\n      );\n      assert.isUndefined(validAttrValueCheck.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'noIdShadow',\n        needsReview: 'aria-labelledby=\"test\"'\n      });\n    }\n  );\n\n  it('should return undefined on aria-current with invalid value', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-current=\"test\">Button</button>'\n    );\n    assert.isUndefined(\n      validAttrValueCheck.call(checkContext, null, null, vNode)\n    );\n  });\n\n  it('should return true on valid aria-labelledby value within img elm', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" role=\"button\" aria-labelledby=\"foo\"/>'\n    );\n    assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n  });\n\n  it('should return undefined on invalid aria-labelledby value within img elm', function () {\n    var vNode = queryFixture(\n      '<div id=\"foo\">hello world</div>' +\n        '<img id=\"target\" role=\"button\" aria-labelledby=\"hazaar\"/>'\n    );\n    assert.isUndefined(\n      validAttrValueCheck.call(checkContext, null, null, vNode)\n    );\n  });\n\n  describe('null values', function () {\n    afterEach(() => {\n      axe.reset();\n    });\n\n    it('returns undefined when a boolean attribute is null', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" role=\"checkbox\" aria-checked></div>'\n      );\n      assert.isUndefined(\n        validAttrValueCheck.call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'empty',\n        needsReview: 'aria-checked'\n      });\n    });\n\n    it('returns undefined when a boolean attribute is empty', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" role=\"checkbox\" aria-checked=\"\"></div>'\n      );\n      assert.isUndefined(\n        validAttrValueCheck.call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'empty',\n        needsReview: 'aria-checked'\n      });\n    });\n\n    it('returns false for empty string values that are not allowed to be empty', function () {\n      axe.configure({\n        standards: {\n          ariaAttrs: {\n            'aria-valuetext': {\n              allowEmpty: false\n            }\n          }\n        }\n      });\n      var vNode = queryFixture(\n        '<div id=\"target\" aria-valuetext=\"\" role=\"range\"></div>'\n      );\n      assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n    });\n\n    it('returns false if there are other issues', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" role=\"checkbox\" aria-checked aria-invalid=\"none\"></div>'\n      );\n      assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));\n      assert.deepEqual(checkContext._data, ['aria-invalid=\"none\"']);\n    });\n  });\n\n  describe('options', function () {\n    it('should exclude supplied attributes', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" aria-live=\"nope\" aria-describedby=\"no exist k thx\"></div>'\n      );\n      assert.isTrue(\n        validAttrValueCheck.call(\n          checkContext,\n          null,\n          ['aria-live', 'aria-describedby'],\n          vNode\n        )\n      );\n    });\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return undefined for idref attribute', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'button',\n        attributes: {\n          'aria-owns': 'test'\n        }\n      });\n\n      assert.isUndefined(\n        validAttrValueCheck.call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'idrefs',\n        needsReview: 'aria-owns=\"test\"'\n      });\n    });\n\n    it('should return true for empty idref attribute', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'button',\n        attributes: {\n          'aria-owns': ''\n        }\n      });\n      assert.isTrue(validAttrValueCheck.call(checkContext, null, null, vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/valid-attr.js",
    "content": "describe('aria-valid-attr', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return false if any invalid ARIA attributes are found', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" tabindex=\"1\" aria-cats=\"true\" aria-dogs=\"true\"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('aria-valid-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.deepEqual(checkContext._data, ['aria-cats', 'aria-dogs']);\n  });\n\n  it('should return true if no invalid ARIA attributes are found', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" tabindex=\"1\" aria-selected=\"true\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-valid-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true for unsupported ARIA attributes', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var vNode = queryFixture(\n      '<div id=\"target\" tabindex=\"1\" aria-mccheddarton=\"true\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('aria-valid-attr')\n        .call(checkContext, null, null, vNode)\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  describe('options', function () {\n    it('should exclude provided attribute names', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" aria-bats=\"cat\" aria-puppies=\"2\"></div>'\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('aria-valid-attr')\n          .call(checkContext, null, ['aria-bats', 'aria-puppies'], vNode)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/aria/valid-scrollable-semantics.js",
    "content": "describe('valid-scrollable-semantics', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext._data = null;\n  });\n\n  it('should return false for role=banner', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', '\"banner');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return false for role=search', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'search');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=form', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'form');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=navigation', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'navigation');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=complementary', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'complementary');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=contentinfo', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'contentinfo');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=main', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'main');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=region', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'region');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=alertdialog', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'alertdialog');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=article', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'article');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=dialog', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'dialog');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for nav elements', function () {\n    var node = document.createElement('nav');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for section elements', function () {\n    var node = document.createElement('section');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for article elements', function () {\n    var node = document.createElement('article');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for aside elements', function () {\n    var node = document.createElement('aside');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=tabpanel', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'tabpanel');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true for role=tooltip', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'tooltip');\n    fixture.appendChild(node);\n    flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('valid-scrollable-semantics')\n        .call(checkContext, node)\n    );\n  });\n\n  describe('options', function () {\n    it('should allow options.roles to return true for role', function () {\n      var node = document.createElement('div');\n      node.setAttribute('role', 'banner');\n      fixture.appendChild(node);\n      flatTreeSetup(fixture);\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('valid-scrollable-semantics')\n          .call(checkContext, node, { roles: ['banner'] })\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/color/color-contrast.js",
    "content": "describe('color-contrast', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var contrastEvaluate = axe.testUtils.getCheckEvaluate('color-contrast');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('should return undefined if cannot handle color', function () {\n    var params = checkSetup(\n      '<div id=\"divundertest\" style=\"color: oklch(0.961073 0.000047911 none / 0.2); background-color: black; font-size: 14pt; font-weight: 900;\">' +\n        '<span id=\"target\" style=\"font-weight:lighter;\">My text</span></div>'\n    );\n\n    var expectedRelatedNodes = fixture.querySelector('#divundertest');\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [expectedRelatedNodes]);\n    assert.deepEqual(checkContext._data.messageKey, 'colorParse');\n    assert.equal(\n      checkContext._data.colorParse,\n      'oklch(0.961073 0.000047911 none / 0.2)'\n    );\n  });\n\n  it('should return undefined if cannot handle backgroundcolor', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: oklch(0.961073 0.000047911 none / 0.2); font-size: 14pt; font-weight: 900;\">' +\n        '<span id=\"target\" style=\"font-weight:lighter;\">My text</span></div>'\n    );\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n    assert.deepEqual(checkContext._data.messageKey, 'colorParse');\n    assert.equal(\n      checkContext._data.colorParse,\n      'oklch(0.961073 0.000047911 none / 0.2)'\n    );\n  });\n\n  it('should return undefined if cannot handle text-shadow', function () {\n    var params = checkSetup(\n      '<div id=\"target\" style=\"background-color: #fff; color:#000; text-shadow: 1px 1px oklch(0.961073 0.000047911 none / 0.2);\">My text</div>'\n    );\n\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true for hidden element', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 14pt; font-weight: 100;\">' +\n        '<span  id=\"target\" style=\"font-weight:bolder; opacity: 0;\">My text</span></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true for child of hidden element', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 14pt; font-weight: 100; overflow: scroll; height: 0\">' +\n        '<span id=\"target\" style=\"font-weight:bolder\">My text</span></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return the proper values stored in data', function () {\n    var params = checkSetup(\n      '<div id=\"parent\" style=\"color: black; background-color: white; font-size: 14pt\">' +\n        '<b id=\"target\">My text</b></div>'\n    );\n    var white = new axe.commons.color.Color(255, 255, 255, 1);\n    var black = new axe.commons.color.Color(0, 0, 0, 1);\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.bgColor, white.toHexString());\n    assert.equal(checkContext._data.fgColor, black.toHexString());\n    assert.equal(checkContext._data.contrastRatio, '21.00');\n    assert.equal(checkContext._data.fontWeight, 'bold');\n    assert.isAtLeast(parseFloat(checkContext._data.fontSize), 14, 0.5);\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true when there is sufficient contrast because of bold tag', function () {\n    var params = checkSetup(\n      '<div id=\"parent\" style=\"color: gray; background-color: white; font-size: 14pt\">' +\n        '<b id=\"target\">My text</b></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true when there is sufficient contrast because of font weight', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 14pt; font-weight: 900\" id=\"target\">' +\n        '<span style=\"font-weight:lighter\">My text</span></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return false when there is not sufficient contrast because of font weight', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 14pt; font-weight: 100\" id=\"target\">' +\n        '<span style=\"font-weight:bolder\">My text</span></div>'\n    );\n\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [params[0]]);\n  });\n\n  it('should return true when there is sufficient contrast because of font size', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 18pt;\" id=\"target\">' +\n        'My text</div>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return false when there is not sufficient contrast because of font size', function () {\n    var params = checkSetup(\n      '<div style=\"color: gray; background-color: white; font-size: 8pt; -webkit-text-size-adjust: none;\" id=\"target\">' +\n        'My text</div>'\n    );\n\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [params[0]]);\n  });\n\n  it('should return true when there is sufficient contrast with explicit transparency', function () {\n    var params = checkSetup(\n      '<div id=\"parent\" style=\"color: white; background-color: white;\">' +\n        '<span style=\"color: black; background-color: rgba(0,0,0,0)\" id=\"target\">My text</span></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true when there is sufficient contrast with implicit transparency', function () {\n    var params = checkSetup(\n      '<div id=\"parent\" style=\"color: white; background-color: white;\">' +\n        '<span style=\"color: black;\" id=\"target\">My text</span></div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true when there is sufficient contrast', function () {\n    var params = checkSetup(\n      '<div style=\"color: black; background-color: white;\" id=\"target\">' +\n        'My text</div>'\n    );\n\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true for inline elements with sufficient contrast spanning multiple lines', function () {\n    var params = checkSetup(\n      '<p>Text oh heyyyy <a href=\"#\" id=\"target\">and here\\'s <br>a link</a></p>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return undefined for inline elements spanning multiple lines that are overlapped', function () {\n    var params = checkSetup(\n      '<div style=\"position:relative;\"><div style=\"background-color:rgba(0,0,0,1);position:absolute;width:300px;height:200px;\"></div>' +\n        '<p>Text oh heyyyy <a href=\"#\" id=\"target\">and here\\'s <br>a link</a></p></div>'\n    );\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true for truncated inline elements', function () {\n    var params = checkSetup(\n      '<p>Text oh heyyyy <b id=\"target\" style=\"display: block;overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100px;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin quam. Fusce mi odio, egestas pulvinar erat eget, vehicula tempus est. Proin vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim nulla, sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna, mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh, quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim. Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor felis ante non libero. Donec dapibus, diam sit amet posuere commodo, magna orci hendrerit ipsum, eu egestas mauris nulla ut ipsum. Sed luctus, orci in fringilla finibus, odio leo porta dolor, eu dignissim risus eros eget erat</b></p>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true for inline elements with sufficient contrast', function () {\n    var params = checkSetup(\n      '<p>Text oh heyyyy <b id=\"target\">and here\\'s bold text</b></p>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return false when there is not sufficient contrast between foreground and background', function () {\n    var params = checkSetup(\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n        'My text</div>'\n    );\n\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [params[0]]);\n    assert.deepEqual(checkContext._data.messageKey, null);\n  });\n\n  it('should ignore position:fixed elements above the target', function () {\n    var params = checkSetup(\n      '<div style=\"background-color: #e5f1e5;\" id=\"background\">' +\n        '<div style=\"width:100%; position:fixed; top:0; height:50px; background: #F0F0F0; z-index: 200; color:#fff\" >header</div>' +\n        '<div style=\"height: 6000px;\"></div>' +\n        'stuff <span id=\"target\" style=\"color: rgba(91, 91, 90, 0.7)\">This is some text</span>' +\n        '<div style=\"height: 6000px;\"></div>' +\n        '</div>'\n    );\n    var expectedRelatedNodes = fixture.querySelector('#background');\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [expectedRelatedNodes]);\n  });\n\n  it('should ignore position:fixed elements directly above the target', function () {\n    var params = checkSetup(\n      '<div style=\"background-color: #e5f1e5;\" id=\"background\">' +\n        '<div style=\"width:100%; position:fixed; top:0; height:400px; background: #F0F0F0; z-index: 200; color:#fff\" >header</div>' +\n        '<div style=\"height: 10px;\"></div>' +\n        'stuff <span id=\"target\" style=\"color: rgba(91, 91, 90, 0.7)\">This is some text</span>' +\n        '<div style=\"height: 10px;\"></div>' +\n        '</div>'\n    );\n    var expectedRelatedNodes = fixture.querySelector('#background');\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [expectedRelatedNodes]);\n  });\n\n  it('should find contrast issues on position:fixed elements', function () {\n    var params = checkSetup(\n      '<div style=\"background-color: #e5f1e5;\" id=\"background\">' +\n        '<div style=\"width:100%; position:fixed; top:0; height:50px; background: #F0F0F0; z-index: 200; color:#fff\" id=\"target\">header</div>' +\n        '<div style=\"height: 6000px;\"></div>' +\n        'stuff <span style=\"color: rgba(91, 91, 90, 0.7)\">This is some text</span>' +\n        '<div style=\"height: 6000px;\"></div>' +\n        '</div>'\n    );\n    assert.isFalse(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, [params[0]]);\n  });\n\n  it('should return undefined for background-image elements', function () {\n    var dataURI =\n      'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/' +\n      'XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkA' +\n      'ABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKU' +\n      'E1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7';\n\n    var params = checkSetup(\n      '<div id=\"background\" style=\"background:url(' +\n        dataURI +\n        ') no-repeat left center; padding: 5px 0 5px 25px;\">' +\n        '<p id=\"target\">Text 1</p>' +\n        '</div>'\n    );\n\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.isUndefined(checkContext._data.bgColor);\n    assert.equal(checkContext._data.contrastRatio, 0);\n    assert.equal(checkContext._data.messageKey, 'bgImage');\n  });\n\n  it('should return undefined for background-gradient elements', function () {\n    var params = checkSetup(\n      '<div id=\"background\" style=\"background-image:linear-gradient(red, orange);\">' +\n        '<p id=\"target\">Text 2</p>' +\n        '</div>'\n    );\n\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.isUndefined(checkContext._data.bgColor);\n    assert.equal(checkContext._data.messageKey, 'bgGradient');\n    assert.equal(checkContext._data.contrastRatio, 0);\n  });\n\n  it('should return undefined when there are elements overlapping', function (done) {\n    // Give Edge time to scroll... :/\n    setTimeout(function () {\n      var params = checkSetup(\n        '<div style=\"color: black; background-color: white; width: 200px; height: 100px; position: relative;\" id=\"target\">' +\n          'My text <div style=\"position: absolute; top:0; left: 0; background-color: white; width: 100%; height: 100%;\"></div></div>'\n      );\n\n      var result = contrastEvaluate.apply(checkContext, params);\n      assert.isUndefined(result);\n      assert.equal(checkContext._data.messageKey, 'bgOverlap');\n      assert.equal(checkContext._data.contrastRatio, 0);\n      done();\n    }, 10);\n  });\n\n  it('should return true when a form wraps mixed content', function () {\n    var params = checkSetup(\n      '<form id=\"target\"><p>Some text</p><label for=\"input6\">Text</label><input id=\"input6\"></form>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true when a label wraps a text input', function () {\n    fixtureSetup('<label id=\"target\">My text <input type=\"text\"></label>');\n    var target = fixture.querySelector('#target');\n    var virtualNode = axe.utils.getNodeFromTree(target);\n    var result = contrastEvaluate.call(checkContext, target, {}, virtualNode);\n    assert.isTrue(result);\n  });\n\n  it(\"should return true when a label wraps a text input but doesn't overlap\", function () {\n    var params = checkSetup(\n      '<label id=\"target\">' +\n        'My text <input type=\"text\" style=\"position: absolute; top: 200px;\"></label>'\n    );\n    var result = contrastEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should return true when there is sufficient contrast based on thead', function () {\n    var params = checkSetup(\n      '<table><thead style=\"background: #d00d2c\"><tr><th id=\"target\" style=\"color: #fff; padding: .5em\">Col 1</th></tr></thead></table>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return true when there is sufficient contrast based on tbody', function () {\n    var params = checkSetup(\n      '<table><tbody style=\"background: #d00d2c\"><tr><td id=\"target\" style=\"color: #fff; padding: .5em\">Col 1</td></tr></tbody></table>'\n    );\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return undefined if element overlaps text content', function (done) {\n    // Give Edge time to scroll\n    setTimeout(function () {\n      var params = checkSetup(\n        '<div style=\"background-color: white; height: 60px; width: 80px; border:1px solid;position: relative;\">' +\n          '<div id=\"target\" style=\"color: white; height: 40px; width: 60px; border:1px solid red;\">Hi</div>' +\n          '<div style=\"position: absolute; top: 0; width: 60px; height: 40px;background-color: #000\"></div>' +\n          '</div>'\n      );\n\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n      assert.equal(checkContext._data.messageKey, 'bgOverlap');\n      assert.equal(checkContext._data.contrastRatio, 0);\n      done();\n    }, 10);\n  });\n\n  it('should return undefined if element has same color as background', function () {\n    var params = checkSetup(\n      '<div style=\"background-color: white;\">' +\n        '<div style=\"color:white;\" id=\"target\">Text</div>' +\n        '</div>'\n    );\n\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'equalRatio');\n    assert.equal(checkContext._data.contrastRatio, 1);\n  });\n\n  it('returns relatedNodes with undefined', function () {\n    var dataURI =\n      'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/' +\n      'XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkA' +\n      'ABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKU' +\n      'E1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7';\n\n    var params = checkSetup(\n      '<div id=\"background\" style=\"background:url(' +\n        dataURI +\n        ') no-repeat left center; padding: 5px 0 5px 25px;\">' +\n        '<p id=\"target\">Text 1</p>' +\n        '</div>'\n    );\n\n    assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    assert.equal(\n      checkContext._relatedNodes[0],\n      document.querySelector('#background')\n    );\n  });\n\n  it('should not error if client rects do not fill entire bounding rect', function () {\n    var params = checkSetup(\n      '<pre style=\"overflow-x: auto; background-color: #333\"><span id=\"target\" style=\"color: #000\">' +\n        '\\nx x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\nx' +\n        '\\n</span></pre>'\n    );\n    assert.doesNotThrow(function () {\n      contrastEvaluate.apply(checkContext, params);\n    });\n  });\n\n  it('passes for element outside overflow:hidden', function () {\n    var params = checkSetup(`\n      <style>\n        .container {\n          width: 200px;\n          height: 200px;\n          overflow: hidden;\n        }\n        .foo * {\n          width: 200px;\n          height: 200px;\n        }\n        #target {\n          color: #eee;\n        }\n      </style>\n      <div class=\"container\">\n        <div class=\"foo\" id=\"foo\">\n          <div id=\"one\">hello</div>\n          <div id=\"target\">goodbye</div>\n        </div>\n      </div>\n    `);\n    assert.isTrue(contrastEvaluate.apply(checkContext, params));\n  });\n\n  describe('with pseudo elements', function () {\n    it('should return undefined if :before pseudo element has a background color', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 100%; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        fontSize: '12.0pt (16px)',\n        fontWeight: 'normal',\n        messageKey: 'pseudoContent',\n        expectedContrastRatio: '4.5:1'\n      });\n      assert.equal(\n        checkContext._relatedNodes[0],\n        document.querySelector('#background')\n      );\n    });\n\n    it('should return undefined if :after pseudo element has a background color', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:after { content: \"\"; position: absolute; width: 100%; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        fontSize: '12.0pt (16px)',\n        fontWeight: 'normal',\n        messageKey: 'pseudoContent',\n        expectedContrastRatio: '4.5:1'\n      });\n      assert.equal(\n        checkContext._relatedNodes[0],\n        document.querySelector('#background')\n      );\n    });\n\n    it('should return undefined if pseudo element has a background image', function () {\n      var dataURI =\n        'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/' +\n        'XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkA' +\n        'ABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKU' +\n        'E1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7';\n\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 100%; height: 100%; background: url(' +\n          dataURI +\n          ') no-repeat left center; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, {\n        fontSize: '12.0pt (16px)',\n        fontWeight: 'normal',\n        messageKey: 'pseudoContent',\n        expectedContrastRatio: '4.5:1'\n      });\n      assert.equal(\n        checkContext._relatedNodes[0],\n        document.querySelector('#background')\n      );\n    });\n\n    it('should not return undefined if pseudo element has no content', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { position: absolute; width: 100%; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should not return undefined if pseudo element is not absolutely positioned no content', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; width: 100%; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should not return undefined if pseudo element is has zero dimension', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 0; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it(\"should not return undefined if pseudo element doesn't have a background\", function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 100%; height: 100%; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should not return undefined if pseudo element has visibility: hidden', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 100%; height: 100%; background-color: red; visibility: hidden; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should not return undefined if pseudo element has display: none', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:before { content: \"\"; position: absolute; width: 100%; height: 100%; background-color: red; display: none; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>'\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should return undefined if pseudo element is more than 25% of the element', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; width: 100px; height: 100px; } ' +\n          '.foo:before { content: \"\"; position: absolute; width: 26px; height: 100px; background: red; }</style>' +\n          '<p id=\"target\" class=\"foo\">Content</p>'\n      );\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should not return undefined if pseudo element is 25% of the element', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; width: 100px; height: 100px; } ' +\n          '.foo:before { content: \"\"; position: absolute; width: 25px; height: 100px; background: red; }</style>' +\n          '<p id=\"target\" class=\"foo\">Content</p>'\n      );\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n  });\n\n  describe('with special texts', function () {\n    it('should return undefined for a single character text with insufficient contrast', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#DDD;\" id=\"target\">X</div>' +\n          '</div>'\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isUndefined(actual);\n      assert.equal(checkContext._data.messageKey, 'shortTextContent');\n    });\n\n    it('should return true for a single character text with insufficient contrast', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#DDD;\" id=\"target\">X</div>' +\n          '</div>'\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isUndefined(actual);\n      assert.equal(checkContext._data.messageKey, 'shortTextContent');\n    });\n\n    it('should return undefined when the text only contains nonBmp unicode when the ignoreUnicode option is true', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#DDD;\" id=\"target\">&#x20A0; &#x20A1; &#x20A2; &#x20A3;</div>' +\n          '</div>',\n        {\n          ignoreUnicode: true\n        }\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isUndefined(actual);\n      assert.equal(checkContext._data.messageKey, 'nonBmp');\n    });\n\n    it('should return true when the text only contains nonBmp unicode when the ignoreUnicode option is false, and there is sufficient contrast', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#000;\" id=\"target\">◓</div>' +\n          '</div>',\n        {\n          ignoreUnicode: false\n        }\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isTrue(actual);\n    });\n\n    it('should return undefined when the text only contains nonBmp unicode when the ignoreUnicode option is false and the ignoreLength option is default, and there is insufficient contrast', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#DDD;\" id=\"target\">◓</div>' +\n          '</div>',\n        {\n          ignoreUnicode: false\n        }\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isUndefined(actual);\n      assert.equal(checkContext._data.messageKey, 'shortTextContent');\n    });\n\n    it('should return false when the text only contains nonBmp unicode when the ignoreUnicode option is false and the ignoreLength option is true, and there is insufficient contrast', function () {\n      var params = checkSetup(\n        '<div style=\"background-color: #FFF;\">' +\n          '<div style=\"color:#DDD;\" id=\"target\">◓</div>' +\n          '</div>',\n        {\n          ignoreUnicode: false,\n          ignoreLength: true\n        }\n      );\n\n      var actual = contrastEvaluate.apply(checkContext, params);\n      assert.isFalse(actual);\n    });\n  });\n\n  describe('options', function () {\n    it('should support options.boldValue', function () {\n      var params = checkSetup(\n        '<div style=\"color: gray; background-color: white; font-size: 14pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          boldValue: 100\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should support options.boldTextPt', function () {\n      var params = checkSetup(\n        '<div style=\"color: gray; background-color: white; font-size: 6pt; font-weight: 700\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          boldTextPt: 6\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should support options.largeTextPt', function () {\n      var params = checkSetup(\n        '<div style=\"color: gray; background-color: white; font-size: 6pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          largeTextPt: 6\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should support options.contrastRatio.normal.expected', function () {\n      var params = checkSetup(\n        '<div style=\"color: #999; background-color: white; font-size: 14pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            normal: {\n              expected: 2.5\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should support options.contrastRatio.normal.minThreshold', function () {\n      var params = checkSetup(\n        '<div style=\"color: #999; background-color: white; font-size: 14pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            normal: {\n              minThreshold: 3\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should not report incomplete when  options.contrastRatio.normal.minThreshold is set', function () {\n      var params = checkSetup(\n        `\n        <p id=\"target\" style=\"color: #666; background: linear-gradient(to right, #FFF, #0FF); width: 300px\">\n          Some text in English\n        </p>`,\n        {\n          contrastRatio: {\n            normal: {\n              minThreshold: 3\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.isUndefined(checkContext._data.messageKey);\n    });\n\n    it('should support options.contrastRatio.normal.maxThreshold', function () {\n      var params = checkSetup(\n        '<div style=\"color: #999; background-color: white; font-size: 14pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            normal: {\n              maxThreshold: 2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should not report incomplete when  options.contrastRatio.normal.maxThreshold is set', function () {\n      var params = checkSetup(\n        `\n        <p id=\"target\" style=\"color: #666; background: linear-gradient(to right, #FFF, #0FF); width: 300px\">\n          Some text in English\n        </p>`,\n        {\n          contrastRatio: {\n            normal: {\n              maxThreshold: 2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.isUndefined(checkContext._data.messageKey);\n    });\n\n    it('should support options.contrastRatio.large.expected', function () {\n      var params = checkSetup(\n        '<div style=\"color: #ccc; background-color: white; font-size: 18pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            large: {\n              expected: 1.5\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should support options.contrastRatio.large.minThreshold', function () {\n      var params = checkSetup(\n        '<div style=\"color: #ccc; background-color: white; font-size: 18pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            large: {\n              minThreshold: 2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should not report incomplete when  options.contrastRatio.large.minThreshold is set', function () {\n      var params = checkSetup(\n        `\n        <p id=\"target\" style=\"color: #666; background: linear-gradient(to right, #FFF, #0FF); width: 300px; font-size: 18pt;\">\n          Some text in English\n        </p>`,\n        {\n          contrastRatio: {\n            large: {\n              minThreshold: 2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.isUndefined(checkContext._data.messageKey);\n    });\n\n    it('should support options.contrastRatio.large.maxThreshold', function () {\n      var params = checkSetup(\n        '<div style=\"color: #ccc; background-color: white; font-size: 18pt; font-weight: 100\" id=\"target\">' +\n          '<span style=\"font-weight:bolder\">My text</span></div>',\n        {\n          contrastRatio: {\n            large: {\n              maxThreshold: 1.2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n    });\n\n    it('should not report incomplete when  options.contrastRatio.large.maxThreshold is set', function () {\n      var params = checkSetup(\n        `\n        <p id=\"target\" style=\"color: #666; background: linear-gradient(to right, #FFF, #0FF); width: 300px; font-size: 18pt;\">\n          Some text in English\n        </p>`,\n        {\n          contrastRatio: {\n            large: {\n              maxThreshold: 2\n            }\n          }\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.isUndefined(checkContext._data.messageKey);\n    });\n\n    it('should ignore pseudo element with options.ignorePseudo', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; } .foo:after { content: \"\"; position: absolute; width: 100%; height: 100%; background: red; }</style>' +\n          '<div id=\"background\" class=\"foo\"><p id=\"target\" style=\"#000\">Content</p></div>',\n        {\n          ignorePseudo: true\n        }\n      );\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('should adjust the pseudo element minimum size with the options.pseudoSizeThreshold', function () {\n      var params = checkSetup(\n        '<style>.foo { position: relative; width: 100px; height: 100px }' +\n          '.foo:before { content: \"\"; position: absolute; width: 22%; height: 100%; background: red; }</style>' +\n          '<p id=\"target\" class=\"foo\">Content</p>',\n        {\n          pseudoSizeThreshold: 0.2\n        }\n      );\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n    });\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns colors across Shadow DOM boundaries',\n    function () {\n      var params = shadowCheckSetup(\n        '<div id=\"container\" style=\"background-color:black;\"></div>',\n        '<p style=\"color: #333;\" id=\"target\">Text</p>'\n      );\n      var container = fixture.querySelector('#container');\n      var result = contrastEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._relatedNodes, [container]);\n    }\n  );\n\n  (shadowSupported ? it : xit)('handles <slot> elements', () => {\n    fixture.innerHTML =\n      '<div id=\"container\" style=\"height: 100px;\"><p id=\"target\" style=\"color: #333;\">Slotted text</p></div>';\n    const container = fixture.querySelector('#container');\n    const shadow = container.attachShadow({ mode: 'open' });\n\n    shadow.innerHTML =\n      '<div id=\"shadowContainer\" style=\"position: absolute; background: black;\"><slot></slot></div>';\n    const shadowContainer = shadow.querySelector('#shadowContainer');\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = fixture.querySelector('#target');\n    const vNode = axe.utils.getNodeFromTree(target);\n    const result = contrastEvaluate.call(\n      checkContext,\n      vNode.actualNode,\n      null,\n      vNode\n    );\n    assert.isFalse(result);\n    assert.deepEqual(checkContext._relatedNodes, [shadowContainer]);\n  });\n\n  describe('with text-shadow', function () {\n    it('passes if thin text shadows have sufficient contrast with the text', function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #666; color:#aaa; ' +\n          'text-shadow: 0 0 0.09em #000, 0 0 0.09em #000, 0 0 0.09em #000;\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('does not count text shadows of offset 0, blur 0 as part of the background color', function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #fff; color:#0f833e; ' +\n          'text-shadow: 0 0 0 #000\">' +\n          '  Hello world' +\n          '</div>'\n      );\n\n      var white = new axe.commons.color.Color(255, 255, 255, 1);\n\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n      assert.equal(checkContext._data.bgColor, white.toHexString());\n      assert.equal(checkContext._data.fgColor, '#0f833e');\n      assert.equal(checkContext._data.contrastRatio, '4.84');\n    });\n\n    it('passes if thin text shadows have sufficient contrast with the background', function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 0 0 0.09em #000, 0 0 0.09em #000, 0 0 0.09em #000;\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('fails if text shadows have sufficient contrast with the background if its width is thicker than `shadowOutlineEmMax`', function () {\n      var checkOptions = { shadowOutlineEmMax: 0.05 };\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 0 0 0.09em #000, 0 0 0.09em #000, 0 0 0.09em #000;\">' +\n          '  Hello world' +\n          '</div>',\n        checkOptions\n      );\n      assert.isFalse(contrastEvaluate.apply(checkContext, params));\n      assert.equal(checkContext._data.messageKey, null);\n    });\n\n    it('fails if text shadows do not have sufficient contrast with the foreground', function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 1px 1px 0.01em #000\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isFalse(contrastEvaluate.apply(checkContext, params));\n      assert.isNull(checkContext._data.messageKey);\n    });\n\n    it('fails if text shadows do not have sufficient contrast with the background', function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 0 0 0.01em #000\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isFalse(contrastEvaluate.apply(checkContext, params));\n      assert.equal(checkContext._data.messageKey, 'shadowOnBgColor');\n    });\n\n    it(\"fails if thick text shadows don't have sufficient contrast\", function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 0 0 0.09em #000, 0 0 0.09em #000, 0 0 0.09em #000;\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it(\"passes if thin text shadows don't have sufficient contrast, but the text and background do\", function () {\n      var params = checkSetup(\n        '<div id=\"target\" style=\"background-color: #aaa; color:#666; ' +\n          'text-shadow: 0 0 0.09em #000, 0 0 0.09em #000, 0 0 0.09em #000;\">' +\n          '  Hello world' +\n          '</div>'\n      );\n      assert.isTrue(contrastEvaluate.apply(checkContext, params));\n    });\n\n    it('incompletes if text-shadow is only on part of the text', function () {\n      var params = checkSetup(`\n        <div id=\"target\" style=\"\n          background-color: #aaa;\n          color:#666; \n          text-shadow: 1px 1px #000;\n        \"> Hello world </div>\n      `);\n\n      assert.isUndefined(contrastEvaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, []);\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'complexTextShadows'\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/color/link-in-text-block-style.js",
    "content": "describe('link-in-text-block-style', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowSupport = axe.testUtils.shadowSupport;\n  let styleElm;\n\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  const { queryFixture } = axe.testUtils;\n  const linkInBlockStyleCheck = axe.testUtils.getCheckEvaluate(\n    'link-in-text-block-style'\n  );\n\n  before(() => {\n    styleElm = document.createElement('style');\n    document.head.appendChild(styleElm);\n  });\n\n  const defaultStyle = {\n    color: 'black',\n    textDecoration: 'none'\n  };\n\n  beforeEach(() => {\n    createStyleString('p', defaultStyle);\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    styleElm.innerHTML = '';\n    checkContext.reset();\n  });\n\n  after(() => {\n    styleElm.parentNode.removeChild(styleElm);\n  });\n\n  function createStyleString(selector, outerStyle) {\n    // Merge style with the default\n    const styleObj = {};\n    for (const prop in defaultStyle) {\n      if (defaultStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = defaultStyle[prop];\n      }\n    }\n    for (const prop in outerStyle) {\n      if (outerStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = outerStyle[prop];\n      }\n    }\n\n    const cssLines = Object.keys(styleObj)\n      .map(prop => {\n        // Make camelCase prop dash separated\n        const cssPropName = prop\n          .trim()\n          .split(/(?=[A-Z])/g)\n          .reduce((name, propPiece) => {\n            if (!name) {\n              return propPiece;\n            } else {\n              return name + '-' + propPiece.toLowerCase();\n            }\n          }, null);\n\n        // Return indented line of style code\n        return '  ' + cssPropName + ':' + styleObj[prop] + ';';\n      })\n      .join('\\n');\n\n    // Add to the style element\n    styleElm.innerHTML += selector + ' {\\n' + cssLines + '\\n}\\n';\n  }\n\n  function getLinkElm(linkStyle) {\n    // Get a random id and build the style strings\n    const linkId = 'linkid-' + Math.floor(Math.random() * 100000);\n    const parId = 'parid-' + Math.floor(Math.random() * 100000);\n\n    createStyleString('#' + linkId, linkStyle);\n    createStyleString('#' + parId, {});\n\n    fixture.innerHTML +=\n      '<p id=\"' +\n      parId +\n      '\"> Text ' +\n      '<a href=\"/\" id=\"' +\n      linkId +\n      '\">link</a>' +\n      '</p>';\n    axe.testUtils.flatTreeSetup(fixture);\n    return document.getElementById(linkId);\n  }\n\n  describe('link default state', () => {\n    beforeEach(() => {\n      createStyleString('a', {\n        textDecoration: 'none'\n      });\n    });\n\n    it('passes the selected node and closest ancestral block element', () => {\n      fixture.innerHTML =\n        '<div> <span style=\"display:block; id=\"parent\">' +\n        '\t<p style=\"display:inline\"><a href=\"\" id=\"link\">' +\n        '\t\t link text ' +\n        '\t</a> inside block </p> inside block' +\n        '</span> outside block </div>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const linkElm = document.getElementById('link');\n\n      assert.isFalse(linkInBlockStyleCheck.call(checkContext, linkElm));\n    });\n\n    (shadowSupport.v1 ? it : xit)(\n      'works with the block outside the shadow tree',\n      () => {\n        const parentElm = document.createElement('div');\n        const shadow = parentElm.attachShadow({ mode: 'open' });\n        shadow.innerHTML =\n          '<a href=\"\" style=\"text-decoration:underline;\">Link</a>';\n        const linkElm = shadow.querySelector('a');\n        fixture.appendChild(parentElm);\n\n        axe.testUtils.flatTreeSetup(fixture);\n\n        assert.isTrue(linkInBlockStyleCheck.call(checkContext, linkElm));\n      }\n    );\n\n    (shadowSupport.v1 ? it : xit)(\n      'works with the link inside the shadow tree slot',\n      () => {\n        const div = document.createElement('div');\n        div.setAttribute('style', 'text-decoration:none;');\n        div.innerHTML =\n          '<a href=\"\" style=\"text-decoration:underline;\">Link</a>';\n        const shadow = div.attachShadow({ mode: 'open' });\n        shadow.innerHTML = '<p><slot></slot></p>';\n        fixture.appendChild(div);\n\n        axe.testUtils.flatTreeSetup(fixture);\n        const linkElm = div.querySelector('a');\n\n        assert.isTrue(linkInBlockStyleCheck.call(checkContext, linkElm));\n      }\n    );\n  });\n\n  describe('links distinguished through style', () => {\n    it('returns false if link style matches parent', () => {\n      const linkElm = getLinkElm({});\n      assert.isFalse(linkInBlockStyleCheck.call(checkContext, linkElm));\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns true if link has underline', () => {\n      const linkElm = getLinkElm({\n        textDecoration: 'underline'\n      });\n      assert.isTrue(linkInBlockStyleCheck.call(checkContext, linkElm));\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n      assert.isNull(checkContext._data);\n    });\n\n    it('returns undefined when the link has a :before pseudo element', () => {\n      const link = queryFixture(`\n        <style>\n          a:before { content: '🔗'; }\n          a { text-decoration: none; }\n        </style>\n        <p>A <a href=\"#\" id=\"target\">link</a> inside a block of text</p>\n      `).actualNode;\n      const result = linkInBlockStyleCheck.call(checkContext, link);\n      assert.isUndefined(result);\n      assert.deepEqual(checkContext._data, { messageKey: 'pseudoContent' });\n      assert.equal(checkContext._relatedNodes[0], link.parentNode);\n    });\n\n    it('returns undefined when the link has a :after pseudo element', () => {\n      const link = queryFixture(`\n        <style>\n          a:after { content: \"\"; }\n          a { text-decoration: none; }\n        </style>\n        <p>A <a href=\"#\" id=\"target\">link</a> inside a block of text</p>\n      `).actualNode;\n      const result = linkInBlockStyleCheck.call(checkContext, link);\n      assert.isUndefined(result);\n      assert.deepEqual(checkContext._data, { messageKey: 'pseudoContent' });\n      assert.equal(checkContext._relatedNodes[0], link.parentNode);\n    });\n\n    it('does not return undefined when the pseudo element content is none', () => {\n      const link = queryFixture(`\n        <style>\n          a:after { content: none; position: absolute; }\n          a { text-decoration: none; }\n        </style>\n        <p>A <a href=\"#\" id=\"target\">link</a> inside a block of text</p>\n      `).actualNode;\n      const result = linkInBlockStyleCheck.call(checkContext, link);\n      assert.isFalse(result);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/color/link-in-text-block.js",
    "content": "describe('link-in-text-block', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowSupport = axe.testUtils.shadowSupport;\n  let styleElm;\n\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  before(() => {\n    styleElm = document.createElement('style');\n    document.head.appendChild(styleElm);\n  });\n\n  const defaultStyle = {\n    color: '#000',\n    textDecoration: 'none'\n  };\n\n  beforeEach(() => {\n    createStyleString('p', defaultStyle);\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    styleElm.innerHTML = '';\n    checkContext.reset();\n  });\n\n  after(() => {\n    styleElm.parentNode.removeChild(styleElm);\n  });\n\n  function createStyleString(selector, outerStyle) {\n    // Merge style with the default\n    const styleObj = {};\n    for (const prop in defaultStyle) {\n      if (defaultStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = defaultStyle[prop];\n      }\n    }\n    for (const prop in outerStyle) {\n      if (outerStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = outerStyle[prop];\n      }\n    }\n\n    const cssLines = Object.keys(styleObj)\n      .map(prop => {\n        // Make camelCase prop dash separated\n        const cssPropName = prop\n          .trim()\n          .split(/(?=[A-Z])/g)\n          .reduce((name, propPiece) => {\n            if (!name) {\n              return propPiece;\n            } else {\n              return name + '-' + propPiece.toLowerCase();\n            }\n          }, null);\n\n        // Return indented line of style code\n        return '  ' + cssPropName + ':' + styleObj[prop] + ';';\n      })\n      .join('\\n');\n\n    // Add to the style element\n    styleElm.innerHTML += selector + ' {\\n' + cssLines + '\\n}\\n';\n  }\n\n  function getLinkElm(linkStyle, paragraphStyle) {\n    // Get a random id and build the style strings\n    const linkId = 'linkid-' + Math.floor(Math.random() * 100000);\n    const parId = 'parid-' + Math.floor(Math.random() * 100000);\n\n    createStyleString('#' + linkId, linkStyle);\n    createStyleString('#' + parId, paragraphStyle);\n\n    fixture.innerHTML +=\n      '<p id=\"' +\n      parId +\n      '\"> Text ' +\n      '<a href=\"/\" id=\"' +\n      linkId +\n      '\">link</a>' +\n      '</p>';\n    axe.testUtils.flatTreeSetup(fixture);\n    return document.getElementById(linkId);\n  }\n\n  describe('link default state', () => {\n    beforeEach(() => {\n      createStyleString('a', {\n        color: '#100' // insufficient contrast\n      });\n    });\n\n    it('passes the selected node and closest ancestral block element', () => {\n      fixture.innerHTML =\n        '<div> <span style=\"display:block; color: #010\" id=\"parent\">' +\n        '\t<p style=\"display:inline\"><a href=\"\" id=\"link\">' +\n        '\t\t link text ' +\n        '\t</a> inside block </p> inside block' +\n        '</span> outside block </div>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const linkElm = document.getElementById('link');\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._data.messageKey, 'fgContrast');\n    });\n\n    (shadowSupport.v1 ? it : xit)(\n      'works with the block outside the shadow tree',\n      () => {\n        const parentElm = document.createElement('div');\n        parentElm.setAttribute(\n          'style',\n          'color:#100; background-color:#FFFFFF;'\n        );\n        const shadow = parentElm.attachShadow({ mode: 'open' });\n        shadow.innerHTML =\n          '<a href=\"\" style=\"color:#000; background-color:#FFFFFF; text-decoration:none;\">Link</a>';\n        const linkElm = shadow.querySelector('a');\n        fixture.appendChild(parentElm);\n\n        axe.testUtils.flatTreeSetup(fixture);\n\n        assert.isFalse(\n          axe.testUtils\n            .getCheckEvaluate('link-in-text-block')\n            .call(checkContext, linkElm)\n        );\n        assert.equal(checkContext._data.messageKey, 'fgContrast');\n      }\n    );\n\n    (shadowSupport.v1 ? it : xit)(\n      'works with the link inside the shadow tree slot',\n      () => {\n        const div = document.createElement('div');\n        div.setAttribute('style', 'color:#100; background-color:#FFFFFF;');\n        div.innerHTML =\n          '<a href=\"\" style=\"color:#000;background-color:#FFFFFF;\">Link</a>';\n        const shadow = div.attachShadow({ mode: 'open' });\n        shadow.innerHTML = '<p><slot></slot></p>';\n        fixture.appendChild(div);\n\n        axe.testUtils.flatTreeSetup(fixture);\n        const linkElm = div.querySelector('a');\n\n        assert.isFalse(\n          axe.testUtils\n            .getCheckEvaluate('link-in-text-block')\n            .call(checkContext, linkElm)\n        );\n        assert.equal(checkContext._data.messageKey, 'fgContrast');\n      }\n    );\n  });\n\n  describe('links distinguished through color', () => {\n    beforeEach(() => {\n      createStyleString('a:active, a:focus', {\n        textDecoration: 'underline'\n      });\n    });\n\n    it('returns undefined if the background contrast can not be determined', () => {\n      const linkElm = getLinkElm(\n        {},\n        {\n          color: '#000010',\n          backgroundImage:\n            'url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)'\n        },\n        {\n          color: '#000000'\n        }\n      );\n      assert.isUndefined(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._data.messageKey, 'bgImage');\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n    });\n\n    it('returns false with fgContrast key if nodes have insufficient foreground contrast and same background color', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black'\n        },\n        {\n          color: '#100'\n        }\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._data.messageKey, 'fgContrast');\n    });\n\n    it('returns false with fgContrast key if nodes have insufficient foreground contrast and insufficient background color', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black',\n          backgroundColor: 'white'\n        },\n        {\n          color: '#100',\n          backgroundColor: '#F0F0F0'\n        }\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._data.messageKey, 'fgContrast');\n    });\n\n    it('returns false with bgContrast key if nodes have same foreground color and insufficient background contrast', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black',\n          backgroundColor: 'white'\n        },\n        {\n          color: 'black',\n          backgroundColor: '#F0F0F0'\n        }\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._data.messageKey, 'bgContrast');\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n    });\n\n    it('returns true if nodes have sufficient foreground contrast and insufficient background contrast', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black',\n          backgroundColor: 'white'\n        },\n        {\n          color: '#606060',\n          backgroundColor: '#F0F0F0'\n        }\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n    });\n\n    it('returns true if nodes have insufficient foreground contrast and sufficient background contrast', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black',\n          backgroundColor: 'white'\n        },\n        {\n          color: '#100',\n          backgroundColor: '#808080'\n        }\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n      assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n    });\n\n    it('should return the proper values stored in data (fgContrast)', () => {\n      fixture.innerHTML =\n        '<div> <span style=\"display:block; color: #100\" id=\"parent\">' +\n        ' <p style=\"display:inline\"><a href=\"\" id=\"link\" style=\"color: #00e\">' +\n        '    link text ' +\n        ' </a> inside block </p> inside block' +\n        '</span> outside block </div>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const linkElm = document.getElementById('link');\n\n      axe.testUtils\n        .getCheckEvaluate('link-in-text-block')\n        .call(checkContext, linkElm);\n\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'fgContrast',\n        contrastRatio: 2.18,\n        requiredContrastRatio: 3,\n        nodeColor: '#0000ee',\n        parentColor: '#110000'\n      });\n    });\n\n    it('should return the proper values stored in data (bgContrast)', () => {\n      const linkElm = getLinkElm(\n        {\n          color: 'black',\n          backgroundColor: 'white'\n        },\n        {\n          color: 'black',\n          backgroundColor: '#F0F0F0'\n        }\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('link-in-text-block')\n          .call(checkContext, linkElm)\n      );\n\n      assert.deepEqual(checkContext._data, {\n        messageKey: 'bgContrast',\n        contrastRatio: 1.13,\n        requiredContrastRatio: 3,\n        nodeBackgroundColor: '#ffffff',\n        parentBackgroundColor: '#f0f0f0'\n      });\n    });\n\n    describe('options.allowSameColor', () => {\n      it('when true, passes when link and text colors are identical', () => {\n        const linkElm = getLinkElm(\n          {\n            color: 'black'\n          },\n          {\n            color: '#000'\n          }\n        );\n        assert.isTrue(\n          axe.testUtils\n            .getCheckEvaluate('link-in-text-block')\n            .call(checkContext, linkElm, { allowSameColor: true })\n        );\n        assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n      });\n\n      it('when false, fails when link and text colors are identical', () => {\n        const linkElm = getLinkElm(\n          {\n            color: 'black'\n          },\n          {\n            color: '#000'\n          }\n        );\n        assert.isFalse(\n          axe.testUtils\n            .getCheckEvaluate('link-in-text-block')\n            .call(checkContext, linkElm, { allowSameColor: false })\n        );\n        assert.equal(checkContext._data.messageKey, 'fgContrast');\n        assert.equal(checkContext._relatedNodes[0], linkElm.parentNode);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/forms/autocomplete-appropriate.js",
    "content": "describe('autocomplete-appropriate', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var evaluate = axe.testUtils.getCheckEvaluate('autocomplete-appropriate');\n\n  beforeEach(function () {\n    axe._tree = undefined;\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  function autocompleteCheckParams(term, type, options) {\n    return checkSetup(\n      '<input autocomplete=\"' + term + '\" type=' + type + ' id=\"target\" />',\n      options\n    );\n  }\n\n  it('returns true for non-select elements', function () {\n    ['div', 'button', 'select', 'textarea'].forEach(function (tagName) {\n      var elm = document.createElement(tagName);\n      elm.setAttribute('autocomplete', 'foo');\n      elm.setAttribute('type', 'email');\n      var params = checkSetup(elm);\n\n      assert.isTrue(\n        evaluate.apply(checkContext, params),\n        'failed for ' + tagName\n      );\n    });\n  });\n\n  it('returns true if the input type is in the map', function () {\n    var options = { foo: ['url'] };\n    var params = autocompleteCheckParams('foo', 'url', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns false if the input type is not in the map', function () {\n    var options = { foo: ['url'] };\n    var params = autocompleteCheckParams('foo', 'email', options);\n    assert.isFalse(evaluate.apply(checkContext, params));\n  });\n\n  it('returns true if the input type is text and the term is undefined', function () {\n    var options = {};\n    var params = autocompleteCheckParams('foo', 'text', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns true if the input type is tel and the term is off', function () {\n    var options = {};\n    var params = autocompleteCheckParams('off', 'tel', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns true if the input type is url and the term is on', function () {\n    var options = {};\n    var params = autocompleteCheckParams('on', 'url', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns true if the input type is foobar and the term is undefined', function () {\n    var options = {};\n    var params = autocompleteCheckParams('foo', 'foobar', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns true if the input type is email and the term is username', function () {\n    var options = {};\n    var params = autocompleteCheckParams('username', 'email', options);\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns false if the input type is text and the term maps to an empty array', function () {\n    var options = { foo: [] };\n    var params = autocompleteCheckParams('foo', 'text', options);\n    assert.isFalse(evaluate.apply(checkContext, params));\n  });\n\n  it('returns false if the input type is month and term is bday-month', function () {\n    var options = {};\n    var params = autocompleteCheckParams('bday-month', 'month', options);\n    assert.isFalse(evaluate.apply(checkContext, params));\n  });\n\n  it('returns false if the input type is MONTH (case-insensitive & sanitized) and term is bday-month', function () {\n    var options = {};\n    var params = autocompleteCheckParams('bday-month', '   MONTH    ', options);\n    assert.isFalse(evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/forms/autocomplete-valid.js",
    "content": "describe('autocomplete-valid', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var evaluate = axe.testUtils.getCheckEvaluate('autocomplete-valid');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('returns true if autocomplete is valid', function () {\n    var params = checkSetup('<input autocomplete=\"on\" id=\"target\" />');\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n\n  it('returns false if autocomplete is not valid', function () {\n    var params = checkSetup('<input autocomplete=\"foo\" id=\"target\" />');\n    assert.isFalse(evaluate.apply(checkContext, params));\n  });\n\n  it('returns undefined (incomplete) if autocomplete is ignored', function () {\n    var params = checkSetup('<input autocomplete=\"text\" id=\"target\" />');\n    assert.isUndefined(evaluate.apply(checkContext, params));\n  });\n\n  it('uses options to change what is valid autocomplete', function () {\n    var options = { stateTerms: ['foo'] };\n    var params = checkSetup(\n      '<input autocomplete=\"foo\" id=\"target\" />',\n      options\n    );\n    assert.isTrue(evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/accesskeys.js",
    "content": "describe('accesskeys', () => {\n  'use strict';\n\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('accesskeys');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return true and record accesskey', () => {\n    const params = checkSetup('<div id=\"target\" accesskey=\"A\"></div>');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n\n    assert.equal(checkContext._data, 'A');\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.equal(checkContext._relatedNodes[0], params[0]);\n  });\n\n  it('ignores hidden nodes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" accesskey=\"A\" style=\"display: none\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n\n    assert.isNull(checkContext._data);\n  });\n\n  it('does not ignore visibly hidden nodes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" accesskey=\"A\" style=\"opacity: 0\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n\n    assert.equal(checkContext._data, 'A');\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.equal(checkContext._relatedNodes[0], params[0]);\n  });\n\n  it('does not ignore screen reader hidden nodes', () => {\n    const params = checkSetup(\n      '<div id=\"target\" accesskey=\"A\" aria-hidden=\"true\"></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n\n    assert.equal(checkContext._data, 'A');\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.equal(checkContext._relatedNodes[0], params[0]);\n  });\n\n  describe('after', () => {\n    it('should push duplicates onto relatedNodes', () => {\n      const results = [\n        { data: 'A', relatedNodes: ['bob'] },\n        { data: 'A', relatedNodes: ['fred'] }\n      ];\n\n      const result = checks.accesskeys.after(results);\n\n      assert.lengthOf(result, 1);\n      assert.equal(result[0].data, 'A');\n      assert.lengthOf(result[0].relatedNodes, 1);\n      assert.equal(result[0].relatedNodes[0], 'fred');\n    });\n\n    it('should remove non-unique accesskeys and toggle result', () => {\n      const results = [\n        { data: 'A', relatedNodes: ['bob'] },\n        { data: 'A', relatedNodes: ['joe'] },\n        { data: 'B', relatedNodes: ['fred'] }\n      ];\n\n      const result = checks.accesskeys.after(results);\n\n      assert.lengthOf(result, 2);\n      assert.isTrue(result[0].result);\n      assert.isFalse(result[1].result);\n    });\n\n    it('should consider accesskeys with different cases as the same result', () => {\n      const result = checks.accesskeys.after([\n        { data: 'A', relatedNodes: ['bob'] },\n        { data: 'a', relatedNodes: ['fred'] }\n      ]);\n\n      assert.lengthOf(result, 1);\n      assert.equal(result[0].data, 'A');\n      assert.lengthOf(result[0].relatedNodes, 1);\n      assert.equal(result[0].relatedNodes[0], 'fred');\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-content.js",
    "content": "describe('focusable-content tests', function () {\n  'use strict';\n\n  var check;\n  var fixture = document.getElementById('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  before(function () {\n    check = checks['focusable-content'];\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('returns false when there are no focusable content elements (content element `div` is not focusable)', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' + '<div> Content </div>' + '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when content element is taken out of focusable order (tabindex = -1)', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' + '<input type=\"text\" tabindex=\"-1\">' + '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element is focusable (only checks if contents are focusable)', function () {\n    var params = checkSetup(\n      '<div id=\"target\" tabindex=\"0\">' +\n        '<p style=\"height: 200px;\"></p>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when all content elements are not focusable', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' +\n        '<input type=\"text\" tabindex=\"-1\">' +\n        '<select tabindex=\"-1\"></select>' +\n        '<textarea tabindex=\"-1\"></textarea>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when one deeply nested content element is focusable', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' +\n        '<div style=\"height: 200px\"> ' +\n        '<div style=\"height: 200px\">' +\n        '<input type=\"text\">' +\n        '</div>' +\n        '</div>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when content element can be focused', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' + '<input type=\"text\">' + '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when any one of the many content elements can be focused', function () {\n    var params = checkSetup(\n      '<div id=\"target\">' +\n        '<input type=\"text\" tabindex=\"-1\">' +\n        '<select tabindex=\"-1\"></select>' +\n        '<textarea tabindex=\"-1\"></textarea>' +\n        '<p style=\"height: 200px;\" tabindex=\"0\"></p>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  describe('shadowDOM - focusable content', function () {\n    before(function () {\n      if (!shadowSupported) {\n        this.skip();\n      }\n    });\n\n    it('returns true when content element can be focused', function () {\n      fixtureSetup('<div id=\"target\">' + '</div>');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<input type=\"text\">';\n      axe._tree = axe.utils.getFlattenedTree(fixture);\n      axe._selectorData = axe.utils.getSelectorData(axe._tree);\n      var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n      var actual = check.evaluate.call(checkContext, node, {}, virtualNode);\n      assert.isTrue(actual);\n    });\n\n    it('returns false when no focusable content', function () {\n      fixtureSetup('<div id=\"target\">' + '</div>');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<input type=\"text\" tabindex=\"-1\"> <p>just some text</p>';\n      axe._tree = axe.utils.getFlattenedTree(fixture);\n      axe._selectorData = axe.utils.getSelectorData(axe._tree);\n      var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n      var actual = check.evaluate.call(checkContext, node, {}, virtualNode);\n      assert.isFalse(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-disabled.js",
    "content": "describe('focusable-disabled', function () {\n  'use strict';\n\n  var check;\n  var fixture = document.getElementById('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  before(function () {\n    check = checks['focusable-disabled'];\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n    checkContext.reset();\n  });\n\n  it('returns true when content not focusable by default (no tabbable elements)', function () {\n    var params = checkSetup('<p id=\"target\" aria-hidden=\"true\">Some text</p>');\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when content hidden through CSS (no tabbable elements)', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\"><a href=\"/\" style=\"display:none\">Link</a></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when content made unfocusable through disabled (no tabbable elements)', function () {\n    var params = checkSetup(\n      '<input id=\"target\" disabled aria-hidden=\"true\" />'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when content made unfocusable through disabled fieldset', function () {\n    var params = checkSetup(\n      '<fieldset id=\"target\" disabled aria-hidden=\"true\"><input /></fieldset>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns false when content is in a disabled fieldset but in another shadow tree',\n    function () {\n      var fieldset = document.createElement('fieldset');\n      fieldset.setAttribute('disabled', 'true');\n      fieldset.setAttribute('aria-hidden', 'true');\n      fieldset.setAttribute('id', 'target');\n      var disabledInput = document.createElement('input');\n      fieldset.appendChild(disabledInput);\n      var shadowRoot = document.createElement('div');\n      fieldset.appendChild(shadowRoot);\n      var shadow = shadowRoot.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<label>Shadow input <input /></label>';\n      var params = checkSetup(fieldset);\n\n      var actual = check.evaluate.apply(checkContext, params);\n\n      assert.isFalse(actual);\n    }\n  );\n\n  it('returns false when content is in the legend of a disabled fieldset', function () {\n    var params = checkSetup(\n      '<fieldset id=\"target\" disabled aria-hidden=\"true\"><legend><input /></legend></fieldset>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when content is in an aria-hidden but not disabled fieldset', function () {\n    var params = checkSetup(\n      '<fieldset id=\"target\" aria-hidden=\"true\"><input /></fieldset>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when focusable off screen link (cannot be disabled)', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\"><a href=\"/\" style=\"position:absolute; top:-999em\">Link</a></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n    assert.lengthOf(checkContext._relatedNodes, 0);\n  });\n\n  it('returns false when focusable form field only disabled through ARIA', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\"><input type=\"text\" aria-disabled=\"true\"/></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.deepEqual(\n      checkContext._relatedNodes,\n      Array.from(fixture.querySelectorAll('input'))\n    );\n  });\n\n  it('returns false when focusable SELECT element that can be disabled', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<label>Choose:' +\n        '<select>' +\n        '<option selected=\"selected\">Chosen</option>' +\n        '<option>Not Selected</option>' +\n        '</select>' +\n        '</label>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.deepEqual(\n      checkContext._relatedNodes,\n      Array.from(fixture.querySelectorAll('select'))\n    );\n  });\n\n  it('returns true when focusable AREA element (cannot be disabled)', function () {\n    var params = checkSetup(\n      '<main id=\"target\" aria-hidden=\"true\">' +\n        '<map name=\"infographic\">' +\n        '<area shape=\"rect\" coords=\"184,6,253,27\" href=\"https://mozilla.org\"' +\n        'target=\"_blank\" alt=\"Mozilla\" />' +\n        '</map>' +\n        '</main>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns false when focusable content inside shadowDOM, that can be disabled',\n    function () {\n      // Note:\n      // `testUtils.checkSetup` does not work for shadowDOM\n      // as `axe._tree` and `axe._selectorData` needs to be updated after shadowDOM construction\n      fixtureSetup('<div id=\"target\"></div>');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<button>Some text</button>';\n      axe.testUtils.flatTreeSetup(fixture);\n      axe._selectorData = axe.utils.getSelectorData(axe._tree);\n      var virtualNode = axe.utils.getNodeFromTree(node);\n      var actual = check.evaluate.call(checkContext, node, {}, virtualNode);\n      assert.isFalse(actual);\n    }\n  );\n\n  it('returns true when focusable target that cannot be disabled', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\"><a id=\"target\" href=\"\">foo</a><button>bar</button></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when focusable target that can be disabled', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\"><a href=\"\">foo</a><button id=\"target\">bar</button></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true if there is a focusable element and modal is open', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<button>Some button</button>' +\n        '</div>' +\n        '<div role=\"dialog\">Modal</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns undefined when the control has onfocus', function () {\n    var params = checkSetup(\n      '<button aria-hidden=\"true\" id=\"target\" onfocus=\"redirectFocus()\">Button</button>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns undefined when all focusable controls have onfocus events', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\" id=\"target\">' +\n        '  <button onfocus=\"redirectFocus()\">button</button>' +\n        '</div>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns false when some, but not all focusable controls have onfocus events', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\" id=\"target\">' +\n        '  <button onfocus=\"redirectFocus()\">button</button>' +\n        '  <button>button</button>' +\n        '</div>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns undefined when control has 0 width and height and pointer events: none (focus trap bumper)', () => {\n    var params = checkSetup(\n      '<button id=\"target\" aria-hidden=\"true\" style=\"pointer-events: none; width: 0; height: 0; margin: 0; padding: 0; border: 0\"></button>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-element.js",
    "content": "describe('focusable-element tests', function () {\n  'use strict';\n\n  var check;\n  var fixture = document.getElementById('fixture');\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  before(function () {\n    check = checks['focusable-element'];\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('returns true when element is focusable', function () {\n    var params = checkSetup('<input id=\"target\" type=\"radio\">');\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when element made not focusable by tabindex', function () {\n    var params = checkSetup(\n      '<input id=\"target\" type=\"checkbox\" tabindex=\"-1\">'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element is not focusable by default', function () {\n    var params = checkSetup('<p id=\"target\">I hold some text </p>');\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when element made focusable by tabindex', function () {\n    var params = checkSetup(\n      '<p id=\"target\" tabindex=\"0\">I hold some text </p>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element made focusable by contenteditable', function () {\n    var params = checkSetup(\n      '<p id=\"target\" contenteditable>I hold some text </p>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element made focusable by contenteditable=\"true\"', function () {\n    var params = checkSetup(\n      '<p id=\"target\" contenteditable=\"true\">I hold some text </p>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when element made focusable by contenteditable=\"false\"', function () {\n    var params = checkSetup(\n      '<p id=\"target\" contenteditable=\"false\">I hold some text </p>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when element made focusable by contenteditable=\"invalid\" and parent is contenteditable', function () {\n    var params = checkSetup(\n      '<div contenteditable><p id=\"target\" contenteditable=\"invalid\">I hold some text </p></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when element made focusable by contenteditable=\"invalid\" and parent is not contenteditable', function () {\n    var params = checkSetup(\n      '<div><p id=\"target\" contenteditable=\"invalid\">I hold some text </p></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element made focusable by contenteditable=\"invalid\" and parent is contenteditable=\"false\"', function () {\n    var params = checkSetup(\n      '<div contenteditable=\"false\"><p id=\"target\" contenteditable=\"invalid\">I hold some text </p></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-modal-open.js",
    "content": "describe('focusable-modal-open', function () {\n  'use strict';\n\n  var check;\n  var fixture = document.getElementById('fixture');\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  before(function () {\n    check = checks['focusable-modal-open'];\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n    checkContext.reset();\n  });\n\n  it('returns true when no modal is open', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<button>Some button</button>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns undefined if a modal is open', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<button>Some button</button>' +\n        '</div>' +\n        '<div role=\"dialog\">Modal</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isUndefined(actual);\n  });\n\n  it('sets the tabbable elements as related nodes', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<button>Some button</button>' +\n        '</div>' +\n        '<div role=\"dialog\">Modal</div>'\n    );\n    check.evaluate.apply(checkContext, params);\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.deepEqual(\n      checkContext._relatedNodes,\n      Array.from(fixture.querySelectorAll('button'))\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-no-name.js",
    "content": "describe('focusable-no-name', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should pass if tabindex < 0', function () {\n    var params = checkSetup('<a href=\"#\" tabindex=\"-1\" id=\"target\"></a>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('focusable-no-name')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass element is not natively focusable', function () {\n    var params = checkSetup('<span role=\"link\" href=\"#\" id=\"target\"></span>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('focusable-no-name')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should fail if element is tabbable with no name - native', function () {\n    var params = checkSetup('<a href=\"#\" id=\"target\"></a>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('focusable-no-name')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should fail if element is tabbable with no name - ARIA', function () {\n    var params = checkSetup(\n      '<span tabindex=\"0\" role=\"link\" id=\"target\" href=\"#\"></spam>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('focusable-no-name')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should pass if the element is tabbable but has an accessible name', function () {\n    var params = checkSetup('<a href=\"#\" title=\"Hello\" id=\"target\"></a>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('focusable-no-name')\n        .apply(checkContext, params)\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should pass if the content is passed in with shadow DOM',\n    function () {\n      var params = shadowCheckSetup(\n        '<div>Content!</div>',\n        '<a href=\"#\" id=\"target\"><slot></slot></a>'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('focusable-no-name')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  describe('Serial Virtual Node', function () {\n    it('should pass if tabindex < 0', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'a',\n        attributes: {\n          tabindex: '-1',\n          href: '#'\n        }\n      });\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('focusable-no-name')(\n          null,\n          {},\n          serialNode\n        )\n      );\n    });\n\n    it('should pass element is not natively focusable', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'span',\n        attributes: {\n          role: 'link',\n          href: '#'\n        }\n      });\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('focusable-no-name')(\n          null,\n          {},\n          serialNode\n        )\n      );\n    });\n\n    it('should fail if element is tabbable with no name - native', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'a',\n        attributes: {\n          href: '#'\n        }\n      });\n      serialNode.children = [];\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('focusable-no-name')(\n          null,\n          {},\n          serialNode\n        )\n      );\n    });\n\n    it('should return undefined if element is tabbable with no name nor children - native', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'a',\n        attributes: {\n          href: '#'\n        }\n      });\n\n      assert.isUndefined(\n        axe.testUtils.getCheckEvaluate('focusable-no-name')(\n          null,\n          {},\n          serialNode\n        )\n      );\n    });\n\n    it('should pass if the element is tabbable but has an accessible name', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'a',\n        attributes: {\n          href: '#',\n          title: 'Hello'\n        }\n      });\n      serialNode.children = [];\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('focusable-no-name')(\n          null,\n          {},\n          serialNode\n        )\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/focusable-not-tabbable.js",
    "content": "describe('focusable-not-tabbable', function () {\n  'use strict';\n\n  var check;\n  var fixture = document.getElementById('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n\n  before(function () {\n    check = checks['focusable-not-tabbable'];\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n    checkContext.reset();\n  });\n\n  it('returns true when BUTTON removed from tab order through tabindex', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<button tabindex=\"-1\">Some button</button>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when LINK is in tab order', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<a href=\"abc.html\">ABC Site</a>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.deepEqual(\n      checkContext._relatedNodes,\n      Array.from(fixture.querySelectorAll('a'))\n    );\n  });\n\n  it('returns true when focusable SUMMARY element, that cannot be disabled', function () {\n    var params = checkSetup(\n      '<details id=\"target\" aria-hidden=\"true\"><summary>Some button</summary><p>Some details</p></details>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n    assert.lengthOf(checkContext._relatedNodes, 1);\n    assert.deepEqual(\n      checkContext._relatedNodes,\n      Array.from(fixture.querySelectorAll('summary'))\n    );\n  });\n\n  it('returns true when TEXTAREA is in tab order, but 0 related nodes as TEXTAREA can be disabled', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<label>Enter your comments:' +\n        '<textarea></textarea>' +\n        '</label>' +\n        '</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.lengthOf(checkContext._relatedNodes, 0);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when focusable AREA element', function () {\n    var params = checkSetup(\n      '<main id=\"target\" aria-hidden=\"true\">' +\n        '<map name=\"infographic\">' +\n        '<area shape=\"rect\" coords=\"184,6,253,27\" href=\"https://mozilla.org\"' +\n        'target=\"_blank\" alt=\"Mozilla\" />' +\n        '</map>' +\n        '</main>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when aria-hidden=false does not negate aria-hidden true', function () {\n    // Note: aria-hidden can't be reset once you've set it to true on an ancestor\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\"><div aria-hidden=\"false\"><button tabindex=\"-1\">Some button</button></div></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns false when focusable text in shadowDOM',\n    function () {\n      // Note:\n      // `testUtils.checkSetup` does not work for shadowDOM\n      // as `axe._tree` and `axe._selectorData` needs to be updated after shadowDOM construction\n      fixtureSetup('<div aria-hidden=\"true\" id=\"target\"></div>`');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<p tabindex=\"0\">btn</p>';\n      axe.testUtils.flatTreeSetup(fixture);\n      axe._selectorData = axe.utils.getSelectorData(axe._tree);\n      var virtualNode = axe.utils.getNodeFromTree(node);\n      var actual = check.evaluate.call(checkContext, node, {}, virtualNode);\n      assert.isFalse(actual);\n    }\n  );\n\n  it('returns false when focusable content through tabindex', function () {\n    var params = checkSetup(\n      '<p id=\"target\" tabindex=\"0\" aria-hidden=\"true\">Some text</p>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when focusable target that cannot be disabled', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\"><a id=\"target\" href=\"\">foo</a><button>bar</button></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when focusable target that can be disabled', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\"><a href=\"\">foo</a><button id=\"target\">bar</button></div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns true if there is a focusable element and modal is open', function () {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\">' +\n        '<a href=\"\">foo</a>' +\n        '</div>' +\n        '<div role=\"dialog\">Modal</div>'\n    );\n    var actual = check.evaluate.apply(checkContext, params);\n    assert.isTrue(actual);\n  });\n\n  it('returns undefined when the control has onfocus', function () {\n    var params = checkSetup(\n      '<a href=\"/\" aria-hidden=\"true\" id=\"target\" onfocus=\"redirectFocus()\">Link</a>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns undefined when all focusable controls have onfocus events', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\" id=\"target\">' +\n        '  <a href=\"/\" onfocus=\"redirectFocus()\">First link</a>' +\n        '  <a href=\"/\" onfocus=\"redirectFocus()\">Second link</a>' +\n        '</div>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns false when some, but not all focusable controls have onfocus events', function () {\n    var params = checkSetup(\n      '<div aria-hidden=\"true\" id=\"target\">' +\n        '  <a href=\"/\" onfocus=\"redirectFocus()\">First link</a>' +\n        '  <a href=\"/\"\">Second link</a>' +\n        '</div>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('returns undefined when control has 0 width and height and pointer events: none (focus trap bumper)', () => {\n    var params = checkSetup(\n      '<div id=\"target\" aria-hidden=\"true\" tabindex=\"0\" style=\"pointer-events: none\"></div>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/frame-focusable-content.js",
    "content": "describe('frame-focusable-content tests', function () {\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var frameFocusableContent = axe.testUtils.getCheckEvaluate(\n    'frame-focusable-content'\n  );\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if element has no focusable content', function () {\n    var vNode = queryFixture('<div id=\"target\"><span>Hello</span></div>');\n    assert.isTrue(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element is empty', function () {\n    var vNode = queryFixture('<div id=\"target\"></div>');\n    assert.isTrue(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element only has text content', function () {\n    var vNode = queryFixture('<div id=\"target\">Hello</div>');\n    assert.isTrue(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return false if element has focusable content', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><span tabindex=\"0\">Hello</span></div>'\n    );\n    assert.isFalse(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return false if element has natively focusable content', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><a href=\"foo.html\">Hello</a></div>'\n    );\n    assert.isFalse(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element is natively focusable but has tabindex=-1', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><button tabindex=\"-1\">Hello</button></div>'\n    );\n    assert.isTrue(frameFocusableContent(null, null, vNode));\n  });\n\n  it('should return false if element is natively focusable but has tabindex=0', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><button tabindex=\"0\">Hello</button></div>'\n    );\n    assert.isFalse(frameFocusableContent(null, null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/landmark-is-top-level.js",
    "content": "describe('landmark-is-top-level', () => {\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n  const checkSetup = axe.testUtils.checkSetup;\n  const shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  const check = checks['landmark-is-top-level'];\n  const checkContext = new axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return false if the main landmark is in another landmark', () => {\n    const params = checkSetup(\n      '<div role=\"banner\"><main id=\"target\"></main></div>'\n    );\n    // landmark-is-top-level requires a complete tree to work properly\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'main' });\n  });\n\n  it('should return false if the complementary landmark is in another landmark', () => {\n    const params = checkSetup(\n      '<nav><div role=\"complementary\" id=\"target\"></div></nav>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'complementary' });\n  });\n\n  it('should return true if the complementary landmark is in main landmark', () => {\n    const params = checkSetup(\n      '<main><div role=\"complementary\" id=\"target\"></div></main>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'complementary' });\n  });\n\n  it('should return false if div with role set to main is in another landmark', () => {\n    const params = checkSetup(\n      '<div role=\"navigation\"><div role=\"main\" id=\"target\"></div></div>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'main' });\n  });\n\n  it('should return true if the landmark is not in another landmark', () => {\n    const params = checkSetup(\n      '<div><footer id=\"target\"></footer><div role=\"banner\"></div></div>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'contentinfo' });\n  });\n\n  it('should return true if div with role set to main is not in another landmark', () => {\n    const params = checkSetup(\n      '<div><div role=\"main\" id=\"target\"></div><div role=\"navigation\"></div></div>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'main' });\n  });\n\n  it('should return true if the banner landmark is not in form landmark', () => {\n    const params = checkSetup(\n      '<div><div role=\"banner\" id=\"target\"></div><div role=\"form\"></div></div>'\n    );\n    axe.utils.getFlattenedTree(document.documentElement);\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { role: 'banner' });\n  });\n\n  (shadowSupported ? it : xit)(\n    'should test if the landmark in shadow DOM is top level',\n    () => {\n      const params = shadowCheckSetup(\n        '<div></div>',\n        '<main id=\"target\">Main content</main>'\n      );\n      axe.utils.getFlattenedTree(document.documentElement);\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._data, { role: 'main' });\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/keyboard/no-focusable-content.js",
    "content": "describe('no-focusable-content tests', function () {\n  var queryFixture = axe.testUtils.queryFixture;\n  var noFocusableContent = axe.testUtils.getCheckEvaluate(\n    'no-focusable-content'\n  );\n  var checkSetup = axe.testUtils.checkSetup;\n  var check = checks['no-focusable-content'];\n  var checkContext = new axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if element has no focusable content', function () {\n    var vNode = queryFixture('<button id=\"target\"><span>Hello</span></button>');\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element is empty', function () {\n    var vNode = queryFixture('<button id=\"target\"></button>');\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element only has text content', function () {\n    var vNode = queryFixture('<button id=\"target\">Hello</button>');\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return true if element has content which is focusable (tabindex=0) and does not have a widget role', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><span tabindex=\"0\">Hello</span></button>'\n    );\n\n    assert.isTrue(noFocusableContent.apply(checkContext, params));\n  });\n\n  it('should return true if element has content which has negative tabindex and non-widget role', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\"><span tabindex=\"-1\">Hello</span></button>'\n    );\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return false if element has content which has negative tabindex and an explicit widget role', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><span role=\"link\" tabindex=\"-1\">Hello</span></button>'\n    );\n\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });\n    assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);\n  });\n\n  it('should return false if element has content which is natively focusable and has a widget role', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><a href=\"foo.html\">Hello</a></button>'\n    );\n\n    assert.isFalse(noFocusableContent.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, null);\n    assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);\n  });\n\n  it('should add each focusable child as related nodes', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><input type=\"checkbox\"><a href=\"foo.html\">Hello</a></button>'\n    );\n\n    assert.isFalse(noFocusableContent.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, null);\n    assert.deepEqual(checkContext._relatedNodes, [\n      params[2].children[0],\n      params[2].children[1]\n    ]);\n  });\n\n  it('should return false if element has natively focusable widget role content with negative tabindex', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><a href=\"foo.html\" tabindex=\"-1\">Hello</a></button>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });\n    assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);\n  });\n\n  it('should return true if element has content which is natively focusable and has a widget role but is disabled', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\"><input value=\"hello\" disabled></button>'\n    );\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return false if \"disabled\" is specified on an element which doesn\\'t allow it', function () {\n    var params = checkSetup(\n      '<button id=\"target\"><a href=\"foo.html\" disabled>Hello</a></button>'\n    );\n    assert.isFalse(noFocusableContent.apply(checkContext, params));\n  });\n\n  it('should return true on span with negative tabindex (focusable, does not have a widget role)', function () {\n    var vNode = queryFixture(\n      '<span id=\"target\" role=\"text\"> some text ' +\n        '<span tabIndex=\"-1\">JavaScript is able to focus this</span> ' +\n        '</span>'\n    );\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return true on aria-hidden span with negative tabindex (focusable, does not have a widget role)', function () {\n    var vNode = queryFixture(\n      '<span id=\"target\" role=\"text\"> some text ' +\n        '<span tabIndex=\"-1\" aria-hidden=\"true\">JavaScript is able to focus this</span> ' +\n        '</span>'\n    );\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n\n  it('should return true on nested span with tabindex=0 (focusable, does not have a widget role)', function () {\n    var vNode = queryFixture(\n      '<span id=\"target\" role=\"text\"> some text ' +\n        '<span tabIndex=\"0\">anyone is able to focus this</span> ' +\n        '</span>'\n    );\n    assert.isTrue(noFocusableContent(null, null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/page-has-elm.js",
    "content": "describe('page-has-*', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkContext = new axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  describe('evaluate', function () {\n    var evaluate = axe.testUtils.getCheckEvaluate('page-has-main');\n\n    it('throws if there is no selector', function () {\n      assert.throws(function () {\n        var params = checkSetup('<div id=\"target\">No role</div>', undefined);\n        checks['page-has-main'].evaluate.apply(checkContext, params);\n      });\n\n      assert.throws(function () {\n        var params = checkSetup('<div id=\"target\">No role</div>', {});\n        checks['page-has-main'].evaluate.apply(checkContext, params);\n      });\n\n      assert.throws(function () {\n        var badOptions = { selector: [] };\n        var params = checkSetup('<div id=\"target\">No role</div>', badOptions);\n        checks['page-has-main'].evaluate.apply(checkContext, params);\n      });\n    });\n\n    it('returns true if there are any matching elements', function () {\n      var options = { selector: 'b' };\n      var params = checkSetup('<div id=\"target\"><b>No role</b></div>', options);\n      assert.isTrue(evaluate.apply(checkContext, params));\n    });\n\n    it('returns false if there are no matching elements', function () {\n      var options = { selector: 'i' };\n      var params = checkSetup('<div id=\"target\"><b>No role</b></div>', options);\n      assert.isFalse(evaluate.apply(checkContext, params));\n    });\n\n    it('does not find hidden elements', function () {\n      var options = { selector: 'b' };\n      var params = checkSetup(\n        '<div id=\"target\"><b style=\"display: none;\">No role</b></div>',\n        options\n      );\n      assert.isFalse(evaluate.apply(checkContext, params));\n    });\n\n    it('does find screen-reader only elements', function () {\n      var options = { selector: 'b' };\n      var params = checkSetup(\n        '<style type=\"text/css\">' +\n          '.sr-only {' +\n          'border: 0;' +\n          'clip: rect(0 0 0 0);' +\n          'clip-path: polygon(0px 0px, 0px 0px, 0px 0px);' +\n          '-webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);' +\n          'height: 1px;' +\n          'margin: -1px;' +\n          'overflow: hidden;' +\n          'padding: 0;' +\n          'position: absolute;' +\n          'width: 1px;' +\n          'white-space: nowrap;' +\n          '}' +\n          '</style>' +\n          '<div id=\"target\"><b class=\"sr-only\">No role</b></div>',\n        options\n      );\n      assert.isTrue(evaluate.apply(checkContext, params));\n    });\n  });\n\n  describe('after', function () {\n    var after = checks['page-has-main'].after;\n\n    it('sets all results to true if any are true', function () {\n      var results = [\n        { result: true },\n        { result: false },\n        { result: undefined }\n      ];\n      assert.deepEqual(after(results), [\n        { result: true },\n        { result: true },\n        { result: true }\n      ]);\n    });\n\n    it('Leave the results as is if none of them were true', function () {\n      var results = [\n        { result: false },\n        { result: false },\n        { result: undefined }\n      ];\n      assert.equal(after(results), results);\n    });\n  });\n\n  describe('page-has-main', function () {\n    var check = checks['page-has-main'];\n\n    it('should return false if no div has role property', function () {\n      var params = checkSetup('<div id=\"target\">No role</div>', check.options);\n      var mainIsFound = check.evaluate.apply(checkContext, params);\n      assert.isFalse(mainIsFound);\n    });\n\n    it('should return false if div has role not equal to main', function () {\n      var params = checkSetup(\n        '<div id=\"target\" role=\"bananas\">Wrong role</div>',\n        check.options\n      );\n      var mainIsFound = check.evaluate.apply(checkContext, params);\n      assert.isFalse(mainIsFound);\n    });\n\n    it('should return true if main landmark exists', function () {\n      var params = checkSetup(\n        '<main id=\"target\">main landmark</main>',\n        check.options\n      );\n      var mainIsFound = check.evaluate.apply(checkContext, params);\n      assert.isTrue(mainIsFound);\n    });\n\n    it('should return true if one div has role equal to main', function () {\n      var params = checkSetup(\n        '<div id=\"target\" role=\"main\">Div with role main</div>',\n        check.options\n      );\n      var mainIsFound = check.evaluate.apply(checkContext, params);\n      assert.isTrue(mainIsFound);\n    });\n\n    (shadowSupported ? it : xit)(\n      'should return true if main is inside of shadow dom',\n      function () {\n        var params = shadowCheckSetup(\n          '<div id=\"target\"></div>',\n          '<main>main landmark</main>',\n          check.options\n        );\n        var mainIsFound = check.evaluate.apply(checkContext, params);\n        assert.isTrue(mainIsFound);\n      }\n    );\n  });\n\n  describe('page-has-heading-one', function () {\n    var check = checks['page-has-heading-one'];\n\n    it('should return false if div has role not equal to heading', function () {\n      var params = checkSetup(\n        '<div id=\"target\" role=\"bananas\">Wrong role</div>',\n        check.options\n      );\n      var h1IsFound = check.evaluate.apply(checkContext, params);\n      assert.isFalse(h1IsFound);\n    });\n\n    it('should return false if div has role heading but not aria-level=1', function () {\n      var params = checkSetup(\n        '<div id=\"target\" role=\"heading\" aria-level=\"one\">Wrong role</div>',\n        check.options\n      );\n      var h1IsFound = check.evaluate.apply(checkContext, params);\n      assert.isFalse(h1IsFound);\n    });\n\n    it('should return true if h1 exists', function () {\n      var params = checkSetup('<h1 id=\"target\">My heading</h1>', check.options);\n      var h1IsFound = check.evaluate.apply(checkContext, params);\n      assert.isTrue(h1IsFound);\n    });\n\n    it('should return true if a div has role=heading and aria-level=1', function () {\n      var params = checkSetup(\n        '<div id=\"target\" role=\"heading\" aria-level=\"1\">Diversity heading</div>',\n        check.options\n      );\n      var h1IsFound = check.evaluate.apply(checkContext, params);\n      assert.isTrue(h1IsFound);\n    });\n\n    (shadowSupported ? it : xit)(\n      'should return true if h1 is inside of shadow dom',\n      function () {\n        var params = shadowCheckSetup(\n          '<div id=\"target\"></div>',\n          '<h1>Shady Heading</h1>',\n          check.options\n        );\n        var h1IsFound = check.evaluate.apply(checkContext, params);\n        assert.isTrue(h1IsFound);\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/page-no-duplicate.js",
    "content": "describe('page-no-duplicate', () => {\n  const fixture = document.getElementById('fixture');\n  const checkContext = new axe.testUtils.MockCheckContext();\n  const checkSetup = axe.testUtils.checkSetup;\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  const check = checks['page-no-duplicate-main'];\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  describe('options.selector', () => {\n    it('throws if there is no selector', () => {\n      assert.throws(() => {\n        const params = checkSetup('<div id=\"target\"></div>', undefined);\n        assert.isFalse(check.evaluate.apply(checkContext, params));\n      });\n    });\n\n    it('should return false if there is more than one element matching the selector', () => {\n      const options = { selector: 'main' };\n      const params = checkSetup(\n        '<div><main id=\"target\"></main><main id=\"dup\"></main></div>',\n        options\n      );\n\n      assert.isFalse(check.evaluate.apply(checkContext, params));\n      assert.deepEqual(\n        checkContext._relatedNodes,\n        Array.from(fixture.querySelectorAll('#dup'))\n      );\n    });\n\n    it('should return true if there is only one element matching the selector', () => {\n      const options = { selector: 'main' };\n      const params = checkSetup('<div role=\"main\" id=\"target\"></div>', options);\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should return true if there are no element matching the selector', () => {\n      const options = { selector: 'footer' };\n      const params = checkSetup(\n        '<div><main id=\"target\"></main><main></main></div>',\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should return true if there is more than one element matching the selector but only one is visible', () => {\n      const options = { selector: 'main' };\n      const params = checkSetup(\n        '<div><main id=\"target\"></main><main id=\"dup\" style=\"display:none;\"></main></div>',\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should return true if there is more than one element matching the selector but only one is visible to screenreaders', () => {\n      const options = { selector: 'main' };\n      const params = checkSetup(\n        '<div><main id=\"target\" aria-hidden=\"true\"></main><main id=\"dup\"></main></div>',\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    (shadowSupported ? it : xit)(\n      'should return false if there is a second matching element inside the shadow dom',\n      () => {\n        const options = { selector: 'main' };\n        const div = document.createElement('div');\n        div.innerHTML = '<div id=\"shadow\"></div><main id=\"target\"></main>';\n\n        const shadow = div\n          .querySelector('#shadow')\n          .attachShadow({ mode: 'open' });\n        shadow.innerHTML = '<main></main>';\n        axe.testUtils.fixtureSetup(div);\n        const vNode = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n\n        assert.isFalse(\n          check.evaluate.call(checkContext, vNode.actualNode, options, vNode)\n        );\n        assert.deepEqual(checkContext._relatedNodes, [\n          shadow.querySelector('main')\n        ]);\n      }\n    );\n\n    (shadowSupported ? it : xit)(\n      'should return true if there is a second matching element inside the shadow dom but only one is visible to screenreaders',\n      () => {\n        const options = { selector: 'main' };\n        const div = document.createElement('div');\n        div.innerHTML =\n          '<div id=\"shadow\"></div><main id=\"target\" aria-hidden=\"true\"></main>';\n\n        const shadow = div\n          .querySelector('#shadow')\n          .attachShadow({ mode: 'open' });\n        shadow.innerHTML = '<main></main>';\n        axe.testUtils.fixtureSetup(div);\n        const vNode = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n\n        assert.isTrue(\n          check.evaluate.call(checkContext, vNode.actualNode, options, vNode)\n        );\n        assert.deepEqual(checkContext._relatedNodes, [\n          shadow.querySelector('main')\n        ]);\n      }\n    );\n  });\n\n  describe('option.nativeScopeFilter', () => {\n    it('should ignore element contained in a nativeScopeFilter match', () => {\n      const options = {\n        selector: 'footer',\n        nativeScopeFilter: 'main'\n      };\n      const params = checkSetup(\n        '<div><footer id=\"target\"></footer>' +\n          '<main><footer></footer></main>' +\n          '</div>',\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should not ignore element contained in a nativeScopeFilter match with their roles redefined', () => {\n      const options = {\n        selector: 'footer, [role=\"contentinfo\"]',\n        nativeScopeFilter: 'main'\n      };\n      const params = checkSetup(\n        '<div><footer id=\"target\"></footer>' +\n          '<main><div role=\"contentinfo\"></div></main>' +\n          '</div>',\n        options\n      );\n      assert.isFalse(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should pass when there are two elements and the first is contained within a nativeSccopeFilter', () => {\n      const options = {\n        selector: 'footer, [role=\"contentinfo\"]',\n        nativeScopeFilter: 'article'\n      };\n      const params = checkSetup(\n        '<article>' +\n          '<footer id=\"target\">Article footer</footer>' +\n          '</article>' +\n          '<footer>Body footer</footer>',\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    (shadowSupported ? it : xit)(\n      'elements if its ancestor is outside the shadow DOM tree',\n      () => {\n        const options = {\n          selector: 'footer',\n          nativeScopeFilter: 'header'\n        };\n\n        const div = document.createElement('div');\n        div.innerHTML =\n          '<header id=\"shadow\"></header><footer id=\"target\"></footer>';\n        div.querySelector('#shadow').attachShadow({ mode: 'open' }).innerHTML =\n          '<footer></footer>';\n        axe.testUtils.fixtureSetup(div);\n        const vNode = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n\n        assert.isTrue(\n          check.evaluate.call(checkContext, vNode.actualNode, options, vNode)\n        );\n      }\n    );\n  });\n\n  describe('options.role', () => {\n    it('should pass when element does not match the role', () => {\n      const options = {\n        selector: 'footer',\n        role: 'contentinfo'\n      };\n      const params = checkSetup(\n        `<div>\n          <footer id=\"target\"></footer>\n          <div role=\"main\">\n            <footer></footer>\n          </div>\n        </div>`,\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    it('should fail when element matches the role', () => {\n      const options = {\n        selector: 'footer',\n        role: 'contentinfo'\n      };\n      const params = checkSetup(\n        `<div>\n          <footer id=\"target\"></footer>\n          <div>\n            <footer id=\"fail\"></footer>\n          </div>\n        </div>`,\n        options\n      );\n      assert.isFalse(check.evaluate.apply(checkContext, params));\n      assert.deepEqual(checkContext._relatedNodes, [\n        fixture.querySelector('#fail')\n      ]);\n    });\n\n    it('should pass when there are two elements and the first does not match the role', () => {\n      const options = {\n        selector: 'footer, [role=\"contentinfo\"]',\n        role: 'contentinfo'\n      };\n      const params = checkSetup(\n        `<article>\n          <footer id=\"target\">Article footer</footer>\n        </article>\n        <footer>Body footer</footer>`,\n        options\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, params));\n    });\n\n    (shadowSupported ? it : xit)(\n      \"should pass if element's ancestor is outside the shadow DOM tree\",\n      () => {\n        const options = {\n          selector: 'footer',\n          role: 'contentinfo'\n        };\n\n        const div = document.createElement('div');\n        div.innerHTML =\n          '<article id=\"shadow\"></article><footer id=\"target\"></footer>';\n        div.querySelector('#shadow').attachShadow({ mode: 'open' }).innerHTML =\n          '<footer></footer>';\n        axe.testUtils.fixtureSetup(div);\n        const vNode = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n\n        assert.isTrue(\n          check.evaluate.call(checkContext, vNode.actualNode, options, vNode)\n        );\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/keyboard/tabindex.js",
    "content": "describe('tabindex', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should fail if the testutils.jstabindex is >= 0', function () {\n    var vNode = queryFixture('<div id=\"target\" tabindex=\"1\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('tabindex')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should pass if the tabindex is <= 0', function () {\n    var vNode = queryFixture('<div id=\"target\" tabindex=\"0\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('tabindex')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should look at the attribute and not the property', function () {\n    var node = document.createElement('div');\n    node.setAttribute('tabindex', '1');\n    node.tabindex = null;\n    var tree = axe.testUtils.flatTreeSetup(node);\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('tabindex')\n        .call(checkContext, null, {}, tree[0])\n    );\n  });\n\n  it('should pass if tabindex is NaN', function () {\n    var vNode = queryFixture('<div id=\"target\" tabindex=\"foobar\"></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('tabindex')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/label/alt-space-value.js",
    "content": "describe('alt-space-value', function () {\n  'use strict';\n\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var check = checks['alt-space-value'];\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if alt contains a space character', function () {\n    var params = checkSetup('<img id=\"target\" alt=\" \" />');\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return true if alt contains a non-breaking space character', function () {\n    var params = checkSetup('<img id=\"target\" alt=\"&nbsp;\" />');\n    assert.isTrue(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return false if alt attribute is empty', function () {\n    var params = checkSetup('<img id=\"target\" alt=\"\" />');\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n\n  it('should return false if alt attribute has a proper text value', function () {\n    var params = checkSetup('<img id=\"target\" alt=\"text content\" />');\n    assert.isFalse(check.evaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/label/duplicate-img-label.js",
    "content": "describe('duplicate-img-label', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should return false if no text is present', function () {\n    fixture.innerHTML = '<button><img id=\"target\" alt=\"Plain text\"></button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    var result = axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n      node,\n      undefined,\n      axe.utils.getNodeFromTree(node)\n    );\n    assert.isFalse(result);\n  });\n\n  it('should return false if aria-label duplicates img alt', function () {\n    fixture.innerHTML =\n      '<button aria-label=\"Plain text\"><img id=\"target\" alt=\"Plain text\"></button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return false if img and text have different text', function () {\n    fixture.innerHTML =\n      '<button><img id=\"target\" alt=\"Alt text\">Plain text</button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return true if img and text have the same text', function () {\n    fixture.innerHTML =\n      '<button><img id=\"target\" alt=\"Plain text\">Plain text</button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return true if img has ARIA label with the same text', function () {\n    fixture.innerHTML =\n      '<button><img id=\"target\" aria-label=\"Plain text\">Plain text</button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return false if img and text are both blank', function () {\n    fixture.innerHTML = '<button><img id=\"target\" alt=\"\"></button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return false if img and text have superset/subset text', function () {\n    fixture.innerHTML =\n      '<button><img id=\"target\" alt=\"Plain text and more\">Plain text</button>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return false if img does not have required parent', function () {\n    fixture.innerHTML =\n      '<main><img id=\"target\" alt=\"Plain text and more\"><p>Plain text</p></main>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should support options.parentSelector', function () {\n    fixture.innerHTML =\n      '<div aria-label=\"Plain text\"><img id=\"target\" alt=\"Plain text\"></div>';\n    var node = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n        node,\n        { parentSelector: 'div' },\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true if the img is part of a shadow tree',\n    function () {\n      var button = document.createElement('div');\n      button.setAttribute('role', 'button');\n      button.innerHTML = 'My button';\n      var shadow = button.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<slot></slot><img id=\"target\" alt=\"My button\">';\n      fixture.appendChild(button);\n      axe.testUtils.flatTreeSetup(fixture);\n      var node = shadow.querySelector('#target');\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n          node,\n          undefined,\n          axe.utils.getNodeFromTree(node)\n        )\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true if the img is a slotted element',\n    function () {\n      var button = document.createElement('div');\n      button.setAttribute('role', 'button');\n      button.innerHTML = '<img id=\"target\" alt=\"My button\">';\n      var shadow = button.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<span>My button</span> <slot></slot>';\n\n      fixture.appendChild(button);\n      axe.testUtils.flatTreeSetup(fixture);\n      var node = button.querySelector('#target');\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('duplicate-img-label')(\n          node,\n          undefined,\n          axe.utils.getNodeFromTree(node)\n        )\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return false if the shadow img has a different text',\n    function () {\n      var button = document.createElement('div');\n      button.setAttribute('role', 'button');\n      button.innerHTML = 'My button';\n      var shadow = button.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<slot></slot><img alt=\"My image\">';\n      var checkArgs = checkSetup(button);\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('duplicate-img-label')\n          .apply(null, checkArgs)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/label/explicit.js",
    "content": "describe('explicit-label', () => {\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('explicit-label');\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns false if an empty label is present', () => {\n    const params = checkSetup(\n      '<label for=\"target\"></label><input type=\"text\" id=\"target\">'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false if the label is empty except for the target value', () => {\n    const params = checkSetup(\n      '<label for=\"target\"> <input type=\"text\" id=\"target\" value=\"snacks\"> </label>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false if an empty label is present that uses aria-labelledby', () => {\n    const params = checkSetup(\n      '<input type=\"text\" id=\"target\">' +\n        '<label for=\"target\" aria-labelledby=\"lbl\"></label>' +\n        '<span id=\"lbl\">aria label</span>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true if a non-empty label is present', () => {\n    const params = checkSetup(\n      '<label for=\"target\">Text</label><input type=\"text\" id=\"target\">'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true if an invisible non-empty label is present, to defer to hidden-explicit-label', () => {\n    const params = checkSetup(\n      '<label for=\"target\" style=\"display: none;\">Text</label><input type=\"text\" id=\"target\">'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false if a label is not present', () => {\n    const params = checkSetup('<input type=\"text\" id=\"target\" />');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should work for multiple labels', () => {\n    const params = checkSetup(\n      '<label for=\"target\"></label><label for=\"target\">Text</label><input type=\"text\" id=\"target\">'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  describe('.data', () => {\n    it('is null if there is no label', () => {\n      const params = checkSetup('<input type=\"text\" id=\"target\" />');\n      checkEvaluate.apply(checkContext, params);\n      assert.isNull(checkContext._data);\n    });\n\n    it('includes the `explicitLabel` text of the first non-empty label', () => {\n      const params = checkSetup(\n        '<label for=\"target\">  </label>' +\n          '<label for=\"target\"> text </label>' +\n          '<label for=\"target\"> more text </label>' +\n          '<input type=\"text\" id=\"target\" />'\n      );\n      checkEvaluate.apply(checkContext, params);\n      assert.deepEqual(checkContext._data, { explicitLabel: 'text' });\n    });\n\n    it('is empty { explicitLabel: \"\" } if the label is empty', () => {\n      const params = checkSetup(\n        '<label for=\"target\">  </label>' +\n          '<label for=\"target\"></label>' +\n          '<input type=\"text\" id=\"target\" />'\n      );\n      checkEvaluate.apply(checkContext, params);\n      assert.deepEqual(checkContext._data, { explicitLabel: '' });\n    });\n  });\n\n  describe('related nodes', () => {\n    it('is empty when there are no labels', () => {\n      const params = checkSetup('<input type=\"text\" id=\"target\" />');\n      checkEvaluate.apply(checkContext, params);\n      assert.isEmpty(checkContext._relatedNodes);\n    });\n\n    it('includes each associated label', () => {\n      const params = checkSetup(\n        '<label for=\"target\" id=\"lbl1\"></label>' +\n          '<label for=\"target\" id=\"lbl2\"></label>' +\n          '<input type=\"text\" id=\"target\" />'\n      );\n      checkEvaluate.apply(checkContext, params);\n      const ids = checkContext._relatedNodes.map(node => '#' + node.id);\n      assert.deepEqual(ids, ['#lbl1', '#lbl2']);\n    });\n  });\n\n  describe('with shadow DOM', () => {\n    it('returns true if input and label are in the same shadow root', () => {\n      const root = document.createElement('div');\n      const shadow = root.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<label for=\"target\">American band</label><input id=\"target\">';\n      fixtureSetup(root);\n\n      const vNode = axe.utils.getNodeFromTree(shadow.querySelector('#target'));\n      assert.isTrue(checkEvaluate.call(checkContext, null, {}, vNode));\n    });\n\n    it('returns true if label content is slotted', () => {\n      const root = document.createElement('div');\n      root.innerHTML = 'American band';\n      const shadow = root.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<label for=\"target\"><slot></slot></label><input id=\"target\">';\n      fixtureSetup(root);\n\n      const vNode = axe.utils.getNodeFromTree(shadow.querySelector('#target'));\n      assert.isTrue(checkEvaluate.call(checkContext, null, {}, vNode));\n    });\n\n    it('returns false if input is inside shadow DOM and the label is not', () => {\n      const root = document.createElement('div');\n      root.innerHTML = '<label for=\"target\">American band</label>';\n      const shadow = root.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<slot></slot><input id=\"target\">';\n      fixtureSetup(root);\n\n      const vNode = axe.utils.getNodeFromTree(shadow.querySelector('#target'));\n      assert.isFalse(checkEvaluate.call(checkContext, null, {}, vNode));\n    });\n\n    it('returns false if label is inside shadow DOM and the input is not', () => {\n      const root = document.createElement('div');\n      root.innerHTML = '<input id=\"target\">';\n      const shadow = root.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<label for=\"target\">American band</label><slot></slot>';\n      fixtureSetup(root);\n\n      const vNode = axe.utils.getNodeFromTree(root.querySelector('#target'));\n      assert.isFalse(checkEvaluate.call(checkContext, null, {}, vNode));\n    });\n  });\n\n  describe('SerialVirtualNode', () => {\n    it('returns undefined', () => {\n      const virtualNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'text'\n        }\n      });\n      assert.isFalse(checkEvaluate.call(checkContext, null, {}, virtualNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/label/help-same-as-label.js",
    "content": "describe('help-same-as-label', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should return true if an element has a label and a title with the same text', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.title = 'Duplicate';\n    node.setAttribute('aria-label', 'Duplicate');\n\n    fixture.appendChild(node);\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('help-same-as-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return true if an element has a label and aria-describedby with the same text', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.setAttribute('aria-label', 'Duplicate');\n    node.setAttribute('aria-describedby', 'dby');\n    var dby = document.createElement('div');\n    dby.id = 'dby';\n    dby.innerHTML = 'Duplicate';\n\n    fixture.appendChild(node);\n    fixture.appendChild(dby);\n\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('help-same-as-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return false if input only has a title', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.title = 'Duplicate';\n\n    fixture.appendChild(node);\n\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('help-same-as-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return true if an input only has aria-describedby', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.setAttribute('aria-describedby', 'dby');\n    var dby = document.createElement('div');\n    dby.id = 'dby';\n    dby.innerHTML = 'Duplicate';\n\n    fixture.appendChild(node);\n    fixture.appendChild(dby);\n\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('help-same-as-label')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/label/hidden-explicit-label.js",
    "content": "describe('hidden-explicit-label', function () {\n  'use strict';\n\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var check = checks['hidden-explicit-label'];\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return true if a hidden non-empty label is present', function () {\n    var args = checkSetup(\n      '<label for=\"target\" style=\"display:none\">Text</label><input type=\"text\" id=\"target\">',\n      {},\n      '#target'\n    );\n    assert.isTrue(check.evaluate.apply(check, args));\n  });\n\n  it('should return false if a visible non-empty label is present', function () {\n    var args = checkSetup(\n      '<label for=\"target\">Label</label><input type=\"text\" id=\"target\">'\n    );\n    assert.isFalse(check.evaluate.apply(check, args));\n  });\n\n  it('should return true if an invisible empty label is present', function () {\n    var args = checkSetup(\n      '<label for=\"target\" style=\"display: none;\"></label><input type=\"text\" id=\"target\">'\n    );\n    assert.isTrue(check.evaluate.apply(check, args));\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true if content is inside of shadow DOM',\n    function () {\n      var params = shadowCheckSetup(\n        '<div></div>',\n        '<label for=\"target\" style=\"display:none\">Text</label><input type=\"text\" id=\"target\">'\n      );\n\n      assert.isTrue(check.evaluate.apply(shadowCheckSetup, params));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return false if part of the pairing is inside of shadow DOM',\n    function () {\n      var params = shadowCheckSetup(\n        '<div><label for=\"target\" style=\"display:none\">Text</label></div>',\n        '<input type=\"text\" id=\"target\">'\n      );\n\n      assert.isFalse(check.evaluate.apply(shadowCheckSetup, params));\n    }\n  );\n\n  it('should fail when the label has aria-hidden=true', function () {\n    var html = '';\n    html += '<div>';\n    html += '  <label for=\"target\" aria-hidden=\"true\">';\n    html += '    Hello world';\n    html += '  </label>';\n    html += '  <input id=\"target\">';\n    html += '</div>';\n    var args = checkSetup(html, {}, '#target');\n    assert.isTrue(check.evaluate.apply(check, args));\n  });\n\n  describe('if the label is hidden', function () {\n    describe('and the element has an accessible name', function () {\n      it('should not fail', function () {\n        var html = '';\n\n        html += '<div>';\n        html += '  <label for=\"target\" style=\"display:none\">';\n        html += '    Hello world';\n        html += '  </label>';\n        html += '  <input id=\"target\" title=\"Hi\">';\n        html += '</div>';\n\n        var args = checkSetup(html, {}, '#target');\n        assert.isFalse(check.evaluate.apply(check, args));\n      });\n    });\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return false if no id', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'text'\n        }\n      });\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('hidden-explicit-label')(null, {}, vNode)\n      );\n    });\n\n    it('should return undefined if it has id', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'text',\n          id: 'foobar'\n        }\n      });\n      assert.isUndefined(\n        axe.testUtils.getCheckEvaluate('hidden-explicit-label')(null, {}, vNode)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/label/implicit.js",
    "content": "describe('implicit-label', () => {\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('implicit-label');\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns false if an empty label is present', () => {\n    const params = checkSetup('<label><input type=\"text\" id=\"target\"></label>');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false on an empty label when then control has a value', () => {\n    const params = checkSetup(\n      '<label><input type=\"text\" id=\"target\" value=\"snacks\"></label>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false if an invisible non-empty label is present', () => {\n    const params = checkSetup(\n      '<label><span style=\"display: none\">Text</span> <input type=\"text\" id=\"target\"></label>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns true if a non-empty label is present', () => {\n    const params = checkSetup(\n      '<label>Text <input type=\"text\" id=\"target\"></label>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('returns false if a label is not present', () => {\n    const node = document.createElement('input');\n    node.type = 'text';\n    fixtureSetup(node);\n    const virtualNode = axe.utils.getNodeFromTree(node);\n    assert.isFalse(checkEvaluate.call(checkContext, null, {}, virtualNode));\n  });\n\n  describe('data', () => {\n    it('is null if there is no label', () => {\n      const params = checkSetup('<input type=\"text\" id=\"target\">');\n      checkEvaluate.apply(checkContext, params);\n      assert.isNull(checkContext._data);\n    });\n\n    it('includes the implicit label if one is set', () => {\n      const params = checkSetup(\n        '<label>Some <input type=\"text\" id=\"target\"> text</label>'\n      );\n      checkEvaluate.apply(checkContext, params);\n      assert.deepEqual(checkContext._data, { implicitLabel: 'Some text' });\n    });\n\n    it('has { implicitLabel: \"\" } when the label is empty', () => {\n      const params = checkSetup(\n        '<label> <input type=\"text\" id=\"target\"> </label>'\n      );\n      checkEvaluate.apply(checkContext, params);\n      assert.deepEqual(checkContext._data, { implicitLabel: '' });\n    });\n  });\n\n  describe('relatedNodes', () => {\n    it('is null if there is no label', () => {\n      const params = checkSetup('<input type=\"text\" id=\"target\">');\n      checkEvaluate.apply(checkContext, params);\n      assert.isEmpty(checkContext._relatedNodes);\n    });\n\n    it('includes the nearest label as its related node', () => {\n      const params = checkSetup(\n        '<label id=\"lbl\"> <input type=\"text\" id=\"target\"> </label>'\n      );\n      checkEvaluate.apply(checkContext, params);\n      const ids = checkContext._relatedNodes.map(node => '#' + node.id);\n      assert.deepEqual(ids, ['#lbl']);\n    });\n  });\n\n  describe('SerialVirtualNode', () => {\n    it('returns false if no implicit label', () => {\n      const virtualNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'text'\n        }\n      });\n      virtualNode.parent = null;\n      assert.isFalse(checkEvaluate.call(checkContext, null, {}, virtualNode));\n    });\n\n    it('returns undefined if tree is not complete', () => {\n      const virtualNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'text'\n        }\n      });\n      assert.isUndefined(\n        checkEvaluate.call(checkContext, null, {}, virtualNode)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/label/label-content-name-mismatch.js",
    "content": "describe('label-content-name-mismatch tests', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var check = checks['label-content-name-mismatch'];\n  var options = undefined;\n\n  var fontApiSupport = !!document.fonts;\n\n  before(function (done) {\n    if (!fontApiSupport) {\n      done();\n    }\n\n    var materialFont = new FontFace(\n      'Material Icons',\n      'url(https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2)'\n    );\n    materialFont.load().then(function () {\n      document.fonts.add(materialFont);\n      done();\n    });\n  });\n\n  it('returns true when visible text and accessible name (`aria-label`) matches (text sanitized)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" role=\"link\" aria-label=\"next page &nbsp \">next page</div>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when visible text and accessible name (`aria-label`) matches (character insensitive)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" role=\"link\" aria-label=\"Next Page\">next pAge</div>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when visible text and accessible name (`aria-labelledby`) matches (character insensitive & text sanitized)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"yourLabel\">UNTIL THE VeRy EnD</div>' +\n        '<div id=\"yourLabel\">uNtIl the very end  &nbsp</div>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when visible text is contained in the accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" name=\"link\" aria-label=\"Next Page in the list\">Next Page</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when visible text doesn’t match accessible name', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" role=\"link\" aria-label=\"OK\">Next</div>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when not all of visible text is included in accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" name=\"link\" aria-label=\"the full\">The full label</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has non-matching accessible name (`aria-labelledby`) and text content', function () {\n    var vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-labelledby=\"foo\">some content</div>' +\n        '<div id=\"foo\">123</div>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when visible text excluding emoji is part of accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"I would like a burger\">I would like a 🍔 </button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when visible text excluding punctuations/ symbols is part of accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"next page\">next page &gt;&gt;</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  (fontApiSupport ? it : it.skip)(\n    'returns true when visible text excluding ligature icon is part of accessible name',\n    function () {\n      var vNode = queryFixture(\n        '<button id=\"target\" aria-label=\"next page\">next page <span style=\"font-family: \\'Material Icons\\'\">delete</span></button>'\n      );\n      var actual = check.evaluate(vNode.actualNode, options, vNode);\n      assert.isTrue(actual);\n    }\n  );\n\n  it('returns true when visible text excluding private use unicode is part of accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"Favorites\"> Favorites</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns undefined (needs review) when visible text name is only an emoji', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"comet\">☄️</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) when accessible name is an emoji', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"☄️\">shooting star</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) for visible text is single characters (punctuation) used as icon', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"help\">?</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) for unicode as accessible name and text content', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"&#x1F354\">&#x1F354</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) for unicode text content', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"close\">&#10060;</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined (needs review) when punctuation is used as text content', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"wink\">;)</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns true when normal text content which is punctuated', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"I like football but I prefer cycling more\">I like football, but I prefer cycling more.</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when normal puntuated text content is not contained in accessible name is punctuated', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"I like football\">I like cycling more!!!</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when text contains <br/>', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"button label\">button<br>label</button>'\n    );\n    var actual = check.evaluate(vNode.actualNode, options, vNode);\n    assert.isTrue(actual);\n  });\n});\n"
  },
  {
    "path": "test/checks/label/multiple-label.js",
    "content": "describe('multiple-label', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return undefined if there are multiple implicit labels', function () {\n    fixtureSetup(\n      '<label id=\"l2\"><label id=\"l1\"><input type=\"text\" id=\"target\"></label></label>'\n    );\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2]);\n  });\n\n  it('should return false if there is only one implicit label', function () {\n    fixtureSetup('<label id=\"l1\"><input type=\"text\" id=\"target\"></label>');\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1]);\n  });\n\n  it('should return undefined if there are multiple explicit labels', function () {\n    fixtureSetup(\n      '<label id=\"l1\" for=\"target\">Foo</label>' +\n        '<label id=\"l2\" for=\"target\">Bar</label>' +\n        '<label id=\"l3\" for=\"target\">Bat</label>' +\n        '<input type=\"text\" id=\"target\">'\n    );\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    var l3 = fixture.querySelector('#l3');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2, l3]);\n  });\n\n  it('should return false if there is only one explicit label', function () {\n    fixtureSetup(\n      '<label id=\"l1\" for=\"target\">Foo</label><input type=\"text\" id=\"target\">'\n    );\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1]);\n  });\n\n  it('should return false if there are multiple explicit labels but one is hidden', function () {\n    fixtureSetup(\n      '<label for=\"test-input2\" id=\"l1\">label one</label>' +\n        '<label for=\"test-input2\" style=\"display:none\" id=\"lnone\">label two</label>' +\n        '<input id=\"test-input2\" type=\"text\">'\n    );\n    var target = fixture.querySelector('#test-input2');\n    var l1 = fixture.querySelector('#l1');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1]);\n  });\n\n  it('should return undefined if there are multiple implicit labels and one is visually hidden', function () {\n    fixtureSetup(\n      '<label id=\"l2\"><label id=\"l1\" style=\"opacity: 0\"><input type=\"text\" id=\"target\"></label></label>'\n    );\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2]);\n  });\n\n  it('should return undefined if there are multiple explicit labels but some are hidden', function () {\n    fixtureSetup(\n      '<label for=\"me\" id=\"l1\">visible</label>' +\n        '<label for=\"me\" style=\"display:none;\" id=\"l2\">hidden</label>' +\n        '<label for=\"me\" id=\"l3\">visible</label>' +\n        '<input id=\"me\" type=\"text\">'\n    );\n    var target = fixture.querySelector('#me');\n    var l1 = fixture.querySelector('#l1');\n    var l3 = fixture.querySelector('#l3');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l3]);\n  });\n\n  it('should return undefined if there are multiple explicit labels and one is visually hidden', function () {\n    fixtureSetup(\n      '<label for=\"me\" id=\"l1\">visible</label>' +\n        '<label for=\"me\" id=\"l2\" style=\"opacity: 0\">visible</label>' +\n        '<input id=\"me\" type=\"text\">'\n    );\n    var target = fixture.querySelector('#me');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2]);\n  });\n\n  it('should return undefined if there are multiple explicit labels and one is screen reader hidden', function () {\n    fixtureSetup(\n      '<label for=\"me\" id=\"l1\">visible</label>' +\n        '<label for=\"me\" id=\"l2\" aria-hidden=\"true\">visible</label>' +\n        '<input id=\"me\" type=\"text\">'\n    );\n    var target = fixture.querySelector('#me');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2]);\n  });\n\n  it('should return undefined if there are implicit and explicit labels', function () {\n    fixtureSetup(\n      '<label id=\"l1\" for=\"target\">Foo</label><label id=\"l2\"><input type=\"text\" id=\"target\"></label>'\n    );\n    var target = fixture.querySelector('#target');\n    var l1 = fixture.querySelector('#l1');\n    var l2 = fixture.querySelector('#l2');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [l1, l2]);\n  });\n\n  it('should return false if there an implicit label uses for attribute', function () {\n    fixtureSetup(\n      '<label for=\"target\">Foo<input type=\"text\" id=\"target\"></label>'\n    );\n    var target = fixture.querySelector('#target');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return undefined given multiple labels and no aria-labelledby', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"A\">' +\n        '<label for=\"A\">Please</label>' +\n        '<label for=\"A\">Excuse</label>' +\n        '<label for=\"A\">My</label>' +\n        '<label for=\"A\">Dear</label>' +\n        '<label for=\"A\">Aunt</label>' +\n        '<label for=\"A\">Sally</label>'\n    );\n    var target = fixture.querySelector('#A');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return undefined given multiple labels, one label AT visible, and no aria-labelledby', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"B\">' +\n        '<label for=\"B\">Please</label>' +\n        '<label for=\"B\" aria-hidden=\"true\">Excuse</label>' +\n        '<label for=\"B\" aria-hidden=\"true\">My</label>' +\n        '<label for=\"B\" aria-hidden=\"true\">Dear</label>' +\n        '<label for=\"B\" aria-hidden=\"true\">Aunt</label>' +\n        '<label for=\"B\" aria-hidden=\"true\">Sally</label>'\n    );\n    var target = fixture.querySelector('#B');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return false given multiple labels, one label AT visible, and aria-labelledby for AT visible', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"D\" aria-labelledby=\"E\"/>' +\n        '<label for=\"D\" aria-hidden=\"true\">Please</label>' +\n        '<label for=\"D\" id=\"E\">Excuse</label>'\n    );\n    var target = fixture.querySelector('#D');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return false given multiple labels, one label AT visible, and aria-labelledby for all', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"F\" aria-labelledby=\"G H\"/>' +\n        '<label for=\"F\" id=\"G\" aria-hidden=\"true\">Please</label>' +\n        '<label for=\"F\" id=\"H\">Excuse</label>'\n    );\n    var target = fixture.querySelector('#F');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return false given multiple labels, one label visible, and no aria-labelledby', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"I\"/>' +\n        '<label for=\"I\" style=\"display:none\">Please</label>' +\n        '<label for=\"I\" >Excuse</label>'\n    );\n    var target = fixture.querySelector('#I');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return undefined given multiple labels, all visible, aria-labelledby for all', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"J\" aria-labelledby=\"K L M N O P\">' +\n        '<label for=\"J\" id=\"K\">Please</label>' +\n        '<label for=\"J\" id=\"L\">Excuse</label>' +\n        '<label for=\"J\" id=\"M\">My</label>' +\n        '<label for=\"J\" id=\"N\">Dear</label>' +\n        '<label for=\"J\" id=\"O\">Aunt</label>' +\n        '<label for=\"J\" id=\"P\">Sally</label>'\n    );\n    var target = fixture.querySelector('#J');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  it('should return undefined given multiple labels, one AT visible, no aria-labelledby', function () {\n    fixtureSetup(\n      '<input type=\"checkbox\" id=\"Q\"/>' +\n        '<label for=\"Q\" aria-hidden=\"true\"></label>' +\n        '<label for=\"Q\" >Excuse</label>'\n    );\n    var target = fixture.querySelector('#Q');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('multiple-label')\n        .call(checkContext, target)\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should consider labels in the same document/shadow tree',\n    function () {\n      fixture.innerHTML = '<div id=\"target\"></div>';\n      var target = document.querySelector('#target');\n      var shadowRoot = target.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<input id=\"myinput\" /><label for=\"myinput\">normal</label>';\n      var shadowTarget = target.shadowRoot;\n      fixtureSetup();\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('multiple-label')\n          .call(checkContext, shadowTarget.firstElementChild)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false for valid multiple labels in the same document/shadow tree',\n    function () {\n      fixture.innerHTML = '<div id=\"target\"></div>';\n      var target = document.querySelector('#target');\n      var shadowRoot = target.attachShadow({ mode: 'open' });\n      var innerHTML = '<input type=\"checkbox\" id=\"D\" aria-labelledby=\"E\"/>';\n      innerHTML += '<label for=\"D\" aria-hidden=\"true\">Please</label>';\n      innerHTML += '<label for=\"D\" id=\"E\">Excuse</label>';\n      shadowRoot.innerHTML = innerHTML;\n      fixtureSetup();\n      var shadowTarget = target.shadowRoot;\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('multiple-label')\n          .call(checkContext, shadowTarget.firstElementChild)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return undefined for invalid multiple labels in the same document/shadow tree',\n    function () {\n      fixture.innerHTML = '<div id=\"target\"></div>';\n      var target = document.querySelector('#target');\n      var shadowRoot = target.attachShadow({ mode: 'open' });\n      var innerHTML = '<input type=\"checkbox\" id=\"Q\"/>';\n      innerHTML += '<label for=\"Q\" aria-hidden=\"true\"></label>';\n      innerHTML += '<label for=\"Q\" >Excuse</label>';\n      shadowRoot.innerHTML = innerHTML;\n      fixtureSetup();\n      var shadowTarget = target.shadowRoot;\n      assert.isUndefined(\n        axe.testUtils\n          .getCheckEvaluate('multiple-label')\n          .call(checkContext, shadowTarget.firstElementChild)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/label/title-only.js",
    "content": "describe('title-only', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should return true if an element only has a title', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.title = 'Duplicate';\n\n    fixture.appendChild(node);\n\n    axe.testUtils.flatTreeSetup(fixture);\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('title-only')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n    node.setAttribute('aria-label', 'woop');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('title-only')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n\n  it('should return true if an element only has aria-describedby', function () {\n    var node = document.createElement('input');\n    node.type = 'text';\n    node.setAttribute('aria-describedby', 'dby');\n    var dby = document.createElement('div');\n    dby.id = 'dby';\n    dby.innerHTML = 'woop';\n\n    fixture.appendChild(node);\n    fixture.appendChild(dby);\n\n    axe.testUtils.flatTreeSetup(fixture);\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('title-only')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n    node.setAttribute('aria-label', 'woop');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('title-only')(\n        node,\n        undefined,\n        axe.utils.getNodeFromTree(node)\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/landmarks/landmark-is-unique-after.js",
    "content": "describe('landmark-is-unique-after', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  function createResult(result, data) {\n    return {\n      result: result,\n      data: data\n    };\n  }\n\n  function createResultWithSameRelatedNodes(result, data) {\n    return Object.assign(createResult(result, data), {\n      relatedNodes: [createResult(result, data)]\n    });\n  }\n\n  function createResultWithProvidedRelatedNodes(result, data, relatedNodes) {\n    return Object.assign(createResult(result, data), {\n      relatedNodes: relatedNodes\n    });\n  }\n\n  afterEach(function () {\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should update duplicate landmarks with failed result', function () {\n    var result = checks['landmark-is-unique'].after([\n      createResultWithSameRelatedNodes(true, {\n        role: 'some role',\n        accessibleText: 'some accessibleText'\n      }),\n      createResultWithSameRelatedNodes(true, {\n        role: 'some role',\n        accessibleText: 'some accessibleText'\n      }),\n      createResultWithSameRelatedNodes(true, {\n        role: 'different role',\n        accessibleText: 'some accessibleText'\n      }),\n      createResultWithSameRelatedNodes(true, {\n        role: 'some role',\n        accessibleText: 'different accessibleText'\n      })\n    ]);\n\n    var expectedResult = [\n      createResultWithProvidedRelatedNodes(\n        false,\n        {\n          role: 'some role',\n          accessibleText: 'some accessibleText'\n        },\n        [\n          createResult(true, {\n            role: 'some role',\n            accessibleText: 'some accessibleText'\n          })\n        ]\n      ),\n      createResultWithProvidedRelatedNodes(\n        true,\n        {\n          role: 'different role',\n          accessibleText: 'some accessibleText'\n        },\n        []\n      ),\n      createResultWithProvidedRelatedNodes(\n        true,\n        {\n          role: 'some role',\n          accessibleText: 'different accessibleText'\n        },\n        []\n      )\n    ];\n    assert.deepEqual(result, expectedResult);\n  });\n});\n"
  },
  {
    "path": "test/checks/landmarks/landmark-is-unique.js",
    "content": "describe('landmark-is-unique', function () {\n  'use strict';\n\n  var checkContext = new axe.testUtils.MockCheckContext();\n  var fixture;\n  var axeFixtureSetup;\n\n  beforeEach(function () {\n    fixture = document.getElementById('fixture');\n    axeFixtureSetup = axe.testUtils.fixtureSetup;\n  });\n\n  afterEach(function () {\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should return true, with correct role and no accessible text', function () {\n    axeFixtureSetup('<div role=\"main\">test</div>');\n    var node = fixture.querySelector('div');\n    var expectedData = {\n      accessibleText: null,\n      role: 'main'\n    };\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('landmark-is-unique')\n        .call(checkContext, node, {}, virtualNode)\n    );\n    assert.deepEqual(checkContext._data, expectedData);\n    assert.deepEqual(checkContext._relatedNodes, [node]);\n  });\n\n  it('should return true, with correct role and the accessible text lowercased', function () {\n    axeFixtureSetup('<div role=\"main\" aria-label=\"TEST text\">test</div>');\n    var node = fixture.querySelector('div');\n    var expectedData = {\n      accessibleText: 'test text',\n      role: 'main'\n    };\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('landmark-is-unique')\n        .call(checkContext, node, {}, virtualNode)\n    );\n    assert.deepEqual(checkContext._data, expectedData);\n    assert.deepEqual(checkContext._relatedNodes, [node]);\n  });\n});\n"
  },
  {
    "path": "test/checks/language/has-lang.js",
    "content": "describe('has-lang', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var hasLangEvaluate = axe.testUtils.getCheckEvaluate('has-lang');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if a lang attribute is present', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"woohoo\"></div>');\n\n    assert.isTrue(hasLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if only `xml:lang` attribute is present', function () {\n    var params = checkSetup('<div id=\"target\" xml:lang=\"cats\"></div>');\n\n    assert.isFalse(hasLangEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noXHTML');\n  });\n\n  it('should return true if both `lang` and `xml:lang` attribute is present', function () {\n    var params = checkSetup(\n      '<div id=\"target\" lang=\"cats\" xml:lang=\"cats\"></div>'\n    );\n\n    assert.isTrue(hasLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if xml:lang and lang attributes are not present', function () {\n    var params = checkSetup('<div id=\"target\"></div>');\n\n    assert.isFalse(hasLangEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noLang');\n  });\n\n  it('should return false if lang is left empty', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"\"></div>');\n\n    assert.isFalse(hasLangEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noLang');\n  });\n\n  it('should support options.attributes', function () {\n    var params = checkSetup('<div id=\"target\" foo=\"cats\"></div>', {\n      attributes: ['foo']\n    });\n\n    assert.isTrue(hasLangEvaluate.apply(checkContext, params));\n  });\n});\n"
  },
  {
    "path": "test/checks/language/valid-lang.js",
    "content": "describe('valid-lang', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var validLangEvaluate = axe.testUtils.getCheckEvaluate('valid-lang');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return false if a lang attribute is present in options', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"woohoo\">text</div>', {\n      value: ['blah', 'blah', 'woohoo']\n    });\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should lowercase options and attribute first', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"wooHOo\">text</div>', {\n      value: ['blah', 'blah', 'wOohoo']\n    });\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if a lang attribute is not present in options', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"FOO\">text</div>');\n\n    assert.isTrue(validLangEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['lang=\"FOO\"']);\n  });\n\n  it('should return false (and not throw) when given no present in options', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"en\">text</div>');\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should return true if the language is badly formatted', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"en_US\">text</div>');\n\n    assert.isTrue(validLangEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['lang=\"en_US\"']);\n  });\n\n  it('should return false if it matches a substring proceeded by -', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"en-LOL\">text</div>');\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should work with xml:lang', function () {\n    var params = checkSetup('<div id=\"target\" xml:lang=\"en-LOL\">text</div>');\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should accept options.attributes', function () {\n    var params = checkSetup('<div id=\"target\" custom-lang=\"en_US\">text</div>', {\n      attributes: ['custom-lang']\n    });\n\n    assert.isTrue(validLangEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, ['custom-lang=\"en_US\"']);\n  });\n\n  it('should return true if lang value is just whitespace', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"  \">text</div>');\n\n    assert.isTrue(validLangEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if a lang attribute element has no content', function () {\n    var params = checkSetup('<div id=\"target\" lang=\"FOO\"></div>');\n\n    assert.isFalse(validLangEvaluate.apply(checkContext, params));\n    assert.deepEqual(checkContext._data, null);\n  });\n});\n"
  },
  {
    "path": "test/checks/language/xml-lang-mismatch.js",
    "content": "describe('xml-lang-mismatch', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    // using a div element (instead of html), as the check is agnostic of element type\n  });\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  // the rule matches filters out node of type HTML, and tests cover this scenario to ensure other elements are not allowed for this check\n  // hence below tests are only for HTML element, although the logic in the check looks for matches in value os lang and xml:lang\n  // rather than node type match - hence the check can be re-used.\n\n  it('should return false if a only lang is supplied', function () {\n    var vNode = queryFixture('<div id=\"target\" lang=\"en\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('xml-lang-mismatch')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false if a only xml:lang is supplied albeit with region', function () {\n    var vNode = queryFixture('<div id=\"target\" xml:lang=\"fr-FR\"></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('xml-lang-mismatch')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false if lang is undefined', function () {\n    var node = document.createElement('div');\n    node.setAttribute('lang', undefined);\n    var tree = axe.testUtils.flatTreeSetup(node);\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('xml-lang-mismatch')\n        .call(checkContext, null, {}, tree[0])\n    );\n  });\n\n  it('should return true if lang and xml:lang is identical', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" xml:lang=\"en-GB\" lang=\"en-GB\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('xml-lang-mismatch')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return true if lang and xml:lang have identical primary sub tag', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" xml:lang=\"en-US\" lang=\"en-GB\"></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('xml-lang-mismatch')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false if lang and xml:lang are not identical', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" xml:lang=\"fr-FR\" lang=\"en\"></div>'\n    );\n    var actual = axe.testUtils\n      .getCheckEvaluate('xml-lang-mismatch')\n      .call(checkContext, null, {}, vNode);\n    assert.isFalse(actual);\n  });\n});\n"
  },
  {
    "path": "test/checks/lists/dlitem.js",
    "content": "describe('dlitem', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should pass if the dlitem has a parent <dl>', function () {\n    var checkArgs = checkSetup('<dl><dt id=\"target\">My list item</dt></dl>');\n\n    assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should fail if the dt element has an incorrect parent', function () {\n    var checkArgs = checkSetup(\n      '<video><dt id=\"target\">My list item</dt></video>'\n    );\n\n    assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should pass if the dt element has a parent <dl> with role=\"list\"', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"list\"><dt id=\"target\">My list item</dt></dl>'\n    );\n    assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should pass if the dt element has a parent <dl> with role=\"presentation\"', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"presentation\"><dt id=\"target\">My list item</dt></dl>'\n    );\n    assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should fail if the dt element has a parent <dl> with a changed role', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"menubar\"><dt id=\"target\">My list item<</dt>/dl>'\n    );\n    assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should pass if the dt element has a parent <dl> with an abstract role', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"section\"><dt id=\"target\">My list item</dt></dl>'\n    );\n    assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should pass if the dt element has a parent <dl> with an invalid role', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"invalid-role\"><dt id=\"target\">My list item</dt></dl>'\n    );\n    assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('should fail if the dt element has a parent <dl> with a changed role', function () {\n    var checkArgs = checkSetup(\n      '<dl role=\"menubar\"><dt id=\"target\">My list item</dt></dl>'\n    );\n    assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));\n  });\n\n  it('returns true if the dd/dt is in a div with a dl as grandparent', function () {\n    var nodeNames = ['dd', 'dt'];\n    nodeNames.forEach(function (nodeName) {\n      var checkArgs = checkSetup(\n        '<dl><div><' +\n          nodeName +\n          ' id=\"target\">My list item</' +\n          nodeName +\n          '></div></dl>'\n      );\n      assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n    });\n  });\n\n  it('returns false if the dd/dt is in a div with a role with a dl as grandparent with a list role', function () {\n    var nodeNames = ['dd', 'dt'];\n    nodeNames.forEach(function (nodeName) {\n      var checkArgs = checkSetup(\n        '<dl><div role=\"list\"><' +\n          nodeName +\n          ' id=\"target\">My list item</' +\n          nodeName +\n          '></div></dl>'\n      );\n      assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));\n    });\n  });\n\n  it('returns false if the dd/dt is in a div[role=presentation] with a dl as grandparent', function () {\n    var nodeNames = ['dd', 'dt'];\n    nodeNames.forEach(function (nodeName) {\n      var checkArgs = checkSetup(\n        '<dl><div role=\"presentation\"><' +\n          nodeName +\n          ' id=\"target\">My list item</' +\n          nodeName +\n          '></div></dl>'\n      );\n      assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n    });\n  });\n\n  it('returns false if the dd/dt is in a div[role=none] with a dl as grandparent', function () {\n    var nodeNames = ['dd', 'dt'];\n    nodeNames.forEach(function (nodeName) {\n      var checkArgs = checkSetup(\n        '<dl><div role=\"none\"><' +\n          nodeName +\n          ' id=\"target\">My list item</' +\n          nodeName +\n          '></div></dl>'\n      );\n      assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n    });\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true in a shadow DOM pass',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<dt>My list item </dt>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><slot></slot></dl>';\n\n      var checkArgs = checkSetup(node, 'dt');\n      assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return false in a shadow DOM fail',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<dt>My list item </dt>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div><slot></slot></div>';\n\n      var checkArgs = checkSetup(node, 'dt');\n      assert.isFalse(checks.dlitem.evaluate.apply(null, checkArgs));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true when the item is grouped in dl > div in a shadow DOM',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<dt>My list item </dt>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><div><slot></slot></div></dl>';\n\n      var checkArgs = checkSetup(node, 'dt');\n      assert.isTrue(checks.dlitem.evaluate.apply(null, checkArgs));\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/lists/listitem.js",
    "content": "describe('listitem', function () {\n  'use strict';\n\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('listitem');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should pass if the listitem has a parent <ol>', function () {\n    var params = checkSetup('<ol><li id=\"target\">My list item</li></ol>');\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should pass if the listitem has a parent <ul>', function () {\n    var params = checkSetup('<ul><li id=\"target\">My list item</li></ul>');\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should pass if the listitem has a parent role=list', function () {\n    var params = checkSetup(\n      '<div role=\"list\"><li id=\"target\">My list item</li></div>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should pass if the listitem has a parent role=none', function () {\n    var params = checkSetup(\n      '<ul role=\"none\"><li id=\"target\">My list item</li></ul>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should pass if the listitem has a parent role=presentation', function () {\n    var params = checkSetup(\n      '<ul role=\"presentation\"><li id=\"target\">My list item</li></ul>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should fail if the listitem has an incorrect parent', function () {\n    var params = checkSetup('<div><li id=\"target\">My list item</li></div>');\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isFalse(result);\n  });\n\n  it('should fail if the listitem has a parent <ol> with changed role', function () {\n    var params = checkSetup(\n      '<ol role=\"menubar\"><li id=\"target\">My list item</li></ol>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isFalse(result);\n    assert.equal(checkContext._data.messageKey, 'roleNotValid');\n  });\n\n  it('should pass if the listitem has a parent <ol> with an invalid role', function () {\n    var params = checkSetup(\n      '<ol role=\"invalid-role\"><li id=\"target\">My list item</li></ol>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  it('should pass if the listitem has a parent <ol> with an abstract role', function () {\n    var params = checkSetup(\n      '<ol role=\"section\"><li id=\"target\">My list item</li></ol>'\n    );\n    var result = checkEvaluate.apply(checkContext, params);\n    assert.isTrue(result);\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true in a shadow DOM pass',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<li id=\"target\">My list item </li>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<ul><slot></slot></ul>';\n      fixtureSetup(node);\n      var target = node.querySelector('#target');\n      var virtualTarget = axe.utils.getNodeFromTree(target);\n      var result = checkEvaluate.apply(checkContext, [\n        target,\n        {},\n        virtualTarget\n      ]);\n      assert.isTrue(result);\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return false in a shadow DOM fail',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<li id=\"target\">My list item </li>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div><slot></slot></div>';\n      fixtureSetup(node);\n      var target = node.querySelector('#target');\n      var virtualTarget = axe.utils.getNodeFromTree(target);\n      var result = checkEvaluate.apply(checkContext, [\n        target,\n        {},\n        virtualTarget\n      ]);\n      assert.isFalse(result);\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/lists/only-dlitems.js",
    "content": "describe('only-dlitems', () => {\n  const fixture = document.getElementById('fixture');\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('only-dlitems');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return false if the list has no contents', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"></dl>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the list has non-dd/dt contents', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"><p>Not a list</p></dl>');\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, [fixture.querySelector('p')]);\n    assert.deepEqual(checkContext._data, { values: 'p' });\n  });\n\n  it('should return true if the list has non-dd content through role change', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dd role=\"menuitem\">Not a list</dd></dl>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: '[role=menuitem]' });\n  });\n\n  it('should return true if the list has non-dt content through role change', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dt role=\"menuitem\">Not a list</dt></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: '[role=menuitem]' });\n  });\n\n  it('should return false if the list has only a dd', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"><dd>A list</dd></dl>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if <link> is used along side dt with its role changed', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><link rel=\"stylesheet\" href=\"theme.css\"><dt role=\"menuitem\">A list</dt></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: '[role=menuitem]' });\n  });\n\n  it('should return false if the list has only a dt', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"><dt>A list</dt></dl>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has dt and dd with child content', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dt><p>An item</p></dt><dd>A list</dd></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has dt and dd', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dt>An item</dt><dd>A list</dd></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has dt, dd and a comment', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dt>An item</dt><dd>A list</dd><!-- foo --></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the list has a dt and dd with other content', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><dt>Item one</dt><dd>Description</dd><p>Not a list</p></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, [fixture.querySelector('p')]);\n    assert.deepEqual(checkContext._data, { values: 'p' });\n  });\n\n  it('should return true if the list has a textNode as a child', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><!--hi--><dt>hi</dt>hello<dd>hi</dd></dl>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, []);\n    assert.deepEqual(checkContext._data, { values: '#text' });\n  });\n\n  it('should return false if <link> is used along side dt', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><link rel=\"stylesheet\" href=\"theme.css\"><dt>A list</dt></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <meta> is used along side dt', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><meta name=\"description\" content=\"\"><dt>A list</dt></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <script> is used along side dt', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><script src=\"script.js\"></script><dt>A list</dt></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <style> is used along side dt', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><style></style><dt>A list</dt></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <template> is used along side dt', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><template></template><dt>A list</dt></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has dt and dd inside a div group', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><div><dt>An item</dt><dd>A list</dd></div></dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the list has dt and dd inside a div group with a role', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><div role=\"list\"><dt>An item</dt><dd>A list</dd></div></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: '[role=list]' });\n  });\n\n  it('should return true if the list mixed items inside a div group with a role', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><div><dt>An item</dt><dd>A list</dd><p>Not a list</p></div></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: 'div > p' });\n  });\n\n  it('should return false if there is an empty div', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"><div></div></dl>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false if there are display:none elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"> <dt>An item</dt> <dd>A list</dd> <h1 style=\"display:none\">heading</h1> </dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if there is a div with text', () => {\n    const checkArgs = checkSetup('<dl id=\"target\"><div>text</div></dl>');\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: 'div > #text' });\n  });\n\n  it('returns false if there are visibility:hidden elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"> <dt>An item</dt> <dd>A list</dd> <h1 style=\"visibility:hidden\">heading</h1> </dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if there is a div with non-dd / dt elements', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"><div> <p>text</p> </div></dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: 'div > p' });\n  });\n\n  it('returns false if there are aria-hidden=true elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"> <dt>An item</dt> <dd>A list</dd> <h1 aria-hidden=\"true\">heading</h1> </dl>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true if there are aria-hidden=false elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<dl id=\"target\"> <dt>An item</dt> <dd>A list</dd> <h1 aria-hidden=\"false\">heading</h1> </dl>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { values: 'h1' });\n  });\n\n  describe('shadow DOM', () => {\n    it('should return false in a shadow DOM pass', () => {\n      const node = document.createElement('div');\n      node.innerHTML = '<dt>My list item </dt>';\n      const shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><slot></slot></dl>';\n\n      const checkArgs = checkSetup(node, 'dl');\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    });\n\n    it('should return true in a shadow DOM fail', () => {\n      const node = document.createElement('div');\n      node.innerHTML = '<p>Not a list</p>';\n      const shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><slot></slot></dl>';\n\n      const checkArgs = checkSetup(node, 'dl');\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, { values: 'p' });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/lists/only-listitems.js",
    "content": "describe('only-listitems', () => {\n  const fixture = document.getElementById('fixture');\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('only-listitems');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return false if the list has no contents', () => {\n    const checkArgs = checkSetup('<ol id=\"target\"></ol>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has only spaces as content', () => {\n    const checkArgs = checkSetup('<ol id=\"target\">   </ol>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has whitespace', () => {\n    const checkArgs = checkSetup('<ol id=\"target\"><li>Item</li>    </ol>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has only an element with role listitem', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><div role=\"listitem\">A list</div></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has only multiple mixed elements with role listitem', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><div role=\"listitem\">list</div><li role=\"listitem\">list</li><div role=\"listitem\">list</div></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has non-li comments', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><li>Item</li><!--comment--></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the list has non-li text contents', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><li>Item</li>Not an item</ol>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      values: '#text'\n    });\n  });\n\n  it('should return true if the list has non-li contents', () => {\n    const checkArgs = checkSetup('<ol id=\"target\"><p>Not a list</p></ol>');\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, [fixture.querySelector('p')]);\n    assert.deepEqual(checkContext._data, {\n      values: 'p'\n    });\n  });\n\n  it('should return false if the list has only an li with child content', () => {\n    const checkArgs = checkSetup('<ol id=\"target\"><li>A <i>list</i></li></ol>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if the list has only an li', () => {\n    const checkArgs = checkSetup('<ol id=\"target\"><li>A list</li></ol>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the list has an li with other content', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><li>A list</li><p>Not a list</p></ol>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, [fixture.querySelector('p')]);\n    assert.deepEqual(checkContext._data, {\n      values: 'p'\n    });\n  });\n\n  it('should return true if the list has at least one li while others have their roles changed', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><li >A list item</li><li role=\"menuitem\">Not a list item</li></ol>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._relatedNodes, [\n      fixture.querySelector('[role=\"menuitem\"]')\n    ]);\n    assert.deepEqual(checkContext._data, {\n      values: '[role=menuitem]'\n    });\n  });\n\n  it('should return true if the list has only li items with their roles changed', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><li id=\"fail1\" role=\"menuitem\">Not a list item</li><li id=\"fail2\" role=\"menuitem\">Not a list item</li></ol>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      values: '[role=menuitem]'\n    });\n    assert.deepEqual(checkContext._relatedNodes, [\n      fixture.querySelector('#fail1'),\n      fixture.querySelector('#fail2')\n    ]);\n  });\n\n  it('should return true if <link> is used along side only li items with their roles changed', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><link rel=\"stylesheet\" href=\"theme.css\"><li role=\"menuitem\">Not a list item</li></ol>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      values: '[role=menuitem]'\n    });\n  });\n\n  it('should return false if <link> is used along side li', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><link rel=\"stylesheet\" href=\"theme.css\"><li>A list</li></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <meta> is used along side li', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><meta name=\"description\" content=\"\"><li>A list</li></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <script> is used along side li', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><script src=\"script.js\"></script><li>A list</li></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <style> is used along side li', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><style></style><li>A list</li></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false if <template> is used along side li', () => {\n    const checkArgs = checkSetup(\n      '<ol id=\"target\"><template></template><li>A list</li></ol>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false if there are display:none elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<ul id=\"target\"> <li>An item</li> <h1 style=\"display:none\">heading</h1> </ul>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false if there are visibility:hidden elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<ul id=\"target\"> <li>An item</li> <h1 style=\"visibility:hidden\">heading</h1> </ul>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false if there are aria-hidden=true elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<ul id=\"target\"> <li>An item</li> <h1 aria-hidden=\"true\">heading</h1> </ul>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true if there are aria-hidden=false elements that normally would not be allowed', () => {\n    const checkArgs = checkSetup(\n      '<ul id=\"target\"> <li>An item</li> <h1 aria-hidden=\"false\">heading</h1> </ul>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      values: 'h1'\n    });\n  });\n\n  describe('nodeNames', () => {\n    it('returns multiple node names', () => {\n      const checkArgs = checkSetup(\n        '<ul id=\"target\"> <a></a><b></b><s></s> </ul>'\n      );\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        values: 'a, b, s'\n      });\n    });\n\n    it('does only shows unique nodes names', () => {\n      const checkArgs = checkSetup(\n        '<ul id=\"target\"> <a></a><b></b><a></a> </ul>'\n      );\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        values: 'a, b'\n      });\n    });\n  });\n\n  describe('shadow DOM', () => {\n    it('should return false in a shadow DOM pass', () => {\n      const node = document.createElement('div');\n      node.innerHTML = '<li>My list item </li>';\n      const shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<ul><slot></slot></ul>';\n\n      const checkArgs = checkSetup(node, 'ul');\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    });\n\n    it('should return true in a shadow DOM fail', () => {\n      const node = document.createElement('div');\n      node.innerHTML = '<p>Not a list item</p>';\n      const shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<ul><slot></slot></ul>';\n\n      const checkArgs = checkSetup(node, 'ul');\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        values: 'p'\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/lists/structured-dlitems.js",
    "content": "describe('structured-dlitems', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return false if the list has no contents', function () {\n    var checkArgs = checkSetup('<dl id=\"target\"></dl>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if the list has only a dd', function () {\n    var checkArgs = checkSetup('<dl id=\"target\"><dd>A list</dd></dl>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if the list has only a dt', function () {\n    var checkArgs = checkSetup('<dl id=\"target\"><dt>A list</dt></dl>');\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if the list has dt and dd in the incorrect order', function () {\n    var checkArgs = checkSetup(\n      '<dl id=\"target\"><dd>A list</dd><dt>An item</dt></dl>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if the list has dt and dd in the correct order as non-child descendants', function () {\n    var checkArgs = checkSetup(\n      '<dl id=\"target\"><dd><dl><dt>An item</dt><dd>A list</dd></dl></dd></dl>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return false if the list has dt and dd in the correct order', function () {\n    var checkArgs = checkSetup(\n      '<dl id=\"target\"><dt>An item</dt><dd>A list</dd></dl>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  it('should return false if the list has a correctly-ordered dt and dd with other content', function () {\n    var checkArgs = checkSetup(\n      '<dl id=\"target\"><dt>Stuff</dt><dt>Item one</dt><dd>Description</dd><p>Not a list</p></dl>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('structured-dlitems')\n        .apply(null, checkArgs)\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return false in a shadow DOM pass',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<dt>Grayhound bus</dt><dd>at dawn</dd>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><slot></slot></dl>';\n\n      var checkArgs = checkSetup(node, 'dl');\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('structured-dlitems')\n          .apply(null, checkArgs)\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should return true in a shadow DOM fail',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<dd>Galileo</dd><dt>Figaro</dt>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<dl><slot></slot></dl>';\n\n      var checkArgs = checkSetup(node, 'dl');\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('structured-dlitems')\n          .apply(null, checkArgs)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/media/caption.js",
    "content": "describe('caption', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var checkSetup = axe.testUtils.checkSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return undefined if there is no track element', function () {\n    var checkArgs = checkSetup('<audio></audio>', 'audio');\n    assert.isUndefined(checks.caption.evaluate.apply(null, checkArgs));\n  });\n\n  it('should return undefined if there is no kind=captions attribute', function () {\n    var checkArgs = checkSetup(\n      '<audio><track kind=descriptions></audio>',\n      'audio'\n    );\n    assert.isUndefined(checks.caption.evaluate.apply(null, checkArgs));\n  });\n\n  it('should pass if there is a kind=captions attribute', function () {\n    var checkArgs = checkSetup('<audio><track kind=captions></audio>', 'audio');\n    assert.isFalse(checks.caption.evaluate.apply(null, checkArgs));\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should get track from composed tree',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<track kind=captions>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<audio><slot></slot></audio>';\n\n      var checkArgs = checkSetup(node, {}, 'audio');\n      assert.isFalse(checks.caption.evaluate.apply(null, checkArgs));\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/media/frame-tested.js",
    "content": "describe('frame-tested', function () {\n  'use strict';\n\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('frame-tested');\n  var frameTestedAfter = checks['frame-tested'].after;\n\n  describe('evaluate', function () {\n    it('returns undefined', function () {\n      assert.isUndefined(checkEvaluate());\n    });\n\n    it('returns false if passed isViolation:true', function () {\n      assert.isFalse(checkEvaluate(null, { isViolation: true }));\n    });\n  });\n\n  describe('after', function () {\n    it('changes result to true if frame has been tested', function () {\n      var results = [\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#2']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1', 'html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#2', 'html']\n          }\n        }\n      ];\n\n      var afterResults = frameTestedAfter(results);\n      assert.lengthOf(afterResults, 2);\n\n      assert.isTrue(afterResults[0].result);\n      assert.deepEqual(afterResults[0].node.ancestry, [\n        'html > body > iframe#1'\n      ]);\n\n      assert.isTrue(afterResults[1].result);\n      assert.deepEqual(afterResults[1].node.ancestry, [\n        'html > body > iframe#2'\n      ]);\n    });\n\n    it('does not change result when iframe has not been tested', function () {\n      var results = [\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#2']\n          }\n        },\n        {\n          result: false,\n          node: {\n            ancestry: ['html > body > iframe#3']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1', 'html']\n          }\n        }\n      ];\n\n      var afterResults = frameTestedAfter(results);\n      assert.lengthOf(afterResults, 3);\n\n      assert.isTrue(afterResults[0].result);\n      assert.deepEqual(afterResults[0].node.ancestry, [\n        'html > body > iframe#1'\n      ]);\n\n      assert.isUndefined(afterResults[1].result);\n      assert.deepEqual(afterResults[1].node.ancestry, [\n        'html > body > iframe#2'\n      ]);\n\n      assert.isFalse(afterResults[2].result);\n      assert.deepEqual(afterResults[2].node.ancestry, [\n        'html > body > iframe#3'\n      ]);\n    });\n\n    it('works with shadow DOM', function () {\n      var results = [\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [['html > body > custom-elm1', 'iframe#1']]\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [['html > body > custom-elm1', 'iframe#2']]\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [['html > body > custom-elm1', 'iframe#1'], 'html']\n          }\n        }\n      ];\n\n      var afterResults = frameTestedAfter(results);\n      assert.lengthOf(afterResults, 2);\n\n      assert.isTrue(afterResults[0].result);\n      assert.deepEqual(afterResults[0].node.ancestry, [\n        ['html > body > custom-elm1', 'iframe#1']\n      ]);\n\n      assert.isUndefined(afterResults[1].result);\n      assert.deepEqual(afterResults[1].node.ancestry, [\n        ['html > body > custom-elm1', 'iframe#2']\n      ]);\n    });\n\n    it('works with nested shadow DOM and iframes', function () {\n      var results = [\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [['html > body > custom-elm1', 'iframe#2']]\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: ['html > body > iframe#1', 'html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [['html > body > custom-elm1', 'iframe#2'], 'html']\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [\n              ['html > body > custom-elm1', 'iframe#2'],\n              ['html > body > other-element', 'iframe#3']\n            ]\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [\n              ['html > body > custom-elm1', 'iframe#2'],\n              ['html > body > other-element', 'iframe#4']\n            ]\n          }\n        },\n        {\n          result: undefined,\n          node: {\n            ancestry: [\n              ['html > body > custom-elm1', 'iframe#2'],\n              ['html > body > other-element', 'iframe#3'],\n              'html'\n            ]\n          }\n        }\n      ];\n\n      var afterResults = frameTestedAfter(results);\n      assert.lengthOf(afterResults, 4);\n\n      assert.isTrue(afterResults[0].result);\n      assert.deepEqual(afterResults[0].node.ancestry, [\n        'html > body > iframe#1'\n      ]);\n\n      assert.isTrue(afterResults[1].result);\n      assert.deepEqual(afterResults[1].node.ancestry, [\n        ['html > body > custom-elm1', 'iframe#2']\n      ]);\n\n      assert.isTrue(afterResults[2].result);\n      assert.deepEqual(afterResults[2].node.ancestry, [\n        ['html > body > custom-elm1', 'iframe#2'],\n        ['html > body > other-element', 'iframe#3']\n      ]);\n\n      assert.isUndefined(afterResults[3].result);\n      assert.deepEqual(afterResults[3].node.ancestry, [\n        ['html > body > custom-elm1', 'iframe#2'],\n        ['html > body > other-element', 'iframe#4']\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/media/no-autoplay-audio.js",
    "content": "describe('no-autoplay-audio', () => {\n  const check = checks['no-autoplay-audio'];\n  const checkSetup = axe.testUtils.checkSetup;\n  const checkContext = axe.testUtils.MockCheckContext();\n  const preloadOptions = { preload: { assets: ['media'] } };\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns undefined when <audio> has no source (duration cannot be interpreted)', async () => {\n    const checkArgs = checkSetup('<audio id=\"target\"></audio>');\n    await axe.utils.preload(preloadOptions);\n    assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns undefined when <video> has no source (duration cannot be interpreted)', async () => {\n    const checkArgs = checkSetup('<video id=\"target\"><source src=\"\"/></video>');\n    await axe.utils.preload(preloadOptions);\n    assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false when <audio> can autoplay and has no controls mechanism', async () => {\n    const checkArgs = checkSetup(\n      '<audio id=\"target\" src=\"/test/assets/moon-speech.mp3\" autoplay=\"true\"></audio>'\n    );\n    await axe.utils.preload(preloadOptions);\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false when <video> can autoplay and has no controls mechanism', async () => {\n    const checkArgs = checkSetup(`\n      <video id=\"target\" autoplay=\"true\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    `);\n    await axe.utils.preload(preloadOptions);\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false when <audio> plays less than allowed dutation but loops', async () => {\n    const checkArgs = checkSetup(\n      '<audio id=\"target\" src=\"/test/assets/moon-speech.mp3#t=2,4\" autoplay=\"true\" loop=\"true\"></audio>'\n    );\n    await axe.utils.preload(preloadOptions);\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false when <video> loops and has no controls mechanism when duration is unknown', () => {\n    const checkArgs = checkSetup(`\n      <video id=\"target\" loop>\n        <source src=\"/test/assets/video.webm#t=7,9\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=7,9\" type=\"video/mp4\" />\n      </video>\n    `);\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false when <audio> loops and has no controls mechanism when duration is unknown', () => {\n    const checkArgs = checkSetup(\n      '<audio id=\"target\" src=\"/test/assets/moon-speech.mp3#t=2,4\" loop=\"true\"></audio>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true when <video> can autoplay and duration is below allowed duration (by passing options)', async () => {\n    const checkArgs = checkSetup(\n      `\n      <video id=\"target\" autoplay=\"true\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>`,\n      { allowedDuration: 15 }\n    );\n    await axe.utils.preload(preloadOptions);\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true when <video> can autoplay and duration is below allowed duration (by setting playback range)', async () => {\n    const checkArgs = checkSetup(`\n      <video id=\"target\" autoplay=\"true\">\n        <source src=\"/test/assets/video.webm#t=7,9\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=7,9\" type=\"video/mp4\" />\n      </video>`);\n    // Note: default allowed duration is 3s\n    await axe.utils.preload(preloadOptions);\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true when <audio> can autoplay but has controls mechanism', async () => {\n    const checkArgs = checkSetup(\n      '<audio id=\"target\" src=\"/test/assets/moon-speech.mp3\" autoplay=\"true\" controls></audio>'\n    );\n    await axe.utils.preload(preloadOptions);\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true when <video> can autoplay and has controls mechanism', async () => {\n    const checkArgs = checkSetup(`\n      <video id=\"target\" autoplay=\"true\" controls>\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    `);\n    await axe.utils.preload(preloadOptions);\n    assert.isTrue(check.evaluate.apply(null, checkArgs));\n  });\n\n  it('returns true when <video> loops and has controls mechanism when duration is unknown', () => {\n    const checkArgs = checkSetup(`\n      <video id=\"target\" loop controls>\n        <source src=\"/test/assets/video.webm#t=7,9\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=7,9\" type=\"video/mp4\" />\n      </video>\n    `);\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true when <audio> loops and has controls mechanism when duration is unknown', () => {\n    const checkArgs = checkSetup(\n      '<audio id=\"target\" src=\"/test/assets/moon-speech.mp3#t=2,4\" controls=\"true\" loop=\"true\"></audio>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n  });\n});\n"
  },
  {
    "path": "test/checks/mobile/css-orientation-lock.js",
    "content": "describe('css-orientation-lock tests', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var check = checks['css-orientation-lock'];\n  var dynamicDoc = document.implementation.createHTMLDocument(\n    'Dynamic document for CSS Orientation Lock tests'\n  );\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  function getSheet(data) {\n    var style = dynamicDoc.createElement('style');\n    style.type = 'text/css';\n    style.appendChild(dynamicDoc.createTextNode(data));\n    dynamicDoc.head.appendChild(style);\n    return style.sheet;\n  }\n\n  it('returns undefined when CSSOM is undefined', function () {\n    var actual = check.evaluate.call(checkContext, document);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when CSSOM is empty', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [] // pass context with cssom as empty\n    });\n    assert.isUndefined(actual);\n  });\n\n  it('returns true when CSSOM has no rules', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          sheet: {} // empty sheet, no css rules\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has no CSS media features', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          sheet: getSheet('body { color: inherit; }')\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has no CSS media features targeting orientation', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          sheet: getSheet('body { color: inherit; }')\n        },\n        {\n          shadowId: 'a',\n          sheet: getSheet(\n            '@media (min-width: 400px) { background-color: red; }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has empty Orientation CSS media features', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          sheet: getSheet('body { color: inherit; }')\n        },\n        {\n          shadowId: 'a',\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) {  }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features that does not target transform property', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 2000px) and (orientation: portrait) { #mocha { color: red; } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and transformation function of translateX, which does not affect rotation', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: translateX(10px); -webkit-transform: translateX(10px); } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and tranformation function of rotate, which affects rotation but does not lock orientation (rotate(180deg))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { transform: rotate(180deg); -webkit-transform: rotate(180deg); } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and tranformation function of rotate, which affects rotation but does not lock orientation (rotate(-178deg))', function () {\n    var actual = check.evaluate.call(\n      checkContext,\n      document,\n      { degreeThreshold: 3 },\n      undefined,\n      {\n        cssom: [\n          {\n            shadowId: 'a',\n            root: document,\n            sheet: getSheet(\n              '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { transform: rotate(-178deg); -webkit-transform: rotate(-178deg); } }'\n            )\n          }\n        ]\n      }\n    );\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and tranformation function of rotateZ, which affects rotation but does not lock orientation (rotateZ(1turn))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { transform: rotateZ(1turn); } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and tranformation function of rotate3d, which affects rotation but does not lock orientation (rotate3d(0,0,0,400grad))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            // Note: values set on rotate3d cascasdes over rotate\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { transform: rotate(90deg) rotate3d(0,0,1, 400grad); } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns true when CSSOM has Orientation CSS media features with transform property and tranformation function of matrix3d, which affects rotation but does not lock orientation (matrix3d(-1,0,0.00,0,0.00,-1,0.00,0,0,0,1,0,0,0,0,1))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            // Here the target is rotated by 180deg\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { transform: matrix3d(-1,0,0.00,0,0.00,-1,0.00,0,0,0,1,0,0,0,0,1);  } }'\n          )\n        }\n      ]\n    });\n    assert.isTrue(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of rotate, which affects rotation and locks orientation (rotate(270deg))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: rotate(270deg); -webkit-transform: rotate(270deg); } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of rotate3d, which affects rotation and locks orientation (rotate3d(0,0,1,90deg))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          root: document,\n          sheet: getSheet(\n            // apply 0 on the z-axis (3rd parameter) does not apply given rotation on z-axis\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: rotate3d(0,0,1,90deg); -webkit-transform: rotate3d(0,0,1,90deg) } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of rotate3d, which affects rotation and locks orientation (rotate3d(0,0,1,93deg))', function () {\n    var actual = check.evaluate.call(\n      checkContext,\n      document,\n      { degreeThreshold: 3 },\n      undefined,\n      {\n        cssom: [\n          {\n            shadowId: undefined,\n            root: document,\n            sheet: getSheet(\n              // apply 0 on the z-axis (3rd parameter) does not apply given rotation on z-axis\n              '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: rotate3d(0,0,1,93deg); -webkit-transform: rotate3d(0,0,1,93deg) } }'\n            )\n          }\n        ]\n      }\n    );\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of rotate3d, which affects rotation and locks orientation (rotate3d(0,0,1,1.5708rad))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: rotate3d(0,0,1,1.5708rad); -webkit-transform: rotate3d(0,0,1,1.5708rad) } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of matrix, which affects rotation and locks orientation (matrix(0.00,1.00,-1.00,0.00,0,0))', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          root: document,\n          sheet: getSheet(\n            // this rotates by 90deg\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform:matrix(0.00,1.00,-1.00,0.00,0,0); -webkit-transform:matrix(0.00,1.00,-1.00,0.00,0,0); } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform property and transformation function of matrix3d, which affects rotation and locks orientation (matrix3d(0,-1,0.00,0,1.00,0,0.00,0,0,0,1,0,0,0,0,1);)', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: undefined,\n          root: document,\n          sheet: getSheet(\n            // this rotates by 90deg\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { #mocha { transform: matrix3d(0,-1,0.00,0,1.00,0,0.00,0,0,0,1,0,0,0,0,1); -webkit-transform: matrix3d(0,-1,0.00,0,1.00,0,0.00,0,0,0,1,0,0,0,0,1); } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with rotate property', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { rotate: 90deg; } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with rotate property matrix', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { rotate: 0 0 1 1.5708rad; } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  it('returns false when CSSOM has Orientation CSS media features with transform: rotate and rotate property', function () {\n    var actual = check.evaluate.call(checkContext, document, {}, undefined, {\n      cssom: [\n        {\n          shadowId: 'a',\n          root: document,\n          sheet: getSheet(\n            '@media screen and (min-width: 1px) and (max-width: 3000px) and (orientation: landscape) { body { rotate: 45deg; transform: rotate(45deg); -webkit-transform: rotate(45deg); } }'\n          )\n        }\n      ]\n    });\n    assert.isFalse(actual);\n  });\n\n  // Note:\n  // external stylesheets is tested in integration tests\n  // shadow DOM is tested in integration tests\n});\n"
  },
  {
    "path": "test/checks/mobile/meta-viewport-scale.js",
    "content": "describe('meta-viewport', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  describe('; separator', function () {\n    it('should return false on user-scalable=no', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=no\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, 'user-scalable=no');\n    });\n\n    it('should return false on user-scalable=no', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=no, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return false on user-scalable in the range <-1, 1>', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=0, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return false on user-scalable in the range <-1, 1>', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=-0.5, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return true on user-scalable=yes', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=yes, more-stuff=ok\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('meta-viewport')(null, null, vNode)\n      );\n    });\n\n    it('should return false on maximum-scale=yes (translates to 1)', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"maximum-scale=yes\">'\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return true on negative maximum scale (should be ignored)', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"maximum-scale=-1\">'\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return true if maximum-scale >= options.scaleMinimum', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, maximum-scale=5, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('meta-viewport').call(\n          checkContext,\n          null,\n          {\n            scaleMinimum: 2\n          },\n          vNode\n        )\n      );\n\n      vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, maximum-scale=3, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return false on maximum-scale < options.scaleMinimum', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=yes, maximum-scale=1.5\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('meta-viewport').call(\n          checkContext,\n          null,\n          {\n            scaleMinimum: 2\n          },\n          vNode\n        )\n      );\n      assert.deepEqual(checkContext._data, 'maximum-scale');\n    });\n\n    it('should return true if neither user-scalable or maximum-scale are set', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should not crash if viewport property does not have a value', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"user-scalable=1, minimal-ui\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('meta-viewport')(null, null, vNode)\n      );\n    });\n\n    it('should not crash if viewport property does not have a value', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"user-scalable=1, minimal-ui\">'\n      );\n\n      assert.isTrue(\n        checks['meta-viewport'].evaluate.call(checkContext, null, null, vNode)\n      );\n    });\n  });\n\n  describe(', separator', function () {\n    it('should return false on user-scalable=no', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=no\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, 'user-scalable=no');\n    });\n\n    it('should return false on user-scalable=no', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=no, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n      assert.deepEqual(checkContext._data, 'user-scalable=no');\n    });\n\n    it('should return false on user-scalable in the range <-1, 1>', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=0, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return false on user-scalable in the range <-1, 1>', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=-0.5, more-stuff=ok\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return true on user-scalable=yes', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=yes, more-stuff=ok\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n\n    it('should return true if maximum-scale >= options.scaleMinimum', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, maximum-scale=5, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, maximum-scale=2, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('meta-viewport').call(\n          checkContext,\n          null,\n          {\n            scaleMinimum: 2\n          },\n          vNode\n        )\n      );\n    });\n\n    it('should return false on maximum-scale < options.scaleMinimum', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs, user-scalable=yes, maximum-scale=1.5\">'\n      );\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('meta-viewport').call(\n          checkContext,\n          null,\n          {\n            scaleMinimum: 2\n          },\n          vNode\n        )\n      );\n    });\n\n    it('should return true if neither user-scalable or maximum-scale are set', function () {\n      var vNode = queryFixture(\n        '<meta id=\"target\" name=\"viewport\" content=\"foo=bar, cats=dogs\">'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('meta-viewport')\n          .call(checkContext, null, null, vNode)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/mobile/target-offset.js",
    "content": "describe('target-offset tests', () => {\n  const checkContext = axe.testUtils.MockCheckContext();\n  const { checkSetup, getCheckEvaluate } = axe.testUtils;\n  const checkEvaluate = getCheckEvaluate('target-offset');\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns true when there are no other nearby targets', () => {\n    const checkArgs = checkSetup(\n      '<a href=\"#\" id=\"target\" style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</a>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  it('returns true when the offset is 24px', () => {\n    const checkArgs = checkSetup(\n      '<a href=\"#\" id=\"target\" style=\"' +\n        'display: inline-block; width:16px; height:16px; margin-right: 8px' +\n        '\">x</a>' +\n        '<a href=\"#\" style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</a>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  it('returns true when wrapped inline elements offset is 24px', () => {\n    const checkArgs = checkSetup(`\n      <div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.3;\">\n        <a id=\"target\" href=\"/foo\" class=\"A\"> Hello hello hello</a>\n        <a href=\"/bar\" class=\"B\">Hello hello hello</a>\n      </div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  describe('when the offset is insufficient', () => {\n    it('returns false for targets in the tab order', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:16px; height:16px; margin-right: 7px' +\n          '\">x</a>' +\n          '<a href=\"#\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>'\n      );\n\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n      assert.isUndefined(checkContext._data.messageKey);\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 22, 0.2);\n    });\n\n    it('returns undefined for targets not in the tab order', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" tabindex=\"-1\" style=\"' +\n          'display: inline-block; width:16px; height:16px; margin-right: 7px' +\n          '\">x</a>' +\n          '<a href=\"#\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>'\n      );\n\n      assert.isUndefined(checkEvaluate.apply(checkContext, checkArgs));\n      assert.isUndefined(checkContext._data.messageKey);\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 22, 0.2);\n    });\n\n    it('returns false when wrapped inline elements offset is <24px', () => {\n      const checkArgs = checkSetup(`\n        <div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.1;\">\n          <a id=\"target\" href=\"/foo\" class=\"A\"> Hello hello hello</a>\n          <a href=\"/bar\" class=\"B\">Hello hello hello</a>\n        </div>\n      `);\n\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 20, 3);\n    });\n\n    it('returns false when one line of a wrapped inline elements offset is <24px', () => {\n      const checkArgs = checkSetup(`\n        <div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.3;\">\n          <a id=\"target\" href=\"/foo\" class=\"A\"> Hello hello hello</a>\n          <a style=\"margin-left: -30px\" href=\"/bar\" class=\"B\">Hello hello hello</a>\n        </div>\n      `);\n\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 15.5, 5);\n    });\n  });\n\n  it('ignores non-widget elements as neighbors', () => {\n    const checkArgs = checkSetup(\n      '<a href=\"#\" id=\"target\" style=\"' +\n        'display: inline-block; width:16px; height:16px; margin-right: 7px' +\n        '\">x</a>' +\n        '<div style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  it('ignores non-focusable widget elements as neighbors', () => {\n    const checkArgs = checkSetup(\n      '<a href=\"#\" id=\"target\" style=\"' +\n        'display: inline-block; width:16px; height:16px; margin-right: 7px' +\n        '\">x</a>' +\n        '<button disabled style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</button>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  it('ignores obscured widget elements as neighbors', () => {\n    const checkArgs = checkSetup(`\n      <div style=\"position: fixed; bottom: 0\">\n        <a href=\"#\">Go to top</a>\n      </div>\n      <div id=\"target\" style=\"position: fixed; bottom: 0; left: 0; right: 0; background: #eee\">\n        Cookies: <a href=\"#\">Accept all cookies</a>\n      </div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 24, 0.2);\n  });\n\n  it('sets all elements that are too close as related nodes', () => {\n    const checkArgs = checkSetup(\n      '<a href=\"#\" id=\"left\" style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</a>' +\n        '<a href=\"#\" id=\"target\" style=\"' +\n        'display: inline-block; width:16px; height:16px; margin-right: 4px' +\n        '\">x</a>' +\n        '<a href=\"#\" id=\"right\" style=\"' +\n        'display: inline-block; width:16px; height:16px;' +\n        '\">x</a>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.minOffset, 24);\n    assert.closeTo(checkContext._data.closestOffset, 8, 0.2);\n\n    const relatedIds = checkContext._relatedNodes.map(function (node) {\n      return '#' + node.id;\n    });\n    assert.deepEqual(relatedIds, ['#left', '#right']);\n  });\n\n  it('returns undefined if there are too many focusable widgets', () => {\n    let html = '';\n    for (let i = 0; i < 100; i++) {\n      html += `\n        <tr>\n          <td><a href=\"#\">A</a></td>\n          <td><button>B</button></td>\n          <td><button>C</button></td>\n          <td><button>D</button></td>\n        </tr>\n      `;\n    }\n    const checkArgs = checkSetup(`\n      <div id=\"target\" role=\"tabpanel\" tabindex=\"0\" style=\"display:inline-block\">\n        <table id=\"tab-table\">${html}</table>\n      </div>\n    `);\n    assert.isUndefined(checkEvaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'tooManyRects',\n      closestOffset: 0,\n      minOffset: 24\n    });\n  });\n\n  describe('when neighbors are focusable but not tabbable', () => {\n    it('returns undefined if all neighbors are not tabbable', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"left\" tabindex=\"-1\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:16px; height:16px; margin-right: 4px' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"right\" tabindex=\"-1\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>'\n      );\n      assert.isUndefined(checkEvaluate.apply(checkContext, checkArgs));\n      assert.equal(checkContext._data.messageKey, 'nonTabbableNeighbor');\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 8, 0.2);\n\n      const relatedIds = checkContext._relatedNodes.map(function (node) {\n        return '#' + node.id;\n      });\n      assert.deepEqual(relatedIds, ['#left', '#right']);\n    });\n\n    it('returns false if some but not all neighbors are not tabbable', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"left\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:16px; height:16px; margin-right: 4px' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"right\" tabindex=\"-1\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>'\n      );\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n      assert.isUndefined(checkContext._data.messageKey);\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.closeTo(checkContext._data.closestOffset, 8, 0.2);\n\n      const relatedIds = checkContext._relatedNodes.map(function (node) {\n        return '#' + node.id;\n      });\n      assert.deepEqual(relatedIds, ['#left', '#right']);\n    });\n\n    it('returns true if the target is 10x the minOffset', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"left\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:240px; height:240px; margin-right: 4px' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"right\" style=\"' +\n          'display: inline-block; width:16px; height:16px;' +\n          '\">x</a>'\n      );\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.equal(checkContext._data.minOffset, 24);\n      assert.equal(checkContext._data.messageKey, 'large');\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/mobile/target-size.js",
    "content": "describe('target-size tests', () => {\n  const checkContext = axe.testUtils.MockCheckContext();\n  const checkSetup = axe.testUtils.checkSetup;\n  const shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  const check = checks['target-size'];\n  const fixture = document.querySelector('#fixture');\n\n  function elmIds(elms) {\n    return Array.from(elms).map(elm => {\n      return '#' + elm.id;\n    });\n  }\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('returns false for targets smaller than minSize', () => {\n    const checkArgs = checkSetup(\n      '<button id=\"target\" style=\"' +\n        'display: inline-block; width:20px; height:30px;' +\n        '\">x</button>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      minSize: 24,\n      width: 20,\n      height: 30\n    });\n  });\n\n  it('returns undefined for non-tabbable targets smaller than minSize', () => {\n    const checkArgs = checkSetup(\n      '<button id=\"target\" tabindex=\"-1\" style=\"' +\n        'display: inline-block; width:20px; height:30px;' +\n        '\">x</button>'\n    );\n    assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      minSize: 24,\n      width: 20,\n      height: 30\n    });\n  });\n\n  it('returns true for unobscured targets larger than minSize', () => {\n    const checkArgs = checkSetup(\n      '<button id=\"target\" style=\"' +\n        'display: inline-block; width:40px; height:30px;' +\n        '\">x</button>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      minSize: 24,\n      width: 40,\n      height: 30\n    });\n  });\n\n  it('returns true for very large targets', () => {\n    const checkArgs = checkSetup(\n      '<button id=\"target\" style=\"' +\n        'display: inline-block; width:240px; height:300px;' +\n        '\">x</button>'\n    );\n    assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, { messageKey: 'large', minSize: 24 });\n  });\n\n  describe('when fully obscured', () => {\n    it('returns true, regardless of size', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:20px; height:20px;' +\n          '\">x</a>' +\n          '<div id=\"obscurer\" style=\"' +\n          'display: inline-block; width:20px; height:20px; margin-left: -20px;' +\n          '\">x</div>'\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, { messageKey: 'obscured' });\n      assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n    });\n\n    it('returns true when obscured by another focusable widget', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:20px; height:20px;' +\n          '\">x</a>' +\n          '<a href=\"#\" id=\"obscurer\" style=\"' +\n          'display: inline-block; width:20px; height:20px; margin-left: -20px;' +\n          '\">x</a>'\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, { messageKey: 'obscured' });\n      assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n    });\n\n    it('ignores obscuring element has pointer-events:none', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" style=\"' +\n          'display: inline-block; width:20px; height:20px;' +\n          '\">x</a>' +\n          '<span style=\"' +\n          'display: inline-block; pointer-events: none; width:20px; height:20px; margin-left: -20px;' +\n          '\">x</span>'\n      );\n      assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        minSize: 24,\n        width: 20,\n        height: 20\n      });\n    });\n  });\n\n  describe('when partially obscured', () => {\n    it('returns true for focusable non-widgets', () => {\n      const checkArgs = checkSetup(\n        '<button id=\"target\" style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n          '\">x</button>' +\n          '<button id=\"obscurer\" style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n          '\">x</button>' +\n          '<span tabindex=\"0\" style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n          '\">x</span>'\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        minSize: 24,\n        width: 30,\n        height: 30\n      });\n      assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n    });\n\n    it('returns true for non-focusable widgets', () => {\n      const checkArgs = checkSetup(\n        '<button id=\"target\" style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n          '\">x</button>' +\n          '<button id=\"obscurer\" style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n          '\">x</button>' +\n          '<button disabled style=\"' +\n          'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n          '\">x</button>'\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        minSize: 24,\n        width: 30,\n        height: 30\n      });\n      assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n    });\n\n    describe('by a focusable widget', () => {\n      it('returns true for obscured targets with sufficient space', () => {\n        const checkArgs = checkSetup(\n          '<button id=\"target\" style=\"' +\n            'display: inline-block; width:40px; height:30px;' +\n            '\">x</button>' +\n            '<button id=\"obscurer\" style=\"' +\n            'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n            '\">x</button>'\n        );\n        assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n        assert.deepEqual(checkContext._data, {\n          minSize: 24,\n          width: 30,\n          height: 30\n        });\n        assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n      });\n\n      it('returns true for obscured target when an unobscured rect has sufficient size but smaller area', () => {\n        const checkArgs = checkSetup(`\n          <div style=\"position: relative;\">\n            <button id=\"target\" style=\"width: 80px; height: 40px;\">X</button>\n            <button id=\"obscurer\" style=\"width: 80px; height: 40px; position: absolute; left: 30px; top: 20px;\">x</button>\n          </div>\n        `);\n        assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n        assert.deepEqual(checkContext._data, {\n          minSize: 24,\n          width: 30,\n          height: 40\n        });\n        assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n      });\n\n      it('returns true for multiline inline target with sufficient space', () => {\n        const checkArgs = checkSetup(`\n          <div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.3\">\n            <a id=\"not-obscurer\" href=\"/foo\" class=\"A\"> Hello hello</a>\n            <a id=\"target\" href=\"/bar\" class=\"B\">Hello hello hello</a>\n            <a id=\"obscurer\" href=\"/bar\" class=\"C\">Hello hello hello</a>\n          </div>\n        `);\n        assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n        assert.closeTo(checkContext._data.width, 40.5, 10);\n        assert.closeTo(checkContext._data.height, 40.5, 5);\n        assert.deepEqual(elmIds(checkContext._relatedNodes), ['#obscurer']);\n      });\n\n      it('returns undefined if there are too many focusable widgets', () => {\n        let html = '';\n        for (let i = 0; i < 100; i++) {\n          html += `\n            <tr>\n              <td><a href=\"#\">A</a></td>\n              <td><button>B</button></td>\n              <td><button>C</button></td>\n              <td><button>D</button></td>\n            </tr>\n          `;\n        }\n        const checkArgs = checkSetup(`\n          <div id=\"target\" role=\"tabpanel\" tabindex=\"0\" style=\"display:inline-block\">\n            <table id=\"tab-table\">${html}</table>\n          </div>\n        `);\n        assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n        assert.deepEqual(checkContext._data, {\n          messageKey: 'tooManyRects',\n          minSize: 24\n        });\n      });\n\n      describe('for obscured targets with insufficient space', () => {\n        it('returns false if all elements are tabbable', () => {\n          const checkArgs = checkSetup(\n            '<button id=\"target\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer1\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer2\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n              '\">x</button>'\n          );\n          assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'partiallyObscured',\n            minSize: 24,\n            width: 20,\n            height: 30\n          });\n          assert.deepEqual(elmIds(checkContext._relatedNodes), [\n            '#obscurer1',\n            '#obscurer2'\n          ]);\n        });\n\n        it('returns undefined if the target is not tabbable', () => {\n          const checkArgs = checkSetup(\n            '<button id=\"target\" tabindex=\"-1\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer1\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer2\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n              '\">x</button>'\n          );\n          assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'partiallyObscured',\n            minSize: 24,\n            width: 20,\n            height: 30\n          });\n          assert.deepEqual(elmIds(checkContext._relatedNodes), [\n            '#obscurer1',\n            '#obscurer2'\n          ]);\n        });\n\n        it('returns undefined if the obscuring node is not tabbable', () => {\n          const checkArgs = checkSetup(\n            '<button id=\"target\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer1\" tabindex=\"-1\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n              '\">x</button>' +\n              '<button id=\"obscurer2\" style=\"' +\n              'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n              '\">x</button>'\n          );\n          assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n          assert.deepEqual(checkContext._data, {\n            messageKey: 'partiallyObscuredNonTabbable',\n            minSize: 24,\n            width: 20,\n            height: 30\n          });\n          assert.deepEqual(elmIds(checkContext._relatedNodes), [\n            '#obscurer1',\n            '#obscurer2'\n          ]);\n        });\n      });\n\n      describe('that is a descendant', () => {\n        it('returns false if the widget is tabbable', () => {\n          const checkArgs = checkSetup(\n            `<a role=\"link\" aria-label=\"play\" tabindex=\"0\" style=\"display:inline-block\" id=\"target\">\n              <button style=\"margin:1px; line-height:20px\">Play</button>\n            </a>`\n          );\n          const out = check.evaluate.apply(checkContext, checkArgs);\n          assert.isFalse(out);\n        });\n\n        it('returns true if the widget is not tabbable', () => {\n          const checkArgs = checkSetup(\n            `<a role=\"link\" aria-label=\"play\" tabindex=\"0\" style=\"display:inline-block\" id=\"target\">\n              <button tabindex=\"-1\" style=\"margin:1px; line-height:20px\">Play</button>\n            </a>`\n          );\n          const out = check.evaluate.apply(checkContext, checkArgs);\n          assert.isTrue(out);\n        });\n      });\n\n      describe('that is a descendant', () => {\n        it('returns false if the widget is tabbable', () => {\n          const checkArgs = checkSetup(\n            `<a role=\"link\" aria-label=\"play\" tabindex=\"0\" style=\"display:inline-block\" id=\"target\">\n              <button style=\"margin:1px; line-height:20px\">Play</button>\n            </a>`\n          );\n          const out = check.evaluate.apply(checkContext, checkArgs);\n          assert.isFalse(out);\n        });\n\n        it('returns true if the widget is not tabbable', () => {\n          const checkArgs = checkSetup(\n            `<a role=\"link\" aria-label=\"play\" tabindex=\"0\" style=\"display:inline-block\" id=\"target\">\n              <button tabindex=\"-1\" style=\"margin:1px; line-height:20px\">Play</button>\n            </a>`\n          );\n          const out = check.evaluate.apply(checkContext, checkArgs);\n          assert.isTrue(out);\n        });\n      });\n    });\n  });\n\n  describe('with overflowing content', () => {\n    it('returns undefined target is too small', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\"><img width=\"24\" height=\"24\"></a>'\n      );\n      assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n      assert.deepEqual(checkContext._data, {\n        minSize: 24,\n        messageKey: 'contentOverflow'\n      });\n    });\n\n    it('returns true if target has sufficient size', () => {\n      const checkArgs = checkSetup(\n        '<a href=\"#\" id=\"target\" style=\"font-size:24px;\"><img width=\"24\" height=\"24\"></a>'\n      );\n      assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n    });\n\n    describe('and partially obscured', () => {\n      it('is undefined when unobscured area is too small', () => {\n        const checkArgs = checkSetup(\n          '<a href=\"#\" id=\"target\" style=\"font-size:24px;\">' +\n            '  <img width=\"24\" height=\"36\" style=\"vertical-align: bottom;\">' +\n            '</a><br>' +\n            '<a href=\"\" style=\"margin-top:-10px; position:absolute; width:24px;\">&nbsp;</a>'\n        );\n        assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n        assert.deepEqual(checkContext._data, {\n          minSize: 24,\n          messageKey: 'contentOverflow'\n        });\n      });\n\n      it('is true when unobscured area is sufficient', () => {\n        const checkArgs = checkSetup(\n          '<a href=\"#\" id=\"target\" style=\"font-size:24px;\">' +\n            '  <img width=\"24\" height=\"36\" style=\"vertical-align: bottom;\">' +\n            '</a><br>' +\n            '<a href=\"\" style=\"margin-top:-2px; position:absolute; width:24px;\">&nbsp;</a>'\n        );\n        assert.isTrue(check.evaluate.apply(checkContext, checkArgs));\n      });\n    });\n\n    describe('and fully obscured', () => {\n      it('is undefined', () => {\n        const checkArgs = checkSetup(\n          '<a href=\"#\" id=\"target\" style=\"font-size:24px;\">' +\n            '  <img width=\"24\" height=\"36\" style=\"vertical-align: bottom;\">' +\n            '</a><br>' +\n            '<a href=\"\" style=\"margin-top:-40px; position:absolute; width:100px; height:100px;\">&nbsp;</a>'\n        );\n        assert.isUndefined(check.evaluate.apply(checkContext, checkArgs));\n        assert.deepEqual(checkContext._data, {\n          minSize: 24,\n          messageKey: 'contentOverflow'\n        });\n      });\n    });\n  });\n\n  it('works across shadow boundaries', () => {\n    const checkArgs = shadowCheckSetup(\n      '<span id=\"shadow\"></span>' +\n        '<button id=\"obscurer1\" style=\"' +\n        'display: inline-block; width:40px; height:30px; margin-left: -10px;' +\n        '\">x</button>' +\n        '<button id=\"obscurer2\" style=\"' +\n        'display: inline-block; width:40px; height:30px; margin-left: -100px;' +\n        '\">x</button>',\n      '<button id=\"target\" style=\"' +\n        'display: inline-block; width:40px; height:30px; margin-left:30px;' +\n        '\">x</button>'\n    );\n    assert.isFalse(check.evaluate.apply(checkContext, checkArgs));\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'partiallyObscured',\n      minSize: 24,\n      width: 20,\n      height: 30\n    });\n    assert.deepEqual(elmIds(checkContext._relatedNodes), [\n      '#obscurer1',\n      '#obscurer2'\n    ]);\n  });\n\n  it('ignores descendants of the target that are in shadow dom', () => {\n    fixture.innerHTML =\n      '<button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\"><span id=\"shadow\"></span></button>';\n    const target = fixture.querySelector('#target');\n    const shadow = fixture\n      .querySelector('#shadow')\n      .attachShadow({ mode: 'open' });\n    shadow.innerHTML =\n      '<div style=\"position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;\"></div>';\n\n    axe.setup(fixture);\n    const vNode = axe.utils.getNodeFromTree(target);\n    assert.isTrue(check.evaluate.apply(checkContext, [target, {}, vNode]));\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/header-present.js",
    "content": "describe('header-present', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should return true if h1-h6 is found', function () {\n    var params = checkSetup('<h1 id=\"target\">Hi</h1>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n\n    params = checkSetup('<h2 id=\"target\">Hi</h2>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n\n    params = checkSetup('<h3 id=\"target\">Hi</h3>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n\n    params = checkSetup('<h4 id=\"target\">Hi</h4>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n\n    params = checkSetup('<h5 id=\"target\">Hi</h5>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n\n    params = checkSetup('<h6 id=\"target\">Hi</h6>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true if role=heading is found', function () {\n    var params = checkSetup('<div role=\"heading\" id=\"target\">Hi</div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should otherwise return false', function () {\n    var params = checkSetup('<p id=\"target\">Some stuff and stuff</p>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return false if heading has a different role', function () {\n    var params = checkSetup(\n      '<h1 role=\"none\" id=\"target\">Some stuff and stuff</h1>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('header-present')\n        .apply(checkContext, params)\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return true if heading is in shadow dom',\n    function () {\n      var params = shadowCheckSetup('<div id=\"target\"><div>', '<h1></h1>');\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('header-present')\n          .apply(checkContext, params)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/navigation/heading-order.js",
    "content": "describe('heading-order', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should store the heading order path and level for [role=heading] elements and return true', function () {\n    var vNode = queryFixture(\n      '<div role=\"heading\" aria-level=\"1\" id=\"target\">One</div><div role=\"heading\" aria-level=\"3\">Three</div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > div:nth-child(1)'],\n          level: 1\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > div:nth-child(2)'],\n          level: 3\n        }\n      ]\n    });\n  });\n\n  it('should handle incorrect aria-level values', function () {\n    var vNode = queryFixture(\n      '<div role=\"heading\" aria-level=\"-1\" id=\"target\">One</div><div role=\"heading\">Two</div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > div:nth-child(1)'],\n          level: 2\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > div:nth-child(2)'],\n          level: 2\n        }\n      ]\n    });\n  });\n\n  it('should allow high aria-level values', function () {\n    var vNode = queryFixture(\n      '<div role=\"heading\" aria-level=\"12\" id=\"target\">One</div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > div'],\n          level: 12\n        }\n      ]\n    });\n  });\n\n  it('should store the correct header level for hn tags and return true', function () {\n    var vNode = queryFixture('<h1 id=\"target\">One</h1><h3>Three</h3>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > h1:nth-child(1)'],\n          level: 1\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > h3:nth-child(2)'],\n          level: 3\n        }\n      ]\n    });\n  });\n\n  it('should allow aria-level to override semantic level for hn tags and return true', function () {\n    var vNode = queryFixture(\n      '<h1 aria-level=\"2\" id=\"target\">Two</h1><h3 aria-level=\"4\">Four</h3>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > h1:nth-child(1)'],\n          level: 2\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > h3:nth-child(2)'],\n          level: 4\n        }\n      ]\n    });\n  });\n\n  it('should ignore aria-level on iframe when not used with role=heading', function () {\n    var vNode = queryFixture('<iframe aria-level=\"2\" id=\"target\"></iframe>');\n    axe.testUtils\n      .getCheckEvaluate('heading-order')\n      .call(checkContext, null, {}, vNode, { initiator: true });\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > iframe'],\n          level: -1\n        }\n      ]\n    });\n  });\n\n  it('should correctly give level on hn tag with role=heading', function () {\n    var vNode = queryFixture(\n      '<h1 role=\"heading\" id=\"target\">One</h1><h3 role=\"heading\">Three</h3>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > h1:nth-child(1)'],\n          level: 1\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > h3:nth-child(2)'],\n          level: 3\n        }\n      ]\n    });\n  });\n\n  it('should return the heading level when an hn tag has an invalid aria-level', function () {\n    var vNode = queryFixture('<h1 aria-level=\"-1\" id=\"target\">One</h1>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('heading-order')\n        .call(checkContext, null, {}, vNode, {})\n    );\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > h1'],\n          level: 1\n        }\n      ]\n    });\n  });\n\n  it('should store the location of iframes', function () {\n    var vNode = queryFixture(\n      '<h1 id=\"target\">One</h1><iframe></iframe><h3>Three</h3>'\n    );\n    axe.testUtils\n      .getCheckEvaluate('heading-order')\n      .call(checkContext, null, {}, vNode, { initiator: true });\n    assert.deepEqual(checkContext._data, {\n      headingOrder: [\n        {\n          ancestry: ['html > body > div:nth-child(1) > h1:nth-child(1)'],\n          level: 1\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > iframe:nth-child(2)'],\n          level: -1\n        },\n        {\n          ancestry: ['html > body > div:nth-child(1) > h3:nth-child(3)'],\n          level: 3\n        }\n      ]\n    });\n  });\n\n  describe('after', function () {\n    it('should return false when header level increases by 2', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['path2'],\n                level: 3\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        },\n        {\n          node: { ancestry: ['path2'] },\n          result: true\n        }\n      ];\n      assert.isFalse(checks['heading-order'].after(results)[1].result);\n    });\n\n    it('should return true when header level decreases by 1', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 2\n              },\n              {\n                ancestry: ['path2'],\n                level: 1\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        },\n        {\n          node: { ancestry: ['path2'] },\n          result: true\n        }\n      ];\n      assert.isTrue(checks['heading-order'].after(results)[1].result);\n    });\n\n    it('should return true when header level decreases by 2', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 3\n              },\n              {\n                ancestry: ['path2'],\n                level: 1\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        },\n        {\n          node: { ancestry: ['path2'] },\n          result: true\n        }\n      ];\n      assert.isTrue(checks['heading-order'].after(results)[1].result);\n    });\n\n    it('should return true when there is only one header', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        }\n      ];\n      assert.isTrue(checks['heading-order'].after(results)[0].result);\n    });\n\n    it('should return true when header level increases by 1', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['path2'],\n                level: 2\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path2']\n          },\n          result: true\n        }\n      ];\n      assert.isTrue(checks['heading-order'].after(results)[1].result);\n    });\n\n    it('should return true if heading levels are correct across iframes', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['iframe'],\n                level: -1\n              },\n              {\n                ancestry: ['path3'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: 'path2',\n                level: 2\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'path2']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path3']\n          },\n          result: true\n        }\n      ];\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n    });\n\n    it('should return false if heading levels are incorrect across iframes', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['iframe'],\n                level: -1\n              },\n              {\n                ancestry: ['path3'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path2'],\n                level: 4\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'path2']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path3']\n          },\n          result: true\n        }\n      ];\n      var afterResults = checks['heading-order'].after(results);\n      assert.isFalse(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n    });\n\n    it('should handle nested iframes', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['iframe'],\n                level: -1\n              },\n              {\n                ancestry: ['path4'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path2'],\n                level: 2\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'iframe2', 'path2']\n          },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: 'iframe2',\n                level: -1\n              },\n              {\n                ancestry: 'path3',\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'path3']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path4']\n          },\n          result: true\n        }\n      ];\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n      assert.isTrue(afterResults[3].result);\n    });\n\n    it('sets the result to undefined when the heading is not in the map', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['unknown']\n          },\n          result: true\n        }\n      ];\n\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[0].result);\n      assert.isUndefined(afterResults[1].result);\n    });\n\n    it('ignores frames for which there are no results', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['iframe1'],\n                level: -1\n              },\n              {\n                ancestry: ['path2'],\n                level: 2\n              },\n              {\n                ancestry: ['iframe2'],\n                level: -1\n              },\n              {\n                ancestry: ['path3'],\n                level: 4\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path2']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path3']\n          },\n          result: true\n        }\n      ];\n\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[0].result);\n      assert.isTrue(afterResults[1].result);\n      assert.isFalse(afterResults[2].result);\n    });\n\n    it('should not error if iframe is first result', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path2'],\n                level: 1\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'path2']\n          },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['iframe'],\n                level: -1\n              },\n              {\n                ancestry: ['path1'],\n                level: 2\n              },\n              {\n                ancestry: ['path3'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path1']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['path3']\n          },\n          result: true\n        }\n      ];\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n    });\n\n    it('runs when the top frame has no heading', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['path2'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['iframe', 'path1']\n          },\n          result: true\n        },\n        {\n          node: {\n            ancestry: ['iframe', 'path2']\n          },\n          result: true\n        }\n      ];\n\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[0].result);\n      assert.isFalse(afterResults[1].result);\n    });\n\n    it('understand shadow DOM in ancestries', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: [['custom-elm1', 'iframe1']],\n                level: -1\n              },\n              {\n                ancestry: [['custom-elm2', 'iframe2']],\n                level: -1\n              },\n              {\n                ancestry: [['custom-elm3', 'path4']],\n                level: 4\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path2'],\n                level: 2\n              }\n            ]\n          },\n          node: { ancestry: [['custom-elm1', 'iframe1'], 'path2'] },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path3'],\n                level: 3\n              }\n            ]\n          },\n          node: { ancestry: [['custom-elm2', 'iframe2'], 'path3'] },\n          result: true\n        },\n        {\n          node: { ancestry: [['custom-elm3', 'path4']] },\n          result: true\n        }\n      ];\n\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[0].result);\n      assert.isTrue(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n      assert.isTrue(afterResults[3].result);\n    });\n\n    it('run when an in-between frame has no heading', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['iframe1'],\n                level: -1\n              },\n              {\n                ancestry: ['path4'],\n                level: 4\n              }\n            ]\n          },\n          node: { ancestry: ['path1'] },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path2'],\n                level: 2\n              }\n            ]\n          },\n          node: { ancestry: ['iframe1', 'iframe2', 'path2'] },\n          result: true\n        },\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path3'],\n                level: 3\n              }\n            ]\n          },\n          node: { ancestry: ['iframe1', 'iframe3', 'path3'] },\n          result: true\n        },\n        {\n          node: { ancestry: ['path4'] },\n          result: true\n        }\n      ];\n\n      var afterResults = checks['heading-order'].after(results);\n      assert.isTrue(afterResults[0].result);\n      assert.isTrue(afterResults[1].result);\n      assert.isTrue(afterResults[2].result);\n      assert.isTrue(afterResults[3].result);\n    });\n\n    it('can fail the second heading, if the first is excluded', function () {\n      var results = [\n        {\n          data: {\n            headingOrder: [\n              {\n                ancestry: ['path1'],\n                level: 1\n              },\n              {\n                ancestry: ['path2'],\n                level: 3\n              }\n            ]\n          },\n          node: {\n            ancestry: ['path2']\n          },\n          result: true\n        }\n      ];\n      var afterResults = checks['heading-order'].after(results);\n      assert.isFalse(afterResults[0].result);\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/identical-links-same-purpose-after.js",
    "content": "describe('identical-links-same-purpose-after tests', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var check = checks['identical-links-same-purpose'];\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns results by clearing relatedNodes after ignoring nodes which has no data (or result is undefined)', function () {\n    var nodeOneData = {\n      data: null,\n      relatedNodes: ['nodeOne'],\n      result: undefined\n    };\n    var nodeTwoData = {\n      data: {\n        name: 'read more',\n        urlProps: { hostname: 'abc.com' }\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n\n    var results = check.after(checkResults);\n    assert.lengthOf(results, 1);\n\n    var result = results[0];\n    assert.deepEqual(result.data, nodeTwoData.data);\n    assert.deepEqual(result.relatedNodes, []);\n    assert.equal(result.result, true);\n  });\n\n  it('sets results of check result to `undefined` one of the native links do not have `urlProps` (and therefore removed as relatedNode)', function () {\n    var nodeOneData = {\n      data: {\n        name: 'read more',\n        urlProps: undefined\n      },\n      relatedNodes: ['nodeOne'],\n      result: true\n    };\n    var nodeTwoData = {\n      data: {\n        name: 'read more',\n        urlProps: { hostname: 'abc.com' }\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n\n    var results = check.after(checkResults);\n    assert.lengthOf(results, 1);\n\n    var result = results[0];\n    assert.deepEqual(result.data, nodeOneData.data);\n    assert.deepEqual(result.relatedNodes, ['nodeTwo']);\n    assert.equal(result.result, undefined);\n  });\n\n  it('sets results of check result to `undefined` if native links do not have same `urlProps` (values are different)', function () {\n    var nodeOneData = {\n      data: {\n        name: 'follow us',\n        urlProps: { hostname: 'facebook.com' }\n      },\n      relatedNodes: ['nodeOne'],\n      result: true\n    };\n    var nodeTwoData = {\n      data: {\n        name: 'follow us',\n        urlProps: { hostname: 'instagram.com' }\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n\n    var results = check.after(checkResults);\n    assert.lengthOf(results, 1);\n\n    var result = results[0];\n    assert.deepEqual(result.data, nodeOneData.data);\n    assert.deepEqual(result.relatedNodes, ['nodeTwo']);\n    assert.equal(result.result, undefined);\n  });\n\n  it('sets results of check result to `undefined` if native links do not have same `urlProps` (keys are different)', function () {\n    var nodeOneData = {\n      data: {\n        name: 'follow us',\n        urlProps: { abc: 'abc.com' }\n      },\n      relatedNodes: ['nodeOne'],\n      result: true\n    };\n    var nodeTwoData = {\n      data: {\n        name: 'follow us',\n        urlProps: { xyz: 'abc.com' }\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n\n    var results = check.after(checkResults);\n    assert.lengthOf(results, 1);\n\n    var result = results[0];\n    assert.deepEqual(result.data, nodeOneData.data);\n    assert.deepEqual(result.relatedNodes, ['nodeTwo']);\n    assert.equal(result.result, undefined);\n  });\n\n  it('sets results of check result to `true` if native links serve identical purpose', function () {\n    var nodeOneData = {\n      data: {\n        name: 'Axe Core',\n        urlProps: { hostname: 'deque.com', pathname: 'axe-core' }\n      },\n      relatedNodes: ['nodeOne'],\n      result: true\n    };\n    var nodeTwoData = {\n      data: {\n        name: 'Axe Core',\n        urlProps: { hostname: 'deque.com', pathname: 'axe-core' }\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n\n    var results = check.after(checkResults);\n\n    assert.lengthOf(results, 1);\n\n    var result = results[0];\n    assert.deepEqual(result.data, nodeOneData.data);\n    assert.deepEqual(result.relatedNodes, ['nodeTwo']);\n    assert.equal(result.result, true);\n  });\n\n  it('sets results of check result to `true` if ARIA links have different accessible names', function () {\n    var nodeOneData = {\n      data: {\n        name: 'earth',\n        urlProps: {}\n      },\n      relatedNodes: ['nodeOne'],\n      result: true\n    };\n\n    var nodeTwoData = {\n      data: {\n        name: 'venus',\n        urlProps: {}\n      },\n      relatedNodes: ['nodeTwo'],\n      result: true\n    };\n    var checkResults = [nodeOneData, nodeTwoData];\n    var results = check.after(checkResults);\n    assert.lengthOf(results, 2);\n\n    assert.deepEqual(results[0].data, nodeOneData.data);\n    assert.deepEqual(results[0].relatedNodes, []);\n    assert.equal(results[0].result, true);\n\n    assert.deepEqual(results[1].data, nodeTwoData.data);\n    assert.deepEqual(results[1].relatedNodes, []);\n    assert.equal(results[1].result, true);\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/identical-links-same-purpose.js",
    "content": "describe('identical-links-same-purpose tests', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var check = checks['identical-links-same-purpose'];\n  var checkContext = axe.testUtils.MockCheckContext();\n  var options = {};\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('returns undefined for native link with `href` but no accessible name', function () {\n    var vNode = queryFixture('<a id=\"target\" href=\"/home/#/foo\"></a>');\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isUndefined(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns undefined when ARIA link that has no accessible name', function () {\n    var vNode = queryFixture('<span role=\"link\" id=\"target\"></span>');\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isUndefined(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns undefined when ARIA link has only any combination of unicode (emoji, punctuations, nonBmp) characters as accessible name', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" role=\"link\">☀️!!!₨   </button>'\n    );\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isUndefined(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true for native links with `href` and accessible name', function () {\n    var vNode = queryFixture('<a id=\"target\" href=\"/home/#/foo\">Pass 1</a>');\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isTrue(actual);\n    assert.hasAllKeys(checkContext._data, ['name', 'urlProps']);\n    assert.equal(checkContext._data.name, 'Pass 1'.toLowerCase());\n    assert.equal(checkContext._data.urlProps.hash, '#/foo');\n    assert.equal(checkContext._data.urlProps.pathname, '/home/');\n  });\n\n  it('returns true for ARIA links has accessible name (AREA with `MAP` which is used in `IMG`)', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic\" alt=\"MDN infographic\" />'\n    );\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isTrue(actual);\n    assert.hasAllKeys(checkContext._data, ['name', 'urlProps']);\n    assert.equal(checkContext._data.name, 'MDN'.toLowerCase());\n    assert.isFalse(!!checkContext._data.resource);\n  });\n\n  it('returns true for native links with `href` and accessible name (that also has emoji, nonBmp and punctuation characters)', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"/contact/foo.html\">The ☀️ is orange, the ◓ is white.</a>'\n    );\n    var actual = check.evaluate.call(\n      checkContext,\n      vNode.actualNode,\n      options,\n      vNode\n    );\n    assert.isTrue(actual);\n    assert.hasAllKeys(checkContext._data, ['name', 'urlProps']);\n    assert.equal(\n      checkContext._data.name,\n      'The is orange the is white'.toLowerCase()\n    );\n    assert.equal(checkContext._data.urlProps.filename, 'foo.html');\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/internal-link-present.js",
    "content": "describe('internal-link-present', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    checkContext.reset();\n  });\n\n  it('should return true when an internal link is found', function () {\n    var vNode = queryFixture('<div id=\"target\"><a href=\"#haha\">hi</a></div>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('internal-link-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false when a hashbang URL was used', function () {\n    var vNode = queryFixture('<div id=\"target\"><a href=\"#!foo\">hi</a></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('internal-link-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false when a hash route URL was used', function () {\n    var vNode = queryFixture('<div id=\"target\"><a href=\"#/home\">hi</a></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('internal-link-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false when a hashbang + slash route URL was used', function () {\n    var vNode = queryFixture('<div id=\"target\"><a href=\"#!/home\">hi</a></div>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('internal-link-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should otherwise return false', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><a href=\"http://www.deque.com/#haha\">hi</a></div>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('internal-link-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return true when internal link is found in shadow dom',\n    function () {\n      var params = shadowCheckSetup(\n        '<div id=\"target\"></div>',\n        '<a href=\"#haha\">hi</a>'\n      );\n      var vNode = params[2];\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('internal-link-present')\n          .call(checkContext, null, {}, vNode)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/navigation/landmark.js",
    "content": "describe('landmark', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('landmark');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true when role=main is found', function () {\n    var checkArgs = checkSetup('<div role=\"main\"></div>', '#fixture');\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when <main> is found', function () {\n    var checkArgs = checkSetup('<main></main>', '#fixture');\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should otherwise return false', function () {\n    var checkArgs = checkSetup('<div role=\"contentinfo\"></div>', '#fixture');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should not automatically pass if there is a shadow tree',\n    function () {\n      var node = document.createElement('div');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div></div>';\n      var checkArgs = checkSetup(node, '#fixture');\n\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find elements inside shadow trees',\n    function () {\n      var node = document.createElement('div');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<main></main>';\n      var checkArgs = checkSetup(node, '#fixture');\n\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find elements slotted in shadow trees',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<main></main>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<slot></slot>';\n      var checkArgs = checkSetup(node, '#fixture');\n\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/navigation/meta-refresh.js",
    "content": "describe('meta-refresh', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var metaRefreshCheck = axe.testUtils.getCheckEvaluate('meta-refresh');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('returns false if there is a number', function () {\n    var checkArgs = checkSetup('<meta id=\"target\" name=\"refresh\" content=\"3\">');\n    assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n  });\n\n  describe('returns false when valid', function () {\n    it('there is a decimal', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3.1\">'\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('there is a number followed by a dot', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3.\">'\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('there is a dot followed by a number', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\".5\">'\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('there is whitespace before the number', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"  \\n\\t3\">'\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    describe('with a valid separator', function () {\n      it('the number is followed by a semicolon', function () {\n        var checkArgs = checkSetup(\n          '<meta id=\"target\" name=\"refresh\" content=\"3;\">'\n        );\n        assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n      });\n\n      it('the number is followed by a comma', function () {\n        var checkArgs = checkSetup(\n          '<meta id=\"target\" name=\"refresh\" content=\"3,\">'\n        );\n        assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n      });\n\n      it('the number is followed spaces, and then a separator', function () {\n        var checkArgs = checkSetup(\n          '<meta id=\"target\" name=\"refresh\" content=\"3 \\t\\n;\">'\n        );\n        assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n      });\n\n      it('the separator is followed by non-separator characters', function () {\n        var checkArgs = checkSetup(\n          '<meta id=\"target\" name=\"refresh\" content=\"3; https://deque.com/\">'\n        );\n        assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n      });\n\n      it('the separator is a space', function () {\n        var checkArgs = checkSetup(\n          '<meta id=\"target\" name=\"refresh\" content=\"3 https://deque.com/\">'\n        );\n        assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n      });\n    });\n  });\n\n  describe('returns true when invalid', function () {\n    it('the number is prefaced with a plus', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"+3\">'\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('the number is prefaced with a minus', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"-3\">'\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('the number is prefaced with a letter', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"a3\">'\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('the number is followed by an invalid separator character', function () {\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3: https://deque.com/\">'\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n  });\n\n  describe('options.minDelay', function () {\n    it('returns false when the redirect number is greater than minDelay', function () {\n      var options = { minDelay: 2 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('returns true when the redirect number equals minDelay', function () {\n      var options = { minDelay: 3 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('returns true when the redirect number is less than minDelay', function () {\n      var options = { minDelay: 4 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('ignores minDelay when set to false', function () {\n      var options = { minDelay: false };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"0\">',\n        options\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n  });\n\n  describe('options.maxDelay', function () {\n    it('returns true when the redirect number is greater than maxDelay', function () {\n      var options = { maxDelay: 2 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('returns false when the redirect number equals maxDelay', function () {\n      var options = { maxDelay: 3 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('returns false when the redirect number is less than maxDelay', function () {\n      var options = { maxDelay: 4 };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"3\">',\n        options\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n\n    it('ignores maxDelay when set to false', function () {\n      var options = { maxDelay: false };\n      var checkArgs = checkSetup(\n        '<meta id=\"target\" name=\"refresh\" content=\"9999\">',\n        options\n      );\n      assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/p-as-heading.js",
    "content": "describe('p-as-heading', function () {\n  'use strict';\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n\n  var testOptions = {\n    margins: [{ weight: 100 }, { italic: true }, { size: 1.2 }]\n  };\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('returns true if the styles are identical', function () {\n    var params = checkSetup(\n      '<p id=\"target\">elm 1</p> <p>elm 2</p>',\n      testOptions\n    );\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns true if there is no p element following it', function () {\n    var params = checkSetup('<p id=\"target\">lone elm</p>', testOptions);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns false if the font-weight is heavier', function () {\n    var params = checkSetup(\n      '<p id=\"target\" style=\"font-weight:bold\">elm 1</p>' + '<p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns false if the font-size is bigger', function () {\n    var params = checkSetup(\n      '<p id=\"target\" style=\"font-size:150%\">elm 1</p> <p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns false if the fake heading is italic and the text is not', function () {\n    var params = checkSetup(\n      '<p id=\"target\" style=\"font-style:italic\">elm 1</p> <p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns true if both texts are bold, italic and larger', function () {\n    var params = checkSetup(\n      '<p id=\"target\" style=\"font-weight:bold; font-size:120%; font-style:italic\">elm 1</p>' +\n        '<p style=\"font: italic bold 120% bold\">elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('considers styles of elements inside the paragraph', function () {\n    var params = checkSetup(\n      '<p id=\"target\"><b>elm 1</b></p> <p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('ignores empty child element for style', function () {\n    var params = checkSetup(\n      '<p id=\"target\"><span> </span><b>elm 1</b></p> <p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('considers styles of elements that do not contain all the text', function () {\n    var params = checkSetup(\n      '<p id=\"target\"><b>elm</b> 1</p> <p>elm 2elm 2</p>',\n      testOptions\n    );\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns undefined instead of false if the element is inside a blockquote', function () {\n    var params = checkSetup(\n      '<blockquote>' +\n        '<p style=\"font-weight:bold\" id=\"target\">elm 1</p> <p>elm 2elm 2</p>' +\n        '</blockquote>',\n      testOptions\n    );\n    assert.isUndefined(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns true over undefined from within a blockquote', function () {\n    var params = checkSetup(\n      '<blockquote>' +\n        '<p id=\"target\">elm 1</p> <p>elm 2elm 2</p>' +\n        '</blockquote>',\n      testOptions\n    );\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns undefined if a previous sibling has a similar font-weight', function () {\n    var params = checkSetup(\n      '<p><b>elm 1</b></p>' +\n        '<p id=\"target\"><b>elm 2</b></p>' +\n        '<p>elm 3</p>',\n      testOptions\n    );\n    assert.isUndefined(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns true if the heading is greater than the paragraph', function () {\n    var params = checkSetup(\n      '<p id=\"target\">elm1elm1</p>' + '<p>elm2</p>',\n      testOptions\n    );\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  it('returns undefined if the heading is twice as long but not greater than the length of the pararaph', function () {\n    var params = checkSetup(\n      '<p id=\"target\" style=\"font-weight:bold\">elm1elm</p>' + '<p>elm2elm2</p>',\n      testOptions\n    );\n    assert.isUndefined(\n      axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)\n    );\n  });\n\n  describe('options.passLength and options.failLength', function () {\n    it('returns true if the heading is greater than the paragraph using options.passLength', function () {\n      var options = {\n        margins: [{ weight: 100 }, { italic: true }, { size: 1.2 }],\n        passLength: 2\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\">elm1elm1elm1</p>' + '<p>elm2</p>',\n        options\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('returns undefined if the heading is twice as long but not greater than the length of the pararaph using options.failLength ', function () {\n      var options = {\n        margins: [{ weight: 100 }, { italic: true }, { size: 1.2 }],\n        failLength: 0.6\n      };\n      var params = checkSetup(\n        '<p id=\"target\" style=\"font-weight:bold\">elm1elm</p>' +\n          '<p>elm2elm2elm2</p>',\n        options\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n  });\n\n  describe('option.margin', function () {\n    it('passes if no margins are set', function () {\n      var options = {};\n\n      var params = checkSetup(\n        '<p id=\"target\"><b>elm 1</b></p> <p>elm 2elm 2</p>',\n        options\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('takes an array of margins', function () {\n      var options = {\n        margins: [{ size: 1.2 }]\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\"><b>elm 1</b></p> <p>elm 2elm 2</p>',\n        options\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('returns false if all values in the margin are passed', function () {\n      var options = {\n        margins: [{ size: 1.2, weight: 100 }]\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\" style=\"font-size:1.5em; font-weight:bold\">elm 1</p>' +\n          '<p>elm 2elm 2</p>',\n        options\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('returns true if any of the values is not passed', function () {\n      var options = {\n        margins: [{ size: 1.2, weight: 100 }]\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\" style=\"font-weight:bold\">elm 1</p>' + '<p>elm 2</p>',\n        options\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('returns false if any of the margins is passed', function () {\n      var options = {\n        margins: [{ size: 1.2, weight: 100 }, { size: 1.5 }, { italic: true }]\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\" style=\"font-style:italic\">elm 1</p>' +\n          '<p>elm 2elm 2</p>',\n        options\n      );\n      assert.isFalse(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n\n    it('returns true if none of the set margins is passed', function () {\n      /*eslint indent: 0*/\n      var options = {\n        margins: [\n          { size: 1.2, weight: 100 },\n          { size: 1.5 },\n          { size: 1.2, italic: true }\n        ]\n      };\n\n      var params = checkSetup(\n        '<p id=\"target\" style=\"font-size:1.5em\">elm 1</p>' + '<p>elm 2</p>',\n        options\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    });\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns undefined instead of false if the element is inside a blockquote in light dom',\n    function () {\n      var params = shadowCheckSetup(\n        '<blockquote></blockquote>',\n        '<p style=\"font-weight:bold\" id=\"target\">elm 1</p> <p>elm 2</p>',\n        testOptions\n      );\n      assert.isUndefined(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'returns true over undefined from within a blockquote in light dom',\n    function () {\n      var params = shadowCheckSetup(\n        '<blockquote></blockquote>',\n        '<p id=\"target\">elm 1</p> <p>elm 2</p>',\n        testOptions\n      );\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('p-as-heading')\n          .apply(checkContext, params)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/navigation/region-after.js",
    "content": "describe('region-after', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should always pass iframes', function () {\n    var results = checks.region.after([\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe']\n        },\n        result: false\n      },\n      {\n        data: { isIframe: false },\n        node: {\n          ancestry: ['html > body > iframe', 'html > body > p']\n        },\n        result: false\n      }\n    ]);\n    assert.equal(results[0].result, true);\n    assert.equal(results[1].result, false);\n  });\n\n  it('should pass children of iframes if the iframe contained in it is in a region', function () {\n    var results = checks.region.after([\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe']\n        },\n        result: true\n      },\n      {\n        data: { isIframe: false },\n        node: {\n          ancestry: ['html > body > iframe', 'html > body > p']\n        },\n        result: false\n      }\n    ]);\n\n    assert.equal(results[0].result, true);\n    assert.equal(results[1].result, true);\n  });\n\n  it('should pass nested iframes', function () {\n    var results = checks.region.after([\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe']\n        },\n        result: false\n      },\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe', 'html > body > iframe']\n        },\n        result: false\n      },\n      {\n        data: { isIframe: false },\n        node: {\n          ancestry: [\n            'html > body > iframe',\n            'html > body > iframe',\n            'html > body > p'\n          ]\n        },\n        result: false\n      }\n    ]);\n\n    assert.equal(results[0].result, true);\n    assert.equal(results[1].result, true);\n    assert.equal(results[2].result, false);\n  });\n\n  it('should pass children of nested iframes if the nested iframe is in a region', function () {\n    var results = checks.region.after([\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe']\n        },\n        result: false\n      },\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe', 'html > body > iframe']\n        },\n        result: true\n      },\n      {\n        data: { isIframe: false },\n        node: {\n          ancestry: [\n            'html > body > iframe',\n            'html > body > iframe',\n            'html > body > p'\n          ]\n        },\n        result: false\n      }\n    ]);\n\n    assert.equal(results[0].result, true);\n    assert.equal(results[1].result, true);\n    assert.equal(results[2].result, true);\n  });\n\n  it('should pass content if a grandparent frame passes', function () {\n    var results = checks.region.after([\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe']\n        },\n        result: true\n      },\n      {\n        data: { isIframe: true },\n        node: {\n          ancestry: ['html > body > iframe', 'html > body > iframe']\n        },\n        result: false\n      },\n      {\n        data: { isIframe: false },\n        node: {\n          ancestry: [\n            'html > body > iframe',\n            'html > body > iframe',\n            'html > body > p'\n          ]\n        },\n        result: false\n      }\n    ]);\n    assert.equal(results[0].result, true);\n    assert.equal(results[1].result, true);\n    assert.equal(results[2].result, true);\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/region.js",
    "content": "// NOTE: due to how the region check works to return the top-most\n// node that is outside the region, all fixture content will need\n// a region node (in most cases the <div role=\"main\">Content</div>)\n// in order for the check to not give false positives/negatives.\n// adding the region node forces the check to not return the #fixture\n// as the top-most element but instead use the #target element.\ndescribe('region', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var checkSetup = axe.testUtils.checkSetup;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('region');\n\n  var checkContext = new axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe.reset();\n  });\n\n  it('should return true when content is inside the region', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"main\"><a id=\"target\" href=\"a.html#mainheader\">Click Here</a><div><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when a region role is added to standards', () => {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          feed: {\n            type: 'landmark'\n          }\n        }\n      }\n    });\n    var checkArgs = checkSetup(\n      '<div role=\"feed\" id=\"target\">This is random content.</div>' +\n        '<div role=\"main\"><h1 id=\"mainheader\">Introduction</h1></div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when img content is outside the region, no alt attribute at all', function () {\n    const checkArgs = checkSetup(`\n      <img id=\"target\" src=\"data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7\">\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when img content outside of the region is decorative, via an empty alt attr', function () {\n    const checkArgs = checkSetup(`\n      <img id=\"target\" src=\"#\" alt=\"\" />\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when img content outside of the region is explicitly decorative, via a presentation role', function () {\n    const checkArgs = checkSetup(`\n      <img id=\"target\" src=\"#\" role=\"presentation\" />\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when img content outside of the region is focusable (implicit role=img)', function () {\n    const checkArgs = checkSetup(`\n      <img id=\"target\" src=\"#\" tabindex=\"0\" alt=\"\" />\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when img content outside of the region has a global aria attribute (implicit role=img)', function () {\n    const checkArgs = checkSetup(`\n      <img id=\"target\" src=\"#\" aria-atomic=\"true\" alt=\"\" />\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when canvas role=none', function () {\n    const checkArgs = checkSetup(`\n      <canvas id=\"target\" role=\"none\" />\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when object has an aria-label', function () {\n    const checkArgs = checkSetup(`\n      <object id=\"target\" aria-label=\"bar\"></object>\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when a non-landmark has text content but a role=none', function () {\n    const checkArgs = checkSetup(`\n      <div id=\"target\" role=\"none\">apples</div>\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when a non-landmark does NOT have text content and a role=none', function () {\n    const checkArgs = checkSetup(`\n      <div id=\"target\" role=\"none\"></div>\n      <div role=\"main\">Content</div>\n    `);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when textless text content is outside the region', function () {\n    var checkArgs = checkSetup(\n      '<p id=\"target\"></p><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when wrapper content is outside the region', function () {\n    var checkArgs = checkSetup(\n      '<div id=\"target\"><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when invisible content is outside the region', function () {\n    var checkArgs = checkSetup(\n      '<p id=\"target\" style=\"display: none\">Click Here</p><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when there is a skiplink', function () {\n    var checkArgs = checkSetup(\n      '<a id=\"target\" href=\"#mainheader\">Click Here</a><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when there is an Angular skiplink', function () {\n    var checkArgs = checkSetup(\n      '<a id=\"target\" href=\"/#mainheader\">Click Here</a><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when there is a non-region element', function () {\n    var checkArgs = checkSetup(\n      '<div id=\"target\">This is random content.</div><div role=\"main\"><h1 id=\"mainheader\">Introduction</h1></div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return false when there is a non-skiplink', function () {\n    var checkArgs = checkSetup(\n      '<a id=\"target\" href=\"something.html#mainheader\">Click Here</a><div role=\"main\"><h1 id=\"mainheader\">Introduction</h1></div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true if the non-region element is a script', function () {\n    var checkArgs = checkSetup(\n      '<script id=\"target\">axe.run()</script><div role=\"main\">Content</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should considered aria labelled elements as content', function () {\n    var checkArgs = checkSetup(\n      '<div id=\"target\" aria-label=\"axe-core logo\" role=\"img\"></div><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should allow native header elements', function () {\n    var checkArgs = checkSetup(\n      '<header id=\"target\">branding</header><main>Content </main><aside>stuff</aside><footer>copyright</footer>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should allow native main elements', function () {\n    var checkArgs = checkSetup(\n      '<header>branding</header><main id=\"target\">Content </main><aside>stuff</aside><footer>copyright</footer>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should allow native aside elements', function () {\n    var checkArgs = checkSetup(\n      '<header>branding</header><main>Content </main><aside id=\"target\">stuff</aside><footer>copyright</footer>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should allow native footer elements', function () {\n    var checkArgs = checkSetup(\n      '<header>branding</header><main>Content </main><aside>stuff</aside><footer id=\"target\">copyright</footer>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('ignores native landmark elements with an overwriting role', function () {\n    var checkArgs = checkSetup(\n      '<main id=\"target\" role=\"none\">Content</main><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('ignores native landmark elements with an overwriting role with a nested child', function () {\n    var checkArgs = checkSetup(`\n      <main id=\"target\" role=\"none\"><p>Content</p></main>\n\t\t\t<div role=\"main\">Content</div>\n    `);\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false for content outside of form tags with accessible names', function () {\n    var checkArgs = checkSetup(\n      '<p id=\"target\">Text</p><form aria-label=\"form\"></form>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('ignores unlabeled forms as they are not landmarks', function () {\n    var checkArgs = checkSetup(\n      '<form id=\"target\"><fieldset>foo</fieldset></form><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats <forms> with aria label as landmarks', function () {\n    var checkArgs = checkSetup(\n      '<form id=\"target\" aria-label=\"foo\"><p>This is random content.</p></form><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats role=forms with aria label as landmarks', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"form\" id=\"target\" aria-label=\"foo\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats forms without aria label as not a landmarks', function () {\n    var checkArgs = checkSetup(\n      '<form id=\"target\"><p>This is random content.</p></form><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats forms with an empty aria label as not a landmarks', function () {\n    var checkArgs = checkSetup(\n      '<form id=\"target\" aria-label=\" \"><p>This is random content.</p></form><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats forms with empty titles not as landmarks', function () {\n    var checkArgs = checkSetup(\n      '<form id=\"target\" title=\"\"><p>This is random content.</p></form><div role=\"main\">Content</div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats ARIA forms with no label or title as landmarks', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"form\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in aria-live=assertive', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"assertive\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in aria-live=polite', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"polite\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('does not allow content in aria-live=off', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"off\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in aria-live=assertive with explicit role set', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"assertive\" role=\"alert\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in aria-live=polite with explicit role set', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"polite\" role=\"status\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in implicit aria-live role alert', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"alert\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in implicit aria-live role log', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"log\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('allows content in implicit aria-live role status', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"status\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats role=dialog elements as regions', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"dialog\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats role=alertdialog elements as regions', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"alertdialog\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('treats svg elements as regions', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"></svg><div role=\"main\">Content</div>'\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns the outermost element as the error', function () {\n    var checkArgs = checkSetup(\n      '<div id=\"target\"><p>This is random content.</p></div><div role=\"main\"><h1 id=\"mainheader\" tabindex=\"0\">Introduction</h1></div>'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('supports options.regionMatcher', function () {\n    var checkArgs = checkSetup(\n      '<div aria-live=\"off\" id=\"target\"><p>This is random content.</p></div><div role=\"main\">Content</div>',\n      {\n        regionMatcher: {\n          attributes: {\n            'aria-live': 'off'\n          }\n        }\n      }\n    );\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('should return true when there is a button', function () {\n    // Some pages have a skiplink menu, that opens through a button\n    // ARIA practices is an example of this.\n    var checkArgs = checkSetup(\n      '<button id=\"target\">Skip menu</button><main><h1>Introduction</h1></main>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  (shadowSupport.v1 ? it : xit)('should test Shadow tree content', function () {\n    var div = document.createElement('div');\n    var shadow = div.attachShadow({ mode: 'open' });\n    shadow.innerHTML = 'Some text';\n    fixtureSetup(div);\n    var virutalNode = axe._tree[0];\n\n    // fixture is the outermost element\n    assert.isFalse(\n      checkEvaluate.call(\n        checkContext,\n        virutalNode.actualNode,\n        null,\n        virutalNode\n      )\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)('should test slotted content', function () {\n    var div = document.createElement('div');\n    div.innerHTML = 'Some content';\n    var shadow = div.attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<div role=\"main\"><slot></slot></div>';\n    var checkArgs = checkSetup(div);\n\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should ignore skiplink targets inside shadow trees',\n    function () {\n      var div = document.createElement('div');\n      div.innerHTML =\n        '<a id=\"target\" href=\"#foo\">skiplink</a><div>Content</div>';\n\n      var shadow = div.querySelector('div').attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div role=\"main\" id=#foo\"><slot></slot></div>';\n      fixtureSetup(div);\n      var virutalNode = axe.utils.getNodeFromTree(div.querySelector('#target'));\n\n      assert.isFalse(\n        checkEvaluate.call(\n          checkContext,\n          virutalNode.actualNode,\n          null,\n          virutalNode\n        )\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find the skiplink in shadow DOM',\n    function () {\n      var div = document.createElement('div');\n      div.innerHTML = '<span id=\"foo\">Content!</span>';\n      var shadow = div.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<a href=\"#foo\">skiplink</a><div role=\"main\"><slot></slot></div>';\n      var checkArgs = checkSetup(div);\n\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n      assert.lengthOf(checkContext._relatedNodes, 0);\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/navigation/skip-link.js",
    "content": "describe('skip-link', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  it('should return true if the href points to an element with an ID', function () {\n    fixture.innerHTML =\n      '<a href=\"#target\">Click Here</a><h1 id=\"target\">Introduction</h1>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.testUtils.getCheckEvaluate('skip-link')(node));\n  });\n\n  it('should return true if the href points to an element with an name', function () {\n    fixture.innerHTML = '<a href=\"#target\">Click Here</a><a name=\"target\"></a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.testUtils.getCheckEvaluate('skip-link')(node));\n  });\n\n  it('should return false if the href points to a non-existent element', function () {\n    fixture.innerHTML =\n      '<a href=\"#spacecamp\">Click Here</a><h1 id=\"mainheader\">Introduction</h1>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.testUtils.getCheckEvaluate('skip-link')(node));\n  });\n\n  it('should return undefined if the target has display:none', function () {\n    fixture.innerHTML =\n      '<a href=\"#target\">Click Here</a>' +\n      '<h1 id=\"target\" style=\"display:none\">Introduction</h1>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isUndefined(axe.testUtils.getCheckEvaluate('skip-link')(node));\n  });\n\n  it('should return undefined if the target has aria-hidden=true', function () {\n    fixture.innerHTML =\n      '<a href=\"#target\">Click Here</a>' +\n      '<h1 id=\"target\" aria-hidden=\"true\">Introduction</h1>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isUndefined(axe.testUtils.getCheckEvaluate('skip-link')(node));\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/unique-frame-title-after.js",
    "content": "/*eslint indent: 0*/\ndescribe('unique-frame-title-after', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should remove any check whose data only appears once', function () {\n    var result = checks['unique-frame-title'].after([\n      {\n        data: 'bananas'\n      },\n      {\n        data: 'monkeys'\n      },\n      {\n        data: 'bananas'\n      },\n      {\n        data: 'apples'\n      },\n      {\n        data: 'monkeys'\n      }\n    ]);\n\n    assert.deepEqual(result, [\n      {\n        data: 'bananas',\n        result: true\n      },\n      {\n        data: 'monkeys',\n        result: true\n      },\n      {\n        data: 'bananas',\n        result: true\n      },\n      {\n        data: 'apples',\n        result: false\n      },\n      {\n        data: 'monkeys',\n        result: true\n      }\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/checks/navigation/unique-frame-title.js",
    "content": "describe('unique-frame-title', function () {\n  'use strict';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should log title to data and return true', function () {\n    var vNode = queryFixture('<iframe id=\"target\" title=\"bananas\"></iframe>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('unique-frame-title')\n        .call(checkContext, null, {}, vNode)\n    );\n    assert.equal(checkContext._data, 'bananas');\n  });\n\n  it('should convert text to lower case', function () {\n    var vNode = queryFixture(\n      '<iframe id=\"target\" title=\"\\t  app\\t \\n \\rle  \"></iframe>'\n    );\n    axe.testUtils\n      .getCheckEvaluate('unique-frame-title')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(checkContext._data, 'app le');\n  });\n\n  it('should take out space differences', function () {\n    var vNode = queryFixture('<iframe id=\"target\" title=\"APPLE\"></iframe>');\n    axe.testUtils\n      .getCheckEvaluate('unique-frame-title')\n      .call(checkContext, null, {}, vNode);\n    assert.equal(checkContext._data, 'apple');\n  });\n});\n"
  },
  {
    "path": "test/checks/parser/duplicate-id.js",
    "content": "describe('duplicate-id', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if there is only one element with an ID', function () {\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    var node = fixture.querySelector('#target');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n    );\n    assert.equal(checkContext._data, node.id);\n    assert.deepEqual(checkContext._relatedNodes, []);\n  });\n\n  it('should return false if there are multiple elements with an ID', function () {\n    fixture.innerHTML = '<div id=\"target\"></div><div id=\"target\"></div>';\n    var node = fixture.querySelector('#target');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n    );\n    assert.equal(checkContext._data, node.id);\n    assert.deepEqual(checkContext._relatedNodes, [node.nextSibling]);\n  });\n\n  it('should return remove duplicates', function () {\n    assert.deepEqual(\n      checks['duplicate-id'].after([\n        { data: 'a' },\n        { data: 'b' },\n        { data: 'b' }\n      ]),\n      [{ data: 'a' }, { data: 'b' }]\n    );\n  });\n\n  it('should ignore empty ids', function () {\n    fixture.innerHTML =\n      '<div data-testelm=\"1\" id=\"\"></div><div data-testelm=\"2\"  id=\"\"></div>';\n    var node = fixture.querySelector('[data-testelm=\"1\"]');\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n    );\n  });\n\n  it('should allow overwrote ids', function () {\n    fixture.innerHTML =\n      '<form data-testelm=\"1\" id=\"target\"><label>mylabel' +\n      '<input name=\"id\">' +\n      '</label></form>';\n    var node = fixture.querySelector('[data-testelm=\"1\"]');\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find duplicate IDs in the same shadow DOM',\n    function () {\n      var div = document.createElement('div');\n      div.id = 'target';\n      var shadow = div.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<span id=\"target\"></span><p id=\"target\">text</p>';\n      var node = shadow.querySelector('span');\n      fixture.appendChild(div);\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n      );\n      assert.lengthOf(checkContext._relatedNodes, 1);\n      assert.deepEqual(checkContext._relatedNodes, [shadow.querySelector('p')]);\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should ignore duplicate IDs if they are in different document roots',\n    function () {\n      var node = document.createElement('div');\n      node.id = 'target';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<span id=\"target\"></span>';\n      fixture.appendChild(node);\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n      );\n      assert.lengthOf(checkContext._relatedNodes, 0);\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should ignore same IDs outside shadow trees',\n    function () {\n      var div = document.createElement('div');\n      div.id = 'target';\n      var shadow = div.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<span id=\"target\"></span>';\n      var node = shadow.querySelector('#target');\n      fixture.appendChild(div);\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n      );\n      assert.lengthOf(checkContext._relatedNodes, 0);\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should compare slotted content with the light DOM',\n    function () {\n      var node = document.createElement('div');\n      node.id = 'target';\n      node.innerHTML = '<p id=\"target\">text</p>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<span id=\"target\"><slot></slot></span>';\n      fixture.appendChild(node);\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('duplicate-id').call(checkContext, node)\n      );\n      assert.lengthOf(checkContext._relatedNodes, 1);\n      assert.deepEqual(checkContext._relatedNodes, [node.querySelector('p')]);\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/shared/abstractrole.js",
    "content": "describe('abstractrole', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return false if applied to a concrete role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.abstractrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        'radio',\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if applied to a nonsensical role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"foo\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.abstractrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        'radio',\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true if applied to an abstract role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"widget\">Contents</div>'\n    );\n    assert.isTrue(\n      checks.abstractrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        'radio',\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['widget']);\n  });\n\n  it('should return false if applied to multiple concrete roles', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert button\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.abstractrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        'radio',\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return true if applied to at least one abstract role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert widget structure\">Contents</div>'\n    );\n    assert.isTrue(\n      checks.abstractrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        'radio',\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['widget', 'structure']);\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/aria-label.js",
    "content": "describe('aria-label', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if an aria-label is present', function () {\n    var checkArgs = checkSetup('<div id=\"target\" aria-label=\"woohoo\"></div>');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('aria-label').apply(null, checkArgs)\n    );\n  });\n\n  it('should return false if an aria-label is not present', function () {\n    var checkArgs = checkSetup('<div id=\"target\"></div>');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('aria-label').apply(null, checkArgs)\n    );\n  });\n\n  it('should return false if an aria-label is present, but empty', function () {\n    var checkArgs = checkSetup('<div id=\"target\" aria-label=\" \"></div>');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('aria-label').apply(null, checkArgs)\n    );\n  });\n\n  it('should collapse whitespace', function () {\n    var checkArgs = checkSetup(\n      '<div id=\"target\" aria-label=\" \\t \\n \\r \\t  \\t\\r\\n \"></div>'\n    );\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('aria-label').apply(null, checkArgs)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/aria-labelledby.js",
    "content": "describe('aria-labelledby', () => {\n  const queryFixture = axe.testUtils.queryFixture;\n  const checkEvaluate = axe.testUtils.getCheckEvaluate('aria-labelledby');\n\n  it('should return true if an aria-labelledby and its target is present', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo\"></div><div id=\"woohoo\">bananas</div>'\n    );\n    assert.isTrue(checkEvaluate(null, {}, node));\n  });\n\n  it('should return true if only one element referenced by aria-labelledby has visible text', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo noexist hehe\"></div><div id=\"woohoo\">bananas</div>'\n    );\n    assert.isTrue(checkEvaluate(null, {}, node));\n  });\n\n  it('should return false if an aria-labelledby is not present', () => {\n    const node = queryFixture('<div id=\"target\"></div>');\n    assert.isFalse(checkEvaluate(null, {}, node));\n  });\n\n  it('should return true if an aria-labelledby is present that references hidden elements', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo noexist hehe\"></div><div id=\"woohoo\" style=\"display:none\">bananas</div>'\n    );\n    assert.isTrue(checkEvaluate(null, {}, node));\n  });\n\n  it('should return false if an aria-labelledby is present, but references an element with only hidden content', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo noexist hehe\"></div><div id=\"woohoo\"><span style=\"display: none\">bananas</span></div>'\n    );\n    assert.isFalse(checkEvaluate(null, {}, node));\n  });\n\n  it('returns false if aria-labelledby refers to the own element', () => {\n    const vNode = queryFixture(\n      '<input aria-labelledby=\"target\" value=\"in the sky\" id=\"target\">'\n    );\n    assert.isFalse(checkEvaluate(null, {}, vNode));\n  });\n\n  it('returns false if aria-labelledby refers to parent, and there are no sibling', () => {\n    const vNode = queryFixture(\n      '<div id=\"lbl\"> <input aria-labelledby=\"lbl\" value=\"in the sky\" id=\"target\"> </div>'\n    );\n    assert.isFalse(checkEvaluate(null, {}, vNode));\n  });\n\n  it('should return true if an aria-labelledby is present that references elements with has aria-hidden=true', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo\"></div><div id=\"woohoo\" aria-hidden=\"true\">bananas</div>'\n    );\n    assert.isTrue(checkEvaluate(null, {}, node));\n  });\n\n  it('should return false if an aria-labelledby is present that references elements with has aria-hidden=true in the content', () => {\n    const node = queryFixture(\n      '<div id=\"target\" aria-labelledby=\"woohoo\"></div><div id=\"woohoo\"><span aria-hidden=\"true\">bananas</span></div>'\n    );\n    assert.isFalse(checkEvaluate(null, {}, node));\n  });\n\n  describe('SerialVirtualNode', () => {\n    it('should return false if an aria-labelledby is not present', () => {\n      const node = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      assert.isFalse(checkEvaluate(null, {}, node));\n    });\n\n    it('should return undefined if an aria-labelledby is present', () => {\n      const node = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-labelledby': 'woohoo'\n        }\n      });\n      assert.isUndefined(checkEvaluate(null, {}, node));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/avoid-inline-spacing.js",
    "content": "describe('avoid-inline-spacing tests', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('avoid-inline-spacing');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('returns true when no inline spacing styles are specified', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"font-size: 200%;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when inline spacing styles has invalid value', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"line-height: 5invalid;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when inline spacing styles has invalid value and `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"line-height: invalid !important;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when `line-height` style specified has no `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"line-height: 1.5;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when `letter-spacing` style specified has no `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"letter-spacing: 50px;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when `word-spacing` style specified has no `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"word-spacing: 10px;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns true when none of the multiple inline spacing styles specified have priority of `!important`', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"word-spacing: 20ch; letter-spacing: 50rem; line-height: 3;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isTrue(actual);\n    assert.isNull(checkContext._data);\n  });\n\n  it('returns false when `line-height` style specified has `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"line-height: 1.5 !important;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['line-height']);\n  });\n\n  it('returns false when `letter-spacing` style specified has `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"letter-spacing: 100em !important;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['letter-spacing']);\n  });\n\n  it('returns false when `word-spacing` style specified has `!important` priority', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"word-spacing: -.4ch !important;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['word-spacing']);\n  });\n\n  it('returns false when any of the multiple inline spacing styles specifies priority of `!important`', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"word-spacing: 200%; letter-spacing: 50rem !important; line-height: 3;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['letter-spacing']);\n  });\n\n  it('returns false when multiple inline spacing styles specifies priority of `!important`', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"line-height: 3 !important; letter-spacing: 50rem !important;\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode);\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['line-height', 'letter-spacing']);\n  });\n\n  it('supports options.cssProperties', function () {\n    var vNode = queryFixture(\n      '<p id=\"target\" style=\"font-size: 14px !important; line-height: 3 !important; letter-spacing: 50rem !important\">The quick brown fox jumped over the lazy dog</p>'\n    );\n    var actual = checkEvaluate.call(checkContext, vNode.actualNode, {\n      cssProperties: ['font-size']\n    });\n    assert.isFalse(actual);\n    assert.deepEqual(checkContext._data, ['font-size']);\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/button-has-visible-text.js",
    "content": "describe('button-has-visible-text', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return false if button element is empty', function () {\n    var checkArgs = checkSetup('<button></button>', 'button');\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('button-has-visible-text')\n        .apply(checkContext, checkArgs)\n    );\n  });\n\n  it('should return true if a button element has text', function () {\n    var checkArgs = checkSetup('<button>Name</button>', 'button');\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('button-has-visible-text')\n        .apply(checkContext, checkArgs)\n    );\n  });\n\n  it('should return true if ARIA button has text', function () {\n    var checkArgs = checkSetup(\n      '<div role=\"button\">Text</div>',\n      '[role=button]'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('button-has-visible-text')\n        .apply(checkContext, checkArgs)\n    );\n  });\n\n  it('should return false if ARIA button has no text', function () {\n    var checkArgs = checkSetup('<div role=\"button\"></div>', '[role=button]');\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('button-has-visible-text')\n        .apply(checkContext, checkArgs)\n    );\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return incomplete if no children are passed', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'button'\n      });\n\n      assert.isUndefined(\n        axe.testUtils.getCheckEvaluate('button-has-visible-text')(\n          null,\n          {},\n          node\n        )\n      );\n    });\n\n    it('should return false if button element is empty', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'button'\n      });\n      node.children = [];\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('button-has-visible-text')(\n          null,\n          {},\n          node\n        )\n      );\n    });\n\n    it('should return true if a button element has text', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'button'\n      });\n      var child = new axe.SerialVirtualNode({\n        nodeName: '#text',\n        nodeType: 3,\n        nodeValue: 'Text'\n      });\n      node.children = [child];\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('button-has-visible-text')(\n          null,\n          {},\n          node\n        )\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/doc-has-title.js",
    "content": "describe('doc-has-title', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return false if title is empty', function () {\n    var orig = document.title;\n    document.title = '';\n    assert.isFalse(axe.testUtils.getCheckEvaluate('doc-has-title')(fixture));\n    document.title = orig;\n  });\n\n  it('should return false if title contains only whitespace', function () {\n    var orig = document.title;\n    document.title = ' \\t\\r\\n \\n   \\r \\n\\t';\n    assert.isFalse(axe.testUtils.getCheckEvaluate('doc-has-title')(fixture));\n    document.title = orig;\n  });\n\n  it('should return true if title is non-empty', function () {\n    var orig = document.title;\n    document.title = 'Bananas';\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('doc-has-title')(fixture));\n    document.title = orig;\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/exists.js",
    "content": "describe('exists', function () {\n  'use strict';\n\n  it('should return undefined', function () {\n    assert.isUndefined(checks.exists.evaluate());\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/has-alt.js",
    "content": "describe('has-alt', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if an alt is present', function () {\n    var checkArgs = checkSetup('<img id=\"target\" alt=\"woohoo\" />');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('has-alt').apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if an empty alt is present', function () {\n    var checkArgs = checkSetup('<img id=\"target\" alt=\"\" />');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('has-alt').apply(null, checkArgs)\n    );\n  });\n\n  it('should return true if a null alt is present', function () {\n    var checkArgs = checkSetup('<img id=\"target\" alt />');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('has-alt').apply(null, checkArgs)\n    );\n  });\n\n  it('should return false if an alt is not present', function () {\n    var checkArgs = checkSetup('<img id=\"target\" />');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('has-alt').apply(null, checkArgs)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/has-visible-text.js",
    "content": "describe('has-visible-text', function () {\n  'use strict';\n\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('should return false if there is no visible text', function () {\n    var params = checkSetup('<p id=\"target\"></p>');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-visible-text')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return false if there is text, but its hidden', function () {\n    var params = checkSetup(\n      '<p id=\"target\"><span style=\"display:none\">hello!</span></p>'\n    );\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('has-visible-text')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true if there is visible text', function () {\n    var params = checkSetup('<p id=\"target\">hello!</p>');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('has-visible-text')\n        .apply(checkContext, params)\n    );\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return false if element is not named from contents', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'article'\n      });\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('has-visible-text')(null, {}, node)\n      );\n    });\n\n    it('should return incomplete if no other properties are set', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'button'\n      });\n\n      assert.isUndefined(\n        axe.testUtils.getCheckEvaluate('has-visible-text')(null, {}, node)\n      );\n    });\n\n    it('should return false if there is no visible text', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'button'\n      });\n      node.children = [];\n\n      assert.isFalse(\n        axe.testUtils.getCheckEvaluate('has-visible-text')(null, {}, node)\n      );\n    });\n\n    it('should return true if there is visible text', function () {\n      var node = new axe.SerialVirtualNode({\n        nodeName: 'p'\n      });\n      var child = new axe.SerialVirtualNode({\n        nodeName: '#text',\n        nodeType: 3,\n        nodeValue: 'hello!'\n      });\n      node.children = [child];\n\n      assert.isTrue(\n        axe.testUtils.getCheckEvaluate('has-visible-text')(null, {}, node)\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/inline-style-property.js",
    "content": "describe('inline-style-property tests', () => {\n  const fixture = document.getElementById('fixture');\n  const checkSetup = axe.testUtils.checkSetup;\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  describe('important-letter-spacing check', () => {\n    const checkEvaluate = axe.testUtils.getCheckEvaluate(\n      'important-letter-spacing'\n    );\n    const checkContext = axe.testUtils.MockCheckContext();\n    afterEach(() => {\n      checkContext.reset();\n    });\n\n    it('is true when the property is not set in the style attribute', () => {\n      const params = checkSetup(\n        '<p style=\"width: 60%\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is false when letter-spacing is less than 0.12em and !important', () => {\n      const params = checkSetup(\n        '<p style=\"letter-spacing: 0.1em !important\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 0.1,\n        minValue: 0.12\n      });\n    });\n\n    it('is true when !important is not used', () => {\n      const params = checkSetup(\n        '<p style=\"letter-spacing: 0.1em\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is true when letter-spacing is 0.15 times the font-size', () => {\n      const params = checkSetup(\n        '<p style=\"letter-spacing: 0.15em !important\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 0.15,\n        minValue: 0.12\n      });\n    });\n\n    it('uses the highest priority value if multiple are set', () => {\n      const style = [\n        'letter-spacing: 0.15em !important',\n        'letter-spacing: 0.1em !important',\n        'letter-spacing: 0.2em'\n      ].join('; ');\n      const params = checkSetup(\n        '<p style=\"' + style + '\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 0.1,\n        minValue: 0.12\n      });\n    });\n\n    describe('handles different font-sizes', () => {\n      it('is true when the font is 0.15 time the spacing', () => {\n        const params = checkSetup(\n          '<p style=\"font-size: 20px; letter-spacing: 3px !important\" id=\"target\">Hello world</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isTrue(result);\n        assert.deepEqual(checkContext._data, {\n          value: 0.15,\n          minValue: 0.12\n        });\n      });\n\n      it('is false when the font is 0.10 times the spacing', () => {\n        const params = checkSetup(\n          '<p style=\"font-size: 30px; letter-spacing: 3px !important\" id=\"target\">Hello world</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isFalse(result);\n        assert.deepEqual(checkContext._data, {\n          value: 0.1,\n          minValue: 0.12\n        });\n      });\n    });\n\n    describe('with non-number values', () => {\n      it('is false when `normal` (which is 0) is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: normal !important\" id=\"target\">Hello world</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isFalse(result);\n        assert.deepEqual(checkContext._data, {\n          value: 0,\n          minValue: 0.12\n        });\n      });\n\n      it('is false when `initial` (meaning `normal`) is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: initial !important\" id=\"target\">Hello world</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isFalse(result);\n        assert.deepEqual(checkContext._data, {\n          value: 0,\n          minValue: 0.12\n        });\n      });\n\n      it('is true when `inherited` is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: 0.1em\">' +\n            '<span style=\"letter-spacing: inherit !important;\" id=\"target\">Hello world</span</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isTrue(result);\n        assert.deepEqual(checkContext._data, {\n          value: 'inherit',\n          minValue: 0.12\n        });\n      });\n\n      it('is true when `unset` is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: 0.1em\">' +\n            '<span style=\"letter-spacing: unset !important;\" id=\"target\">Hello world</span</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isTrue(result);\n        assert.deepEqual(checkContext._data, {\n          value: 'unset',\n          minValue: 0.12\n        });\n      });\n\n      it('is true when `revert` is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: 0.1em\">' +\n            '<span style=\"letter-spacing: revert !important;\" id=\"target\">Hello world</span</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isTrue(result);\n        assert.deepEqual(checkContext._data, {\n          value: 'revert',\n          minValue: 0.12\n        });\n      });\n\n      it('is true when `revert-layer` is used along with !important', () => {\n        const params = checkSetup(\n          '<p style=\"letter-spacing: 0.1em\">' +\n            '<span style=\"letter-spacing: revert-layer !important;\" id=\"target\">Hello world</span</p>'\n        );\n        const result = checkEvaluate.apply(checkContext, params);\n        assert.isTrue(result);\n        assert.deepEqual(checkContext._data, {\n          value: 'revert-layer',\n          minValue: 0.12\n        });\n      });\n    });\n  });\n\n  describe('important-word-spacing check', () => {\n    const checkEvaluate = axe.testUtils.getCheckEvaluate(\n      'important-word-spacing'\n    );\n    const checkContext = axe.testUtils.MockCheckContext();\n    afterEach(() => {\n      checkContext.reset();\n    });\n\n    it('is true when word-spacing is not set in the style attribute', () => {\n      const params = checkSetup(\n        '<p style=\"width: 60%\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is true when below 0.16em and not !important', () => {\n      const params = checkSetup(\n        '<p style=\"word-spacing: 0.1em\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is false when below 0.16em and !important', () => {\n      const params = checkSetup(\n        '<p style=\"word-spacing: 0.1em !important\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 0.1,\n        minValue: 0.16\n      });\n    });\n\n    it('is true when 0.16em and !important', () => {\n      const params = checkSetup(\n        '<p style=\"word-spacing: 0.16em !important\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 0.16,\n        minValue: 0.16\n      });\n    });\n  });\n\n  describe('important-line-height check', () => {\n    const checkEvaluate = axe.testUtils.getCheckEvaluate(\n      'important-line-height'\n    );\n    const checkContext = axe.testUtils.MockCheckContext();\n    afterEach(() => {\n      checkContext.reset();\n    });\n\n    it('is true when line-height is not set in the style attribute', () => {\n      const params = checkSetup(\n        '<p style=\"width: 60%\" id=\"target\">Hello world</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is true when below 1.5em and not !important', () => {\n      const params = checkSetup(\n        '<p style=\"line-height: 1.2em; max-width: 200px;\" id=\"target\">' +\n          '\tThe toy brought back fond memories of being lost in the rain forest.' +\n          '</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is false when below 1.5em and !important', () => {\n      const params = checkSetup(\n        '<p style=\"line-height: 1.2em !important; max-width: 200px;\" id=\"target\">' +\n          '\tThe toy brought back fond memories of being lost in the rain forest.' +\n          '</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 1.2,\n        minValue: 1.5\n      });\n    });\n\n    it('is true when 1.5em and !important', () => {\n      const params = checkSetup(\n        '<p style=\"line-height: 1.5em !important; max-width: 200px;\" id=\"target\">' +\n          '\tThe toy brought back fond memories of being lost in the rain forest.' +\n          '</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 1.5,\n        minValue: 1.5\n      });\n    });\n\n    it('returns the 1em for `normal !important`', () => {\n      const params = checkSetup(\n        '<p style=\"line-height: normal !important; max-width: 200px;\" id=\"target\">' +\n          '\tThe toy brought back fond memories of being lost in the rain forest.' +\n          '</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 1,\n        minValue: 1.5\n      });\n    });\n\n    it('is true for single line texts', () => {\n      const params = checkSetup(\n        '<p style=\"line-height: 1.2em !important; max-width: 200px;\" id=\"target\">' +\n          '\tShort' +\n          '</p>'\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n  });\n\n  describe('With options configured for font-size', () => {\n    const checkEvaluate = axe.testUtils.getCheckEvaluate(\n      'important-letter-spacing'\n    );\n    const checkContext = axe.testUtils.MockCheckContext();\n    const options = {\n      cssProperty: 'font-size',\n      minValue: 16,\n      maxValue: 42,\n      absoluteValues: true,\n      noImportant: false\n    };\n\n    afterEach(() => {\n      checkContext.reset();\n    });\n\n    it('is false when !important and below the minValue', () => {\n      const params = checkSetup(\n        '<p style=\"font-size: 12px !important\" id=\"target\">Hello world</p>',\n        options\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 12,\n        minValue: 16,\n        maxValue: 42\n      });\n    });\n\n    it('is false when !important and above the maxValue', () => {\n      const params = checkSetup(\n        '<p style=\"font-size: 43px !important\" id=\"target\">Hello world</p>',\n        options\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 43,\n        minValue: 16,\n        maxValue: 42\n      });\n    });\n\n    it('is true when not !important', () => {\n      const params = checkSetup(\n        '<p style=\"font-size: 12px\" id=\"target\">Hello world</p>',\n        options\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.isNull(checkContext._data);\n    });\n\n    it('is false when not !important and {noImportant: true}', () => {\n      const opt = Object.assign({}, options, { noImportant: true });\n      const params = checkSetup(\n        '<p style=\"font-size: 12px\" id=\"target\">Hello world</p>',\n        opt\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 12,\n        minValue: 16,\n        maxValue: 42\n      });\n    });\n\n    it('returns the normal value when `normal` is used', () => {\n      // Using line-height, since font-size cannot be normal\n      const opts = {\n        cssProperty: 'line-height',\n        normalValue: 3,\n        minValue: 5\n      };\n      const params = checkSetup(\n        '<p style=\"line-height: normal !important\" id=\"target\">Hello world</p>',\n        opts\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isFalse(result);\n      assert.deepEqual(checkContext._data, {\n        value: 3,\n        minValue: 5\n      });\n    });\n\n    it('is true when above the minValue', () => {\n      const params = checkSetup(\n        '<p style=\"font-size: 16px !important\" id=\"target\">Hello world</p>',\n        options\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 16,\n        minValue: 16,\n        maxValue: 42\n      });\n    });\n\n    it('ignores minValue when not a number', () => {\n      const opt = Object.assign({}, options, { minValue: '16' });\n      const params = checkSetup(\n        '<p style=\"font-size: 12px !important\" id=\"target\">Hello world</p>',\n        opt\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 12,\n        maxValue: 42\n      });\n    });\n\n    it('ignores maxValue when not a number', () => {\n      const opt = Object.assign({}, options, { maxValue: '42' });\n      const params = checkSetup(\n        '<p style=\"font-size: 50px !important\" id=\"target\">Hello world</p>',\n        opt\n      );\n      const result = checkEvaluate.apply(checkContext, params);\n      assert.isTrue(result);\n      assert.deepEqual(checkContext._data, {\n        value: 50,\n        minValue: 16\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/invalidrole.js",
    "content": "describe('invalidrole', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if applied to an empty role', function () {\n    var virtualNode = queryFixture('<div id=\"target\" role=\"\">Contents</div>');\n    assert.isTrue(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['']);\n  });\n\n  it('should return true if applied to a nonsensical role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"foo\">Contents</div>'\n    );\n    assert.isTrue(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['foo']);\n  });\n\n  it('should return false if applied to a concrete role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if applied to an abstract role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"widget\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if applied to multiple valid roles', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert button\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.isNull(checkContext._data);\n  });\n\n  it('should return false if atleast one role is valid', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"alert button foo bar\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n  });\n\n  it('should return true if all roles are invalid', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"foo bar\">Contents</div>'\n    );\n    assert.isTrue(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['foo', 'bar']);\n  });\n\n  it('should return true if applied to an uppercase nonsensical role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"FOO\">Contents</div>'\n    );\n    assert.isTrue(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n    assert.deepEqual(checkContext._data, ['FOO']);\n  });\n\n  it('should return false if applied to an uppercase valid role', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\" role=\"BUTTON\">Contents</div>'\n    );\n    assert.isFalse(\n      checks.invalidrole.evaluate.call(\n        checkContext,\n        virtualNode.actualNode,\n        null,\n        virtualNode\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/is-on-screen.js",
    "content": "describe('is-on-screen', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n\n  it('should return true for visible elements', function () {\n    var vNode = queryFixture('<div id=\"target\">elm</div>');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('is-on-screen')(vNode));\n  });\n\n  it('should return true for aria-hidden=true elements', function () {\n    var vNode = queryFixture('<div id=\"target\" aria-hidden=\"true\">elm</div>');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('is-on-screen')(vNode));\n  });\n\n  it('should return false for display:none elements', function () {\n    var vNode = queryFixture('<div id=\"target\" style=\"display:none\">elm</div>');\n\n    assert.isFalse(axe.testUtils.getCheckEvaluate('is-on-screen')(vNode));\n  });\n\n  it('should return false for off screen elements', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position:absolute; top:-10000px\">elm</div>'\n    );\n\n    assert.isFalse(axe.testUtils.getCheckEvaluate('is-on-screen')(vNode));\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/non-empty-alt.js",
    "content": "describe('non-empty-alt', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-alt');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if an alt is present', function () {\n    var params = checkSetup('<img id=\"target\" alt=\"woohoo\" />');\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if an alt is not present', function () {\n    var params = checkSetup('<img id=\"target\" />');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noAttr');\n  });\n\n  it('should return false if an alt is present, but empty', function () {\n    var params = checkSetup('<img id=\"target\" alt=\" \" />');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n\n  it('should collapse whitespace', function () {\n    var params = checkSetup('<img id=\"target\" alt=\" \\t \\n \\r \\t  \\t\\r\\n \" />');\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/non-empty-if-present.js",
    "content": "describe('non-empty-if-present', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  // These defaults are only available in IE and Edge\n  var input = document.createElement('input');\n  input.type = 'submit';\n  var isEdgeOrIe = typeof input.getAttribute('value') === 'string';\n\n  var checkContext = axe.testUtils.MockCheckContext();\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return false if a value is present', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"submit\" value=\"woohoo\" />'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('non-empty-if-present', { verifyMessage: false })\n        .call(checkContext, null, {}, vNode)\n    );\n    assert.equal(checkContext._data.messageKey, 'has-label');\n  });\n\n  (isEdgeOrIe ? xit : it)(\n    'should return true if a value is not present',\n    function () {\n      var vNode = queryFixture('<input id=\"target\" type=\"submit\" />');\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('non-empty-if-present')\n          .call(checkContext, null, {}, vNode)\n      );\n      assert.isNull(checkContext._data);\n    }\n  );\n\n  it('should return false if an value is present, but empty', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"submit\" value=\"\" />');\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('non-empty-if-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n\n  it('should return false if the element is not a submit or reset input', function () {\n    var vNode = queryFixture('<input id=\"target\" type=\"text\" />');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('non-empty-if-present')\n        .call(checkContext, null, {}, vNode)\n    );\n\n    var vNode = queryFixture('<input id=\"target\" type=\"button\" />');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('non-empty-if-present')\n        .call(checkContext, null, {}, vNode)\n    );\n\n    var vNode = queryFixture('<button id=\"target\" type=\"submit\"></button');\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('non-empty-if-present')\n        .call(checkContext, null, {}, vNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/non-empty-placeholder.js",
    "content": "describe('non-empty-placeholder', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-placeholder');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if a placeholder is present', function () {\n    var params = checkSetup('<input id=\"target\" placeholder=\"woohoo\" />');\n\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if a placeholder is not present', function () {\n    var params = checkSetup('<input id=\"target\" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noAttr');\n  });\n\n  it('should return false if a placeholder is present, but empty', function () {\n    var params = checkSetup('<input id=\"target\" placeholder=\" \" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n\n  it('should collapse whitespace', function () {\n    var params = checkSetup(\n      '<input id=\"target\" placeholder=\" \\t \\n \\r \\t  \\t\\r\\n \" />'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/non-empty-title.js",
    "content": "describe('non-empty-title', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-title');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should return true if a title is present', function () {\n    var params = checkSetup('<img id=\"target\" title=\"woohoo\" />');\n\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if a title is not present', function () {\n    var params = checkSetup('<img id=\"target\" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noAttr');\n  });\n\n  it('should return false if a title is present, but empty', function () {\n    var params = checkSetup('<img id=\"target\" title=\" \" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n\n  it('should collapse whitespace', function () {\n    var params = checkSetup(\n      '<img id=\"target\" title=\" \\t \\n \\r \\t  \\t\\r\\n \" />'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/non-empty-value.js",
    "content": "describe('non-empty-value', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-value');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if an value is present', function () {\n    var params = checkSetup('<input id=\"target\" value=\"woohoo\" />');\n\n    assert.isTrue(checkEvaluate.apply(checkContext, params));\n  });\n\n  it('should return false if an value is not present', function () {\n    var params = checkSetup('<input id=\"target\" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'noAttr');\n  });\n\n  it('should return false if an value is present, but empty', function () {\n    var params = checkSetup('<input id=\"target\" value=\" \" />');\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n\n  it('should collapse whitespace', function () {\n    var params = checkSetup(\n      '<input id=\"target\" value=\" \\t \\n \\r \\t  \\t\\r\\n \" />'\n    );\n\n    assert.isFalse(checkEvaluate.apply(checkContext, params));\n    assert.equal(checkContext._data.messageKey, 'emptyAttr');\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/presentational-role.js",
    "content": "describe('presentational-role', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('presentational-role');\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('should detect role=\"none\" on the element', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"none\"></div>');\n\n    assert.isTrue(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data.role, 'none');\n  });\n\n  it('should detect role=\"presentation\" on the element', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"presentation\"></div>');\n\n    assert.isTrue(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data.role, 'presentation');\n  });\n\n  it('should return false when role !== none', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"cats\"></div>');\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n  });\n\n  it('should return false when there is no role attribute', function () {\n    var vNode = queryFixture('<div id=\"target\"></div>');\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n  });\n\n  it('should return false when the element is focusable', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" role=\"none\">Still a button</button>'\n    );\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data.messageKey, 'focusable');\n  });\n\n  it('should return false when the element has global aria attributes', function () {\n    var vNode = queryFixture(\n      '<img id=\"target\" role=\"none\" aria-live=\"assertive\" />'\n    );\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data.messageKey, 'globalAria');\n  });\n\n  it('should return false when the element has global aria attributes and is focusable', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" role=\"none\" aria-live=\"assertive\">Still a button</button>'\n    );\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data.messageKey, 'both');\n  });\n\n  it('should return false for iframe element with role=none and title', function () {\n    var vNode = queryFixture(\n      '<iframe id=\"target\" role=\"none\" title=\"  \"></iframe>'\n    );\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'iframe',\n      nodeName: 'iframe'\n    });\n  });\n\n  it('should return false for iframe element with role=presentation and title', function () {\n    var vNode = queryFixture(\n      '<iframe id=\"target\" role=\"presentation\" title=\"\"></iframe>'\n    );\n\n    assert.isFalse(checkEvaluate.call(checkContext, null, null, vNode));\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'iframe',\n      nodeName: 'iframe'\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/role-none.js",
    "content": "describe('role-none', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('role-none');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should detect role=\"none\" on the element', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"none\"></div>');\n\n    assert.isTrue(checkEvaluate(null, null, vNode));\n  });\n\n  it('should return false when role !== none', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"cats\"></div>');\n\n    assert.isFalse(checkEvaluate(null, null, vNode));\n  });\n\n  it('should return false when there is no role attribute', function () {\n    var vNode = queryFixture('<div id=\"target\"></div>');\n\n    assert.isFalse(checkEvaluate(null, null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/role-presentation.js",
    "content": "describe('role-presentation', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('role-presentation');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should detect role=\"presentation\" on the element', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"presentation\"></div>');\n\n    assert.isTrue(checkEvaluate(null, null, vNode));\n  });\n\n  it('should return false when role !== presentation', function () {\n    var vNode = queryFixture('<div id=\"target\" role=\"cats\"></div>');\n\n    assert.isFalse(checkEvaluate(null, null, vNode));\n  });\n\n  it('should return false when there is no role attribute', function () {\n    var vNode = queryFixture('<div id=\"target\"></div>');\n\n    assert.isFalse(checkEvaluate(null, null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/checks/shared/svg-non-empty-title.js",
    "content": "describe('svg-non-empty-title tests', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkContext = axe.testUtils.MockCheckContext();\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkEvaluate = axe.testUtils.getCheckEvaluate('svg-non-empty-title');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n  });\n\n  it('returns true if the element has a `title` child', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><title>Time II: Party</title></svg>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true if the `title` child has text nested in another element', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><title><g>Time II: Party</g></title></svg>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns true if the element has a `title` child with `display:none`', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><title style=\"display: none;\">Time II: Party</title></svg>'\n    );\n    assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n  });\n\n  it('returns false if the element has no `title` child', function () {\n    var checkArgs = checkSetup('<svg id=\"target\"></svg>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.messageKey, 'noTitle');\n  });\n\n  it('returns false if the `title` child is empty', function () {\n    var checkArgs = checkSetup('<svg id=\"target\"><title></title></svg>');\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.messageKey, 'emptyTitle');\n  });\n\n  it('returns false if the `title` is a grandchild', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><circle><title>Time II: Party</title></circle></svg>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.messageKey, 'noTitle');\n  });\n\n  it('returns false if the `title` child has only whitespace', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><title> \\t\\r\\n </title></svg>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.messageKey, 'emptyTitle');\n  });\n\n  it('returns false if there are multiple titles, and the first is empty', function () {\n    var checkArgs = checkSetup(\n      '<svg id=\"target\"><title></title><title>Time II: Party</title></svg>'\n    );\n    assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n    assert.equal(checkContext._data.messageKey, 'emptyTitle');\n  });\n\n  describe('Serial Virtual Node', function () {\n    it('returns true if the element has a `title` child', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      var child = new axe.SerialVirtualNode({\n        nodeName: 'title'\n      });\n      var text = new axe.SerialVirtualNode({\n        nodeName: '#text',\n        nodeType: 3,\n        nodeValue: 'Time II: Party'\n      });\n      child.parent = serialNode;\n      child.children = [text];\n      serialNode.children = [child];\n      var checkArgs = [null, {}, serialNode];\n\n      assert.isTrue(checkEvaluate.apply(checkContext, checkArgs));\n    });\n\n    it('returns false if the element has no `title` child', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      serialNode.children = [];\n      var checkArgs = [null, {}, serialNode];\n\n      assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));\n      assert.equal(checkContext._data.messageKey, 'noTitle');\n    });\n\n    it('returns undefined if the element has empty children', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      var checkArgs = [null, {}, serialNode];\n\n      assert.isUndefined(checkEvaluate.apply(checkContext, checkArgs));\n    });\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/caption-faked.js",
    "content": "describe('caption-faked', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  var captionFaked;\n  beforeEach(function () {\n    captionFaked = checks['caption-faked'];\n  });\n\n  it('returns true if the first row has multiple cells', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td></td> <td></td> </tr>' +\n      '  <tr> <td></td> <td></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(captionFaked.evaluate(node));\n  });\n\n  it('returns true if the table has only one column', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td></td> </tr>' +\n      '  <tr> <td></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(captionFaked.evaluate(node));\n  });\n\n  it('returns true if the table has only one <tr>', function () {\n    fixture.innerHTML =\n      '<table>' +\n      // Accessibility: Expect the unexpected\n      '  <tr> <td rowspan=\"2\" colspan=\"2\"></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(captionFaked.evaluate(node));\n  });\n\n  it('returns true if the first column does not span the entire table', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td></td> </tr>' +\n      '  <tr> <td></td> <td></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(captionFaked.evaluate(node));\n  });\n\n  it('returns false if the first is only a single td', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td colspan=\"2\"></td> </tr>' +\n      '  <tr> <td></td> <td></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isFalse(captionFaked.evaluate(node));\n  });\n\n  it('returns false if the first is only a single th', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th colspan=\"2\"></th> </tr>' +\n      '  <tr> <td></td> <td></td> </tr>' +\n      '</table>';\n\n    var node = fixture.querySelector('table');\n    assert.isFalse(captionFaked.evaluate(node));\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/html5-scope.js",
    "content": "describe('html5-scope', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true on THs', function () {\n    fixture.innerHTML = '<table><tr><th scope=\"col\"></th></tr></table>';\n    var node = fixture.querySelector('th');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('html5-scope')(node));\n  });\n\n  it('should return false on TDs', function () {\n    fixture.innerHTML = '<table><tr><td scope=\"col\"></td></tr></table>';\n    var node = fixture.querySelector('td');\n\n    assert.isFalse(axe.testUtils.getCheckEvaluate('html5-scope')(node));\n  });\n\n  it('should return true on non-HTML5 documents', function () {\n    var origPublicId = document.publicId;\n    fixture.innerHTML = '<table><tr><th scope=\"col\"></th></tr></table>';\n    var node = fixture.querySelector('th');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('html5-scope')(node));\n    document.publicId = origPublicId;\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/same-caption-summary.js",
    "content": "describe('same-caption-summary', function () {\n  'use strict';\n\n  var checkSetup = axe.testUtils.checkSetup;\n  var shadowCheckSetup = axe.testUtils.shadowCheckSetup;\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('should return false there is no caption', function () {\n    var params = checkSetup(\n      '<table summary=\"hi\" id=\"target\"><tr><td></td></tr></table>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('same-caption-summary')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return false there is no summary', function () {\n    var params = checkSetup(\n      '<table id=\"target\"><caption>Hi</caption><tr><td></td></tr></table>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('same-caption-summary')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return false if summary and caption are different', function () {\n    var params = checkSetup(\n      '<table summary=\"bye\" id=\"target\"><caption>Hi</caption><tr><td></td></tr></table>'\n    );\n\n    assert.isFalse(\n      axe.testUtils\n        .getCheckEvaluate('same-caption-summary')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true if summary and caption are the same', function () {\n    var params = checkSetup(\n      '<table summary=\"Hi\" id=\"target\"><caption>Hi</caption><tr><td></td></tr></table>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('same-caption-summary')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true if summary and caption are the same with mixed casing', function () {\n    var params = checkSetup(\n      '<table summary=\"My Table\" id=\"target\">' +\n        '<caption> my table </caption>' +\n        '<thead>' +\n        '<tr><th scope=\"col\">Head</th></tr>' +\n        '</thead>' +\n        '<tbody>' +\n        '<tr><td>Data</td></tr>' +\n        '</tbody>' +\n        '</table>'\n    );\n\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('same-caption-summary')\n        .apply(checkContext, params)\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should match slotted caption elements',\n    function () {\n      var params = shadowCheckSetup(\n        '<div>' +\n          '<span slot=\"caption\">Caption</span>' +\n          '<span slot=\"one\">Data element 1</span>' +\n          '<span slot=\"two\">Data element 2</span>' +\n          '</div>',\n        '<table summary=\"Caption\" id=\"target\">' +\n          '<caption><slot name=\"caption\"></slot></caption>' +\n          '<tr><td><slot name=\"one\"></slot></td><td><slot name=\"two\"></slot></td></tr>' +\n          '</table>'\n      );\n\n      assert.isTrue(\n        axe.testUtils\n          .getCheckEvaluate('same-caption-summary')\n          .apply(checkContext, params)\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/checks/tables/scope-value.js",
    "content": "describe('scope-value', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if scope is \"col\"', function () {\n    fixture.innerHTML = '<table><tr><td scope=\"col\"></td></tr></table>';\n    var node = fixture.querySelector('td');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('scope-value')(node));\n  });\n\n  it('should return true if scope is \"row\"', function () {\n    fixture.innerHTML = '<table><tr><td scope=\"row\"></td></tr></table>';\n    var node = fixture.querySelector('td');\n\n    assert.isTrue(axe.testUtils.getCheckEvaluate('scope-value')(node));\n  });\n\n  it('should return false otherwise', function () {\n    fixture.innerHTML =\n      '<table><tr><td scope=\"hahahahanothx\"></td></tr></table>';\n    var node = fixture.querySelector('td');\n\n    assert.isFalse(axe.testUtils.getCheckEvaluate('scope-value')(node));\n  });\n\n  it('should support options.values', function () {\n    fixture.innerHTML =\n      '<table><tr><td scope=\"hahahahanothx\"></td></tr></table>';\n    var node = fixture.querySelector('td');\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('scope-value')(node, {\n        values: ['hahahahanothx']\n      })\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/td-has-header.js",
    "content": "describe('td-has-header', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = null;\n  });\n\n  it('should not be fooled by rowspan and colspan', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<thead>' +\n      '    <tr>' +\n      '      <td rowspan=\"2\">Species</td>' +\n      '      <td colspan=\"2\">Info</td>' +\n      '    </tr>' +\n      '    <tr>' +\n      '      <th>Name</th>' +\n      '      <th>Age</th>' +\n      '    </tr>' +\n      '  </thead>' +\n      '  <tbody>' +\n      '    <tr>' +\n      '      <td>Gorilla</td>' +\n      '      <td>Koko</td>' +\n      '      <td>44</td>' +\n      '    </tr>' +\n      '    <tr>' +\n      '      <td>Human</td>' +\n      '      <td>Matt</td>' +\n      '      <td>33</td>' +\n      '    </tr>' +\n      '  </tbody>' +\n      '</table>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    var result = axe.testUtils\n      .getCheckEvaluate('td-has-header')\n      .call(checkContext, node);\n\n    assert.isFalse(result);\n    assert.equal(checkContext._relatedNodes.length, 4);\n  });\n\n  it('should return true each non-empty cell has a row header', function () {\n    fixture.innerHTML =\n      '<table>' + '  <tr> <th>hi</th> <td>hello</td> </tr>' + '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true each non-empty cell has a column header', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> <th>hello</th> </tr>' +\n      '  <tr> <td>hi</td> <td>hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true each non-empty cell has aria-label', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td aria-label=\"one\">hi</td> <td aria-label=\"two\">hello</td> </tr>' +\n      '  <tr> <td aria-label=\"one\">hi</td> <td aria-label=\"two\">hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true each non-empty cell has aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"one\">one</div><div id=\"two\">two</div>' +\n      '<table>' +\n      '  <tr> <td aria-labelledby=\"one\">hi</td> <td aria-labelledby=\"two\">hello</td> </tr>' +\n      '  <tr> <td aria-labelledby=\"one\">hi</td> <td aria-labelledby=\"two\">hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true each non-empty cell has a headers attribute', function () {\n    // This will fail under td-headers-attr because the headers must be inside the table\n    fixture.innerHTML =\n      '<div id=\"one\">one</div><div id=\"two\">two</div>' +\n      '<table>' +\n      '  <tr> <td headers=\"one\">hi</td> <td headers=\"two\">hello</td> </tr>' +\n      '  <tr> <td headers=\"one\">hi</td> <td headers=\"two\">hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true there is at least one non-empty header', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> <th>hello</th> </tr>' +\n      '  <tr> <th></th> <td>hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true if the only data cells are empty', function () {\n    fixture.innerHTML =\n      '<table>' + '  <tr> <td></td> <td></td> </tr>' + '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return false if a cell has no headers', function () {\n    fixture.innerHTML =\n      '<table>' + '  <tr> <td>hi</td> <td>hello</td> </tr>' + '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [\n      node.rows[0].cells[0],\n      node.rows[0].cells[1]\n    ]);\n  });\n\n  it('should return false if a cell has no headers - complex table', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td colspan=\"3\">Psuedo-Caption</td> </tr>' +\n      '  <tr> <td>hi</td> <td>hello</td> <td>Ok</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n    assert.deepEqual(checkContext._relatedNodes, [\n      node.rows[0].cells[0],\n      node.rows[1].cells[0],\n      node.rows[1].cells[1],\n      node.rows[1].cells[2]\n    ]);\n  });\n\n  it('should return true if the headers element is empty', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>Hello</th> <td headers=\"\">goodbye</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return true if the headers element refers to non-existing elements', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>Hello</th> <td headers=\"beatles\">goodbye</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  it('should return false if all headers are empty', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th></th> <th></th> </tr>' +\n      '  <tr> <td>hi</td> <td>hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = fixture.querySelector('table');\n    assert.isFalse(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n\n  (shadowSupport ? it : xit)('recognizes shadow tree content', function () {\n    fixture.innerHTML = '<div id=\"shadow\"> <b>header</b> </div>';\n    var shadow = fixture\n      .querySelector('#shadow')\n      .attachShadow({ mode: 'open' });\n    shadow.innerHTML =\n      '<table>' +\n      '  <tr> <th><slot></slot> </tr>' +\n      '  <tr> <td> data </td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var node = axe.utils.querySelectorAll(axe._tree, 'table')[0].actualNode;\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('td-has-header').call(checkContext, node)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/td-headers-attr.js",
    "content": "describe('td-headers-attr', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var checkContext = axe.testUtils.MockCheckContext();\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var check = axe.testUtils.getCheckEvaluate('td-headers-attr');\n\n  afterEach(function () {\n    checkContext.reset();\n  });\n\n  it('returns true no headers attribute is present', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th>hi</th> <td>hello</td> </tr>' +\n        '  <tr> <th>hi</th> <td>hello</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('returns true if a valid header is present', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th id=\"hi\">hello</th> </tr>' +\n        '  <tr> <td headers=\"hi\">goodbye</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('returns true if multiple valid headers are present', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th id=\"hi1\">hello</th> <th id=\"hi2\">hello</th> </tr>' +\n        '  <tr> <td headers=\"hi1 \\t\\n hi2\">goodbye</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('returns true with an empty header', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th id=\"hi1\"></th> </tr>' +\n        '  <tr> <td headers=\"hi1\">goodbye</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('returns undefined if headers is empty', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th id=\"hi\"> </th> </tr>' +\n        '  <tr> <td headers=\"\">goodbye</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isUndefined(check.call(checkContext, node));\n  });\n\n  it('returns false if the header is a table cell', function () {\n    var node;\n\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th> <span id=\"hi\">hello</span> </th> </tr>' +\n        '  <tr> <td headers=\"h1\">goodbye</td> </tr>' +\n        '</table>'\n    );\n    node = fixture.querySelector('table');\n    assert.isFalse(check.call(checkContext, node));\n\n    fixtureSetup(\n      '<span id=\"hi\">hello</span>' +\n        '<table>' +\n        '  <tr> <th></th> </tr>' +\n        '  <tr> <td headers=\"h1\">goodbye</td> </tr>' +\n        '</table>'\n    );\n    node = fixture.querySelector('table');\n    assert.isFalse(check.call(checkContext, node));\n    assert.deepEqual(checkContext._data, {\n      messageKey: 'cell-header-not-in-table'\n    });\n\n    fixtureSetup(\n      '<table id=\"hi\">' +\n        '  <tr> <th>hello</th> </tr>' +\n        '  <tr> <td headers=\"h1\">goodbye</td> </tr>' +\n        '</table>'\n    );\n    node = fixture.querySelector('table');\n    assert.isFalse(check.call(checkContext, node));\n  });\n\n  it('returns false if table cell referenced as header', function () {\n    fixtureSetup(`\n      <table>\n        <tr> <td id=\"hi\">hello</td> </tr>\n        <tr> <td headers=\"hi\">goodbye</td> </tr>\n      </table>\n    `);\n\n    var node = fixture.querySelector('table');\n    assert.isFalse(check.call(checkContext, node));\n    assert.deepEqual(checkContext._data, { messageKey: 'cell-header-not-th' });\n  });\n\n  it('returns true if table cell referenced as header with role rowheader or columnheader', function () {\n    var node;\n\n    fixtureSetup(`\n      <table>\n        <tr> <td role=\"rowheader\" id=\"hi\">hello</td> </tr>\n        <tr> <td headers=\"hi\">goodbye</td> </tr>\n      </table>\n    `);\n\n    node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n\n    fixtureSetup(`\n      <table>\n        <tr> <td role=\"columnheader\" id=\"hi\">hello</td> </tr>\n        <tr> <td headers=\"hi\">goodbye</td> </tr>\n      </table>\n    `);\n\n    node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('relatedNodes contains each cell only once', function () {\n    fixtureSetup(`\n      <table>\n        <tr> <td id=\"hi1\">hello</td> </tr>\n        <tr> <td id=\"hi2\">hello</td> </tr>\n        <tr> <td id=\"bye\" headers=\"hi1 hi2\">goodbye</td> </tr>\n      </table>'\n    `);\n\n    var node = fixture.querySelector('table');\n    check.call(checkContext, node);\n    assert.deepEqual(checkContext._relatedNodes, [\n      fixture.querySelector('#bye')\n    ]);\n  });\n\n  it('returns false if the header refers to the same cell', function () {\n    fixtureSetup(\n      '<table id=\"hi\">' +\n        '  <tr> <th>hello</th> </tr>' +\n        '  <tr> <td id=\"bye\" headers=\"bye\">goodbye</td> </tr>' +\n        '</table>'\n    );\n\n    var node = fixture.querySelector('table');\n    assert.isFalse(check.call(checkContext, node));\n    assert.deepEqual(checkContext._data, { messageKey: 'header-refs-self' });\n  });\n\n  it('returns true if td[headers] is hidden', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th>Hello</th> <td headers=\"h1\" hidden>goodbye</td> </tr>' +\n        '</table>'\n    );\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n\n  it('returns true if td[headers] has aria-hidden=true', function () {\n    fixtureSetup(\n      '<table>' +\n        '  <tr> <th>Hello</th> <td headers=\"h1\" aria-hidden=\"true\">goodbye</td> </tr>' +\n        '</table>'\n    );\n    var node = fixture.querySelector('table');\n    assert.isTrue(check.call(checkContext, node));\n  });\n});\n"
  },
  {
    "path": "test/checks/tables/th-has-data-cells.js",
    "content": "describe('th-has-data-cells', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowSupport = axe.testUtils.shadowSupport.v1;\n  const checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(() => {\n    checkContext.reset();\n  });\n\n  it('should return true each row header has a non-empty cell', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> <td>hello</td> </tr>' +\n      '  <tr> <th>hi</th> <td>hello</td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true each non-empty column header has a cell', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>H</th> <th>H</th> </tr>' +\n      '  <tr> <td>hi</td> <td>hello</td></tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true if referred to with headers attr', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td headers=\"a\">hi</td> <td headers=\"b\">hello</td></tr>' +\n      '  <tr> <th id=\"a\">H</th> <th id=\"b\">H</th> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true if referred to with aria-labelledby', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <td aria-labelledby=\"a\">hi</td> <td aria-labelledby=\"b\">hello</td></tr>' +\n      '  <tr> <th id=\"a\">H</th> <th id=\"b\">H</th> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true if the th element is empty', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th></th> <th></th> </tr>' +\n      '  <tr> <th></th> <th></th> </tr>' +\n      '</table>';\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true when the td has a content element', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> <td><input type=\"text\"></td> </tr>' +\n      '  <tr> <th>hi</th> <td><textarea></textarea></td> </tr>' +\n      '  <tr> <th>hi</th> <td><select><option>a</option></select></td> </tr>' +\n      '  <tr> <th>hi</th> <td><img src=\"\" alt=\"foo\"></td> </tr>' +\n      '  <tr> <th>hi</th> <td><video></video></td> </tr>' +\n      '  <tr> <th>hi</th> <td><audio></audio></td> </tr>' +\n      '  <tr> <th>hi</th> <td><span role=\"img\"></span></td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return undefined if a th has no data cells', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> </tr>' +\n      '  <tr> <th>hi</th> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return true if all data cells are empty', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '  <tr> <th>hi</th> <td></td> </tr>' +\n      '  <tr> <th>hi</th> <td></td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return undefined if a td with role=columnheader is used that has no data cells', () => {\n    fixture.innerHTML =\n      '<table id=\"fail4\">' +\n      '  <tr> <td>axe</td> <td role=\"columnheader\">AXE</th> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  it('should return undefined if table cell points to a different header', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr>' +\n      '<th id=\"col1\">Column 1</th>' +\n      '<th id=\"Column2\">Column 2</th>' +\n      '</tr>' +\n      '<tr>' +\n      '<td></td>' +\n      '<td headers=\"col1\"></td>' +\n      '</tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = fixture.querySelector('table');\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n\n  (shadowSupport ? it : xit)('recognizes shadow tree content', () => {\n    fixture.innerHTML = '<div id=\"shadow\"> <b>data</b> </div>';\n    const shadow = fixture\n      .querySelector('#shadow')\n      .attachShadow({ mode: 'open' });\n    shadow.innerHTML =\n      '<table>' +\n      '  <tr> <th> header </th> </tr>' +\n      '  <tr> <td><slot></slot></td> </tr>' +\n      '</table>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    const node = axe.utils.querySelectorAll(axe._tree, 'table')[0].actualNode;\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('th-has-data-cells')\n        .call(checkContext, node)\n    );\n  });\n});\n"
  },
  {
    "path": "test/checks/theme.css",
    "content": ""
  },
  {
    "path": "test/checks/visibility/hidden-content.js",
    "content": "/* global xit */\ndescribe('hidden content', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n  var checkSetup = axe.testUtils.checkSetup;\n  var checkContext = axe.testUtils.MockCheckContext();\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    checkContext.reset();\n    axe._tree = undefined;\n  });\n\n  it('should return undefined with display:none and children', function () {\n    var params = checkSetup(\n      '<div id=\"target\" style=\"display: none;\"><p>Some paragraph text.</p></div>'\n    );\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('hidden-content')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return undefined with visibility:hidden and children', function () {\n    var params = checkSetup(\n      '<div id=\"target\" style=\"visibility: hidden;\"><p>Some paragraph text.</p></div>'\n    );\n    assert.isUndefined(\n      axe.testUtils\n        .getCheckEvaluate('hidden-content')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true with visibility:hidden and parent with visibility:hidden', function () {\n    var params = checkSetup(\n      '<div style=\"visibility: hidden;\"><p id=\"target\" style=\"visibility: hidden;\">Some paragraph text.</p></div>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('hidden-content')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should return true with aria-hidden and no content', function () {\n    var params = checkSetup(\n      '<span id=\"target\" class=\"icon\" aria-hidden=\"true\"></span>'\n    );\n    assert.isTrue(\n      axe.testUtils\n        .getCheckEvaluate('hidden-content')\n        .apply(checkContext, params)\n    );\n  });\n\n  it('should skip whitelisted elements', function () {\n    var node = document.querySelector('head');\n    axe.testUtils.flatTreeSetup(document.documentElement);\n    var virtualNode = axe.utils.getNodeFromTree(node);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('hidden-content')(\n        node,\n        undefined,\n        virtualNode\n      )\n    );\n  });\n\n  (shadowSupport ? it : xit)('works on elements in a shadow DOM', function () {\n    fixture.innerHTML = '<div id=\"shadow\"> <div id=\"content\">text</div> </div>';\n    var shadowRoot = document\n      .getElementById('shadow')\n      .attachShadow({ mode: 'open' });\n    shadowRoot.innerHTML =\n      '<div id=\"target\" style=\"display:none\">' + '<slot></slot>' + '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n\n    var shadow = document.querySelector('#shadow');\n    var virtualShadow = axe.utils.getNodeFromTree(shadow);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('hidden-content')(\n        shadow,\n        undefined,\n        virtualShadow\n      )\n    );\n\n    var target = shadowRoot.querySelector('#target');\n    var virtualTarget = axe.utils.getNodeFromTree(target);\n    assert.isUndefined(\n      axe.testUtils.getCheckEvaluate('hidden-content')(\n        target,\n        undefined,\n        virtualTarget\n      )\n    );\n\n    var content = document.querySelector('#content');\n    var virtualContent = axe.utils.getNodeFromTree(content);\n    assert.isTrue(\n      axe.testUtils.getCheckEvaluate('hidden-content')(\n        content,\n        undefined,\n        virtualContent\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/allowed-attr.js",
    "content": "describe('aria.allowedAttr', function () {\n  'use strict';\n\n  var globalAttrs;\n  before(function () {\n    axe._load({});\n    globalAttrs = axe.commons.standards.getGlobalAriaAttrs();\n  });\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should returned the attributes property for the proper role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            allowedAttrs: ['hello']\n          }\n        }\n      }\n    });\n\n    assert.deepEqual(\n      axe.commons.aria.allowedAttr('cats'),\n      globalAttrs.concat(['hello'])\n    );\n  });\n\n  it('should also check required attributes', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredAttrs: ['hello'],\n            allowedAttrs: ['ok']\n          }\n        }\n      }\n    });\n\n    assert.deepEqual(\n      axe.commons.aria.allowedAttr('cats'),\n      globalAttrs.concat(['ok', 'hello'])\n    );\n  });\n\n  it('should return an array with globally allowed attributes', function () {\n    assert.deepEqual(axe.commons.aria.allowedAttr('cats'), globalAttrs);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/arialabel-text.js",
    "content": "describe('aria.arialabelText', function () {\n  'use strict';\n  var aria = axe.commons.aria;\n\n  it('returns \"\" if there is no aria-label', function () {\n    var vNode = new axe.SerialVirtualNode({ nodeName: 'div' });\n    assert.equal(aria.arialabelText(vNode), '');\n  });\n\n  it('returns the aria-label attribute', function () {\n    var label = ' my label ';\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: { 'aria-label': label }\n    });\n    assert.equal(aria.arialabelText(vNode), label);\n  });\n\n  it('returns \"\" if there is no aria-label', function () {\n    var vNode = new axe.SerialVirtualNode({ nodeName: 'div' });\n    assert.equal(aria.arialabelText(vNode), '');\n  });\n\n  it('looks up the node in the flat tree', function () {\n    var label = 'harambe';\n    var node = document.createElement('div');\n    node.setAttribute('aria-label', label);\n\n    axe.utils.getFlattenedTree(node);\n    assert.equal(aria.arialabelText(node), label);\n  });\n\n  it('returns \"\" if the node is not an element', function () {\n    var node = document.createTextNode('my text node');\n    assert.equal(aria.arialabelText(node), '');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/arialabelledby-text.js",
    "content": "describe('aria.arialabelledbyText', function () {\n  'use strict';\n  var aria = axe.commons.aria;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  it('returns the accessible name of the aria-labelledby references', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, 'Foo text');\n  });\n\n  it('works with virtual nodes', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, 'Foo text');\n  });\n\n  it('returns references in order', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"bar baz foo\"></div>' +\n        '<div id=\"foo\">Foo</div>' +\n        '<div id=\"bar\">Bar</div>' +\n        '<div id=\"baz\">Baz</div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, 'Bar Baz Foo');\n  });\n\n  it('returns \"\" if the node is not an element', function () {\n    var target = queryFixture('<div id=\"target\">foo</div>');\n    var accName = aria.arialabelledbyText(target.actualNode.firstChild);\n    assert.equal(accName, '');\n  });\n\n  it('returns \"\" with context.inLabelledByContext: true', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target, {\n      inLabelledByContext: true\n    });\n    assert.equal(accName, '');\n  });\n\n  it('returns \"\" with context.inControlContext: true', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target, {\n      inControlContext: true\n    });\n    assert.equal(accName, '');\n  });\n\n  it('returns content of a aria-hidden reference', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\" aria-hidden=\"true\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, 'Foo text');\n  });\n\n  it('returns content of a `display:none` reference', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\" style=\"display:none\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, 'Foo text');\n  });\n\n  it('returns does not return hidden content of a visible reference', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\"><div style=\"display:none\">Foo text</div></div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, '');\n  });\n\n  it('does not follow more than one aria-labelledy reference', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\"><div aria-labelledby=\"bar\" role=\"heading\"></div></div>' +\n        '<div id=\"bar\">Foo text</div>'\n    );\n    var accName = aria.arialabelledbyText(target, {\n      inControlContext: true\n    });\n    assert.equal(accName, '');\n  });\n\n  it('preserves spacing', function () {\n    var target = queryFixture(\n      '<div role=\"heading\" id=\"target\" aria-labelledby=\"foo\"></div>' +\n        '<div id=\"foo\"> \\t Foo \\n text \\t </div>'\n    );\n    var accName = aria.arialabelledbyText(target);\n    assert.equal(accName, ' \\t Foo \\n text \\t ');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-accessible-refs.js",
    "content": "describe('aria.getAccessibleRefs', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var getAccessibleRefs = axe.commons.aria.getAccessibleRefs;\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n\n  function setLookup(attrs) {\n    axe.configure({\n      standards: {\n        ariaAttrs: attrs\n      }\n    });\n  }\n\n  before(function () {\n    axe._load({});\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe.reset();\n  });\n\n  it('returns empty array by default', function () {\n    fixture.innerHTML = '<div id=\"foo\"><div>';\n    var node = document.getElementById('foo');\n    assert.lengthOf(getAccessibleRefs(node), 0);\n  });\n\n  it('returns array of nodes for IDs used in aria IDREF attributes', function () {\n    setLookup({ 'aria-foo': { type: 'idref' } });\n    fixture.innerHTML = '<div id=\"ref\" aria-foo=\"foo\"></div><i id=\"foo\"></i>';\n    var node = document.getElementById('foo');\n    var ref = document.getElementById('ref');\n    assert.deepEqual(getAccessibleRefs(node), [ref]);\n  });\n\n  it('returns array of nodes for IDs used in aria IDREFS attributes', function () {\n    setLookup({ 'aria-bar': { type: 'idrefs' } });\n    fixture.innerHTML =\n      '<div id=\"ref\" aria-bar=\"foo bar\"></div><i id=\"foo\"></i><b id=\"bar\"></b>';\n\n    var node1 = document.getElementById('foo');\n    var node2 = document.getElementById('bar');\n    var ref = document.getElementById('ref');\n    assert.deepEqual(getAccessibleRefs(node1), [ref]);\n    assert.deepEqual(getAccessibleRefs(node2), [ref]);\n  });\n\n  it('returns array of nodes for IDs used in label[for] attributes', function () {\n    setLookup({ 'aria-foo': { type: 'idref' } });\n    fixture.innerHTML = '<label id=\"ref\" for=\"baz\">baz</label><input id=\"baz\">';\n    var node = document.getElementById('baz');\n    var ref = document.getElementById('ref');\n    assert.deepEqual(getAccessibleRefs(node), [ref]);\n  });\n\n  it('returns all nodes used in aria IDREF attributes', function () {\n    setLookup({ 'aria-bar': { type: 'idrefs' } });\n    fixture.innerHTML =\n      '<div id=\"ref1\" aria-bar=\"foo\"><div id=\"ref2\" aria-bar=\"foo\"></div><i id=\"foo\"></i>';\n\n    var node = document.getElementById('foo');\n    var ref1 = document.getElementById('ref1');\n    var ref2 = document.getElementById('ref2');\n\n    assert.deepEqual(getAccessibleRefs(node), [ref1, ref2]);\n  });\n\n  it('does not break on a custom .children property', function () {\n    setLookup({ 'aria-foo': { type: 'idref' } });\n    fixture.innerHTML = '<div id=\"ref\" aria-foo=\"foo\"></div><i id=\"foo\"></i>';\n    var node = document.getElementById('foo');\n    var ref = document.getElementById('ref');\n\n    Object.defineProperty(node, 'children', {\n      value: ['#ref']\n    });\n    assert.deepEqual(getAccessibleRefs(node), [ref]);\n  });\n\n  describe('when JavaScript object names are used as IDs', function () {\n    const ids = [\n      'prototype',\n      'constructor',\n      '__proto__',\n      'Element',\n      'nodeName',\n      'valueOf',\n      'toString'\n    ];\n    for (const id of ids) {\n      it(`does not break with id=\"${id}\"`, function () {\n        setLookup({ 'aria-bar': { type: 'idrefs' } });\n        fixture.innerHTML = `<div id=\"ref\" aria-bar=\"${ids.join(\n          ' '\n        )}\"></div><i id=\"${id}\"></i>`;\n\n        var node = document.getElementById(id);\n        var ref = document.getElementById('ref');\n        assert.deepEqual(\n          getAccessibleRefs(node),\n          [ref],\n          `Not equal for ID ${id}`\n        );\n      });\n    }\n  });\n\n  (shadowSupport ? it : xit)('works inside shadow DOM', function () {\n    setLookup({ 'aria-bar': { type: 'idref' } });\n    fixture.innerHTML = '<div id=\"foo\"></div>';\n\n    var shadow = document.getElementById('foo').attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<div id=\"ref\" aria-bar=\"bar\"></div><b id=\"bar\"></b>';\n\n    var node = shadow.getElementById('bar');\n    var ref = shadow.getElementById('ref');\n    assert.deepEqual(getAccessibleRefs(node), [ref]);\n  });\n\n  (shadowSupport ? it : xit)(\n    'returns empty array for IDREFs inside shadow DOM',\n    function () {\n      setLookup({ 'aria-foo': { type: 'idrefs' } });\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      var node1 = document.getElementById('foo');\n      var node2 = document.getElementById('bar');\n\n      var shadow = node1.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div aria-foo=\"foo bar\"><slot></slot></div>';\n\n      assert.lengthOf(getAccessibleRefs(node1), 0);\n      assert.lengthOf(getAccessibleRefs(node2), 0);\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'returns empty array for IDREFs outside shadow DOM',\n    function () {\n      setLookup({ 'aria-bar': { type: 'idref' } });\n      fixture.innerHTML =\n        '<div id=\"foo\" aria-bar=\"bar\"><div aria-bar=\"bar\"></div></div>';\n\n      var shadow = document\n        .getElementById('foo')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div id=\"bar\"><slot></slot></div>';\n\n      var node = shadow.getElementById('bar');\n      assert.lengthOf(getAccessibleRefs(node), 0);\n    }\n  );\n\n  (shadowSupport ? it : xit)('separates IDREFs by roots', function () {\n    setLookup({ 'aria-bar': { type: 'idref' } });\n    fixture.innerHTML =\n      '<div id=\"foo\"></div><div id=\"outside\" aria-bar=\"foo\"></div><div id=\"shadow\"></div>';\n\n    var shadow = document\n      .getElementById('shadow')\n      .attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<div id=\"foo\"><div id=\"inside\" aria-bar=\"foo\"></div>';\n\n    var outsideNode = document.getElementById('foo');\n    var outsideRef = document.getElementById('outside');\n    var insideNode = shadow.getElementById('foo');\n    var insideRef = shadow.getElementById('inside');\n    assert.deepEqual(getAccessibleRefs(outsideNode), [outsideRef]);\n    assert.deepEqual(getAccessibleRefs(insideNode), [insideRef]);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-element-unallowed-roles.js",
    "content": "describe('aria.getElementUnallowedRoles', function () {\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n  var getElementUnallowedRoles = axe.commons.aria.getElementUnallowedRoles;\n\n  it('returns unallowed role=application when used on a input elm', function () {\n    var node = document.createElement('input');\n    var role = 'application';\n    node.setAttribute('type', '');\n    node.setAttribute('aria-pressed', '');\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isNotEmpty(actual);\n    assert.include(actual, role);\n  });\n\n  it('returns empty on type=checkbox and aria-pressed attr on input elm', function () {\n    var node = document.createElement('input');\n    node.setAttribute('type', 'checkbox');\n    node.setAttribute('aria-pressed', '');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=menubar when used on a li elm', function () {\n    var node = document.createElement('li');\n    var role = 'menubar';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isNotEmpty(actual, role);\n  });\n\n  it('returns empty on role=menuitemcheckbox with type=button on input elm', function () {\n    var node = document.createElement('input');\n    var role = 'menuitemcheckbox';\n    node.setAttribute('role', role);\n    node.setAttribute('type', 'button');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=option when used on section elm', function () {\n    var node = document.createElement('section');\n    var role = 'option';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isNotEmpty(actual);\n    assert.include(actual, role);\n  });\n\n  it('returns empty on role=menuitemradio and type=radio on input elm', function () {\n    var node = document.createElement('input');\n    var role = 'menuitemradio';\n    node.setAttribute('role', role);\n    node.setAttribute('type', 'radio');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=textbox on a input elm and  allowImplicit is true (default)', function () {\n    var node = document.createElement('input');\n    var role = 'textbox';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, true);\n    assert.isEmpty(actual, role);\n  });\n\n  it('returns empty on role=button on div elm when role is not implicit and allowImplicit: false', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'button');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=contentinfo on footer elm and allowImplicit: false', function () {\n    var node = document.createElement('footer');\n    node.setAttribute('role', 'contentinfo');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isNotEmpty(actual, 'contentinfo');\n  });\n\n  it('returns unallowed role=banner on header elm and allowImplicit:false', function () {\n    var node = document.createElement('header');\n    node.setAttribute('role', 'banner');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isNotEmpty(actual, 'banner');\n  });\n\n  it('returns empty on role=contentinfo on footer elm when allowImplicit:true', function () {\n    var node = document.createElement('footer');\n    node.setAttribute('role', 'contentinfo');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns empty on role=banner on header elm when allowImplicit:true', function () {\n    var node = document.createElement('header');\n    node.setAttribute('role', 'banner');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns empty role=doc-backlink on anchor elm and allowImplicit:false', function () {\n    var node = document.createElement('a');\n    node.setAttribute('href', '#');\n    node.setAttribute('role', 'doc-backlink');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isEmpty(actual);\n  });\n\n  it('returns empty on role=doc-backlink on anchor elm when allowImplicit:true', function () {\n    var node = document.createElement('a');\n    node.setAttribute('href', '#');\n    node.setAttribute('role', 'doc-backlink');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=doc-backlink on anchor elm without href attr and allowImplicit:false', function () {\n    var node = document.createElement('a');\n    node.setAttribute('role', 'doc-backlink');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isNotEmpty(actual, 'doc-backlink');\n  });\n\n  it('returns unallowed role=doc-backlink on anchor elm without href attr and allowImplicit:true', function () {\n    var node = document.createElement('a');\n    node.setAttribute('role', 'doc-backlink');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node);\n    assert.isNotEmpty(actual, 'doc-backlink');\n  });\n\n  it('returns empty role=banner on header elm when using axe.configure and allowImplicit:false', function () {\n    axe.configure({\n      standards: {\n        htmlElms: {\n          header: {\n            contentTypes: 'flow',\n            allowedRoles: ['banner']\n          }\n        }\n      }\n    });\n    var node = document.createElement('header');\n    node.setAttribute('role', 'banner');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isEmpty(actual);\n  });\n\n  it('returns empty role=contentinfo on footer elm when using axe.configure and allowImplicit:false', function () {\n    axe.configure({\n      standards: {\n        htmlElms: {\n          footer: {\n            contentTypes: 'flow',\n            allowedRoles: ['contentinfo']\n          }\n        }\n      }\n    });\n    var node = document.createElement('footer');\n    node.setAttribute('role', 'contentinfo');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=row when when used on TR element and allowImplicit:false', function () {\n    var node = document.createElement('tr');\n    node.setAttribute('role', 'row');\n    flatTreeSetup(node);\n    var actual = getElementUnallowedRoles(node, false);\n    assert.isNotEmpty(actual, 'row');\n  });\n\n  it('returns empty on type=checkbox and aria-pressed attr on SerialVirtualNode with a input elm', function () {\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'checkbox',\n        'aria-pressed': ''\n      }\n    });\n    var actual = getElementUnallowedRoles(vNode);\n    assert.isEmpty(actual);\n  });\n\n  it('returns unallowed role=application for a SerialVirtualNode with a input elm', function () {\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        role: 'application',\n        type: '',\n        'aria-pressed': ''\n      }\n    });\n    var actual = getElementUnallowedRoles(vNode);\n    assert.isNotEmpty(actual);\n    assert.include(actual, 'application');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-explicit-role.js",
    "content": "describe('aria.getExplicitRole', () => {\n  const aria = axe.commons.aria;\n  const roleDefinitions = aria.lookupTable.role;\n  const flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns valid roles', () => {\n    const node = document.createElement('div');\n    node.setAttribute('role', 'button');\n    const vNode = flatTreeSetup(node)[0];\n    assert.equal(aria.getExplicitRole(vNode), 'button');\n  });\n\n  it('handles case sensitivity', () => {\n    const node = document.createElement('div');\n    node.setAttribute('role', 'BUTTON');\n    const vNode = flatTreeSetup(node)[0];\n    assert.equal(aria.getExplicitRole(vNode), 'button');\n  });\n\n  it('handles whitespacing', () => {\n    const node = document.createElement('div');\n    node.setAttribute('role', ' button  ');\n    const vNode = flatTreeSetup(node)[0];\n    assert.equal(aria.getExplicitRole(vNode), 'button');\n  });\n\n  it('returns null when there is no role', () => {\n    const node = document.createElement('div');\n    const vNode = flatTreeSetup(node)[0];\n    assert.isNull(aria.getExplicitRole(vNode));\n  });\n\n  it('returns the explicit role if it is valid and non-abstract', () => {\n    const node = document.createElement('li');\n    node.setAttribute('role', 'menuitem');\n    const vNode = flatTreeSetup(node)[0];\n    assert.equal(aria.getExplicitRole(vNode), 'menuitem');\n  });\n\n  it('ignores fallback roles by default', () => {\n    const node = document.createElement('div');\n    node.setAttribute('role', 'spinbutton button');\n    const vNode = flatTreeSetup(node)[0];\n    assert.isNull(aria.getExplicitRole(vNode));\n  });\n\n  it('returns null if the node is not an element', () => {\n    const node = document.createTextNode('foo bar baz');\n    const vNode = flatTreeSetup(node)[0];\n    assert.isNull(aria.getExplicitRole(vNode));\n  });\n\n  describe('abstracts', () => {\n    it('ignores abstract roles by default', () => {\n      const node = document.createElement('li');\n      node.setAttribute('role', 'section');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(roleDefinitions.section.type, 'abstract');\n      assert.isNull(aria.getExplicitRole(vNode));\n    });\n\n    it('returns abstract roles with `abstracts: true`', () => {\n      const node = document.createElement('li');\n      node.setAttribute('role', 'section');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(roleDefinitions.section.type, 'abstract');\n      assert.equal(aria.getExplicitRole(vNode, { abstracts: true }), 'section');\n    });\n\n    it('does not returns abstract roles with `abstracts: false`', () => {\n      const node = document.createElement('li');\n      node.setAttribute('role', 'section');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(roleDefinitions.section.type, 'abstract');\n      assert.isNull(aria.getExplicitRole(vNode, { abstracts: false }));\n    });\n  });\n\n  describe('dpub', () => {\n    it('ignores DPUB roles by default', () => {\n      const node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      const vNode = flatTreeSetup(node)[0];\n      assert.isNull(aria.getExplicitRole(vNode));\n    });\n\n    it('returns DPUB roles with `dpub: true`', () => {\n      const node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(aria.getExplicitRole(vNode, { dpub: true }), 'doc-chapter');\n    });\n\n    it('does not returns DPUB roles with `dpub: false`', () => {\n      const node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      const vNode = flatTreeSetup(node)[0];\n      assert.isNull(aria.getExplicitRole(vNode, { dpub: false }));\n    });\n  });\n\n  describe('fallback', () => {\n    it('returns the first valid item in the list', () => {\n      const node = document.createElement('div');\n      node.setAttribute('role', 'link button');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(aria.getExplicitRole(vNode, { fallback: true }), 'link');\n    });\n\n    it('skips over invalid roles', () => {\n      const node = document.createElement('div');\n      node.setAttribute('role', 'foobar button');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(aria.getExplicitRole(vNode, { fallback: true }), 'button');\n    });\n\n    it('returns the null if all roles are invalid and there is no implicit role', () => {\n      const node = document.createElement('div');\n      node.setAttribute('role', 'foo bar baz');\n      const vNode = flatTreeSetup(node)[0];\n      assert.isNull(aria.getExplicitRole(vNode, { fallback: true }));\n    });\n\n    it('respect the `abstracts` option', () => {\n      const node = document.createElement('li');\n      node.setAttribute('role', 'doc-chapter section');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(\n        aria.getExplicitRole(vNode, { fallback: true, abstracts: true }),\n        'section'\n      );\n    });\n\n    it('respect the `dpub` option', () => {\n      const node = document.createElement('li');\n      node.setAttribute('role', 'doc-chapter section');\n      const vNode = flatTreeSetup(node)[0];\n      assert.equal(\n        aria.getExplicitRole(vNode, { fallback: true, dpub: true }),\n        'doc-chapter'\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-owned-virtual.js",
    "content": "describe('aria.getOwnedVirtual', function () {\n  'use strict';\n  var aria = axe.commons.aria;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  it('returns a list of children in order', function () {\n    fixtureSetup(\n      '<div id=\"target\">' +\n        '<h1>heading 1</h1>' +\n        '<h2>heading 2</h2>' +\n        '<h3>heading 3</h3>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 3);\n    assert.equal(owned[0].actualNode.nodeName.toUpperCase(), 'H1');\n    assert.equal(owned[1].actualNode.nodeName.toUpperCase(), 'H2');\n    assert.equal(owned[2].actualNode.nodeName.toUpperCase(), 'H3');\n  });\n\n  it('adds aria-owned reffed elements to the children', function () {\n    fixtureSetup(\n      '<div id=\"target\" aria-owns=\"hdr3 hdr4\">' +\n        '<h1>heading 1</h1>' +\n        '<h2>heading 2</h2>' +\n        '</div>' +\n        '<h4 id=\"hdr4\">heading 4</h4>' +\n        '<h3 id=\"hdr3\">heading 3</h3>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 4);\n    assert.equal(owned[0].actualNode.nodeName.toUpperCase(), 'H1');\n    assert.equal(owned[1].actualNode.nodeName.toUpperCase(), 'H2');\n    assert.equal(owned[2].actualNode.nodeName.toUpperCase(), 'H3');\n    assert.equal(owned[3].actualNode.nodeName.toUpperCase(), 'H4');\n  });\n\n  it('does not return duplicate when child is also aria-owned', function () {\n    fixtureSetup(\n      '<div role=\"tablist\" id=\"target\" aria-owns=\"foo\">' +\n        '<div id=\"foo\" role=\"menuitem\">foo</div>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 1);\n    assert.equal(owned[0].actualNode.id, 'foo');\n  });\n\n  it('does not return duplicate when same ID appears multiple times in aria-owns', function () {\n    fixtureSetup(\n      '<div role=\"list\" id=\"target\" aria-owns=\"a b a\">' +\n        '</div>' +\n        '<div role=\"listitem\" id=\"a\">A</div>' +\n        '<div role=\"listitem\" id=\"b\">B</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 2);\n    assert.equal(owned[0].actualNode.id, 'a');\n    assert.equal(owned[1].actualNode.id, 'b');\n  });\n\n  it('moves aria-owned child to the end', function () {\n    fixtureSetup(\n      '<div role=\"list\" id=\"target\" aria-owns=\"a\">' +\n        '<div role=\"listitem\" id=\"a\">A</div>' +\n        '<div role=\"listitem\" id=\"b\">B</div>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 2);\n    assert.equal(owned[0].actualNode.id, 'b');\n    assert.equal(owned[1].actualNode.id, 'a');\n  });\n\n  it('moves multiple aria-owned children to the end in aria-owns order', function () {\n    fixtureSetup(\n      '<div role=\"list\" id=\"target\" aria-owns=\"c a\">' +\n        '<div role=\"listitem\" id=\"a\">A</div>' +\n        '<div role=\"listitem\" id=\"b\">B</div>' +\n        '<div role=\"listitem\" id=\"c\">C</div>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 3);\n    assert.equal(owned[0].actualNode.id, 'b');\n    assert.equal(owned[1].actualNode.id, 'c');\n    assert.equal(owned[2].actualNode.id, 'a');\n  });\n\n  it('ignores whitespace-only aria-owned', function () {\n    fixtureSetup(\n      '<div id=\"target\" aria-owns=\"  \">' +\n        '<h1>heading 1</h1>' +\n        '<h2>heading 2</h2>' +\n        '<h3>heading 3</h3>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 3);\n    assert.equal(owned[0].actualNode.nodeName.toUpperCase(), 'H1');\n    assert.equal(owned[1].actualNode.nodeName.toUpperCase(), 'H2');\n    assert.equal(owned[2].actualNode.nodeName.toUpperCase(), 'H3');\n  });\n\n  it('ignores broken aria-owned refs', function () {\n    fixtureSetup(\n      '<div id=\"target\" aria-owns=\"nonexisting reference\">' +\n        '<h1>heading 1</h1>' +\n        '<h2>heading 2</h2>' +\n        '<h3>heading 3</h3>' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 3);\n    assert.equal(owned[0].actualNode.nodeName.toUpperCase(), 'H1');\n    assert.equal(owned[1].actualNode.nodeName.toUpperCase(), 'H2');\n    assert.equal(owned[2].actualNode.nodeName.toUpperCase(), 'H3');\n  });\n\n  it('includes text nodes', function () {\n    fixtureSetup(\n      '<div id=\"target\" aria-owns=\"nonexisting reference\">' +\n        'text 1' +\n        '<h1>heading 1</h1>' +\n        'text 2' +\n        '<h2>heading 2</h2>' +\n        ' \\t\\n' +\n        '</div>'\n    );\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n\n    assert.lengthOf(owned, 5);\n    assert.equal(owned[0].actualNode.textContent, 'text 1');\n    assert.equal(owned[1].actualNode.textContent, 'heading 1');\n    assert.equal(owned[2].actualNode.textContent, 'text 2');\n    assert.equal(owned[3].actualNode.textContent, 'heading 2');\n    assert.equal(owned[4].actualNode.textContent, ' \\t\\n');\n  });\n\n  it('returns an empty array if there are no owned elements', function () {\n    fixtureSetup('<div id=\"target\" aria-owns=\"nonexisting reference\"></div>');\n    var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n    var owned = aria.getOwnedVirtual(target);\n    assert.lengthOf(owned, 0);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-role-type.js",
    "content": "describe('aria.getRoleType', function () {\n  'use strict';\n  var queryFixture = axe.testUtils.queryFixture;\n  var getRoleType = axe.commons.aria.getRoleType;\n\n  beforeEach(function () {\n    axe._load({});\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            type: 'stuff'\n          }\n        }\n      }\n    });\n  });\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should return the type from the lookup table', function () {\n    assert.equal(getRoleType('cats'), 'stuff');\n  });\n\n  it('should return null if role is not found in the lookup table', function () {\n    assert.isNull(getRoleType('dogs'));\n  });\n\n  it('should return null when passed null', function () {\n    assert.isNull(getRoleType(null));\n  });\n\n  it('should return null when passed undefined', function () {\n    assert.isNull(getRoleType(undefined));\n  });\n\n  it('returns the type from the role of a virtual node', function () {\n    var vNode = queryFixture('<span id=\"target\" role=\"cats\"></span>');\n    assert.equal(getRoleType(vNode), 'stuff');\n  });\n\n  it('returns the type from the role of a DOM node', function () {\n    var domNode = queryFixture(\n      '<span id=\"target\" role=\"cats\"></span>'\n    ).actualNode;\n    assert.equal(getRoleType(domNode), 'stuff');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-role.js",
    "content": "describe('aria.getRole', function () {\n  'use strict';\n  var aria = axe.commons.aria;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n  var fixture = document.querySelector('#fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns valid roles', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'button');\n    flatTreeSetup(node);\n    assert.equal(aria.getRole(node), 'button');\n  });\n\n  it('handles case sensitivity', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'BUTTON');\n    flatTreeSetup(node);\n    assert.equal(aria.getRole(node), 'button');\n  });\n\n  it('handles whitespacing', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', ' button  ');\n    flatTreeSetup(node);\n    assert.equal(aria.getRole(node), 'button');\n  });\n\n  it('returns null when there is no role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    assert.isNull(aria.getRole(node));\n  });\n\n  it('returns the explit role if it is valid and non-abstract', function () {\n    var node = document.createElement('li');\n    node.setAttribute('role', 'menuitem');\n    flatTreeSetup(node);\n    assert.equal(aria.getRole(node), 'menuitem');\n  });\n\n  it('returns the implicit role if the explicit is invalid', function () {\n    fixture.innerHTML = '<ul><li id=\"target\" role=\"foobar\"></li></ul>';\n    flatTreeSetup(fixture);\n    var node = fixture.querySelector('#target');\n    assert.equal(aria.getRole(node), 'listitem');\n  });\n\n  it('ignores fallback roles by default', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'spinbutton button');\n    flatTreeSetup(node);\n    assert.isNull(aria.getRole(node));\n  });\n\n  it('accepts virtualNode objects', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'button');\n    var vNode = flatTreeSetup(node)[0];\n    assert.equal(aria.getRole(vNode), 'button');\n  });\n\n  it('returns null if the node is not an element', function () {\n    var node = document.createTextNode('foo bar baz');\n    flatTreeSetup(node);\n    assert.isNull(aria.getRole(node));\n  });\n\n  it('runs role resolution with role=none', function () {\n    fixture.innerHTML =\n      '<ul><li id=\"target\" role=\"none\" aria-label=\"foo\"></li></ul>';\n    flatTreeSetup(fixture);\n    var node = fixture.querySelector('#target');\n    assert.equal(aria.getRole(node), 'listitem');\n  });\n\n  it('runs role resolution with role=presentation', function () {\n    fixture.innerHTML =\n      '<ul><li id=\"target\" role=\"presentation\" aria-label=\"foo\"></li></ul>';\n    flatTreeSetup(fixture);\n    var node = fixture.querySelector('#target');\n    assert.equal(aria.getRole(node), 'listitem');\n  });\n\n  it('handles focusable element with role=\"none\"', function () {\n    fixture.innerHTML = '<button id=\"target\" role=\"none\"></button>';\n    flatTreeSetup(fixture);\n    var node = fixture.querySelector('#target');\n    assert.equal(aria.getRole(node), 'button');\n  });\n\n  describe('presentational role inheritance', function () {\n    it('handles presentation role inheritance for ul', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\"><li id=\"target\">foo</li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for ol', function () {\n      fixture.innerHTML =\n        '<ol role=\"presentation\"><li id=\"target\">foo</li></ol>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for dt', function () {\n      fixture.innerHTML =\n        '<dl role=\"presentation\"><dt id=\"target\">foo</dt><dd>bar></dd></dl>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for dd', function () {\n      fixture.innerHTML =\n        '<dl role=\"presentation\"><dt>foo</dt><dd id=\"target\">bar></dd></dl>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for dt with div wrapper', function () {\n      fixture.innerHTML =\n        '<dl role=\"presentation\"><div><dt id=\"target\">foo</dt><dd>bar></dd></div></dl>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for dd with div wrapper', function () {\n      fixture.innerHTML =\n        '<dl role=\"presentation\"><div><dt>foo</dt><dd id=\"target\">bar></dd></div></dl>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for thead', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead id=\"target\"><tr><th>hi</th><th>goodbye</th></tr></thead><tbody><tr><th>hi</th><td>foo</td></tr></tbody></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for td', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead><tr><th>hi</th><th>goodbye</th></tr></thead><tbody><tr><th>hi</th><td id=\"target\">foo</td></tr></tbody></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for th', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead><tr><th>hi</th><th>goodbye</th></tr></thead><tbody><tr><th id=\"target\">hi</th><td>foo</td></tr></tbody></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for tbody', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead><tr><th>hi</th><th>goodbye</th></tr></thead><tbody id=\"target\"><tr><th>hi</th><td>foo</td></tr></tbody></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for tr', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead><tr id=\"target\"><th>hi</th><th>goodbye</th></tr></thead><tbody><tr><th>hi</th><td>foo</td></tr></tbody></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('handles presentation role inheritance for tfoot', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><thead><tr><th>hi</th><th>goodbye</th></tr></thead><tfoot id=\"target\"><tr><th>hi</th><td>foo</td></tr></tfoot></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('returns implicit role for presentation role inheritance if ancestor is not the required ancestor', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><tr><td><ul><li id=\"target\">foo</li></ul></td></tr></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'listitem');\n    });\n\n    it('does not override explicit role with presentation role inheritance', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\"><li id=\"target\" role=\"listitem\">foo</li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'listitem');\n    });\n\n    it('does not continue presentation role with explicit role in between', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><tr role=\"row\"><td id=\"target\">foo</td></tr></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'cell');\n    });\n\n    it('handles presentation role inheritance with invalid role in between', function () {\n      fixture.innerHTML =\n        '<table role=\"presentation\"><tr role=\"tablerow\"><td id=\"target\">foo</td></tr></table>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'presentation');\n    });\n\n    it('does not continue presentation role through nested layers', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\"><li><ul><li id=\"target\">foo</li></ul></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'listitem');\n    });\n\n    it('throws an error if the tree is incomplete', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\"><li id=\"target\">foo</li></ul>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(node);\n      assert.throws(function () {\n        aria.getRole(node);\n      });\n    });\n  });\n\n  describe('noImplicit', function () {\n    it('returns the implicit role by default', function () {\n      fixture.innerHTML = '<ul><li id=\"target\"></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'listitem');\n    });\n\n    it('returns null rather than the implicit role with `noImplicit: true`', function () {\n      var node = document.createElement('li');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { noImplicit: true }));\n    });\n\n    it('does not do role resolution if noImplicit: true', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'none');\n      node.setAttribute('aria-label', 'foo');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { noImplicit: true }), null);\n    });\n\n    it('still returns the explicit role', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'button');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { noImplicit: true }), 'button');\n    });\n\n    it('returns the implicit role with `noImplicit: false`', function () {\n      fixture.innerHTML = '<ul><li id=\"target\"></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node, { noImplicit: false }), 'listitem');\n    });\n  });\n\n  describe('abstracts', function () {\n    it('ignores abstract roles by default', function () {\n      fixture.innerHTML = '<ul><li id=\"target\" role=\"section\"></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node), 'listitem');\n    });\n\n    it('returns abstract roles with `abstracts: true`', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'section');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { abstracts: true }), 'section');\n    });\n\n    it('does not returns abstract roles with `abstracts: false`', function () {\n      fixture.innerHTML = '<ul><li id=\"target\" role=\"section\"></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node, { abstracts: false }), 'listitem');\n    });\n  });\n\n  describe('dpub', function () {\n    it('ignores DPUB roles by default', function () {\n      var node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node));\n    });\n\n    it('returns DPUB roles with `dpub: true`', function () {\n      var node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { dpub: true }), 'doc-chapter');\n    });\n\n    it('does not returns DPUB roles with `dpub: false`', function () {\n      var node = document.createElement('section');\n      node.setAttribute('role', 'doc-chapter');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { dpub: false }));\n    });\n  });\n\n  describe('fallback', function () {\n    it('returns the first valid item in the list', function () {\n      var node = document.createElement('div');\n      node.setAttribute('role', 'link button');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { fallback: true }), 'link');\n    });\n\n    it('skips over invalid roles', function () {\n      var node = document.createElement('div');\n      node.setAttribute('role', 'foobar button');\n      flatTreeSetup(node);\n      assert.equal(aria.getRole(node, { fallback: true }), 'button');\n    });\n\n    it('returns the null if all roles are invalid and there is no implicit role', function () {\n      var node = document.createElement('div');\n      node.setAttribute('role', 'foo bar baz');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { fallback: true }));\n    });\n\n    it('respects the defaults', function () {\n      fixture.innerHTML =\n        '<ul><li id=\"target\" role=\"doc-chapter section\"></li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.equal(aria.getRole(node, { fallback: true }), 'listitem');\n    });\n\n    it('respect the `noImplicit` option', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'doc-chapter section');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { fallback: true, noImplicit: true }));\n    });\n\n    it('respect the `abstracts` option', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'doc-chapter section');\n      flatTreeSetup(node);\n      assert.equal(\n        aria.getRole(node, { fallback: true, abstracts: true }),\n        'section'\n      );\n    });\n\n    it('respect the `dpub` option', function () {\n      var node = document.createElement('li');\n      node.setAttribute('role', 'doc-chapter section');\n      flatTreeSetup(node);\n      assert.equal(\n        aria.getRole(node, { fallback: true, dpub: true }),\n        'doc-chapter'\n      );\n    });\n  });\n\n  describe('noPresentational is honored', function () {\n    it('handles no inheritance role = presentation', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\" id=\"target\"><li>foo</li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.isNull(aria.getRole(node, { noPresentational: true }));\n    });\n\n    it('handles inheritance role = presentation', function () {\n      fixture.innerHTML =\n        '<ul role=\"presentation\"><li id=\"target\">foo</li></ul>';\n      flatTreeSetup(fixture);\n      var node = fixture.querySelector('#target');\n      assert.isNull(aria.getRole(node, { noPresentational: true }));\n    });\n\n    it('handles implicit role', function () {\n      var node = document.createElement('img');\n      node.setAttribute('alt', '');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { noPresentational: true }));\n    });\n\n    it('handles role = none', function () {\n      var node = document.createElement('div');\n      node.setAttribute('role', 'none');\n      flatTreeSetup(node);\n      assert.isNull(aria.getRole(node, { noPresentational: true }));\n    });\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('works with the SerialVirtualNode', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          role: 'button'\n        }\n      });\n      assert.equal(aria.getRole(vNode), 'button');\n    });\n\n    it('does not throw for missing parent in presentational role inheritance', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'li'\n      });\n\n      assert.doesNotThrow(function () {\n        assert.equal(aria.getRole(vNode), 'listitem');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-roles-by-type.js",
    "content": "describe('aria.getRolesByType', function () {\n  'use strict';\n\n  before(function () {\n    axe._load({});\n  });\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should return array if roletype is found in the lookup table', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          dogs: {\n            type: 'things'\n          },\n          cats: {\n            type: 'stuff'\n          }\n        }\n      }\n    });\n    assert.deepEqual(axe.commons.aria.getRolesByType('stuff'), ['cats']);\n  });\n\n  it('should return empty array if role is not found in the lookup table', function () {\n    assert.deepEqual(axe.commons.aria.getRolesByType('blahblahblah'), []);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/get-roles-with-name-from-contents.js",
    "content": "describe('aria.getRolesWithNameFromContents', function () {\n  'use strict';\n\n  before(function () {\n    axe._load({});\n  });\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should return array if nameFrom contents is found in the lookup table', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          dogs: {\n            type: 'things',\n            nameFromContent: true\n          },\n          cats: {\n            type: 'stuff'\n          }\n        }\n      }\n    });\n    assert.include(axe.commons.aria.getRolesWithNameFromContents(), 'dogs');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/implicit-role.js",
    "content": "describe('aria.implicitRole', function () {\n  'use strict';\n  var implicitRole = axe.commons.aria.implicitRole;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n  var fixture = document.querySelector('#fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  // test string role (don't need to test all of them just that\n  // one works)\n  it('should return button for button', function () {\n    fixture.innerHTML = '<button id=\"target\"></button>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'button');\n  });\n\n  it('should error if element is not in the tree', function () {\n    fixture.innerHTML = '<button id=\"target\"></button>';\n    var node = fixture.querySelector('#target');\n    assert.throws(function () {\n      implicitRole(node);\n    });\n  });\n\n  it('should return null if there is no implicit role', function () {\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null if there is no implicit role when not considering chromium', function () {\n    fixture.innerHTML = '<canvas id=\"target\" aria-label=\"hello\"></canvas>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return the chromium implicit role for elements that have one', function () {\n    fixture.innerHTML = '<canvas id=\"target\" aria-label=\"hello\"></canvas>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node, { chromium: true }), 'Canvas');\n  });\n\n  it('should return link for \"a[href]\"', function () {\n    fixture.innerHTML = '<a id=\"target\" href>link</a>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'link');\n  });\n\n  it('should return null for \"a:not([href])\"', function () {\n    fixture.innerHTML = '<a id=\"target\">link</a>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return link for \"area[href]\"', function () {\n    fixture.innerHTML = '<area id=\"target\" href>link</area>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'link');\n  });\n\n  it('should return null for \"area:not([href])\"', function () {\n    fixture.innerHTML = '<area id=\"target\">link</area>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return contentinfo for \"body footer\"', function () {\n    fixture.innerHTML = '<footer id=\"target\"></footer>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'contentinfo');\n  });\n\n  it('should return null for footer with sectioning or main parent', function () {\n    var nodes = ['article', 'aside', 'main', 'nav', 'section'];\n    var roles = ['article', 'complementary', 'main', 'navigation', 'region'];\n\n    for (var i = 0; i < nodes.length; i++) {\n      fixture.innerHTML =\n        '<' + nodes[i] + '><footer id=\"target\"></footer></' + nodes[i] + '>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), nodes[i] + ' not null');\n    }\n\n    for (var i = 0; i < roles.length; i++) {\n      fixture.innerHTML =\n        '<div role=\"' + roles[i] + '\"><footer id=\"target\"></footer></div>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), '[' + roles[i] + '] not null');\n    }\n  });\n\n  it('should return form for form with accessible name aria-label', function () {\n    fixture.innerHTML = '<form id=\"target\" aria-label=\"foo\"></form>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'form');\n  });\n\n  it('should return form for form with accessible name aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"foo\">foo</div><form id=\"target\" aria-labelledby=\"foo\"></form>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'form');\n  });\n\n  it('should return null for form with accessible name title', function () {\n    fixture.innerHTML = '<form id=\"target\" title=\"foo\"></form>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null for form without accessible name', function () {\n    fixture.innerHTML = '<form id=\"target\"></form>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return complementary for aside scoped to body', function () {\n    fixture.innerHTML = '<aside id=\"target\"></aside>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'complementary');\n  });\n\n  it('should return complementary for aside scoped to main', function () {\n    fixture.innerHTML = '<main><aside id=\"target\"></aside></main>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'complementary');\n  });\n\n  it('should return complementary for aside scoped to element with role=main', function () {\n    fixture.innerHTML =\n      '<article role=\"main\"><aside id=\"target\"></aside></article>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'complementary');\n  });\n\n  it('should return null for aside with sectioning parent', function () {\n    var nodes = ['article', 'aside', 'nav', 'section'];\n    var roles = ['article', 'complementary', 'navigation', 'region'];\n\n    for (var i = 0; i < nodes.length; i++) {\n      fixture.innerHTML =\n        '<' + nodes[i] + '><header id=\"target\"></header></' + nodes[i] + '>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), nodes[i] + ' not null');\n    }\n\n    for (var i = 0; i < roles.length; i++) {\n      fixture.innerHTML =\n        '<div role=\"' + roles[i] + '\"><header id=\"target\"></header></div>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), '[' + roles[i] + '] not null');\n    }\n  });\n\n  it('should return complementary for aside with sectioning parent if aside has aria-label', function () {\n    var nodes = ['article', 'aside', 'nav', 'section'];\n    var roles = ['article', 'complementary', 'navigation', 'region'];\n\n    for (var i = 0; i < nodes.length; i++) {\n      fixture.innerHTML =\n        '<' +\n        nodes[i] +\n        '><aside id=\"target\" aria-label=\"test label\"></aside></' +\n        nodes[i] +\n        '>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.equal(implicitRole(node), 'complementary');\n    }\n\n    for (var i = 0; i < roles.length; i++) {\n      fixture.innerHTML =\n        '<div role=\"' +\n        roles[i] +\n        '\"><aside id=\"target\" aria-label=\"test label\"></aside></div>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.equal(implicitRole(node), 'complementary');\n    }\n  });\n\n  it('should return null for sectioned aside with empty aria-label', function () {\n    fixture.innerHTML =\n      '<section><aside id=\"target\" aria-label=\" \"></aside></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return complementary for sectioned aside with title', function () {\n    fixture.innerHTML =\n      '<section><aside id=\"target\" title=\"test title\"></aside></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'complementary');\n  });\n\n  it('should return null for sectioned aside with empty title', function () {\n    fixture.innerHTML =\n      '<section><aside id=\"target\" title=\" \"></aside></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return banner for \"body header\"', function () {\n    fixture.innerHTML = '<header id=\"target\"></header>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'banner');\n  });\n\n  it('should return null for header with sectioning or main parent', function () {\n    var nodes = ['article', 'aside', 'main', 'nav', 'section'];\n    var roles = ['article', 'complementary', 'main', 'navigation', 'region'];\n\n    for (var i = 0; i < nodes.length; i++) {\n      fixture.innerHTML =\n        '<' + nodes[i] + '><header id=\"target\"></header></' + nodes[i] + '>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), nodes[i] + ' not null');\n    }\n\n    for (var i = 0; i < roles.length; i++) {\n      fixture.innerHTML =\n        '<div role=\"' + roles[i] + '\"><header id=\"target\"></header></div>';\n      var node = fixture.querySelector('#target');\n      flatTreeSetup(fixture);\n      assert.isNull(implicitRole(node), '[' + roles[i] + '] not null');\n    }\n  });\n\n  it('should return img for \"img[alt]\"', function () {\n    fixture.innerHTML = '<img id=\"target\" alt=\"value\"></img>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'img');\n  });\n\n  it('should return img for \"img:not([alt])\"', function () {\n    fixture.innerHTML = '<img id=\"target\"></img>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'img');\n  });\n\n  it('should return presentation for \"img\" with empty alt', function () {\n    fixture.innerHTML = '<img id=\"target\" alt=\"\"></img>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'presentation');\n  });\n\n  it('should return img for \"img\" with empty alt and global aria attribute', function () {\n    fixture.innerHTML = '<img id=\"target\" alt=\"\" aria-label></img>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'img');\n  });\n\n  it('should return img for \"img\" with empty alt and focusable', function () {\n    fixture.innerHTML = '<img id=\"target\" alt=\"\" tabindex=\"0\"></img>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'img');\n  });\n\n  it('should return button for \"input[type=button]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"button\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'button');\n  });\n\n  it('should return button for \"input[type=image]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"image\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'button');\n  });\n\n  it('should return button for \"input[type=reset]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"reset\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'button');\n  });\n\n  it('should return button for \"input[type=submit]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"submit\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'button');\n  });\n\n  it('should return checkbox for \"input[type=checkbox]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"checkbox\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'checkbox');\n  });\n\n  it('should return textbox for \"input[type=email]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"email\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=tel]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"tel\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=text]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"text\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=url]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"url\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=password]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"password\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=time]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"time\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=date]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"date\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input:not([type])\"', function () {\n    fixture.innerHTML = '<input id=\"target\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return combobox for \"input[list]\" that points to a datalist', function () {\n    fixture.innerHTML =\n      '<input id=\"target\" list=\"list\"/><datalist id=\"list\"></datalist>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'combobox');\n  });\n\n  it('should return textbox for \"input[list]\" that does not point to a datalist', function () {\n    fixture.innerHTML = '<input id=\"target\" list=\"list\"/><div id=\"list\"></div>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return textbox for \"input[type=password][list]\"', function () {\n    fixture.innerHTML =\n      '<input id=\"target\" type=\"password\" list=\"list\"/>' +\n      '<datalist id=\"list\"></datalist>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return spinbutton for \"input[type=number]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"number\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'spinbutton');\n  });\n\n  it('should return radio for \"input[type=radio]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"radio\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'radio');\n  });\n\n  it('should return slider for \"input[type=range]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"range\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'slider');\n  });\n\n  it('should return searchbox for \"input[type=search]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"search\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'searchbox');\n  });\n\n  it('should return combobox for \"input[type=search][list]\"', function () {\n    fixture.innerHTML =\n      '<input id=\"target\" type=\"search\" list=\"list\"/><datalist id=\"list\"></datalist>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'combobox');\n  });\n\n  it('should return textbox for \"input[type=invalid]\"', function () {\n    fixture.innerHTML = '<input id=\"target\" type=\"invalid\"/>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'textbox');\n  });\n\n  it('should return region for \"section\" with accessible name aria-label', function () {\n    fixture.innerHTML = '<section id=\"target\" aria-label=\"foo\"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'region');\n  });\n\n  it('should return region for section with accessible name aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"foo\">foo</div><section id=\"target\" aria-labelledby=\"foo\"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'region');\n  });\n\n  it('should return null for section with accessible name title', function () {\n    fixture.innerHTML = '<section id=\"target\" title=\"foo\"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null for \"section\" without accessible name', function () {\n    fixture.innerHTML = '<section id=\"target\"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null for \"section\" with empty aria-label', function () {\n    fixture.innerHTML = '<section id=\"target\" aria-label=\" \"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null for \"section\" with empty aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"foo\"> </div><section id=\"target\" aria-labelledby=\"foo\"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return null for \"section\" with empty title', function () {\n    fixture.innerHTML = '<section id=\"target\" title=\" \"></section>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.isNull(implicitRole(node));\n  });\n\n  it('should return listbox for \"select[multiple]\"', function () {\n    fixture.innerHTML = '<select id=\"target\" multiple></select>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'listbox');\n  });\n\n  it('should return listbox for \"select[size]\" > 1', function () {\n    fixture.innerHTML = '<select id=\"target\" size=\"3\"></select>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'listbox');\n  });\n\n  it('should return combobox for \"select[size]\" <= 1', function () {\n    fixture.innerHTML = '<select id=\"target\" size=\"1\"></select>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'combobox');\n  });\n\n  it('should return combobox for \"select\"', function () {\n    fixture.innerHTML = '<select id=\"target\"></select>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'combobox');\n  });\n\n  it('should return cell for \"td\"', function () {\n    fixture.innerHTML = '<table><td id=\"target\"></td></table>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'cell');\n  });\n\n  it('should return gridcell for \"td\" with grid parent', function () {\n    fixture.innerHTML = '<table role=\"grid\"><td id=\"target\"></td></table>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'gridcell');\n  });\n\n  it('should return gridcell for \"td\" with treegrid parent', function () {\n    fixture.innerHTML = '<table role=\"treegrid\"><td id=\"target\"></td></table>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'gridcell');\n  });\n\n  it('should return rowheader for \"th[scope=row]\"', function () {\n    fixture.innerHTML = '<table><th id=\"target\" scope=\"row\"></th></table>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'rowheader');\n  });\n\n  it('should return columnheader for \"th[scope=col]\"', function () {\n    fixture.innerHTML = '<table><th id=\"target\" scope=\"col\"></th></table>';\n    var node = fixture.querySelector('#target');\n    flatTreeSetup(fixture);\n    assert.equal(implicitRole(node), 'columnheader');\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/is-accessible-ref.js",
    "content": "describe('aria.isAccessibleRef', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var isAccessibleRef = axe.commons.aria.isAccessibleRef;\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n\n  function setLookup(attrs) {\n    axe.configure({\n      standards: {\n        ariaAttrs: attrs\n      }\n    });\n  }\n\n  before(function () {\n    axe._load({});\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe.reset();\n  });\n\n  it('returns false by default', function () {\n    fixture.innerHTML = '<div id=\"foo\"><div>';\n    var node = document.getElementById('foo');\n    assert.isFalse(isAccessibleRef(node));\n  });\n\n  it('returns true for IDs used in aria IDREF attributes', function () {\n    setLookup({ 'aria-foo': { type: 'idref' } });\n    fixture.innerHTML = '<div aria-foo=\"foo\"></div><i id=\"foo\"></i>';\n    var node = document.getElementById('foo');\n    assert.isTrue(isAccessibleRef(node));\n  });\n\n  it('returns true for IDs used in aria IDREFS attributes', function () {\n    setLookup({ 'aria-bar': { type: 'idrefs' } });\n    fixture.innerHTML =\n      '<div aria-bar=\"foo bar\"></div><i id=\"foo\"></i><b id=\"bar\"></b>';\n\n    var node1 = document.getElementById('foo');\n    var node2 = document.getElementById('bar');\n    assert.isTrue(isAccessibleRef(node1));\n    assert.isTrue(isAccessibleRef(node2));\n  });\n\n  it('returns true for IDs used in label[for] attributes', function () {\n    setLookup({ 'aria-foo': { type: 'idref' } });\n    fixture.innerHTML = '<label for=\"baz\">baz</label><input id=\"baz\">';\n    var node = document.getElementById('baz');\n    assert.isTrue(isAccessibleRef(node));\n  });\n\n  (shadowSupport ? it : xit)('works inside shadow DOM', function () {\n    setLookup({ 'aria-bar': { type: 'idref' } });\n    fixture.innerHTML = '<div id=\"foo\"></div>';\n\n    var shadow = document.getElementById('foo').attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<div aria-bar=\"bar\"></div><b id=\"bar\"></b>';\n\n    var node = shadow.getElementById('bar');\n    assert.isTrue(isAccessibleRef(node));\n  });\n\n  (shadowSupport ? it : xit)(\n    'returns false for IDREFs inside shadow DOM',\n    function () {\n      setLookup({ 'aria-foo': { type: 'idrefs' } });\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      var node1 = document.getElementById('foo');\n      var node2 = document.getElementById('bar');\n\n      var shadow = node1.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div aria-foo=\"foo bar\"><slot></slot></div>';\n\n      assert.isFalse(isAccessibleRef(node1));\n      assert.isFalse(isAccessibleRef(node2));\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'returns false for IDREFs outside shadow DOM',\n    function () {\n      setLookup({ 'aria-bar': { type: 'idref' } });\n      fixture.innerHTML =\n        '<div id=\"foo\" aria-bar=\"bar\"><div aria-bar=\"bar\"></div></div>';\n\n      var shadow = document\n        .getElementById('foo')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div id=\"bar\"><slot></slot></div>';\n\n      var node = shadow.getElementById('bar');\n      assert.isFalse(isAccessibleRef(node));\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/aria/is-aria-role-allowed-on-element.js",
    "content": "describe('aria.isAriaRoleAllowedOnElement', function () {\n  'use strict';\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns true for SECTION with role alert', function () {\n    var node = document.createElement('section');\n    var role = 'alert';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, role);\n    var expected = true;\n    assert.equal(actual, expected);\n  });\n\n  it('returns false for SECTION with role checkbox', function () {\n    var node = document.createElement('section');\n    var role = 'checkbox';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, role);\n    var expected = false;\n    assert.equal(actual, expected);\n  });\n\n  it('returns true for SVG with role alertdialog', function () {\n    var node = document.createElement('svg');\n    var role = 'alertdialog';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for OBJECT with role application', function () {\n    var node = document.createElement('object');\n    var role = 'application';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, role);\n    assert.isTrue(actual);\n  });\n\n  it('returns false for A with role button', function () {\n    var node = document.createElement('a');\n    var role = 'button';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns false for ARTICLE with role cell', function () {\n    var node = document.createElement('article');\n    var role = 'cell';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isFalse(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for BUTTON with role checkbox', function () {\n    var node = document.createElement('button');\n    var role = 'checkbox';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for IFRAME with role document', function () {\n    var node = document.createElement('iframe');\n    var role = 'document';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for ASIDE with role feed', function () {\n    var node = document.createElement('aside');\n    var role = 'feed';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for FIGURE with role group', function () {\n    var node = document.createElement('figure');\n    var role = 'group';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for SVG with role img', function () {\n    var node = document.createElement('svg');\n    var role = 'img';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for INPUT with type image and role link', function () {\n    var node = document.createElement('input');\n    var role = 'link';\n    node.setAttribute('role', role);\n    node.setAttribute('type', 'image');\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for HEADER with role none', function () {\n    var node = document.createElement('header');\n    var role = 'none';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for LI with role option', function () {\n    var node = document.createElement('li');\n    var role = 'option';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for H1 with role tab', function () {\n    var node = document.createElement('h1');\n    var role = 'tab';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true for OL with role tablist', function () {\n    var node = document.createElement('ol');\n    var role = 'tablist';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true when A has namespace as svg and role menuitem', function () {\n    var node = document.createElementNS('http://www.w3.org/2000/svg', 'a');\n    flatTreeSetup(node);\n    assert.isTrue(\n      axe.commons.aria.isAriaRoleAllowedOnElement(node, 'menuitem')\n    );\n  });\n\n  it('returns true when BUTTON has type menu and role as menuitem', function () {\n    var node = document.createElement('button');\n    var role = 'menuitem';\n    node.setAttribute('type', 'menu');\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns false when MENU has type context and role navigation', function () {\n    var node = document.createElement('menu');\n    var role = 'navigation';\n    node.setAttribute('type', 'context');\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isFalse(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true when B has role navigation', function () {\n    var node = document.createElement('b');\n    var role = 'navigation';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true when NAV has role menubar', function () {\n    var node = document.createElement('nav');\n    var role = 'menubar';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true when NAV has role tablist', function () {\n    var node = document.createElement('nav');\n    var role = 'tablist';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isTrue(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns false when PROGRESS has role button', function () {\n    var node = document.createElement('progress');\n    var role = 'button';\n    node.setAttribute('role', role);\n    flatTreeSetup(node);\n    assert.isFalse(axe.commons.aria.isAriaRoleAllowedOnElement(node, role));\n  });\n\n  it('returns true if given element can have any role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, 'link');\n    assert.isTrue(actual);\n  });\n\n  it('returns false if given element cannot have any role', function () {\n    var node = document.createElement('main');\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, 'alert'); // changed this\n    assert.isFalse(actual);\n  });\n\n  it('returns false if given element cannot have any role', function () {\n    var node = document.createElement('track');\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, 'banner');\n    assert.isFalse(actual);\n  });\n\n  it('returns false if elements implicit role matches the role', function () {\n    var node = document.createElement('area');\n    node.setAttribute('href', '#yay');\n    node.setAttribute('role', 'link');\n    flatTreeSetup(node);\n    var actual = axe.commons.aria.isAriaRoleAllowedOnElement(node, 'link');\n    assert.isFalse(actual);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/is-combobox-popup.js",
    "content": "describe('isComboboxPopup', () => {\n  const { isComboboxPopup } = axe.commons.aria;\n  const { queryFixture } = axe.testUtils;\n\n  it('does not match non-popup roles', () => {\n    const roles = ['main', 'combobox', 'textbox', 'button'];\n    for (const role of roles) {\n      const vNode = queryFixture(\n        `<div role=\"combobox\" aria-controls=\"target\"></div>\n        <div role=\"${role}\" id=\"target\"></div>`\n      );\n      assert.isFalse(isComboboxPopup(vNode));\n    }\n  });\n\n  for (const role of ['menu', 'listbox', 'tree', 'grid', 'dialog']) {\n    describe(role, () => {\n      it('is false when not related to the combobox', () => {\n        const vNode = queryFixture(\n          `<div role=\"combobox\"></div>\n          <div role=\"${role}\" id=\"target\"></div>`\n        );\n        assert.isFalse(isComboboxPopup(vNode));\n      });\n\n      describe('using aria-controls (ARIA 1.2 pattern)', () => {\n        it('is true when referenced', () => {\n          const vNode = queryFixture(\n            `<div role=\"combobox\" aria-controls=\"target\"></div>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isTrue(isComboboxPopup(vNode));\n        });\n\n        it('is false when controlled by a select element', () => {\n          const vNode = queryFixture(\n            `<select role=\"combobox\" aria-controls=\"target\"></select>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n\n        it('is false when not controlled by a combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"button combobox\" aria-controls=\"target\"></div>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n      });\n\n      describe('using parent owned (ARIA 1.1 pattern)', () => {\n        it('is true when its a child of the combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"combobox\">\n              <div role=\"${role}\" id=\"target\"></div>\n            </div>`\n          );\n          assert.isTrue(isComboboxPopup(vNode));\n        });\n\n        it('is false when its not a child of a real combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"button combobox\">\n              <div role=\"${role}\" id=\"target\"></div>\n            </div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n\n        it('is false when its nearest parent with a role is not a combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"combobox\">\n              <div role=\"region\">\n                <div role=\"${role}\" id=\"target\"></div>\n              </div>\n            </div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n\n        it('is true when its nearest parent with a role is not a combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"combobox\">\n              <div>\n                <div role=\"none\">\n                  <div role=\"presentation\">\n                    <div role=\"${role}\" id=\"target\"></div>\n                  </div>\n                </div>\n              </div>\n            </div>`\n          );\n          assert.isTrue(isComboboxPopup(vNode));\n        });\n      });\n\n      describe('when using aria-owns (ARIA 1.0 pattern)', () => {\n        it('is true when referenced', () => {\n          const vNode = queryFixture(\n            `<div role=\"combobox\" aria-owns=\"target\"></div>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isTrue(isComboboxPopup(vNode));\n        });\n\n        it('is false when owned by a select element', () => {\n          const vNode = queryFixture(\n            `<select role=\"combobox\" aria-owns=\"target\"></select>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n\n        it('is false when not owned by a combobox', () => {\n          const vNode = queryFixture(\n            `<div role=\"button combobox\" aria-owns=\"target\"></div>\n            <div role=\"${role}\" id=\"target\"></div>`\n          );\n          assert.isFalse(isComboboxPopup(vNode));\n        });\n      });\n    });\n  }\n\n  describe('options.popupRoles', () => {\n    it('allows custom popup roles', () => {\n      const vNode = queryFixture(\n        `<div role=\"combobox\" aria-controls=\"target\"></div>\n        <div role=\"button\" id=\"target\"></div>`\n      );\n      assert.isFalse(isComboboxPopup(vNode));\n      assert.isTrue(isComboboxPopup(vNode, { popupRoles: ['button'] }));\n    });\n\n    it('overrides the default popup roles', () => {\n      const vNode = queryFixture(\n        `<div role=\"combobox\" aria-controls=\"target\"></div>\n        <div role=\"listbox\" id=\"target\"></div>`\n      );\n      assert.isTrue(isComboboxPopup(vNode));\n      assert.isFalse(isComboboxPopup(vNode, { popupRoles: ['button'] }));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/is-unsupported-role.js",
    "content": "describe('aria.isUnsupportedRole', function () {\n  'use strict';\n\n  after(function () {\n    axe.reset();\n  });\n\n  it('should return true if the role is unsupported', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            unsupported: true\n          }\n        }\n      }\n    });\n    assert.isTrue(axe.commons.aria.isUnsupportedRole('cats'));\n  });\n\n  it('should return false if the role is supported', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            unsupported: false\n          }\n        }\n      }\n    });\n    assert.isFalse(axe.commons.aria.isUnsupportedRole('cats'));\n  });\n\n  it('should return false if the role is invalid', function () {\n    assert.isFalse(axe.commons.aria.isUnsupportedRole('cats'));\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/is-valid-role.js",
    "content": "describe('aria.isValidRole', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should return true if role is found in the lookup table', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: true\n        }\n      }\n    });\n    assert.isTrue(axe.commons.aria.isValidRole('cats'));\n  });\n\n  it('should return false if role is not found in the lookup table', function () {\n    assert.isFalse(axe.commons.aria.isValidRole('cats'));\n  });\n\n  it('returns false for abstract roles by default', function () {\n    assert.isFalse(axe.commons.aria.isValidRole('input'));\n  });\n\n  it('returns true for abstract roles with { allowAbstract: true } ', function () {\n    assert.isTrue(\n      axe.commons.aria.isValidRole('input', { allowAbstract: true })\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/label-virtual.js",
    "content": "describe('aria.labelVirtual', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('is called through aria.label with a DOM node', function () {\n    fixtureSetup(\n      '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\">'\n    );\n    var target = fixture.querySelector('#target');\n\n    assert.equal(axe.commons.aria.label(target), 'monkeys bananas');\n  });\n\n  describe('aria-labelledby', function () {\n    it('should join text with a single space', function () {\n      fixtureSetup(\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n          '<input id=\"target\" aria-labelledby=\"monkeys bananas\">'\n      );\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.equal(axe.commons.aria.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should filter invisible elements', function () {\n      fixtureSetup(\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\" style=\"display: none\">bananas</div>' +\n          '<input id=\"target\" aria-labelledby=\"monkeys bananas\">'\n      );\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.equal(axe.commons.aria.labelVirtual(target), 'monkeys');\n    });\n\n    it('should take precedence over aria-label', function () {\n      fixtureSetup(\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n          '<input id=\"target\" aria-labelledby=\"monkeys bananas\" aria-label=\"nope\">'\n      );\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.equal(axe.commons.aria.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should ignore whitespace only labels', function () {\n      fixtureSetup(\n        '<div id=\"monkeys\">\t\\n  </div><div id=\"bananas\"></div>' +\n          '<input id=\"target\" aria-labelledby=\"monkeys bananas\">'\n      );\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.isNull(axe.commons.aria.labelVirtual(target));\n    });\n  });\n\n  describe('aria-label', function () {\n    it('should detect it', function () {\n      fixtureSetup('<input id=\"target\" aria-label=\"monkeys\">');\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.equal(axe.commons.aria.labelVirtual(target), 'monkeys');\n    });\n\n    it('should ignore whitespace only labels', function () {\n      fixtureSetup('<input id=\"target\" aria-label=\"   \\n\t\">');\n      var target = axe.utils.querySelectorAll(axe._tree[0], '#target')[0];\n\n      assert.isNull(axe.commons.aria.labelVirtual(target));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/named-from-contents.js",
    "content": "describe('aria.namedFromContents', function () {\n  var aria = axe.commons.aria;\n  var namedFromContents = aria.namedFromContents;\n  var fixture = document.querySelector('#fixture');\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true when the element has an explicit role named from content', function () {\n    fixture.innerHTML = '<div role=\"foo\"></div>';\n    flatTreeSetup(fixture);\n    assert.isTrue(namedFromContents(fixture.firstChild));\n  });\n\n  it('works on virtual nodes', function () {\n    var vNode = axe.testUtils.queryFixture(\n      '<div id=\"target\" role=\"foo\"></div>'\n    );\n    assert.isTrue(namedFromContents(vNode));\n  });\n\n  it('returns true when the element has an implicit role named from content', function () {\n    fixture.innerHTML = '<h1>foo</h1>';\n    flatTreeSetup(fixture);\n    assert.isTrue(namedFromContents(fixture.firstChild));\n  });\n\n  it('returns false when the element has a role not named from content', function () {\n    fixture.innerHTML = '<main role=\"main\"></main>';\n    flatTreeSetup(fixture);\n    assert.isFalse(namedFromContents(fixture.firstChild));\n  });\n\n  it('returns false node is not a DOM element', function () {\n    fixture.innerHTML = 'text node';\n    flatTreeSetup(fixture);\n    assert.isFalse(namedFromContents(fixture.firstChild));\n  });\n\n  describe('{ strict: false }', function () {\n    it('returns true when the element has no role named from content', function () {\n      fixture.innerHTML = '<div>foo</div>';\n      flatTreeSetup(fixture);\n      assert.isNull(aria.getRole(fixture.firstChild));\n      assert.isTrue(namedFromContents(fixture.firstChild, { strict: false }));\n    });\n\n    it('is default for aria.namedFromContents', function () {\n      fixture.innerHTML = '<div>foo</div>';\n      flatTreeSetup(fixture);\n      assert.isNull(aria.getRole(fixture.firstChild));\n      assert.isTrue(namedFromContents(fixture.firstChild));\n    });\n\n    it('returns true when the element has role=presentation', function () {\n      fixture.innerHTML = '<div role=\"presentation\">foo</div>';\n      flatTreeSetup(fixture);\n      assert.isTrue(namedFromContents(fixture.firstChild, { strict: false }));\n    });\n\n    it('returns true when the element has role=none', function () {\n      fixture.innerHTML = '<div role=\"none\">foo</div>';\n      flatTreeSetup(fixture);\n      assert.isTrue(namedFromContents(fixture.firstChild, { strict: false }));\n    });\n\n    it('returns true when the implicit role is null', function () {\n      fixture.innerHTML = '<div role=\"bar\">foo</div>';\n      flatTreeSetup(fixture);\n      assert.isTrue(namedFromContents(fixture.firstChild, { strict: false }));\n    });\n  });\n\n  describe('{ strict: true }', function () {\n    it('returns false when the element has no role named from content', function () {\n      fixture.innerHTML = '<div>foo</div>';\n      flatTreeSetup(fixture);\n      assert.isNull(aria.getRole(fixture.firstChild));\n      assert.isFalse(namedFromContents(fixture.firstChild, { strict: true }));\n    });\n\n    it('returns false when the element has role=presentation', function () {\n      fixture.innerHTML = '<div role=\"presentation\">foo</div>';\n      flatTreeSetup(fixture);\n      assert.isFalse(namedFromContents(fixture.firstChild, { strict: true }));\n    });\n\n    it('returns false when the element has role=none', function () {\n      fixture.innerHTML = '<div role=\"none\">foo</div>';\n      flatTreeSetup(fixture);\n      assert.isFalse(namedFromContents(fixture.firstChild, { strict: true }));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/required-attr.js",
    "content": "describe('aria.requiredAttr', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should returned the attributes property for the proper role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredAttrs: ['yes']\n          }\n        }\n      }\n    });\n\n    assert.deepEqual(axe.commons.aria.requiredAttr('cats'), ['yes']);\n  });\n\n  it('should returned empty array if the required attributes is not an array', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredAttrs: 'yes'\n          }\n        }\n      }\n    });\n    assert.deepEqual(axe.commons.aria.requiredAttr('cats'), []);\n  });\n\n  it('should return an empty array if there are no required attributes', function () {\n    var result = axe.commons.aria.requiredAttr('cats');\n\n    assert.deepEqual(result, []);\n  });\n\n  it('should return a unique copy of the attributes', function () {\n    var attrs = ['yes', 'no'];\n\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredAttrs: attrs\n          }\n        }\n      }\n    });\n\n    var result = axe.commons.aria.requiredAttr('cats');\n    assert.notEqual(result, attrs);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/required-context.js",
    "content": "describe('aria.requiredContext', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should returned the context property for the proper role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredContext: ['yes']\n          }\n        }\n      }\n    });\n    assert.deepEqual(axe.commons.aria.requiredContext('cats'), ['yes']);\n  });\n\n  it('should returned null if the required context is not an array', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredContext: 'yes'\n          }\n        }\n      }\n    });\n    assert.isNull(axe.commons.aria.requiredContext('cats'));\n  });\n\n  it('should return null if there are no required context nodes', function () {\n    var result = axe.commons.aria.requiredContext('cats');\n\n    assert.isNull(result);\n  });\n\n  it('should return a unique copy of the context', function () {\n    var context = ['yes', 'no'];\n\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredContext: context\n          }\n        }\n      }\n    });\n\n    var result = axe.commons.aria.requiredContext('cats');\n    assert.notEqual(result, context);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/required-owned.js",
    "content": "describe('aria.requiredOwned', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should returned the context property for the proper role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredOwned: ['yes']\n          }\n        }\n      }\n    });\n    assert.deepEqual(axe.commons.aria.requiredOwned('cats'), ['yes']);\n  });\n\n  it('should returned null if the required context is not an array', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredOwned: 'yes'\n          }\n        }\n      }\n    });\n    assert.isNull(axe.commons.aria.requiredOwned('cats'));\n  });\n\n  it('should return null if there are no required context nodes', function () {\n    var result = axe.commons.aria.requiredOwned('cats');\n\n    assert.isNull(result);\n  });\n\n  it('should return a unique copy of the context', function () {\n    var context = ['yes', 'no'];\n\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          cats: {\n            requiredOwned: context\n          }\n        }\n      }\n    });\n\n    var result = axe.commons.aria.requiredOwned('cats');\n    assert.notEqual(result, context);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/roles.js",
    "content": "describe('aria.implicitNodes', function () {\n  'use strict';\n\n  var orig;\n  beforeEach(function () {\n    orig = axe.commons.aria.lookupTable.role;\n  });\n\n  afterEach(function () {\n    axe.commons.aria.lookupTable.role = orig;\n  });\n\n  it('should return the implicit property for the proper role', function () {\n    axe.commons.aria.lookupTable.role = {\n      cats: {\n        implicit: 'yes'\n      }\n    };\n    assert.equal(axe.commons.aria.implicitNodes('cats'), 'yes');\n  });\n\n  it('should return null if there are no implicit roles', function () {\n    axe.commons.aria.lookupTable.role = {};\n    var result = axe.commons.aria.implicitNodes('cats');\n\n    assert.isNull(result);\n  });\n});\n"
  },
  {
    "path": "test/commons/aria/validate-attr-value.js",
    "content": "describe('aria.validateAttrValue', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowSupport = axe.testUtils.shadowSupport;\n  let node;\n\n  function setAttr(elm, attrName, attrValue) {\n    elm.setAttribute(attrName, attrValue);\n    axe.teardown();\n    return axe.setup(elm);\n  }\n\n  beforeEach(() => {\n    node = document.createElement('div');\n  });\n\n  afterEach(() => {\n    axe.reset();\n  });\n\n  it('should return true if there is no matching attribute (future-compat???)', () => {\n    setAttr(node, 'unknown-attr', 'hello');\n\n    assert.isTrue(axe.commons.aria.validateAttrValue(node, 'unknown-attr'));\n  });\n\n  it('works on virtual nodes', () => {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          cats: {\n            type: 'nmtoken',\n            values: ['valid'],\n            allowEmpty: true\n          }\n        }\n      }\n    });\n    const vNode = axe.testUtils.queryFixture(\n      '<div id=\"target\" cats=\"valid\"></div>'\n    );\n    assert.isTrue(axe.commons.aria.validateAttrValue(vNode, 'cats'));\n  });\n\n  describe('allowEmpty', () => {\n    beforeEach(() => {\n      axe.configure({\n        standards: {\n          ariaAttrs: {\n            cats: {\n              type: 'nmtoken',\n              values: ['valid'],\n              allowEmpty: true\n            },\n            dogs: {\n              type: 'idref',\n              allowEmpty: true\n            },\n            goats: {\n              type: 'idrefs',\n              allowEmpty: true\n            },\n            cows: {\n              type: 'string',\n              allowEmpty: true\n            },\n            sheep: {\n              type: 'decimal',\n              allowEmpty: true\n            },\n            pigs: {\n              type: 'int',\n              allowEmpty: true\n            },\n            horses: {\n              type: 'boolean',\n              allowEmpty: true\n            }\n          }\n        }\n      });\n    });\n\n    it('returns true for empty attributes with allowEmpty:true', () => {\n      setAttr(node, 'cats', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cats'));\n\n      setAttr(node, 'dogs', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'dogs'));\n\n      setAttr(node, 'goats', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'goats'));\n\n      setAttr(node, 'sheep', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n      setAttr(node, 'cows', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cows'));\n\n      setAttr(node, 'pigs', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n      setAttr(node, 'horses', '');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'horses'));\n    });\n\n    it('returns true for whitespace-only attributes with allowEmpty:true', () => {\n      setAttr(node, 'cats', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cats'));\n\n      setAttr(node, 'dogs', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'dogs'));\n\n      setAttr(node, 'goats', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'goats'));\n\n      setAttr(node, 'cows', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cows'));\n\n      setAttr(node, 'pigs', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n      setAttr(node, 'sheep', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n      setAttr(node, 'horses', '  \\r\\n\\t  ');\n      assert.isTrue(axe.commons.aria.validateAttrValue(node, 'horses'));\n    });\n  });\n\n  describe('schema defintions', () => {\n    describe('enumerated values', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              cats: {\n                type: 'nmtoken',\n                values: ['valid']\n              }\n            }\n          }\n        });\n      });\n\n      it('should validate against enumerated .values if present', () => {\n        setAttr(node, 'cats', 'valid');\n\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cats'));\n\n        setAttr(node, 'cats', 'invalid');\n\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'cats'));\n      });\n\n      it('should be case-insensitive for enumerated values', () => {\n        setAttr(node, 'cats', 'vaLiD');\n\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cats'));\n      });\n\n      it('should reject empty strings', () => {\n        setAttr(node, 'cats', '');\n\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'cats'));\n      });\n    });\n\n    describe('idref', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              dogs: {\n                type: 'idref'\n              }\n            }\n          }\n        });\n      });\n\n      it('should validate the referenced node exists', () => {\n        fixture.innerHTML = '<div id=\"target\"></div>';\n        setAttr(node, 'dogs', 'target');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'dogs'));\n\n        setAttr(node, 'dogs', 'invalid');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'dogs'));\n      });\n\n      it('should work in shadow DOM', () => {\n        let shadEl;\n\n        if (shadowSupport.v1) {\n          // shadow DOM v1 - note: v0 is compatible with this code, so no need\n          // to specifically test this\n          fixture.innerHTML = '<div></div>';\n          makeShadowTreeVAV(fixture.firstChild);\n          axe.setup(fixture);\n          shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput');\n          assert.isTrue(\n            axe.commons.aria.validateAttrValue(shadEl, 'aria-labelledby')\n          );\n          shadEl = fixture.firstChild.shadowRoot.querySelector('input#invalid');\n          assert.isFalse(\n            axe.commons.aria.validateAttrValue(shadEl, 'aria-labelledby')\n          );\n        }\n      });\n\n      it('returns false if empty without allowEmpty: true', () => {\n        setAttr(node, 'dogs', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'dogs'));\n      });\n    });\n\n    describe('idrefs', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              goats: {\n                type: 'idrefs'\n              }\n            }\n          }\n        });\n      });\n\n      it('should return false when a single referenced node is not found', () => {\n        setAttr(node, 'goats', 'invalid');\n        // target2 not found\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n\n      it('should return false when no referenced element is found', () => {\n        fixture.innerHTML = '<div id=\"target\"></div>';\n        setAttr(node, 'goats', 'target2 target3');\n        // target2 not found\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n\n      it('should return true when at least one referenced element is found', () => {\n        fixture.innerHTML = '<div id=\"target\"></div>';\n        setAttr(node, 'goats', 'target target2');\n        // target2 not found\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n\n      it('should return true when all targets are found', () => {\n        fixture.innerHTML = '<div id=\"target\"></div><div id=\"target2\"></div>';\n        setAttr(node, 'goats', 'target target2');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n\n      it('should not fail on weird whitespace', () => {\n        fixture.innerHTML = '<div id=\"target\"></div><div id=\"target2\"></div>';\n        setAttr(node, 'goats', ' \\t \\ttarget   \\t   target2      ');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n\n      it('returns false if empty without allowEmpty: true', () => {\n        setAttr(node, 'goats', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'goats'));\n      });\n    });\n\n    describe('string', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              cows: {\n                type: 'string'\n              }\n            }\n          }\n        });\n      });\n\n      it('returns true for non-empty strings', () => {\n        setAttr(node, 'cows', 'hi');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'cows'));\n      });\n\n      it('returns false for non-empty strings without allowEmpty:true', () => {\n        setAttr(node, 'cows', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'cows'));\n      });\n    });\n\n    describe('decimal', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              sheep: {\n                type: 'decimal'\n              }\n            }\n          }\n        });\n      });\n\n      it('should allow, but not require, a preceeding sign', () => {\n        setAttr(node, 'sheep', '+1.12');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-1.12');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '1.12');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should make the decimal separator optional', () => {\n        setAttr(node, 'sheep', '+1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should make the whole number optional', () => {\n        setAttr(node, 'sheep', '+.1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-.1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '.1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should make the right-side optional', () => {\n        setAttr(node, 'sheep', '+1.');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-1.');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '1.');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should validate the entire string', () => {\n        setAttr(node, 'sheep', ' +1.12 ');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', 'invalid +1.12');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '+1.12 invalid');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should only allow for numbers', () => {\n        setAttr(node, 'sheep', '+a.12');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '+1.b');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', 'b1.1');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('should require at least one number', () => {\n        setAttr(node, 'sheep', '+.');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-.');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '+');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '-');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '.');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n\n        setAttr(node, 'sheep', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n\n      it('returns false for empty strings without allowEmpty:true', () => {\n        setAttr(node, 'sheep', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'sheep'));\n      });\n    });\n\n    describe('int', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              pigs: {\n                type: 'int'\n              }\n            }\n          }\n        });\n      });\n\n      it('should only allow for numbers by an optional preceding sign', () => {\n        setAttr(node, 'pigs', '+1234234');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n        setAttr(node, 'pigs', '-137456745');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n        setAttr(node, 'pigs', '1234523452');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n      });\n\n      it('should return true for value greater than or equal to minValue', () => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              pigs: {\n                type: 'int',\n                minValue: -1\n              }\n            }\n          }\n        });\n\n        setAttr(node, 'pigs', '-1');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n        setAttr(node, 'pigs', '0');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n\n        setAttr(node, 'pigs', '1000');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'pigs'));\n      });\n\n      it('returns false for empty strings without allowEmpty:true', () => {\n        setAttr(node, 'pigs', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'pigs'));\n      });\n\n      it('should return false for value less than the minValue', () => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              pigs: {\n                type: 'int',\n                minValue: 0\n              }\n            }\n          }\n        });\n\n        setAttr(node, 'pigs', '-1');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'pigs'));\n      });\n    });\n\n    describe('boolean', () => {\n      beforeEach(() => {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              horses: {\n                type: 'boolean'\n              }\n            }\n          }\n        });\n      });\n\n      it('returns true for boolean value', () => {\n        setAttr(node, 'horses', 'true');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'horses'));\n\n        setAttr(node, 'horses', 'false');\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'horses'));\n      });\n\n      it('should be case-insensitive', () => {\n        setAttr(node, 'horses', 'trUE');\n\n        assert.isTrue(axe.commons.aria.validateAttrValue(node, 'horses'));\n      });\n\n      it('returns false for non-boolean values', () => {\n        setAttr(node, 'horses', 'hi');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'horses'));\n\n        setAttr(node, 'horses', '1');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'horses'));\n      });\n\n      it('returns false for non-empty strings without allowEmpty:true', () => {\n        setAttr(node, 'horses', '');\n        assert.isFalse(axe.commons.aria.validateAttrValue(node, 'horses'));\n      });\n    });\n  });\n});\n\nfunction makeShadowTreeVAV(node) {\n  'use strict';\n  const root = node.attachShadow({ mode: 'open' });\n  const div = document.createElement('div');\n  div.className = 'parent';\n  root.appendChild(div);\n  div.appendChild(createContentVAV());\n}\n\nfunction createContentVAV() {\n  'use strict';\n  const group = document.createElement('div');\n  group.innerHTML =\n    '<label id=\"mylabel\">Label</label>' +\n    '<input id=\"myinput\" aria-labelledby=\"mylabel\" type=\"text\" />' +\n    '<input id=\"invalid\" aria-labelledby=\"doesnotexist\" type=\"text\" />';\n  return group;\n}\n"
  },
  {
    "path": "test/commons/aria/validate-attr.js",
    "content": "describe('aria.validateAttr', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should return true if attribute is found in lut', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          cats: {}\n        }\n      }\n    });\n\n    assert.isTrue(axe.commons.aria.validateAttr('cats'));\n  });\n\n  it('should return false if attribute is found in lut', function () {\n    assert.isFalse(axe.commons.aria.validateAttr('cats'));\n  });\n});\n"
  },
  {
    "path": "test/commons/color/center-point-of-rect.js",
    "content": "describe('color.centerPointOfRect', function () {\n  'use strict';\n\n  it('returns `undefined` when element is placed outside of viewport (left position > window dimension)', function () {\n    var actual = axe.commons.color.centerPointOfRect({\n      left: 9999,\n      top: 0,\n      width: 200,\n      height: 100\n    });\n    assert.isUndefined(actual);\n  });\n\n  it('returns `{x,y}` when element is with in viewport', function () {\n    var actual = axe.commons.color.centerPointOfRect({\n      left: 0,\n      top: 0,\n      width: 200,\n      height: 100\n    });\n    assert.isDefined(actual);\n    assert.hasAllKeys(actual, ['x', 'y']);\n  });\n\n  it('returns `{x,y}` when element is with in viewport (check returned coordinate values)', function () {\n    var actual = axe.commons.color.centerPointOfRect({\n      left: 100,\n      top: 100,\n      width: 250,\n      height: 250\n    });\n\n    assert.isDefined(actual);\n    assert.hasAllKeys(actual, ['x', 'y']);\n    assert.equal(actual.x, 225);\n    assert.equal(actual.y, 225);\n  });\n});\n"
  },
  {
    "path": "test/commons/color/color.js",
    "content": "describe('color.Color', () => {\n  'use strict';\n  const Color = axe.commons.color.Color;\n\n  it('can be constructed without alpha', () => {\n    const c1 = new Color(4, 3, 2);\n    assert.equal(c1.red, 4);\n    assert.equal(c1.green, 3);\n    assert.equal(c1.blue, 2);\n    assert.equal(c1.alpha, 1);\n  });\n\n  it('can be constructed from a Color', () => {\n    const c1 = new Color(4, 3, 2, 0.5);\n    const c2 = new Color(c1);\n    assert.equal(c2.red, 4);\n    assert.equal(c2.green, 3);\n    assert.equal(c2.blue, 2);\n    assert.equal(c2.alpha, 0.5);\n  });\n\n  it('clamps out of gamut values for red, green, blue', () => {\n    const c1 = new Color(-255, 0, 510, 0.5);\n    assert.equal(c1.red, 0);\n    assert.equal(c1.green, 0);\n    assert.equal(c1.blue, 255);\n    assert.equal(c1.alpha, 0.5);\n  });\n\n  it('retains out of gamut values for r, g, b', () => {\n    const c1 = new Color(-255, 0, 510, 0.5);\n    assert.equal(c1.r, -1);\n    assert.equal(c1.g, 0);\n    assert.equal(c1.b, 2);\n    assert.equal(c1.alpha, 0.5);\n  });\n\n  it('can be constructed from a Color preserving out of gamut values', () => {\n    const c1 = new Color(-255, 0, 510, 0.5);\n    const c2 = new Color(c1);\n    assert.equal(c2.r, -1);\n    assert.equal(c2.g, 0);\n    assert.equal(c2.b, 2);\n    assert.equal(c2.alpha, 0.5);\n  });\n\n  it('has a toJSON method', () => {\n    const c1 = new Color(255, 128, 0);\n    assert.deepEqual(c1.toJSON(), {\n      red: 255,\n      green: 128,\n      blue: 0,\n      alpha: 1\n    });\n  });\n\n  describe('parseColorFnString', () => {\n    describe('with rgb()', () => {\n      it('should set values properly via RGB', () => {\n        const c = new Color();\n        c.parseColorFnString('rgb(17, 34,  51)');\n        assert.equal(c.red, 17);\n        assert.equal(c.green, 34);\n        assert.equal(c.blue, 51);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('should set values properly via RGBA', () => {\n        const c = new Color();\n        c.parseColorFnString('rgba(17, 34,51,  0.2)');\n        assert.equal(c.red, 17);\n        assert.equal(c.green, 34);\n        assert.equal(c.blue, 51);\n        assert.closeTo(c.alpha, 0.2, 0.01);\n      });\n\n      it('allows decimal values, with and without the integer', () => {\n        const c = new Color();\n        c.parseColorFnString('rgba(.1, 23.4, 56.7, .89)');\n        assert.equal(c.red, 0);\n        assert.equal(c.green, 23);\n        assert.equal(c.blue, 57);\n        assert.closeTo(c.alpha, 0.89, 0.01);\n      });\n\n      it('allows percentages', () => {\n        const c = new Color();\n        c.parseColorFnString('rgba(100%, 100%, 0%, 50%)');\n        assert.equal(c.red, 255);\n        assert.equal(c.green, 255);\n        assert.equal(c.blue, 0);\n        assert.equal(c.alpha, 0.5);\n      });\n\n      it.skip('allows exponent numbers', () => {\n        const c = new Color();\n        c.parseColorFnString('rgb(2e0, 2e1, 2e2)');\n        assert.equal(c.red, 2);\n        assert.equal(c.green, 20);\n        assert.equal(c.blue, 200);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('supports space separated notation', () => {\n        const c = new Color();\n        c.parseColorFnString('rgba(255 128 0 / 50%)');\n        assert.equal(c.red, 255);\n        assert.equal(c.green, 128);\n        assert.equal(c.blue, 0);\n        assert.equal(c.alpha, 0.5);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('rgb(255 128 0 / 50%)');\n        assert.equal(c.red, 255);\n        assert.equal(c.green, 128);\n        assert.equal(c.blue, 0);\n        assert.equal(c.alpha, 0.5);\n      });\n    });\n\n    describe('with hsl(a)', () => {\n      it('allows hsl', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(160, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows hsla', () => {\n        const c = new Color();\n        c.parseColorFnString('hsla(160, 40%, 50%, .5)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.5);\n      });\n\n      it('allows hsl with space notation', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(160 40% 50% / 5%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.05);\n      });\n\n      it('supports deg on hue', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(160deg, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('supports rad on hue', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(2.79rad, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('supports negative rad on hue', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(-3.49rad, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 145);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('supports turn on hue', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(0.444turn, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('supports negative turn on hue', () => {\n        const c = new Color();\n        c.parseColorFnString('hsl(-0.556turn, 40%, 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n    });\n\n    describe('with hwb()', () => {\n      it('allows hwb', () => {\n        const c = new Color();\n        c.parseColorFnString('hwb(160, 40%, 50%)');\n        assert.equal(c.red, 102);\n        assert.equal(c.green, 128);\n        assert.equal(c.blue, 119);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('hwb(160, 40%, 50% / 50%)');\n        assert.equal(c.red, 102);\n        assert.equal(c.green, 128);\n        assert.equal(c.blue, 119);\n        assert.equal(c.alpha, 0.5);\n      });\n\n      it('allows hsl with space notation', () => {\n        const c = new Color();\n        c.parseColorFnString('hwb(160 40% 50% / 50%)');\n        assert.equal(c.red, 102);\n        assert.equal(c.green, 128);\n        assert.equal(c.blue, 119);\n        assert.equal(c.alpha, 0.5);\n      });\n    });\n\n    describe('with lab()', () => {\n      it('allows lab', () => {\n        const c = new Color();\n        c.parseColorFnString('lab(66.26 -37.50 8.58)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('lab(66.26 -37.50 8.58 / 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.5);\n      });\n    });\n\n    describe('with lch()', () => {\n      it('allows lch', () => {\n        const c = new Color();\n        c.parseColorFnString('lch(66.26 38.47 167.1)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('lch(66.26 38.47 167.1 / 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.5);\n      });\n    });\n\n    describe('with oklab()', () => {\n      it('allows oklab', () => {\n        const c = new Color();\n        c.parseColorFnString('oklab(0.697 -0.107 0.023)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('oklab(0.697 -0.107 0.023 / 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.5);\n      });\n    });\n\n    describe('with oklch()', () => {\n      it('allows oklch', () => {\n        const c = new Color();\n        c.parseColorFnString('oklch(0.6967 0.109 167.711)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 1);\n      });\n\n      it('allows alpha values', () => {\n        const c = new Color();\n        c.parseColorFnString('oklch(0.6967 0.109 167.711 / 50%)');\n        assert.equal(c.red, 77);\n        assert.equal(c.green, 179);\n        assert.equal(c.blue, 144);\n        assert.equal(c.alpha, 0.5);\n      });\n\n      it('clips out of gamut values', () => {\n        const c = new Color();\n        c.parseColorFnString('oklch(25% 0.75 345)');\n        assert.equal(c.red, 186);\n        assert.equal(c.green, 0);\n        assert.equal(c.blue, 103);\n        assert.equal(c.alpha, 1);\n\n        assert.equal(c.red, Math.round(c.r * 255));\n        assert.equal(c.green, Math.round(c.g * 255));\n        assert.equal(c.blue, Math.round(c.b * 255));\n      });\n    });\n  });\n\n  describe('parseHexString', () => {\n    it('returns values from a 6 digit hex string', () => {\n      const color = new Color();\n      color.parseHexString('#123Abc');\n      assert.equal(color.red, 18);\n      assert.equal(color.green, 58);\n      assert.equal(color.blue, 188);\n      assert.equal(color.alpha, 1);\n    });\n\n    it('returns values from a 3 digit hex string', () => {\n      const color = new Color();\n      color.parseHexString('#19E');\n      assert.equal(color.red, 17);\n      assert.equal(color.green, 153);\n      assert.equal(color.blue, 238);\n      assert.equal(color.alpha, 1);\n    });\n\n    it('returns values from a 8 digit hex string', () => {\n      const color = new Color();\n      color.parseHexString('#123ABCde');\n      assert.equal(color.red, 18);\n      assert.equal(color.green, 58);\n      assert.equal(color.blue, 188);\n      assert.closeTo(color.alpha, 222 / 255, 0.001);\n    });\n\n    it('returns values from a 4 digit hex string', () => {\n      const color = new Color();\n      color.parseHexString('#19EC');\n      assert.equal(color.red, 17);\n      assert.equal(color.green, 153);\n      assert.equal(color.blue, 238);\n      assert.closeTo(color.alpha, 204 / 255, 0.01);\n    });\n\n    it('does nothing when passed an invalid string', () => {\n      const color = new Color(1, 2, 3, 0.4);\n      const values = ['abcdef', '#abcde', '#XYZ', '#0123456789'];\n      values.forEach(function (val) {\n        color.parseHexString(val);\n        assert.equal(color.red, 1);\n        assert.equal(color.green, 2);\n        assert.equal(color.blue, 3);\n        assert.equal(color.alpha, 0.4);\n      });\n    });\n  });\n\n  describe('parseString', () => {\n    it('sets the value of a named color', () => {\n      const color = new Color();\n      color.parseString('chocolate');\n      assert.equal(color.red, 210);\n      assert.equal(color.green, 105);\n      assert.equal(color.blue, 30);\n      assert.equal(color.alpha, 1);\n    });\n\n    it('returns everything on 0 with transparent', () => {\n      const color = new Color(255, 255, 255, 1);\n      color.parseString('transparent');\n      assert.equal(color.red, 0);\n      assert.equal(color.green, 0);\n      assert.equal(color.blue, 0);\n      assert.equal(color.alpha, 0);\n    });\n\n    it('sets hex colors', () => {\n      const color = new Color();\n      color.parseString('#F00C');\n      assert.equal(color.red, 255);\n      assert.equal(color.green, 0);\n      assert.equal(color.blue, 0);\n      assert.closeTo(color.alpha, 204 / 255, 0.01);\n    });\n\n    it('sets rgb colors', () => {\n      const color = new Color();\n      color.parseString('rgb(10, 20, 30)');\n      assert.equal(color.red, 10);\n      assert.equal(color.green, 20);\n      assert.equal(color.blue, 30);\n      assert.equal(color.alpha, 1);\n    });\n\n    it('sets rgba colors', () => {\n      const color = new Color();\n      color.parseString('rgba(10, 20, 30, 0.4)');\n      assert.equal(color.red, 10);\n      assert.equal(color.green, 20);\n      assert.equal(color.blue, 30);\n      assert.equal(color.alpha, 0.4);\n    });\n\n    it('allows hsl', () => {\n      const c = new Color();\n      c.parseString('hsl(160, 40%, 50%)');\n      assert.equal(c.red, 77);\n      assert.equal(c.green, 179);\n      assert.equal(c.blue, 144);\n      assert.equal(c.alpha, 1);\n    });\n\n    it('allows hsla', () => {\n      const c = new Color();\n      c.parseString('hsla(160, 40%, 50%, .5)');\n      assert.equal(c.red, 77);\n      assert.equal(c.green, 179);\n      assert.equal(c.blue, 144);\n      assert.equal(c.alpha, 0.5);\n    });\n  });\n\n  describe('toHexString', () => {\n    it('should return hex values properly', () => {\n      const black = new Color(0, 0, 0, 1);\n      const white = new Color(255, 255, 255, 1);\n      const yellow = new Color(255, 255, 0, 1);\n      const darkyellow = new Color(128, 128, 0, 1);\n      const blue = new Color(0, 0, 255, 1);\n      assert.equal(black.toHexString(), '#000000');\n      assert.equal(white.toHexString(), '#ffffff');\n      assert.equal(yellow.toHexString(), '#ffff00');\n      assert.equal(darkyellow.toHexString(), '#808000');\n      assert.equal(blue.toHexString(), '#0000ff');\n    });\n\n    it('should return hex values properly when they are non-integery', () => {\n      const black = new Color(0, 0, 0, 1);\n      const white = new Color(255, 255, 255, 0.1);\n      const grayish = axe.commons.color.flattenColors(white, black);\n      assert.equal(grayish.toHexString(), '#1a1a1a');\n    });\n  });\n\n  describe('getRelativeLuminance', () => {\n    it('should calculate luminance sensibly', () => {\n      const black = new Color(0, 0, 0, 1);\n      const white = new Color(255, 255, 255, 1);\n      const yellow = new Color(255, 255, 0, 1);\n      const darkyellow = new Color(128, 128, 0, 1);\n      const blue = new Color(0, 0, 255, 1);\n      const lBlack = black.getRelativeLuminance();\n      const lWhite = white.getRelativeLuminance();\n      const lYellow = yellow.getRelativeLuminance();\n      const lDarkyellow = darkyellow.getRelativeLuminance();\n      const lBlue = blue.getRelativeLuminance();\n\n      //values range from zero to one\n      assert.equal(lBlack, 0);\n      assert.equal(lWhite, 1);\n\n      //brighter values are more luminant than darker ones\n      assert.isTrue(lWhite > lYellow);\n      assert.isTrue(lYellow > lDarkyellow);\n      assert.isTrue(lYellow > lBlue);\n      assert.isTrue(lBlue > lBlack);\n    });\n  });\n\n  describe('getLuminosity', () => {\n    it('returns luminosity of the Color', () => {\n      const L = new Color(128, 128, 0, 1).getLuminosity();\n      assert.equal(L, 0.44674509803921564);\n    });\n  });\n\n  describe('setLuminosity', () => {\n    it('sets the luminosity of the Color', () => {\n      const color = new Color(0, 0, 0, 1).setLuminosity(0.5);\n      assert.deepEqual(color.toJSON(), {\n        red: 128,\n        green: 128,\n        blue: 128,\n        alpha: 1\n      });\n    });\n\n    it('returns a new Color', () => {\n      const black = new Color(0, 0, 0, 1);\n      const nBlack = black.setLuminosity(0.5);\n      assert.notEqual(black, nBlack);\n    });\n  });\n\n  describe('getSaturation', () => {\n    it('returns the saturation of the Color', () => {\n      const s = new Color(255, 128, 200, 1).getSaturation();\n      assert.equal(s, 0.4980392156862745);\n    });\n  });\n\n  describe('setSaturation', () => {\n    it('sets the saturation of the Color', () => {\n      const color = new Color(128, 100, 0, 1).setSaturation(0.8);\n      assert.deepEqual(color.toJSON(), {\n        red: 204,\n        green: 159,\n        blue: 0,\n        alpha: 1\n      });\n    });\n\n    it('returns a new Color', () => {\n      const black = new Color(0, 0, 0, 1);\n      const nBlack = black.setSaturation(0.5);\n      assert.notEqual(black, nBlack);\n    });\n  });\n\n  describe('clip', () => {\n    it('clips to the lower bound', () => {\n      const color = new Color(255, 0, -1, 1).clip();\n      assert.equal(color.r, 0.9909493297254295);\n      assert.equal(color.g, 0.003870895819239939);\n      assert.equal(color.b, 0);\n    });\n\n    it('clips to the upper bound', () => {\n      const color = new Color(255, 0, 256, 1).clip();\n      assert.equal(color.r, 0.9961043436801178);\n      assert.equal(color.g, 0.002711982110142841);\n      assert.equal(color.b, 1);\n    });\n\n    it('clips both the lower and upper bounds', () => {\n      const color = new Color(-1, 0, 256, 1).clip();\n      assert.equal(color.r, 0.00047889410870861904);\n      assert.equal(color.g, 0.004247986549875488);\n      assert.equal(color.b, 0.9691356514885925);\n    });\n\n    it('returns a new Color', () => {\n      const black = new Color(0, 0, 0, 1);\n      const nBlack = black.clip();\n      assert.notEqual(black, nBlack);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/element-has-image.js",
    "content": "describe('color.elementHasImage', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var elementHasImage = axe.commons.color.elementHasImage;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe.commons.color.incompleteData.clear();\n  });\n\n  it('returns true when `HTMLElement` is of graphical type', function () {\n    ['img', 'canvas', 'object', 'iframe', 'video', 'svg'].forEach(\n      function (nodeName) {\n        var vNode = queryFixture(\n          '<' + nodeName + ' id=\"target\"></' + nodeName + '>'\n        );\n        var actual = elementHasImage(vNode.actualNode);\n        assert.isTrue(actual);\n        assert.equal(\n          axe.commons.color.incompleteData.get('bgColor'),\n          'imgNode'\n        );\n      }\n    );\n  });\n\n  it('returns false when `HTMLElement` has no background-image style set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px;\">No background style</div>'\n    );\n    var actual = elementHasImage(vNode.actualNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when `HTMLElement` has background-image style set to none', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px; background-image: none \"> Some text... </div>'\n    );\n    var actual = elementHasImage(vNode.actualNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when `HTMLElement` has background-image (url)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px; background-image: url(london.png)\"> Some text... </div>'\n    );\n    var actual = elementHasImage(vNode.actualNode);\n    assert.isTrue(actual);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage');\n  });\n\n  it('returns true when `HTMLElement` has background-image (gradient)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px; background-image: linear-gradient(red, orange);\"> Some text... </div>'\n    );\n    var actual = elementHasImage(vNode.actualNode);\n    assert.isTrue(actual);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgGradient');\n  });\n});\n"
  },
  {
    "path": "test/commons/color/element-is-distinct.js",
    "content": "describe('color.elementIsDistinct', () => {\n  let styleElm;\n  let elementIsDistinct;\n\n  const fixture = document.getElementById('fixture');\n\n  before(() => {\n    styleElm = document.createElement('style');\n    document.head.appendChild(styleElm);\n  });\n\n  const defaultStyle = {\n    color: '#000',\n    textDecoration: 'none'\n  };\n\n  function createStyleString(selector, outerStyle) {\n    // Merge style with the default\n    const styleObj = {};\n    for (const prop in defaultStyle) {\n      if (defaultStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = defaultStyle[prop];\n      }\n    }\n    for (const prop in outerStyle) {\n      if (outerStyle.hasOwnProperty(prop)) {\n        styleObj[prop] = outerStyle[prop];\n      }\n    }\n\n    const cssLines = Object.keys(styleObj)\n      .map(prop => {\n        // Make camelCase prop dash separated\n        const cssPropName = prop\n          .trim()\n          .split(/(?=[A-Z])/g)\n          .reduce((name, propPiece) => {\n            if (!name) {\n              return propPiece;\n            } else {\n              return name + '-' + propPiece.toLowerCase();\n            }\n          }, null);\n\n        // Return indented line of style code\n        return '  ' + cssPropName + ':' + styleObj[prop] + ';';\n      })\n      .join('\\n');\n\n    // Add to the style element\n    styleElm.innerHTML += selector + ' {\\n' + cssLines + '\\n}\\n';\n  }\n\n  function getLinkElm(linkStyle, paragraphStyle) {\n    // Get a random id and build the style string\n    const linkId = 'linkid-' + Math.floor(Math.random() * 100000);\n    const parId = 'parid-' + Math.floor(Math.random() * 100000);\n\n    createStyleString('#' + linkId, linkStyle);\n    createStyleString('#' + parId, paragraphStyle);\n\n    fixture.innerHTML +=\n      '<p id=\"' +\n      parId +\n      '\"> Text ' +\n      '<a href=\"/\" id=\"' +\n      linkId +\n      '\">link</a>' +\n      '</p>';\n    return {\n      link: document.getElementById(linkId),\n      par: document.getElementById(parId)\n    };\n  }\n\n  beforeEach(() => {\n    createStyleString('p', defaultStyle);\n    elementIsDistinct = axe.commons.color.elementIsDistinct;\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    styleElm.innerHTML = '';\n  });\n\n  after(() => {\n    styleElm.parentNode.removeChild(styleElm);\n  });\n\n  it('returns false without style adjustments', () => {\n    const elms = getLinkElm({});\n    const result = elementIsDistinct(elms.link, elms.par);\n\n    assert.isFalse(result);\n  });\n\n  it('returns true with background-image set', () => {\n    const elms = getLinkElm({\n      background: 'url(icon.png) no-repeat'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns true with border: dashed 1px black', () => {\n    const elms = getLinkElm({\n      border: 'dashed 1px black'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns true with border-bottom: dashed 1px black', () => {\n    const elms = getLinkElm({\n      borderBottom: 'dashed 1px black'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns false with border: solid 0px black', () => {\n    const elms = getLinkElm({\n      border: 'solid 0px black'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('returns false with border: none 1px black', () => {\n    const elms = getLinkElm({\n      border: 'none 1px black'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('returns false with border: solid 1px transparent', () => {\n    const elms = getLinkElm({\n      border: 'solid 1px transparent'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('returns true with outline: solid 1px black', () => {\n    const elms = getLinkElm({\n      outline: 'solid 1px black'\n    });\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns true if font-weight is different', () => {\n    const elms = getLinkElm(\n      {\n        fontWeight: 'bold'\n      },\n      {\n        fontWeight: 'normal'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns false if font-weight is the same', () => {\n    const elms = getLinkElm(\n      {\n        fontWeight: 'bold'\n      },\n      {\n        fontWeight: 'bold'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('compares font numbers and labels correctly', () => {\n    const elms = getLinkElm(\n      {\n        fontWeight: 'bold'\n      },\n      {\n        fontWeight: '700'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('returns true if text-decoration is different', () => {\n    const elms = getLinkElm(\n      {\n        textDecoration: 'underline'\n      },\n      {\n        textDecoration: 'none'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns false if text-decoration is the same', () => {\n    const elms = getLinkElm(\n      {\n        textDecoration: 'underline'\n      },\n      {\n        textDecoration: 'underline'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n\n  it('returns true if font-size is different', () => {\n    const elms = getLinkElm(\n      {\n        fontSize: '14px'\n      },\n      {\n        fontSize: '12px'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns true if font-family is different', () => {\n    const elms = getLinkElm(\n      {\n        fontFamily: 'Arial'\n      },\n      {\n        fontFamily: 'Arial-black'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isTrue(result);\n  });\n\n  it('returns false if the first font-family is identical', () => {\n    const elms = getLinkElm(\n      {\n        fontFamily: 'Arial-black, Arial'\n      },\n      {\n        fontFamily: 'Arial-black, sans-serif'\n      }\n    );\n\n    const result = elementIsDistinct(elms.link, elms.par);\n    assert.isFalse(result);\n  });\n});\n"
  },
  {
    "path": "test/commons/color/flatten-colors.js",
    "content": "describe('color.flattenColors', function () {\n  const { Color, flattenColors } = axe.commons.color;\n\n  it('should flatten colors properly', function () {\n    const halfBlack = new Color(0, 0, 0, 0.5);\n    const fullBlack = new Color(0, 0, 0, 1);\n    const transparent = new Color(0, 0, 0, 0);\n    const white = new Color(255, 255, 255, 1);\n    const gray = new Color(128, 128, 128, 1);\n    const halfRed = new Color(255, 0, 0, 0.5);\n    const quarterLightGreen = new Color(0, 128, 0, 0.25);\n\n    const flat = flattenColors(halfBlack, white);\n    assert.equal(flat.red, gray.red);\n    assert.equal(flat.green, gray.green);\n    assert.equal(flat.blue, gray.blue);\n\n    const flat2 = flattenColors(fullBlack, white);\n    assert.equal(flat2.red, fullBlack.red);\n    assert.equal(flat2.green, fullBlack.green);\n    assert.equal(flat2.blue, fullBlack.blue);\n\n    const flat3 = flattenColors(transparent, white);\n    assert.equal(flat3.red, white.red);\n    assert.equal(flat3.green, white.green);\n    assert.equal(flat3.blue, white.blue);\n\n    const flat4 = flattenColors(halfRed, white);\n    assert.equal(flat4.red, 255);\n    assert.equal(flat4.green, 128);\n    assert.equal(flat4.blue, 128);\n    assert.equal(flat4.alpha, 1);\n\n    const flat5 = flattenColors(quarterLightGreen, white);\n    assert.equal(flat5.red, 191);\n    assert.equal(flat5.green, 223);\n    assert.equal(flat5.blue, 191);\n    assert.equal(flat5.alpha, 1);\n\n    const flat6 = flattenColors(quarterLightGreen, halfRed);\n    assert.equal(flat6.red, 153);\n    assert.equal(flat6.green, 51);\n    assert.equal(flat6.blue, 0);\n    assert.equal(flat6.alpha, 0.625);\n  });\n\n  it('handles two colors with alpha:0', () => {\n    const transparent1 = new Color(0, 0, 0, 0);\n    const transparent2 = new Color(255, 255, 255, 0);\n    const transparent3 = flattenColors(transparent1, transparent2);\n    assert.deepEqual(transparent3.toJSON(), {\n      red: 0,\n      green: 0,\n      blue: 0,\n      alpha: 0\n    });\n  });\n});\n\ndescribe('color.flattenColors ', function () {\n  'use strict';\n\n  var colourOne = new axe.commons.color.Color(216, 22, 22, 1);\n  var colourTwo = new axe.commons.color.Color(114, 129, 114, 0.25);\n\n  var colourThree = new axe.commons.color.Color(211, 162, 180, 1);\n  var colourFour = new axe.commons.color.Color(115, 255, 0, 0.5);\n\n  it('should flatten colors correctly using blend mode: multiply', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'multiply'\n    );\n    assert.equal(flatten.red, 186);\n    assert.equal(flatten.green, 19);\n    assert.equal(flatten.blue, 19);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'multiply'\n    );\n    assert.equal(flattenTwo.red, 153);\n    assert.equal(flattenTwo.green, 162);\n    assert.equal(flattenTwo.blue, 90);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: screen', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'screen'\n    );\n    assert.equal(flatten.red, 220);\n    assert.equal(flatten.green, 51);\n    assert.equal(flatten.blue, 48);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'screen'\n    );\n    assert.equal(flattenTwo.red, 221);\n    assert.equal(flattenTwo.green, 209);\n    assert.equal(flattenTwo.blue, 180);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: overlay', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'overlay'\n    );\n    assert.equal(flatten.red, 215);\n    assert.equal(flatten.green, 22);\n    assert.equal(flatten.blue, 21);\n    assert.equal(flatten.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: darken', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'darken'\n    );\n    assert.equal(flatten.red, 191);\n    assert.equal(flatten.green, 22);\n    assert.equal(flatten.blue, 22);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'darken'\n    );\n    assert.equal(flattenTwo.red, 163);\n    assert.equal(flattenTwo.green, 162);\n    assert.equal(flattenTwo.blue, 90);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: lighten', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'lighten'\n    );\n    assert.equal(flatten.red, 216);\n    assert.equal(flatten.green, 49);\n    assert.equal(flatten.blue, 45);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'lighten'\n    );\n    assert.equal(flattenTwo.red, 211);\n    assert.equal(flattenTwo.green, 209);\n    assert.equal(flattenTwo.blue, 180);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: color-dodge', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'color-dodge'\n    );\n    assert.equal(flatten.red, 226);\n    assert.equal(flatten.green, 28);\n    assert.equal(flatten.blue, 26);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'color-dodge'\n    );\n    assert.equal(flattenTwo.red, 233);\n    assert.equal(flattenTwo.green, 209);\n    assert.equal(flattenTwo.blue, 180);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: color-burn', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'color-burn'\n    );\n    assert.equal(flatten.red, 204);\n    assert.equal(flatten.green, 17);\n    assert.equal(flatten.blue, 17);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'color-burn'\n    );\n    assert.equal(flattenTwo.red, 184);\n    assert.equal(flattenTwo.green, 162);\n    assert.equal(flattenTwo.blue, 90);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: hard-light', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'hard-light'\n    );\n    assert.equal(flatten.red, 210);\n    assert.equal(flatten.green, 23);\n    assert.equal(flatten.blue, 21);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'hard-light'\n    );\n    assert.equal(flattenTwo.red, 201);\n    assert.equal(flattenTwo.green, 209);\n    assert.equal(flattenTwo.blue, 90);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: soft-light', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'soft-light'\n    );\n    assert.equal(flatten.red, 215);\n    assert.equal(flatten.green, 22);\n    assert.equal(flatten.blue, 21);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'soft-light'\n    );\n    assert.equal(flattenTwo.red, 209);\n    assert.equal(flattenTwo.green, 183);\n    assert.equal(flattenTwo.blue, 154);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: difference', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'difference'\n    );\n    assert.equal(flatten.red, 188);\n    assert.equal(flatten.green, 43);\n    assert.equal(flatten.blue, 40);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'difference'\n    );\n    assert.equal(flattenTwo.red, 154);\n    assert.equal(flattenTwo.green, 128);\n    assert.equal(flattenTwo.blue, 180);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: exclusion', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'exclusion'\n    );\n    assert.equal(flatten.red, 196);\n    assert.equal(flatten.green, 49);\n    assert.equal(flatten.blue, 46);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'exclusion'\n    );\n    assert.equal(flattenTwo.red, 173);\n    assert.equal(flattenTwo.green, 128);\n    assert.equal(flattenTwo.blue, 180);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: hue', function () {\n    var flatten = axe.commons.color.flattenColors(colourTwo, colourOne, 'hue');\n    assert.equal(flatten.red, 162);\n    assert.equal(flatten.green, 50);\n    assert.equal(flatten.blue, 17);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'hue'\n    );\n    assert.equal(flattenTwo.red, 188);\n    assert.equal(flattenTwo.green, 177);\n    assert.equal(flattenTwo.blue, 162);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: saturation', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'saturation'\n    );\n    assert.equal(flatten.red, 185);\n    assert.equal(flatten.green, 35);\n    assert.equal(flatten.blue, 35);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'saturation'\n    );\n    assert.equal(flattenTwo.red, 233);\n    assert.equal(flattenTwo.green, 151);\n    assert.equal(flattenTwo.blue, 181);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: color', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'color'\n    );\n    assert.equal(flatten.red, 180);\n    assert.equal(flatten.green, 38);\n    assert.equal(flatten.blue, 34);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'color'\n    );\n    assert.equal(flattenTwo.red, 161);\n    assert.equal(flattenTwo.green, 204);\n    assert.equal(flattenTwo.blue, 90);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n\n  it('should flatten colors correctly using blend mode: luminosity', function () {\n    var flatten = axe.commons.color.flattenColors(\n      colourTwo,\n      colourOne,\n      'luminosity'\n    );\n    assert.equal(flatten.red, 226);\n    assert.equal(flatten.green, 33);\n    assert.equal(flatten.blue, 33);\n    assert.equal(flatten.alpha, 1);\n\n    var flattenTwo = axe.commons.color.flattenColors(\n      colourFour,\n      colourThree,\n      'luminosity'\n    );\n    assert.equal(flattenTwo.red, 214);\n    assert.equal(flattenTwo.green, 165);\n    assert.equal(flattenTwo.blue, 183);\n    assert.equal(flattenTwo.alpha, 1);\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-background-color.js",
    "content": "describe('color.getBackgroundColor', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  /**\n   * Assert that two Colors are close-to-equal.\n   * @param {axe.commons.color.Color[]} actual\n   * @param {axe.commons.color.Color[]} expected\n   * @param {number} threshold How much each RGB value may differ by\n   * @param {number} alphaThreshold How much the alpha channel may differ by\n   */\n  function assertColorsClose(\n    actual,\n    expected,\n    threshold = 0.5,\n    alphaThreshold = 0.1\n  ) {\n    assert.closeTo(actual.red, expected.red, threshold, 'red');\n    assert.closeTo(actual.green, expected.green, threshold, 'green');\n    assert.closeTo(actual.blue, expected.blue, threshold, 'blue');\n    assert.closeTo(actual.alpha, expected.alpha, alphaThreshold, 'alpha');\n  }\n\n  beforeEach(function () {\n    // This normalizes the default mocha behavior of setting a different background\n    // based on prefers-color-scheme settings.\n    document.body.style.background = '#fff';\n    document.documentElement.style.background = 'unset';\n  });\n\n  afterEach(function () {\n    axe.commons.color.incompleteData.clear();\n    axe._tree = undefined;\n  });\n\n  it('should return the blended color if it has no background set', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(128, 0, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should return the blended color if it is transparent and positioned', function () {\n    fixture.innerHTML =\n      '<div style=\"position: absolute; top: 0px; left: 0px; height: 100px; ' +\n      'width: 90px; background-color: #000080;\">' +\n      '<div id=\"pos\" style=\"position: absolute; top: 50px; left: 40px; height: 40px; ' +\n      'width: 30px; background-color: #800000;\"></div>' +\n      '<div id=\"parent\" style=\"position: absolute; top: 0px; left: 0px; height: 40px; ' +\n      'width: 30px; background-color: #ffffff\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 60px; left: 45px; height: 20px; ' +\n      'width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n      '</div></div></div>';\n    var target = fixture.querySelector('#target');\n    var pos = fixture.querySelector('#pos');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(64, 64, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [target, pos]);\n  });\n\n  it('should do alpha blending from the back forward', function () {\n    fixture.innerHTML =\n      '<div id=\"under\" style=\"height: 20px; width: 15px; background-color: #800000;\">' +\n      '<div id=\"transparent\" style=\"height: 20px; width: 15px; background-color: rgba(0, 0, 0, 0);\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n      '</div></div></div>';\n    var target = fixture.querySelector('#target');\n    var under = fixture.querySelector('#under');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(64, 64, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [target, under]);\n  });\n\n  it('should only look at what is underneath original element when blended and positioned', function () {\n    fixture.innerHTML =\n      '<div style=\"position: absolute; top: 0px; left: 0px; height: 100px; ' +\n      'width: 90px; background-color: #000080;\">' +\n      '<div id=\"under\" style=\"position: absolute; top: 50px; left: 40px; height: 40px; ' +\n      'width: 30px; background-color: #800000;\"></div>' +\n      '<div id=\"pos\" style=\"position: absolute; top: 0px; left: 0px; height: 90px; ' +\n      'width: 70px; background-color: rgba(0, 0, 0, 0);\"></div>' +\n      '<div id=\"parent\" style=\"position: absolute; top: 0px; left: 0px; height: 40px; ' +\n      'width: 30px; background-color: #ffffff\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 60px; left: 45px; height: 20px; ' +\n      'width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n      '</div></div></div>';\n    var target = fixture.querySelector('#target');\n    var under = fixture.querySelector('#under');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(64, 64, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [target, under]);\n  });\n\n  it('should return the proper blended color if it has alpha set', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(64, 64, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [target, parent]);\n  });\n\n  it('should return the blended color if it has opacity set', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; opacity: 0.5; background-color: green;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(64, 64, 0, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [target, parent]);\n  });\n\n  it('should apply opacity after blending', function () {\n    fixture.innerHTML = `\n      <div id=\"parent\" style=\"height: 40px; width: 30px; background-color: rgba(128,0,0,1); opacity: 0.8;\">\n        <div id=\"target\" style=\"height: 20px; width: 15px; background-color: rgba(0,255,0,0.5);\"></div>\n      </div>`;\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(102, 153, 51, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should apply opacity from an ancestor not in the element stack', function () {\n    fixture.innerHTML = `\n      <div style=\"opacity: 0.8;\">\n        <div id=\"parent\" style=\"position: absolute; height: 40px; width: 30px; background-color: rgba(128,0,0,1);\">\n          <div id=\"target\" style=\"height: 20px; width: 15px; background-color: rgba(0,255,0,0.5);\"></div>\n        </div>\n      </div>`;\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(102, 153, 51, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should return null if containing parent has a background image and is non-opaque', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px;' +\n      'background-color: #800000; background-image: url(image.png);\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: green; opacity: 0.5;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    assert.isNull(actual);\n    assert.deepEqual(bgNodes, [target, parent]);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage');\n  });\n\n  it('should return body color if transparency goes all the way up to document', function () {\n    fixture.innerHTML = '<div id=\"target\" style=\"height: 10px; width: 30px;\">';\n    var target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target);\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should return null if there is a background image', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: green; background-image: url(image.png);\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    assert.isNull(actual);\n    assert.deepEqual(bgNodes, [target]);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage');\n  });\n\n  it('should return null if something non-opaque is obscuring it', function () {\n    fixture.innerHTML =\n      '<div style=\"width:100%; height: 100px; background: #000\"></div>' +\n      '<div id=\"target\" style=\"position: relative; top: -50px; z-index:-1;color:#fff;\">Hello</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgOverlap');\n    assert.isNull(actual);\n  });\n\n  it('should return null if something non-opaque is obscuring it, scrolled out of view', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 1em; overflow: auto; position: relative;\">' +\n      '  <div style=\"background: rgba(0, 255, 255, 0.7); ' +\n      '    margin-bottom: -1em; height: 1em; position:relative;\"></div>' +\n      '  <div id=\"target\">foo</div>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target')\n    );\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgOverlap');\n    assert.isNull(actual);\n  });\n\n  it('should return an actual if something opaque is obscuring it', function () {\n    fixture.innerHTML =\n      '<div style=\"width:100%; height: 100px; background: rgba(0, 0, 0, 0.5)\"></div>' +\n      '<div id=\"target\" style=\"position: relative; top: -50px; z-index:-1;color:#fff;\">Hello</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgOverlap');\n    assert.isNull(actual);\n  });\n\n  it('should return the bgcolor if it is solid', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 40px; width: 30px; background-color: red;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: green;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(0, 128, 0, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [target]);\n  });\n\n  it('should return a bgcolor for a multiline inline element fully covering the background', function () {\n    fixture.innerHTML =\n      '<div style=\"position:relative;\">' +\n      '<div style=\"background-color:rgba(0,0,0,1);position:absolute;width:300px;height:200px;\"></div>' +\n      '<p style=\"position: relative;z-index:1;\">Text oh heyyyy <a href=\"#\" id=\"target\">and here\\'s <br>a link</a></p>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    assert.isNotNull(actual);\n    assert.equal(Math.round(actual.blue), 0);\n    assert.equal(Math.round(actual.red), 0);\n    assert.equal(Math.round(actual.green), 0);\n  });\n\n  it('should return null if a multiline inline element does not fully cover background', function () {\n    fixture.innerHTML =\n      '<div style=\"position:relative;\">' +\n      '<div style=\"background-color:rgba(0,0,0,1);position:absolute;width:300px;height:20px;\"></div>' +\n      '<p style=\"position: relative;z-index:1;\">Text oh heyyyy <a href=\"#\" id=\"target\">and here\\'s <br>a link</a></p>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    assert.isNull(actual);\n    assert.equal(\n      axe.commons.color.incompleteData.get('bgColor'),\n      'elmPartiallyObscuring'\n    );\n  });\n\n  it('should return an actual if an absolutely positioned element does not cover background', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:black; height:20px; position:relative;\">' +\n      '<div style=\"color:#333; position:absolute; top:21px;\" id=\"target\">Text</div>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    var expected = new axe.commons.color.Color(255, 255, 255);\n    assertColorsClose(actual, expected);\n  });\n\n  it('should return null if an absolutely positioned element partially obsures background', function () {\n    fixture.innerHTML =\n      '<div style=\"height:40px; position:relative;\">' +\n      '<div style=\"background-color:black; height:20px;\"></div>' +\n      '<div style=\"color:#333; position:absolute; margin-top:-11px;\" id=\"target\">Text</div>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n    assert.isNull(actual);\n    assert.equal(\n      axe.commons.color.incompleteData.get('bgColor'),\n      'elmPartiallyObscured'\n    );\n  });\n\n  it('should count a TR as a background element for TD', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<tr style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td style=\"color:#007acc\" id=\"target\">' +\n      'Cell content</td>' +\n      '</tr>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should count a TR as a background element for TH', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<tr style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<th style=\"color:#007acc\" id=\"target\">' +\n      'Header content</th>' +\n      '</tr>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should count a TR as a background element for a child element', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<tr style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td>' +\n      '<span style=\"color:#007acc\" id=\"target\">Cell content</span>' +\n      '</td></tr>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should count a THEAD as a background element for a child element', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<thead style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td>' +\n      '<span style=\"color:#007acc\" id=\"target\">Cell content</span>' +\n      '</td></thead>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should count a TBODY as a background element for a child element', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<tbody style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td>' +\n      '<span style=\"color:#007acc\" id=\"target\">Cell content</span>' +\n      '</td></tbody>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should count a TFOOT as a background element for a child element', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color:#007acc;\">' +\n      '<table style=\"width:100%\">' +\n      '<tfoot style=\"background-color:#f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td>' +\n      '<span style=\"color:#007acc\" id=\"target\">Cell content</span>' +\n      '</td></tfoot>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it(\"should ignore TR elements that don't overlap\", function () {\n    fixture.innerHTML =\n      '<table style=\"position:relative; width:100%;\">' +\n      '<tr style=\"background-color:black; height:10px; width:100%;\" id=\"parent\">' +\n      '<td style=\"position:absolute; top: 14px;\" id=\"target\">Content</td>' +\n      '</tr></table>';\n    var bgNodes = [];\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assert.deepEqual(actual, expected);\n    assert.notEqual(bgNodes, [parent]);\n  });\n\n  it('should count an implicit label as a background element', function () {\n    fixture.innerHTML =\n      '<label id=\"target\" style=\"background-color: #000;\">My label' +\n      '<input type=\"text\">' +\n      '</label>';\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(0, 0, 0, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('handles nested inline elements in the middle of a text', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 1em; overflow:auto; background: cyan\">' +\n      '  <br>' +\n      '  <b id=\"target\">Text <i style=\"display: inline-block\">' +\n      '    <s><img width=\"100\" height=\"16\"></s>' +\n      '  </i></b>' +\n      '</div>';\n\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(0, 255, 255, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should return null for inline elements with position:absolute', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 1em; overflow:auto; position: relative\">' +\n      '  <br>' +\n      '  <b id=\"target\">' +\n      '    <img style=\"width:100px; height:16px; position:absolute\"> Text' +\n      '  </b>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var actual = axe.commons.color.getBackgroundColor(target);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgOverlap');\n    assert.isNull(actual);\n  });\n\n  it('should ignore inline ancestors of non-overlapping elements', function () {\n    fixture.innerHTML =\n      '<div style=\"position:relative;\">' +\n      '<label style=\"background-color:black;\" id=\"parent\">Label' +\n      '<input style=\"position:absolute; top:20px;\" id=\"target\">' +\n      '</label></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assert.deepEqual(actual, expected);\n    assert.notEqual(bgNodes, [parent]);\n  });\n\n  it('should handle multiple ancestors of the same name', function () {\n    fixture.innerHTML =\n      '<div style=\"background-color: #007acc;\">' +\n      '<table style=\"width: 100%;\">' +\n      '<tr style=\"background-color: #fff;\"><td>' +\n      '<table style=\"width:100%\">' +\n      '<tr style=\"background-color: #f3f3f3; height:40px;\" id=\"parent\">' +\n      '<td style=\"display: table-cell; color:#007acc\" id=\"target\">' +\n      'Cell content</td>' +\n      '</tr>' +\n      '</table>' +\n      '</td></tr>' +\n      '</table></div>';\n    var target = fixture.querySelector('#target'),\n      parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(243, 243, 243, 1);\n    assert.deepEqual(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should use hierarchical DOM traversal if possible', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; ' +\n      ' background-color: white;\">' +\n      '\t<div id=\"target\" style=\"height: 20px; width: 25px; z-index: 25; position:relative;\">' +\n      '\t</div>' +\n      '</div>' +\n      '<div id=\"shifted\" style=\"position: relative; top: -10px; height: 40px; width: 35px; ' +\n      ' background-color: black; z-index: 15;\"></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should ignore 0-height elements', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; ' +\n      'background-color: white; position: relative; z-index: 5\">' +\n      '\t<div float=\"left\" style=\"height: 0px; background-color: black\">' +\n      '\t\t<div id=\"target\" style=\"height: 20px; width: 25px; z-index: 25;\">' +\n      '</div></div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('should use visual traversal when needed', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; ' +\n      ' background-color: white; position: relative; z-index: 5\">' +\n      '\t<div id=\"target\" style=\"position: relative; top: 1px; height: 20px; width: 25px; z-index: 25;\">' +\n      '\t</div>' +\n      '<div id=\"shifted\" style=\"position: relative; top: -30px; height: 40px; width: 35px; ' +\n      ' background-color: black; z-index: 15;\"></div></div>';\n\n    var target = fixture.querySelector('#target');\n    var shifted = fixture.querySelector('#shifted');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes, false);\n    var expected = new axe.commons.color.Color(0, 0, 0, 1);\n\n    assert.deepEqual(bgNodes, [shifted]);\n\n    assertColorsClose(actual, expected);\n  });\n\n  it('should return null when encountering background images during visual traversal', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; ' +\n      ' background-color: white; position: relative; z-index: 5\"> ' +\n      '\t<div id=\"target\" style=\"position: relative; top: 1px; height: 20px;' +\n      '\t width: 25px; z-index: 25; background:rgba(0,125,0,0.5);\"></div> ' +\n      '\t<div id=\"shifted\" style=\"position: absolute; top: 0px; height: 40px; ' +\n      '\t\tbackground-image: url(foobar.png);' +\n      '\t width: 35px; z-index: 15;\">' +\n      '\t</div>' +\n      '</div>';\n\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var outcome = axe.commons.color.getBackgroundColor(target, bgNodes, false);\n    assert.isNull(outcome);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage');\n  });\n\n  it('should return null when encountering image nodes during visual traversal', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; ' +\n      ' background-color: white; position: relative; z-index: 5\"> ' +\n      '\t<div id=\"shifted\" style=\"position: absolute; top: -10px; height: 40px; ' +\n      '\t width: 35px; z-index: 15;\">' +\n      '\t\t<img src=\"some-img.png\" width=\"35\" height=\"40\">' +\n      '\t</div>' +\n      '\t<div id=\"target\" style=\"position: relative; top: 1px; height: 20px;' +\n      '\t width: 25px; z-index: 25; background:rgba(0,125,0,0.5);\"></div> ' +\n      '</div>';\n\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var outcome = axe.commons.color.getBackgroundColor(target, bgNodes, false);\n    assert.isNull(outcome);\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'imgNode');\n  });\n\n  it('returns elements with negative z-index', function () {\n    fixture.innerHTML =\n      '<div id=\"sibling\" ' +\n      'style=\"z-index:-1; position:absolute; width:100%; height:2em; background: #000\"></div>' +\n      '<div id=\"target\">Some text</div>';\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n\n    var expected = new axe.commons.color.Color(0, 0, 0, 1);\n\n    assertColorsClose(actual, expected);\n  });\n\n  it('returns negative z-index elements when body has a background', function () {\n    fixture.innerHTML =\n      '<div id=\"sibling\" ' +\n      'style=\"z-index:-1; position:absolute; width:100%; height:2em; background: #000\"></div>' +\n      '<div id=\"target\">Some text</div>';\n\n    document.body.style.background = '#FFF';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n\n    var expected = new axe.commons.color.Color(0, 0, 0, 1);\n\n    assertColorsClose(actual, expected);\n  });\n\n  it('should return null for negative z-index element when html and body have a background', function () {\n    fixture.innerHTML =\n      '<div style=\"width: 200px; height: 200px;\"></div>' +\n      '<div id=\"target\" ' +\n      'style=\"z-index:-1; position:absolute; top: 100px; width:100%; height:2em; background: #000\"></div>';\n\n    document.documentElement.style.background = '#0F0';\n    document.body.style.background = '#FFF';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(\n      document.getElementById('target'),\n      []\n    );\n\n    assert.isNull(actual);\n  });\n\n  it('should return background color for inline elements that do not fit the viewport', function () {\n    var html = '';\n    for (var i = 0; i < 300; i++) {\n      html += 'foo<br />';\n    }\n    fixture.innerHTML = '<em>' + html + '</em>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(fixture, []);\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assertColorsClose(actual, expected);\n  });\n\n  it('should return the body bgColor when content does not overlap', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 20px; width: 30px; background-color: red;\">' +\n      '<div id=\"target\" style=\"height:20px; top: 25px; width: 45px; position:absolute;\">Text' +\n      '</div></div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var actual = axe.commons.color.getBackgroundColor(target, []);\n    var expected = new axe.commons.color.Color(255, 255, 255, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should return the html canvas inherited from body bgColor when element content does not overlap with body', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: relative; top: 2px; height: 10px;\">Text</div>';\n\n    // size body element so that target element is positioned outside of background\n    var originalHeight = document.body.style.height;\n    var originalMargin = document.body.style.margin;\n    document.body.style.height = '1px';\n    document.body.style.background = '#000';\n    document.body.style.margin = 0;\n\n    try {\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = fixture.querySelector('#target');\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    } finally {\n      document.body.style.height = originalHeight;\n      document.body.style.margin = originalMargin;\n    }\n  });\n\n  it('should return the html canvas bgColor when element content does not overlap with body', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: relative; top: 2px;\">Text</div>';\n\n    // size body element so that target element is positioned outside of background\n    var originalHeight = document.body.style.height;\n    document.body.style.height = '1px';\n    document.body.style.background = '#0f0';\n    document.documentElement.style.background = '#f00';\n\n    try {\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = fixture.querySelector('#target');\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      var expected = new axe.commons.color.Color(255, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    } finally {\n      document.body.style.height = originalHeight;\n    }\n  });\n\n  it('should apply mix-blend-mode', function () {\n    fixture.innerHTML = `\n      <div style=\"background-color: rgba(255, 255, 255, 1)\">\n        <div style=\"background-color: rgba(0, 128, 0, 0.25)\">\n          <div id=\"target\" style=\"background-color: rgba(255, 0, 0, 0.5); mix-blend-mode: exclusion;\">exclusion1</div>\n        </div>\n      </div>\n    `;\n\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var actual = axe.commons.color.getBackgroundColor(target, []);\n\n    assert.closeTo(actual.red, 128, 0);\n    assert.closeTo(actual.green, 223, 0);\n    assert.closeTo(actual.blue, 191, 0);\n    assert.closeTo(actual.alpha, 1, 0);\n  });\n\n  (shadowSupported ? it : xit)(\n    'finds colors in shadow boundaries',\n    function () {\n      fixture.innerHTML = '<div id=\"container\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div style=\"background-color: black;\">' +\n        '<span id=\"shadowTarget\" style=\"color: #ccc;\">Text</span>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      var target = shadow.querySelector('#shadowTarget');\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'finds colors across shadow boundaries',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<span id=\"shadowTarget\" style=\"color:#ccc;\">Text</span>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      var target = shadow.querySelector('#shadowTarget');\n      var actual = axe.commons.color.getBackgroundColor(target, [], false);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should count an implicit label as a background element inside shadow dom',\n    function () {\n      fixture.innerHTML = '<div id=\"container\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div><label id=\"target\" style=\"background-color:#000;\">Text<input type=\"text\"></label></div>';\n\n      var target = shadow.querySelector('#target');\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'finds colors for absolutely positioned elements across shadow boundaries',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black; height:20px; position:relative;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color:#333; height:20px; position:absolute; top:20px;\">Text</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      var target = shadow.querySelector('#shadowTarget');\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      var expected = new axe.commons.color.Color(255, 255, 255, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'finds a color for absolutely positioned content when background is in shadow dom',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"elm1\" style=\"width:10em; height:0; position:absolute;\"></div>' +\n        '<div id=\"elm2\" style=\"color:green; position:absolute;\">Text</div>';\n\n      var elm1 = document.querySelector('#elm1');\n      var shadow1 = elm1.attachShadow({ mode: 'open' });\n      shadow1.innerHTML =\n        '<div style=\"background:rgba(0,0,0,1); height:10em;\"></div>';\n      var elm2 = document.querySelector('#elm2');\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(elm2, []);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'finds colors for content rendered across multiple shadow boundaries',\n    function () {\n      fixture.innerHTML =\n        '<div style=\"position:relative;\"><div id=\"elm1\" style=\"width:10em;\"></div>' +\n        '<div id=\"elm2\"></div></div>';\n\n      var elm1 = document.querySelector('#elm1');\n      var shadow1 = elm1.attachShadow({ mode: 'open' });\n      shadow1.innerHTML =\n        '<div style=\"background:rgba(0,0,0,1); height:10em;\"></div>';\n      var elm2 = document.querySelector('#elm2');\n      var shadow2 = elm2.attachShadow({ mode: 'open' });\n      shadow2.innerHTML =\n        '' +\n        '<div id=\"elm3\" style=\"background:rgba(255,255,255,0.5);color:green;height:10em;top:0;position:absolute;\">' +\n        'Text' +\n        '</div>';\n\n      var elm3 = shadow2.querySelector('#elm3');\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(elm3, []);\n      var expected = new axe.commons.color.Color(128, 128, 128, 1);\n      assertColorsClose(actual, expected, 2, 0);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'finds colors for multiline elements across shadow boundaries',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black; height:40px;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color:#333;\">Text<br>More text</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#shadowTarget');\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  (shadowSupported ? xit : xit)(\n    'returns null for multiline elements not fully covering parents across shadow boundaries',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black; height:20px;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color:#333;\">Text<br>More text</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#shadowTarget');\n      var actual = axe.commons.color.getBackgroundColor(target, []);\n      assert.isNull(actual);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'returns a color for slotted content',\n    function () {\n      fixture.innerHTML = '<div id=\"container\"></div>';\n      var div = fixture.querySelector('#container');\n      div.innerHTML = '<a href=\"\">Link</a>';\n      var shadow = div.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<p style=\"background-color: #000;\"><slot></slot></p>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var linkElm = div.querySelector('a');\n      var actual = axe.commons.color.getBackgroundColor(linkElm, []);\n      var expected = new axe.commons.color.Color(0, 0, 0, 1);\n      assert.deepEqual(actual, expected);\n    }\n  );\n\n  it('should return the text-shadow mixed in with the background', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; text-shadow: red 0 0 1em\">foo' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n\n    // is 128 without the shadow\n    var expected = new axe.commons.color.Color(145, 0, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('ignores thin text-shadows', function () {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; text-shadow: red 0 0 0.05em\">foo' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n    var expected = new axe.commons.color.Color(0, 0, 0, 1);\n    assert.deepEqual(actual, expected);\n  });\n\n  it('filters small shadows, while keeping larger ones', () => {\n    fixture.innerHTML = `\n      <div id=\"parent\" style=\"height: 40px; width: 30px; background-color: #800000;\">\n      <div id=\"target\" style=\"\n        height: 20px; \n        width: 15px; \n        text-shadow: red 0 0 1em, black 1px 1px 0;\">foo\n      </div></div>\n    `;\n    var target = fixture.querySelector('#target');\n    var parent = fixture.querySelector('#parent');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes);\n\n    // is 128 without the shadow\n    var expected = new axe.commons.color.Color(145, 0, 0, 1);\n    assertColorsClose(actual, expected);\n    assert.deepEqual(bgNodes, [parent]);\n  });\n\n  it('ignores text-shadows thinner than shadowOutlineEmMax', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 40px; width: 30px; background-color: #800000;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; text-shadow: red 0 0 1em, green 0 0 0.5em\">foo' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var bgNodes = [];\n    axe.testUtils.flatTreeSetup(fixture);\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes, 1);\n\n    // is 128 without the shadow\n    var expected = new axe.commons.color.Color(145, 0, 0, 1);\n    assertColorsClose(actual, expected);\n  });\n\n  it('calculates background of a textarea', () => {\n    fixture.innerHTML =\n      '<textarea id=\"target\" style=\"background: red\">Hello</textarea>';\n    var target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    var bgNodes = [];\n    var actual = axe.commons.color.getBackgroundColor(target, bgNodes, 1);\n\n    var expected = new axe.commons.color.Color(255, 0, 0, 1);\n    assertColorsClose(actual, expected);\n  });\n\n  describe('body and document', function () {\n    it('returns the body background', function () {\n      fixture.innerHTML = '<div id=\"target\">elm</div>';\n      document.body.style.background = '#F00';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      var expected = new axe.commons.color.Color(255, 0, 0, 1);\n      assertColorsClose(actual, expected);\n    });\n\n    it('returns the body background even when the body is MUCH larger than the screen', function () {\n      fixture.innerHTML = '<div id=\"target\" style=\"height:20000px;\">elm</div>';\n      document.body.style.background = '#F00';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      var expected = new axe.commons.color.Color(255, 0, 0, 1);\n\n      assertColorsClose(actual, expected);\n    });\n\n    it('returns the html background', function () {\n      fixture.innerHTML = '<div id=\"target\"><label>elm<input></label></div>';\n      document.documentElement.style.background = '#0F0';\n      document.body.setAttribute('style', 'background: unset');\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      document.body.removeAttribute('style');\n      var expected = new axe.commons.color.Color(0, 255, 0, 1);\n\n      assertColorsClose(actual, expected);\n    });\n\n    it('returns the html background when body does not cover the element', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; top: 1000px;\">elm<input></div>';\n      document.documentElement.style.background = '#0F0';\n      document.body.style.background = '#00F';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      var expected = new axe.commons.color.Color(0, 255, 0, 1);\n\n      assertColorsClose(actual, expected);\n    });\n\n    it('returns the body background when body does cover the element', function () {\n      fixture.innerHTML = '<div id=\"target\"><label>elm<input></label></div>';\n      document.documentElement.style.background = '#0F0';\n      document.body.style.background = '#00F';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      var expected = new axe.commons.color.Color(0, 0, 255, 1);\n\n      assertColorsClose(actual, expected);\n    });\n\n    it('returns both the html and body background if the body has alpha', function () {\n      fixture.innerHTML = '<div id=\"target\"><label>elm<input></label></div>';\n      document.documentElement.style.background = '#0F0';\n      document.body.style.background = 'rgba(0, 0, 255, 0.5)';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      var actual = axe.commons.color.getBackgroundColor(\n        document.getElementById('target'),\n        []\n      );\n      var expected = new axe.commons.color.Color(0, 128, 128, 1);\n\n      assertColorsClose(actual, expected);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-contrast.js",
    "content": "describe('color.getContrast', function () {\n  'use strict';\n\n  it('should calculate contrast sensibly', function () {\n    var black = new axe.commons.color.Color(0, 0, 0, 1);\n    var transparent = new axe.commons.color.Color(0, 0, 0, 0);\n    var white = new axe.commons.color.Color(255, 255, 255, 1);\n    var yellow = new axe.commons.color.Color(255, 255, 0, 1);\n\n    //Same foreground/background gives 1\n    assert.equal(axe.commons.color.getContrast(black, black), 1);\n    assert.equal(axe.commons.color.getContrast(transparent, black), 1);\n    assert.equal(axe.commons.color.getContrast(white, white), 1);\n    assert.equal(axe.commons.color.getContrast(yellow, yellow), 1);\n\n    //contrast ratio is reversible\n    assert.equal(\n      axe.commons.color.getContrast(yellow, black),\n      axe.commons.color.getContrast(black, yellow)\n    );\n    assert.equal(\n      axe.commons.color.getContrast(yellow, white),\n      axe.commons.color.getContrast(white, yellow)\n    );\n\n    //things that are more contrasty return higher values than things that are less contrasty\n    assert.isTrue(\n      axe.commons.color.getContrast(yellow, white) <\n        axe.commons.color.getContrast(yellow, black)\n    );\n    assert.isTrue(\n      axe.commons.color.getContrast(yellow, black) <\n        axe.commons.color.getContrast(white, black)\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-foreground-color.js",
    "content": "describe('color.getForegroundColor', () => {\n  const { getForegroundColor, Color } = axe.commons.color;\n  const { queryFixture, queryShadowFixture } = axe.testUtils;\n\n  function assertSameColor(actual, expected, margin = 0) {\n    assert.closeTo(actual.red, expected.red, margin);\n    assert.closeTo(actual.green, expected.green, margin);\n    assert.closeTo(actual.blue, expected.blue, margin);\n    // RGB values are 0-255, alpha is 0-1, so divide margin by 255\n    assert.closeTo(actual.alpha, expected.alpha, margin / 255);\n  }\n\n  beforeEach(() => {\n    // This normalizes the default mocha behavior of setting a different background\n    // based on prefers-color-scheme settings.\n    document.body.style.background = '#fff';\n  });\n\n  afterEach(() => {\n    axe.commons.color.incompleteData.clear();\n    document.body.scrollTop = 0;\n  });\n\n  it('returns the CSS color property', () => {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"color: rgb(0 0 128)\">Hello World</div>'\n    ).actualNode;\n    const fgColor = getForegroundColor(target);\n    assertSameColor(fgColor, new Color(0, 0, 128));\n  });\n\n  it('returns the CSS color from inside of Shadow DOM', () => {\n    const target = queryShadowFixture(\n      '<div id=\"shadow\" style=\"height: 40px; width: 30px; background-color: red;\"></div>',\n      '<div id=\"target\" style=\"height:20px; width:15px; color:rgb(0 0 128); background-color:green;\">Hello World</div>'\n    ).actualNode;\n\n    const fgColor = getForegroundColor(target);\n    assertSameColor(fgColor, new Color(0, 0, 128));\n  });\n\n  it('returns null if containing parent has a background image and is non-opaque', () => {\n    const target = queryFixture(\n      '<div style=\"height: 40px; width: 30px;' +\n        'background-color: #800000; background-image: url(image.png);\">' +\n        '<div id=\"target\" style=\"height: 20px; width: 15px; color: blue; background-color: green; opacity: 0.5;\">' +\n        'Hello World' +\n        '</div></div>'\n    ).actualNode;\n    assert.isNull(getForegroundColor(target));\n    assert.equal(axe.commons.color.incompleteData.get('fgColor'), 'bgImage');\n  });\n\n  it('returns `-webkit-text-fill-color` over `color`', () => {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"-webkit-text-fill-color: rgb(0 0 255); color: rgb(0 0 128)\">Hello World</div>'\n    ).actualNode;\n    const fgColor = getForegroundColor(target);\n    assertSameColor(fgColor, new Color(0, 0, 255));\n  });\n\n  describe('text-stroke', () => {\n    it('ignores stroke when equal to 0', () => {\n      const target = queryFixture(\n        '<div style=\"color: rgb(0 0 128); -webkit-text-stroke: 0 #CCC\" id=\"target\">Hello World</div>'\n      ).actualNode;\n      const options = { textStrokeEmMin: 0 };\n      const fgColor = getForegroundColor(target, null, null, options);\n      assertSameColor(fgColor, new Color(0, 0, 128));\n    });\n\n    it('ignores stroke when less then the minimum', () => {\n      const target = queryFixture(\n        '<div style=\"color: rgb(0 0 128); -webkit-text-stroke: 0.1em #CCC\" id=\"target\">Hello World</div>'\n      ).actualNode;\n      const options = { textStrokeEmMin: 0.2 };\n      const fgColor = getForegroundColor(target, null, null, options);\n      assertSameColor(fgColor, new Color(0, 0, 128));\n    });\n\n    it('uses stroke color when thickness is equal to the minimum', () => {\n      const target = queryFixture(\n        '<div style=\"color: #CCC; -webkit-text-stroke: 0.2em rgb(0 0 128);\" id=\"target\">Hello World</div>'\n      ).actualNode;\n      const options = { textStrokeEmMin: 0.2 };\n      const fgColor = getForegroundColor(target, null, null, options);\n      assertSameColor(fgColor, new Color(0, 0, 128));\n    });\n\n    it('blends the stroke color with `color`', () => {\n      const target = queryFixture(\n        '<div style=\"color: rgb(0 0 55); -webkit-text-stroke: 0.2em rgb(0 0 255 / 50%);\" id=\"target\">Hello World</div>'\n      ).actualNode;\n      const options = { textStrokeEmMin: 0.1 };\n      const fgColor = getForegroundColor(target, null, null, options);\n      assertSameColor(fgColor, new Color(0, 0, 155), 0.8);\n    });\n  });\n\n  describe('with transparency', () => {\n    it('returns the blended color if it has alpha set', () => {\n      const target = queryFixture(\n        '<div style=\"height: 40px; background-color: #800000;\">' +\n          '<div id=\"target\" style=\"height: 40px; color: rgba(0, 0, 128, 0.5);' +\n          ' background-color: rgba(0, 128, 0, 0.5);\">' +\n          'This is my text' +\n          '</div></div>'\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(32, 32, 64), 0.8);\n    });\n\n    it('returns the blended color if it has opacity set', () => {\n      const target = queryFixture(\n        '<div style=\"height: 40px; background-color: #800000;\">' +\n          '<div id=\"target\" style=\"height: 40px; color: #000080;' +\n          ' background-color: green; opacity: 0.5;\">' +\n          'This is my text' +\n          '</div></div>'\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(64, 0, 64));\n    });\n\n    it('does not apply opacity to node background', () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"color: #fff; background-color: #00633D; opacity: 0.65\"><span>Hello World</span></div>'\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(255, 255, 255));\n    });\n\n    it('combines opacity with text stroke alpha color', () => {\n      const target = queryFixture(\n        `<div id=\"target\" style=\"\n          opacity: 0.5;\n          color: transparent;\n          -webkit-text-stroke: 0.05em rgb(0 255 255 / 50%);\n        \">Hello world</div>`\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(191, 255, 255), 0.8);\n    });\n\n    it('takes into account parent opacity tree', () => {\n      const target = queryFixture(\n        '<div style=\"background-color: #fafafa\">' +\n          '<div style=\"height: 40px; opacity: 0.6\">' +\n          '<div id=\"target\" style=\"height: 40px; color: rgba(0, 0, 0, 0.87);\">' +\n          'This is my text' +\n          '</div></div></div>'\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(119.5, 119.5, 119.5), 0.8);\n    });\n\n    it('takes into account entire parent opacity tree', () => {\n      const target = queryFixture(\n        '<div style=\"background-color: #fafafa\">' +\n          '<div style=\"height: 40px; opacity: 0.75\">' +\n          '<div style=\"height: 40px; opacity: 0.8\">' +\n          '<div id=\"target\" style=\"height: 40px; color: rgba(0, 0, 0, 0.87);\">' +\n          'This is my text' +\n          '</div></div></div></div>'\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(119.5, 119.5, 119.5), 0.8);\n    });\n  });\n\n  describe('test-shadow', () => {\n    it('returns text shadow color if foreground is transparent', () => {\n      const target = queryFixture(\n        `<div style=\"height: 40px; width: 120px; background-color: white;\">\n          <div id=\"target\" style=\"height: 20px; width: 120px; color: rgba(0,0,0,0); text-shadow: 0 0 0 rgba(32, 32, 64, 1)\">\n            This is my text\n          </div>\n        </div>`\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(32, 32, 64));\n    });\n\n    it('returns a mix of colors if there is a shadow, foreground with alpha < 1, and background color', () => {\n      const target = queryFixture(\n        `<div style=\"height: 40px; width: 120px; background-color: white;\">\n          <div id=\"target\" style=\"height: 20px; width: 120px; color: rgba(128, 0, 0, .5); text-shadow: 0 0 0 rgba(32, 32, 64, 1)\">\n            This is my text\n          </div>\n        </div>`\n      ).actualNode;\n\n      const actual = getForegroundColor(target);\n      const shadowExpected = new Color(32, 32, 64, 1);\n      assert.notEqual(actual.red, shadowExpected.red);\n      assert.notEqual(actual.green, shadowExpected.green);\n      assert.notEqual(actual.blue, shadowExpected.blue);\n\n      const bgExpected = new Color(255, 255, 255, 1);\n      assert.notEqual(actual.red, bgExpected.red);\n      assert.notEqual(actual.green, bgExpected.green);\n      assert.notEqual(actual.blue, bgExpected.blue);\n\n      const fgExpected = new Color(128, 0, 0, 0.5);\n      assert.notEqual(actual.red, fgExpected.red);\n      assert.notEqual(actual.green, fgExpected.green);\n      assert.notEqual(actual.blue, fgExpected.blue);\n    });\n\n    it('applies opacity to text-shadow', () => {\n      const target = queryFixture(\n        `<div id=\"target\" style=\"\n            color: transparent;\n            opacity: 0.5;\n            text-shadow: 0 0 0 rgb(0 255 255 / 50%)\n          \">Hello world</div>`\n      ).actualNode;\n      const fgColor = getForegroundColor(target);\n      assertSameColor(fgColor, new Color(191, 255, 255), 0.8);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-own-background-color.js",
    "content": "describe('color.getOwnBackgroundColor', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var getOwnBackgroundColor = axe.commons.color.getOwnBackgroundColor;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('returns `new axe.commons.color.Color` instance when no background is set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px;\">' + '</div>'\n    );\n    var actual = getOwnBackgroundColor(\n      window.getComputedStyle(vNode.actualNode)\n    );\n    assert.equal(actual.red, 0);\n    assert.equal(actual.green, 0);\n    assert.equal(actual.blue, 0);\n    assert.equal(actual.alpha, 0);\n  });\n\n  it('returns color with rgba values of specified background-color value', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 40px; width: 30px; background-color: pink;\">' +\n        '</div>'\n    );\n    var actual = getOwnBackgroundColor(\n      window.getComputedStyle(vNode.actualNode)\n    );\n    assert.equal(actual.red, 255);\n    assert.equal(actual.green, 192);\n    assert.equal(actual.blue, 203);\n    assert.equal(actual.alpha, 1);\n  });\n\n  it('returns color with rgba values and alpha', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '</div>'\n    );\n    var actual = getOwnBackgroundColor(\n      window.getComputedStyle(vNode.actualNode)\n    );\n    assert.equal(actual.red, 0);\n    assert.equal(actual.green, 128);\n    assert.equal(actual.blue, 0);\n    assert.equal(actual.alpha, 0.5);\n  });\n\n  it('returns color with rgba values and opacity (for blending)', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 20px; width: 15px; opacity: 0.5; background-color: green;\">' +\n        '</div>'\n    );\n    var actual = getOwnBackgroundColor(\n      window.getComputedStyle(vNode.actualNode)\n    );\n    assert.equal(actual.red, 0);\n    assert.equal(actual.green, 128);\n    assert.equal(actual.blue, 0);\n    assert.equal(actual.alpha, 0.5);\n  });\n\n  it('returns color with rgba values, alpha and opacity', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"height: 20px; width: 15px; opacity: 0.5; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '</div>'\n    );\n    var actual = getOwnBackgroundColor(\n      window.getComputedStyle(vNode.actualNode)\n    );\n    assert.equal(actual.red, 0);\n    assert.equal(actual.green, 128);\n    assert.equal(actual.blue, 0);\n    assert.equal(actual.alpha, 0.25);\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-stroke-colors-from-shadows.js",
    "content": "describe('axe.commons.color.getStrokeColorsFromShadow', () => {\n  const { getStrokeColorsFromShadows, parseTextShadows } = axe.commons.color;\n\n  it('should return an empty array if no shadows are passed', () => {\n    const colors = getStrokeColorsFromShadows([]);\n    assert.deepEqual(colors, []);\n  });\n\n  it('combines multiple shadows without blur into a single stroke', () => {\n    const shadows = parseTextShadows(`\n      0 -2px #F00,\n      2px 0 #F00, \n      0 2px #F00,\n      -2px 0 #F00\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.deepEqual(shadowColors[0].toJSON(), {\n      red: 255,\n      green: 0,\n      blue: 0,\n      alpha: 1\n    });\n  });\n\n  it('returns empty when only one side is covered by the shadow', () => {\n    const shadows = parseTextShadows(`0 1px #000`);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 0);\n  });\n\n  it('returns null when two sides is covered by the shadow', () => {\n    const shadows = parseTextShadows(`1px 0 #000, 0 1px #000`);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.isNull(shadowColors);\n  });\n\n  it('returns null when three sides is covered by the shadow', () => {\n    const shadows = parseTextShadows(`1px 0 #000, 0 1px #000, -1px 0 #000`);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.isNull(shadowColors);\n  });\n\n  it('skips shadows offset with 0.5px or less', () => {\n    const shadows = parseTextShadows(`\n      .5px 0 #000,\n      0 .4px #000,\n      -0.3px 0 #000,\n      0 -0.2px #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 0);\n  });\n\n  it('applies an alpha value to shadows of 1.5px or less', () => {\n    const shadows = parseTextShadows(`\n      0 -1.5px #000,\n      1.4px 0 #000,\n      0 1.3px #000,\n      -1.2px 0 #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.closeTo(shadowColors[0].alpha, 0.46, 0.01);\n  });\n\n  it('applies an alpha value if not all shadows are offset by more than 1.5px', () => {\n    const shadows = parseTextShadows(`\n      0 -2px #000,\n      2px 0 #000,\n      0 1.5px #000,\n      -1.5px 0 #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.closeTo(shadowColors[0].alpha, 0.46, 0.01);\n  });\n\n  it('multiplies alpha value for each shadow', () => {\n    const shadows = parseTextShadows(`\n      0 -1px #000,\n      0 -1px #000,\n      1px 0 #000,\n      1px 0 #000,\n      0 1px #000,\n      0 1px #000,\n      -1px 0 #000,\n      -1px 0 #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.closeTo(shadowColors[0].alpha, 0.7, 0.01);\n  });\n\n  it('double-counts shadows on corners', () => {\n    // Corner-shadows overlap on the sides of letters, increasing alpha\n    const shadows = parseTextShadows(`\n      -1px -1px #000,\n      1px -1px #000,\n      1px 1px #000,\n      -1px 1px #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.closeTo(shadowColors[0].alpha, 0.7, 0.01);\n  });\n\n  it('applies an alpha value if not all sides are offset by more than 1.5px', () => {\n    const shadows = parseTextShadows(`\n      -1.5px -2px #000,\n      2px 1.5px #000\n    `);\n    const shadowColors = getStrokeColorsFromShadows(shadows);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.closeTo(shadowColors[0].alpha, 0.46, 0.01);\n  });\n\n  describe('options.ignoreEdgeCount: true', () => {\n    it('returns empty when two sides is covered by the shadow', () => {\n      const shadows = parseTextShadows(`1px 0 #000, 0 1px #000`);\n      const shadowColors = getStrokeColorsFromShadows(shadows, {\n        ignoreEdgeCount: true\n      });\n      assert.deepEqual(shadowColors, []);\n    });\n\n    it('returns empty when three sides is covered by the shadow', () => {\n      const shadows = parseTextShadows(`1px 0 #000, 0 1px #000, -1px 0 #000`);\n      const shadowColors = getStrokeColorsFromShadows(shadows, {\n        ignoreEdgeCount: true\n      });\n      assert.deepEqual(shadowColors, []);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/get-text-shadow-colors.js",
    "content": "describe('axe.commons.color.getTextShadowColors', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var getTextShadowColors = axe.commons.color.getTextShadowColors;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns an empty array when there is no text-shadow', function () {\n    fixture.innerHTML = '<span>Hello world</span>';\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    assert.lengthOf(shadowColors, 0);\n  });\n\n  it('returns a rgb values of each text-shadow color', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '1px 1px 2px #F00, rgb(0, 0, 255) 0 0 1em, \\n0\\t 0  0.2em green;' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n\n    assert.lengthOf(shadowColors, 3);\n    assert.equal(shadowColors[0].red, 255);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n\n    assert.equal(shadowColors[1].red, 0);\n    assert.equal(shadowColors[1].blue, 255);\n    assert.equal(shadowColors[1].green, 0);\n\n    assert.equal(shadowColors[2].red, 0);\n    assert.equal(shadowColors[2].green, 128);\n    assert.equal(shadowColors[2].blue, 0);\n  });\n\n  it('returns transparent if the blur radius is greater than the offset', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '1px 3px 2px red, blue 10px 0 9px, 20px 20px 18px green;' +\n      '\">Hello world</span>';\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n\n    assert.lengthOf(shadowColors, 3);\n    assert.equal(shadowColors[0].alpha, 0);\n    assert.equal(shadowColors[1].alpha, 0);\n    assert.equal(shadowColors[2].alpha, 0);\n  });\n\n  it('returns an estimated alpha value based on blur radius', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '1px 1px 2px red, blue 0 0 10px, \\n0\\t 0  18px green;' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    var expected0 = 3.7 / (2 + 8);\n    var expected1 = 3.7 / (10 + 8);\n    var expected2 = 3.7 / (18 + 8);\n\n    assert.lengthOf(shadowColors, 3);\n    assert.closeTo(shadowColors[0].alpha, expected0, 0.05);\n    assert.closeTo(shadowColors[1].alpha, expected1, 0.05);\n    assert.closeTo(shadowColors[2].alpha, expected2, 0.05);\n  });\n\n  it('returns an alpha of 1 if blur radius is 0', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '0px 0px 0 red, blue 0 0 0, \\n0\\t 0  0 green, 0px 0px red;' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n\n    assert.lengthOf(shadowColors, 4);\n    assert.equal(shadowColors[0].alpha, 1);\n    assert.equal(shadowColors[1].alpha, 1);\n    assert.equal(shadowColors[2].alpha, 1);\n    assert.equal(shadowColors[3].alpha, 1);\n  });\n\n  it('handles floating point values', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '0 0.1px .2px red' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    var expectedAlpha = 3.7 / (0.12 + 8);\n\n    assert.lengthOf(shadowColors, 1);\n    assert.closeTo(shadowColors[0].alpha, expectedAlpha, 0.01);\n  });\n\n  it('combines the blur radius alpha with the alpha of the text-shadow color', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      'rgba(255, 0, 0, 0) 0 0 2px, rgba(255,0,0,0.5) 0 0 2px, rgba(255,0,0,0.8) 0 0 2px' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    var expected1 = (3.7 / (2 + 8)) * 0.5;\n    var expected2 = (3.7 / (2 + 8)) * 0.8;\n\n    assert.lengthOf(shadowColors, 3);\n    assert.closeTo(shadowColors[0].alpha, 0, 0.05);\n    assert.closeTo(shadowColors[1].alpha, expected1, 0.05);\n    assert.closeTo(shadowColors[2].alpha, expected2, 0.05);\n  });\n\n  it('treats the blur radius as 0 when left undefined', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' + '1px 2px red' + '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].alpha, 0);\n  });\n\n  it('uses text color if text-shadow color is ommitted', function () {\n    fixture.innerHTML =\n      '<span style=\"color: red;' +\n      'text-shadow: 1px 1px 1px;' +\n      '\">Hello world</span>';\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 255);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n  });\n\n  it('returns null if a shadows has a ratio less than minRatio', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '0 0 1em #F00, 0 0 0.5em #0F0, 1px 1px 0.2em #00F;' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span, { minRatio: 0.5 });\n    assert.isNull(shadowColors);\n  });\n\n  it('does not return shadows with a ratio less than maxRatio', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: ' +\n      '0 0 1em #F00, 0 0 0.5em #0F0, 1px 1px 0.2em #00F;' +\n      '\">Hello world</span>';\n\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span, { maxRatio: 0.5 });\n\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].blue, 255);\n  });\n\n  it('returns a transparent shadow when x offset is greater than blur', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: 1px 0 0 #F00\">Hello world</span>';\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.equal(shadowColors[0].alpha, 0);\n  });\n\n  it('returns a transparent shadow when y offset is greater than blur', function () {\n    fixture.innerHTML =\n      '<span style=\"text-shadow: 0 1px 0 #F00\">Hello world</span>';\n    var span = fixture.querySelector('span');\n    var shadowColors = getTextShadowColors(span);\n    assert.lengthOf(shadowColors, 1);\n    assert.equal(shadowColors[0].red, 0);\n    assert.equal(shadowColors[0].green, 0);\n    assert.equal(shadowColors[0].blue, 0);\n    assert.equal(shadowColors[0].alpha, 0);\n  });\n\n  describe('with shadows that combine to a stroke', () => {\n    const opt = { minRatio: 0.01 };\n    it('combines multiple shadows without blur into a single stroke', () => {\n      fixture.innerHTML = `\n        <span style=\"text-shadow:\n          0 -2px #F00,\n          2px 0 #F00,\n          0 2px #F00,\n          -2px 0 #F00;\n        \">Hello world</span>\n      `;\n      const shadowColors = getTextShadowColors(fixture.firstElementChild, opt);\n      assert.lengthOf(shadowColors, 1);\n      assert.deepEqual(shadowColors[0].toJSON(), {\n        red: 255,\n        green: 0,\n        blue: 0,\n        alpha: 1\n      });\n    });\n\n    it('only combines shadows thinner than minRatio', () => {\n      const minRatio = 0.1; // .1em (has to be greater than 1px, or this will be null)\n      fixture.innerHTML = `\n        <span style=\"text-shadow:\n          0 -2px ${minRatio}em #000,\n          2px 0 ${minRatio}em #000,\n          0 2px ${minRatio}em #000,\n          -2px 0 ${minRatio}em #000;\n        \">Hello wold</span>\n      `;\n      const shadowColors = getTextShadowColors(fixture.firstElementChild, {\n        minRatio\n      });\n      assert.lengthOf(shadowColors, 4);\n    });\n\n    it('places the combined shadow in the correct order with shadows', () => {\n      fixture.innerHTML = `\n        <span style=\"text-shadow:\n          0 -1px #000, 1px 0 #000, 0 1px #000, -1px 0 #000,\n          0 0 1em #111,\n          0 -1px #222, 1px 0 #222, 0 1px #222, -1px 0 #222,\n          1px 1px 0.2em #333;\n        \">Hello wold</span>\n      `;\n      const shadowColors = getTextShadowColors(fixture.firstElementChild, opt);\n      const hexColors = shadowColors.map(color => color.toHexString()[1]);\n      assert.deepEqual(['0', '1', '2', '3'], hexColors);\n    });\n\n    it('returns null when when thin shadows are not all the way around the text', () => {\n      fixture.innerHTML = `\n        <span style=\"text-shadow:\n          0 -2px #000,\n          2px 0 #000,\n          0 2px #000;\n        \">Hello wold</span>\n      `;\n      const shadowColors = getTextShadowColors(fixture.firstElementChild, opt);\n      assert.isNull(shadowColors);\n    });\n\n    it('ignores partial strokes with ignoreEdgeCount: true', () => {\n      fixture.innerHTML = `\n        <span style=\"text-shadow:\n          2px 1px 2px #F00,\n          0 -2px #000,\n          2px 0 #000,\n          0 2px #000;\n        \">Hello wold</span>\n      `;\n      const shadowColors = getTextShadowColors(fixture.firstElementChild, {\n        ...opt,\n        ignoreEdgeCount: true\n      });\n      assert.lengthOf(shadowColors, 1);\n      assert.equal(shadowColors[0].red, 255);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/has-valid-contrast-ratio.js",
    "content": "describe('color.Color', function () {\n  'use strict';\n\n  it('should give sensible results for WCAG compliance', function () {\n    var black = new axe.commons.color.Color(0, 0, 0, 1);\n    var white = new axe.commons.color.Color(255, 255, 255, 1);\n    var gray = new axe.commons.color.Color(128, 128, 128, 1);\n\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(black, white, 8, false).isValid\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(black, white, 8, false)\n        .contrastRatio > 4.5\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(black, white, 8, false)\n        .expectedContrastRatio === 4.5\n    );\n\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 24, false).isValid\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 24, false)\n        .contrastRatio > 3\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 24, false)\n        .expectedContrastRatio === 3\n    );\n\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 20, true).isValid\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 20, true)\n        .contrastRatio > 3\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 20, true)\n        .expectedContrastRatio === 3\n    );\n\n    assert.isFalse(\n      axe.commons.color.hasValidContrastRatio(white, gray, 8, false).isValid\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 8, false)\n        .contrastRatio < 4.5\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(white, gray, 8, false)\n        .expectedContrastRatio === 4.5\n    );\n  });\n\n  it('should count 1-1 ratios as visually hidden', function () {\n    var black = new axe.commons.color.Color(0, 0, 0, 1);\n\n    assert.isFalse(\n      axe.commons.color.hasValidContrastRatio(black, black, 16, true).isValid\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(black, black, 16, true)\n        .contrastRatio === 1\n    );\n    assert.isTrue(\n      axe.commons.color.hasValidContrastRatio(black, black, 16, true)\n        .expectedContrastRatio === 4.5\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/color/incomplete-data.js",
    "content": "describe('axe.commons.color.incompleteData', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be an object', function () {\n    assert.isObject(axe.commons.color.incompleteData);\n  });\n\n  it('should store a string with a key for the incomplete check', function () {\n    var node = document.createElement('div');\n    fixture.appendChild(node);\n    axe.commons.color.incompleteData.set('fgColor', 'bgImage');\n\n    assert.equal(axe.commons.color.incompleteData.get('fgColor'), 'bgImage');\n  });\n\n  it('should throw an error if key is not a string', function () {\n    assert.throws(function () {\n      axe.commons.color.incompleteData.set();\n    }, /key must be a string/);\n  });\n\n  it('should store a reason that matches a list of possibilities', function () {\n    axe.commons.color.incompleteData.set('bgColor', 'bgImage');\n    assert.equal(axe.commons.color.incompleteData.get('bgColor'), 'bgImage');\n  });\n\n  it('should clear the data object on request', function () {\n    axe.commons.color.incompleteData.set('fgColor', 'bgOverlap');\n    axe.commons.color.incompleteData.clear();\n    assert.isUndefined(\n      axe.commons.color.incompleteData.get('fgColor'),\n      'bgOverlap'\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/color/parse-text-shadows.js",
    "content": "describe('axe.commons.color.parseTextShadows', () => {\n  const { parseTextShadows } = axe.commons.color;\n\n  it('should return an empty array if no text-shadow is provided', () => {\n    const shadows = parseTextShadows(' \\t\\n');\n    assert.equal(shadows.length, 0);\n  });\n\n  it('should return an array of objects with `pixels` and `colorStr` properties', () => {\n    const shadows = parseTextShadows('1px 2px 3px #000, 4px 5px 6px #fff');\n    assert.equal(shadows.length, 2);\n\n    assert.deepEqual(shadows[0], {\n      pixels: [1, 2, 3],\n      colorStr: '#000'\n    });\n\n    assert.deepEqual(shadows[1], {\n      pixels: [4, 5, 6],\n      colorStr: '#fff'\n    });\n  });\n\n  it('accepts color at the start of the shadow', () => {\n    const shadows = parseTextShadows('#000 1px 2px 3px, #fff 4px 5px 6px');\n    assert.deepEqual(shadows[0], {\n      pixels: [1, 2, 3],\n      colorStr: '#000'\n    });\n    assert.deepEqual(shadows[1], {\n      pixels: [4, 5, 6],\n      colorStr: '#fff'\n    });\n  });\n\n  it('accepts named colors', () => {\n    const shadows = parseTextShadows('red 1px 2px 3px, blue 4px 5px 6px');\n    assert.deepEqual(shadows[0], {\n      pixels: [1, 2, 3],\n      colorStr: 'red'\n    });\n    assert.deepEqual(shadows[1], {\n      pixels: [4, 5, 6],\n      colorStr: 'blue'\n    });\n  });\n\n  it('accepts different color functions', () => {\n    const shadows = parseTextShadows(`\n      rgb(0, 202, 148) 0px 0px,\n      hsl(165.58deg 100% 35.07% / .5) 0px 0px,\n      lch(65 49.19 167.45) 0px 0px,\n      oklch(60% 0.57 181) 0px 0px,\n      lab(65 -48.01 10.69) 0px 0px,\n      color(xyz-d65 0.26 0.44 0.35 / 1) 0px 0px,\n    `);\n    assert.equal(shadows[0].colorStr, 'rgb(0, 202, 148)');\n    assert.equal(shadows[1].colorStr, 'hsl(165.58deg 100% 35.07% / .5)');\n    assert.equal(shadows[2].colorStr, 'lch(65 49.19 167.45)');\n    assert.equal(shadows[3].colorStr, 'oklch(60% 0.57 181)');\n    assert.equal(shadows[4].colorStr, 'lab(65 -48.01 10.69)');\n    assert.equal(shadows[5].colorStr, 'color(xyz-d65 0.26 0.44 0.35 / 1)');\n  });\n\n  it('sets default blur to 0 when omitted', () => {\n    const shadows = parseTextShadows('1px 2px #000, 4px 5px #fff');\n    assert.deepEqual(shadows[0], {\n      pixels: [1, 2, 0],\n      colorStr: '#000'\n    });\n    assert.deepEqual(shadows[1], {\n      pixels: [4, 5, 0],\n      colorStr: '#fff'\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/color/stacking-context.js",
    "content": "describe('color.stackingContext', () => {\n  const { Color, getStackingContext, stackingContextToColor } =\n    axe.commons.color;\n  const { getElementStack } = axe.commons.dom;\n  const { querySelectorAll } = axe.utils;\n  const { queryFixture } = axe.testUtils;\n  const fixture = document.querySelector('#fixture');\n\n  beforeEach(() => {\n    // remove html, body, and fixture from the\n    // element stack to make testing easier\n    document.documentElement.style.height = 0;\n    document.body.style.height = 0;\n    fixture.style.height = 0;\n  });\n\n  afterEach(() => {\n    document.documentElement.removeAttribute('style');\n    document.body.removeAttribute('style');\n    fixture.removeAttribute('style');\n  });\n\n  describe('color.getStackingContexts', () => {\n    it('creates a context for a single element', () => {\n      const vNode = queryFixture('<div id=\"target\">Hello World</div>');\n      // html is always added as the last element\n      // so we'll remove it to make testing easier\n      const elmStack = getElementStack(vNode.actualNode).slice(0, -1);\n      const stackingContext = getStackingContext(vNode.actualNode, elmStack);\n\n      assert.deepEqual(stackingContext, [\n        {\n          vNode,\n          ancestor: undefined,\n          opacity: 1,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: []\n        }\n      ]);\n    });\n\n    it('creates a context for every node in the element stack', () => {\n      const vNode = queryFixture(`\n        <div id=\"elm1\">\n          <div id=\"elm2\">\n            <div id=\"target\">Hello World</div>\n          </div>\n        </div>\n      `);\n      const elmStack = getElementStack(vNode.actualNode).slice(0, -1);\n      const stackingContext = getStackingContext(vNode.actualNode, elmStack);\n\n      assert.deepEqual(stackingContext, [\n        {\n          vNode: querySelectorAll(axe._tree[0], '#elm1')[0],\n          ancestor: undefined,\n          opacity: 1,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: []\n        },\n        {\n          vNode: querySelectorAll(axe._tree[0], '#elm2')[0],\n          ancestor: undefined,\n          opacity: 1,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: []\n        },\n        {\n          vNode,\n          ancestor: undefined,\n          opacity: 1,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: []\n        }\n      ]);\n    });\n\n    it('nests contexts', () => {\n      const vNode = queryFixture(`\n        <div id=\"elm1\" style=\"position: absolute; z-index: 2\">\n          <div id=\"elm2\">\n            <div id=\"target\">Hello World</div>\n          </div>\n        </div>\n      `);\n      const elmStack = getElementStack(vNode.actualNode).slice(0, -1);\n      const stackingContext = getStackingContext(vNode.actualNode, elmStack);\n\n      assert.deepEqual(stackingContext, [\n        {\n          vNode: querySelectorAll(axe._tree[0], '#elm1')[0],\n          ancestor: undefined,\n          opacity: 1,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: [\n            {\n              vNode: querySelectorAll(axe._tree[0], '#elm2')[0],\n              ancestor: stackingContext[0],\n              opacity: 1,\n              bgColor: new Color(0, 0, 0, 0),\n              blendMode: 'normal',\n              descendants: []\n            },\n            {\n              vNode,\n              ancestor: stackingContext[0],\n              opacity: 1,\n              bgColor: new Color(0, 0, 0, 0),\n              blendMode: 'normal',\n              descendants: []\n            }\n          ]\n        }\n      ]);\n    });\n\n    it('sets context properties', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"background-color: rgba(255,0,0,0.5); opacity: 0.8; mix-blend-mode: difference;\">Hello World</div>'\n      );\n      const elmStack = getElementStack(vNode.actualNode).slice(0, -1);\n      const stackingContext = getStackingContext(vNode.actualNode, elmStack);\n\n      assert.deepEqual(stackingContext, [\n        {\n          vNode,\n          ancestor: undefined,\n          opacity: 0.8,\n          bgColor: new Color(255, 0, 0, 0.5),\n          blendMode: 'difference',\n          descendants: []\n        }\n      ]);\n    });\n\n    it('creates a context for ancestors that create a stacking context but are not in the element stack', () => {\n      const vNode = queryFixture(`\n        <div id=\"elm1\" style=\"opacity: 0.8\">\n          <div id=\"target\" style=\"position: absolute; z-index: 2\">Hello World</div>\n        </div>\n      `);\n      const elmStack = getElementStack(vNode.actualNode).slice(0, -1);\n      const stackingContext = getStackingContext(vNode.actualNode, elmStack);\n\n      assert.lengthOf(elmStack, 1);\n      assert.deepEqual(stackingContext, [\n        {\n          vNode: querySelectorAll(axe._tree[0], '#elm1')[0],\n          ancestor: undefined,\n          opacity: 0.8,\n          bgColor: new Color(0, 0, 0, 0),\n          blendMode: 'normal',\n          descendants: [\n            {\n              vNode,\n              ancestor: stackingContext[0],\n              opacity: 1,\n              bgColor: new Color(0, 0, 0, 0),\n              blendMode: 'normal',\n              descendants: []\n            }\n          ]\n        }\n      ]);\n    });\n  });\n\n  describe('color.stackingContextToColor', () => {\n    it('reduces a context to a color', () => {\n      const context = {\n        opacity: 1,\n        bgColor: new Color(255, 128, 0, 1),\n        blendMode: 'normal',\n        descendants: []\n      };\n\n      assert.deepEqual(stackingContextToColor(context), {\n        color: new Color(255, 128, 0, 1),\n        blendMode: 'normal'\n      });\n    });\n\n    it('reduces a nested context to a color', () => {\n      const context = {\n        opacity: 1,\n        bgColor: new Color(255, 128, 0, 1),\n        blendMode: 'normal',\n        descendants: [\n          {\n            opacity: 1,\n            bgColor: new Color(0, 255, 128, 0.5),\n            blendMode: 'normal',\n            descendants: []\n          }\n        ]\n      };\n\n      assert.deepEqual(stackingContextToColor(context), {\n        color: new Color(128, 192, 64, 1),\n        blendMode: 'normal'\n      });\n    });\n\n    it('applies opacity after blending', () => {\n      const context = {\n        opacity: 0.8,\n        bgColor: new Color(255, 128, 0, 1),\n        blendMode: 'normal',\n        descendants: [\n          {\n            opacity: 1,\n            bgColor: new Color(0, 255, 128, 0.5),\n            blendMode: 'normal',\n            descendants: []\n          }\n        ]\n      };\n\n      assert.deepEqual(stackingContextToColor(context), {\n        color: new Color(128, 192, 64, 0.8),\n        blendMode: 'normal'\n      });\n    });\n\n    it('applies mix-blend-mode from a sibling context', () => {\n      const context = {\n        opacity: 1,\n        bgColor: new Color(0, 0, 0, 0),\n        blendMode: 'normal',\n        descendants: [\n          {\n            opacity: 1,\n            bgColor: new Color(255, 128, 0, 1),\n            blendMode: 'normal',\n            descendants: []\n          },\n          {\n            opacity: 1,\n            bgColor: new Color(0, 255, 128, 0.5),\n            blendMode: 'difference',\n            descendants: []\n          }\n        ]\n      };\n\n      assert.deepEqual(stackingContextToColor(context), {\n        color: new Color(255, 128, 64, 1),\n        blendMode: 'normal'\n      });\n    });\n\n    it('applies mix-blend-mode from a nested context', () => {\n      const context = {\n        opacity: 1,\n        bgColor: new Color(255, 128, 0, 1),\n        blendMode: 'normal',\n        descendants: [\n          {\n            opacity: 1,\n            bgColor: new Color(0, 255, 128, 0.5),\n            blendMode: 'difference',\n            descendants: []\n          }\n        ]\n      };\n\n      assert.deepEqual(stackingContextToColor(context), {\n        color: new Color(255, 128, 64, 1),\n        blendMode: 'normal'\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/create-grid.js",
    "content": "// Additional tests for createGrid are part of createRectStack tests,\n// which is what createGrid was originally part of\ndescribe('create-grid', () => {\n  let fixture;\n  const createGrid = axe.commons.dom.createGrid;\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const queryFixture = axe.testUtils.queryFixture;\n  const gridSize = axe.constants.gridSize;\n\n  function findPositions(grid, vNode) {\n    const positions = [];\n    grid.loopGridPosition(grid.boundaries, (cell, position) => {\n      if (cell.includes(vNode)) {\n        positions.push(position);\n      }\n    });\n    return positions;\n  }\n\n  it('returns the grid size', () => {\n    axe.setup();\n    assert.equal(createGrid(), axe.constants.gridSize);\n  });\n\n  it('sets ._grid to nodes in the grid', () => {\n    fixture = fixtureSetup('<span>Hello world</span>');\n    assert.isUndefined(fixture._grid);\n    assert.isUndefined(fixture.children[0]._grid);\n\n    createGrid();\n    assert.isDefined(fixture._grid);\n    assert.equal(fixture._grid, fixture.children[0]._grid);\n  });\n\n  it('adds elements to the correct cell in the grid', () => {\n    fixture = fixtureSetup('<span>Hello world</span>');\n    createGrid();\n    const positions = findPositions(fixture._grid, fixture.children[0]);\n    assert.deepEqual(positions, [{ col: 0, row: 0 }]);\n  });\n\n  it('adds large elements to multiple cell', () => {\n    fixture = fixtureSetup(\n      '<span style=\"display: inline-block; width: 300px; height: 300px;\">' +\n        'Hello world</span>'\n    );\n    createGrid();\n\n    const positions = findPositions(fixture._grid, fixture.children[0]);\n    assert.deepEqual(positions, [\n      { col: 0, row: 0 },\n      { col: 1, row: 0 },\n      { col: 0, row: 1 },\n      { col: 1, row: 1 }\n    ]);\n  });\n\n  it('only adds the visible non-overflow area of large elements', () => {\n    const vNode = queryFixture(`\n      <div style=\"overflow: hidden; width: 300px; height: 300px;\">\n        <span id=\"target\" style=\"display: inline-block; width: 1000px; height: 1000px;\">' +\n        'Hello world</span>\n      </div>\n    `);\n    createGrid();\n\n    const positions = findPositions(vNode._grid, vNode);\n    assert.deepEqual(positions, [\n      { col: 0, row: 0 },\n      { col: 1, row: 0 },\n      { col: 0, row: 1 },\n      { col: 1, row: 1 }\n    ]);\n  });\n\n  describe('stackingOrder', () => {\n    it('adds stacking context information', () => {\n      fixture = fixtureSetup('<span>Hello world</span>');\n      createGrid();\n      const vNode = fixture.children[0];\n      assert.lengthOf(vNode._stackingOrder, 1);\n      // purposefully do not test stackLevel and treeOrder values as they are\n      // implementation details that can easily change\n      assert.hasAllKeys(vNode._stackingOrder[0], [\n        'stackLevel',\n        'treeOrder',\n        'vNode'\n      ]);\n      assert.typeOf(vNode._stackingOrder[0].stackLevel, 'number');\n      assert.typeOf(vNode._stackingOrder[0].treeOrder, 'number');\n      assert.isNull(vNode._stackingOrder[0].vNode); // root stack\n    });\n\n    // when to use z-index values can be tested though\n    it('sets the stack level equal to the z-index of positioned elements', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"position: absolute; z-index: 100\">Hello world</div>'\n      );\n      createGrid();\n      assert.equal(vNode._stackingOrder[0].stackLevel, 100);\n    });\n\n    it(\"ignores z-index on elements that aren't positioned\", () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"opacity: 0.2; z-index: 100\">Hello world</div>'\n      );\n      createGrid();\n      assert.notEqual(vNode._stackingOrder[0].stackLevel, 100);\n    });\n\n    it('uses z-index on children of flex elements', () => {\n      const vNode = queryFixture(\n        '<div style=\"display: flex\"><div id=\"target\" style=\"z-index: 100\">Hello world</div></div>'\n      );\n      createGrid();\n      assert.equal(vNode._stackingOrder[0].stackLevel, 100);\n    });\n\n    it('creates multiple stacking context when they are nested', () => {\n      const vNode = queryFixture(`\n        <div style=\"opacity: 0.2\">\n          <div style=\"transform: translate(42px, 18px);\">\n            <div id=\"target\">Hello world</div>\n          </div>\n        </div>\n      `);\n      createGrid();\n      assert.lengthOf(vNode._stackingOrder, 2);\n    });\n  });\n\n  describe('hidden elements', () => {\n    beforeEach(() => {\n      // Ensure the fixture itself is part of the grid, even if its content isn't\n      document\n        .querySelector('#fixture')\n        .setAttribute('style', 'min-height: 10px');\n    });\n\n    it('does not add hidden elements', () => {\n      fixture = fixtureSetup('<div style=\"display: none\">hidden</div>');\n      createGrid();\n      const position = findPositions(fixture._grid, fixture.children[0]);\n      assert.isEmpty(position);\n      assert.isUndefined(fixture.children[0]._grid);\n    });\n\n    it('does not add off screen elements', () => {\n      fixture = fixtureSetup(\n        '<div style=\"position: fixed; top: -3em\">off screen</div>'\n      );\n      createGrid();\n      const position = findPositions(fixture._grid, fixture.children[0]);\n      assert.isEmpty(position);\n      assert.isUndefined(fixture.children[0]._grid);\n    });\n\n    it('does add partially on screen elements', () => {\n      fixture = fixtureSetup(\n        '<div style=\"position: fixed; top: -1em; min-height: 2em\">off screen</div>'\n      );\n      createGrid();\n      const position = findPositions(fixture._grid, fixture.children[0]);\n      assert.deepEqual(position, [\n        { col: 0, row: -1 },\n        { col: 0, row: 0 }\n      ]);\n    });\n  });\n\n  describe('when scrolled', () => {\n    before(() => {\n      document.body.setAttribute('style', 'margin: 0');\n    });\n\n    after(() => {\n      document.body.removeAttribute('style');\n    });\n\n    it('adds elements vertically scrolled out of view', () => {\n      const gridScroll = 2;\n      fixture =\n        fixtureSetup(`<div id=\"scroller\" style=\"height: ${gridSize}px; width: ${gridSize}px; overflow: scroll\">\n          <div style=\"height: ${gridSize}px\">V1</div>\n          <div style=\"height: ${gridSize}px\">V2</div>\n          <div style=\"height: ${gridSize}px\">V3</div>\n          <div style=\"height: ${gridSize}px\">V4</div>\n          <div style=\"height: ${gridSize}px\">V5</div>\n        </div>`);\n      const scroller = fixture.children[0];\n      scroller.actualNode.scroll(0, gridSize * gridScroll);\n      const childElms = scroller.children.filter(\n        ({ props }) => props.nodeName === 'div'\n      );\n\n      createGrid();\n      childElms.forEach((child, index) => {\n        assert.isDefined(child._grid, `Expect child ${index} to be defined`);\n        const position = findPositions(child._grid, child);\n        assert.deepEqual(position, [{ col: 0, row: index - gridScroll }]);\n      });\n    });\n\n    it('adds elements horizontally scrolled out of view', () => {\n      const gridScroll = 2;\n      fixture =\n        fixtureSetup(`<div id=\"scroller\" style=\"width: ${gridSize}px; overflow: scroll\">\n        <div style=\"width: ${gridSize * 6}px;\">\n          <div style=\"width: ${gridSize}px; float: left;\">H1</div>\n          <div style=\"width: ${gridSize}px; float: left;\">H2</div>\n          <div style=\"width: ${gridSize}px; float: left;\">H3</div>\n          <div style=\"width: ${gridSize}px; float: left;\">H4</div>\n          <div style=\"width: ${gridSize}px; float: left;\">H5</div>\n        </div>\n      </div>`);\n      const scroller = fixture.children[0];\n      scroller.actualNode.scroll(gridSize * gridScroll, 0);\n      const childElms = scroller.children[0].children.filter(\n        ({ props }) => props.nodeName === 'span'\n      );\n\n      createGrid();\n      childElms.forEach((child, index) => {\n        assert.isDefined(child._grid, `Expect child ${index} to be defined`);\n        const position = findPositions(child._grid, child);\n        assert.deepEqual(position, [{ col: index - gridScroll, row: 0 }]);\n      });\n    });\n  });\n\n  describe('subGrids', () => {\n    it('sets the .subGrid property', () => {\n      fixture = fixtureSetup(\n        '<div style=\"overflow: scroll; height: 100px;\">' +\n          '<span style=\"display: inline-block; height: 300px\" id=\"x\">x</span>' +\n          '</div>'\n      );\n      const vOverflow = fixture.children[0];\n      assert.isUndefined(vOverflow._subGrid);\n      createGrid();\n      assert.isDefined(vOverflow._subGrid);\n      assert.notEqual(vOverflow._grid, vOverflow._subGrid);\n    });\n\n    it('sets the ._grid of children as the subGrid', () => {\n      fixture = fixtureSetup(\n        '<div style=\"overflow: scroll; height: 100px;\">' +\n          '<span style=\"display: inline-block; height: 300px\" id=\"x\">x</span>' +\n          '</div>'\n      );\n      createGrid();\n      const vOverflow = fixture.children[0];\n      const vSpan = vOverflow.children[0];\n      assert.equal(vOverflow._subGrid, vSpan._grid);\n    });\n\n    it('does not add scrollable children to the root grid', () => {\n      fixture = fixtureSetup(\n        '<div style=\"overflow: scroll; height: 100px;\">' +\n          '<span style=\"display: inline-block; height: 300px\" id=\"x\">x</span>' +\n          '</div>'\n      );\n      createGrid();\n      const vSpan = fixture.children[0].children[0];\n      const position = findPositions(fixture._grid, vSpan);\n      assert.isEmpty(position);\n    });\n\n    it('adds scrollable children to the subGrid', () => {\n      fixture = fixtureSetup(\n        '<div style=\"overflow: scroll; height: 100px;\">' +\n          '<span style=\"display: inline-block; height: 300px\" id=\"x\">x</span>' +\n          '</div>'\n      );\n      createGrid();\n      const vOverflow = fixture.children[0];\n      const vSpan = vOverflow.children[0];\n      const position = findPositions(vOverflow._subGrid, vSpan);\n      assert.deepEqual(position, [\n        { col: 0, row: 0 },\n        { col: 0, row: 1 }\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/find-elms-in-context.js",
    "content": "describe('dom.findElmsInContext', function () {\n  'use strict';\n\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var findElmsInContext = axe.commons.dom.findElmsInContext;\n\n  it('returns an array or elements in the same context', function () {\n    var rootNode = fixtureSetup(\n      '<b name=\"foo\">1</b>' +\n        '<b name=\"foo\">2</b>' +\n        '<b name=\"bar\">3</b>' +\n        '<i name=\"foo\">4</i>'\n    );\n\n    assert.deepEqual(\n      findElmsInContext({\n        elm: 'b',\n        attr: 'name',\n        value: 'foo',\n        context: rootNode.actualNode\n      }),\n      Array.from(document.querySelectorAll('b[name=foo]'))\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'ignores elements inside shadow tree',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<b name=\"foo\">1</b>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<b name=\"foo\">2</b> <slot></slot>';\n      var rootNode = fixtureSetup(node);\n\n      var result = findElmsInContext({\n        elm: 'b',\n        attr: 'name',\n        value: 'foo',\n        context: rootNode.actualNode\n      });\n      assert.lengthOf(result, 1);\n      assert.equal(result[0].innerText, '1');\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'can search elements limited to the shadow tree',\n    function () {\n      var node = document.createElement('div');\n      node.innerHTML = '<b name=\"foo\">1</b>';\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<b name=\"foo\">2</b><slot></slot>';\n      fixtureSetup(node);\n\n      var result = findElmsInContext({\n        elm: 'b',\n        attr: 'name',\n        value: 'foo',\n        context: shadow\n      });\n\n      assert.lengthOf(result, 1);\n      assert.equal(result[0].innerText, '2');\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/find-nearby-elms.js",
    "content": "describe('findNearbyElms', () => {\n  let fixture;\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const findNearbyElms = axe.commons.dom.findNearbyElms;\n\n  function getIds(vNodeList) {\n    const ids = [];\n    vNodeList.forEach(function (vNode) {\n      if (vNode.props.id && vNode.props.id !== 'fixture') {\n        ids.push(vNode.props.id);\n      }\n    });\n    return ids;\n  }\n\n  describe('in the viewport', () => {\n    beforeEach(() => {\n      fixture = fixtureSetup(\n        '<div id=\"n0\" style=\"height:30px; margin-bottom:30px;\">0</div>' +\n          '<div id=\"n1\" style=\"height:30px; margin-bottom:30px;\">1</div>' +\n          '<div id=\"n2\" style=\"height:30px; margin-bottom:30px;\">2</div>' +\n          '<div id=\"n3\" style=\"height:30px; margin-bottom:30px;\">3</div>' +\n          '<div id=\"n4\" style=\"height:30px; margin-bottom:30px;\">4</div>' +\n          '<div id=\"n5\" style=\"height:30px; margin-bottom:30px;\">5</div>' +\n          '<div id=\"n6\" style=\"height:30px; margin-bottom:30px;\">6</div>' +\n          '<div id=\"n7\" style=\"height:30px; margin-bottom:30px;\">7</div>' +\n          '<div id=\"n8\" style=\"height:30px; margin-bottom:30px;\">8</div>' +\n          '<div id=\"n9\" style=\"height:30px; margin-bottom:30px;\">9</div>'\n      );\n    });\n\n    it('returns node from the same grid cell', () => {\n      const nearbyElms = findNearbyElms(fixture.children[1]);\n      assert.deepEqual(getIds(nearbyElms), ['n0', 'n2', 'n3']);\n    });\n\n    it('returns node from multiple grid cells when crossing a boundary', () => {\n      const nearbyElms = findNearbyElms(fixture.children[5]);\n      assert.deepEqual(getIds(nearbyElms), ['n3', 'n4', 'n6']);\n    });\n  });\n\n  describe('on the edge', () => {\n    beforeEach(() => {\n      fixture = fixtureSetup(\n        '<div id=\"n0\" style=\"position: fixed; top:-31px; height: 60px\">0</div>' +\n          '<div id=\"n1\" style=\"position: fixed; top:-31px; height: 30px\">1</div>' +\n          '<div id=\"n2\" style=\"position: fixed; top:0; height: 30px\">2</div>'\n      );\n    });\n\n    it('ignores cells outside the document boundary', () => {\n      const nearbyElms = findNearbyElms(fixture.children[0]);\n      assert.deepEqual(getIds(nearbyElms), ['n2']);\n    });\n\n    it('returns no neighbors for off-screen elements', () => {\n      const nearbyElms = findNearbyElms(fixture.children[1]);\n      assert.deepEqual(getIds(nearbyElms), []);\n    });\n\n    it('returns element partially on screen as neighbors', () => {\n      const nearbyElms = findNearbyElms(fixture.children[2]);\n      assert.deepEqual(getIds(nearbyElms), ['n0']);\n    });\n  });\n\n  describe('when some nodes are fixed', () => {\n    beforeEach(() => {\n      fixture = fixtureSetup(\n        '<div style=\" position: fixed;\" id=\"n0\">' +\n          '  <div id=\"n1\" style=\"height:30px;\">1</div>' +\n          '  <div id=\"n2\" style=\"height:30px;\">2</div>' +\n          '</div>' +\n          '<div id=\"n3\" style=\"height:30px;\">3</div>' +\n          '<div id=\"n4\" style=\"height:30px;\">4</div>'\n      );\n    });\n\n    it('skips fixed position neighbors when not fixed', () => {\n      const n3 = axe.utils.querySelectorAll(fixture, '#n3')[0];\n      const nearbyElms = findNearbyElms(n3);\n      assert.deepEqual(getIds(nearbyElms), ['n4']);\n    });\n\n    it('includes only fixed position neighbors when fixed', () => {\n      const n1 = axe.utils.querySelectorAll(fixture, '#n1')[0];\n      const nearbyElms = findNearbyElms(n1);\n      assert.deepEqual(getIds(nearbyElms), ['n0', 'n2']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/find-up.js",
    "content": "/* global xit */\ndescribe('dom.findUp', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should find parents based on selector', function () {\n    fixture.innerHTML =\n      '<div class=\"target\"><div id=\"target\" class=\"target\"><span><span><span><div>' +\n      '<div><div id=\"start\"></div></div></div></span></span></span></div></div>';\n\n    var start = document.getElementById('start'),\n      target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.equal(\n      axe.commons.dom.findUp(start, '.target'),\n      target,\n      'Should find it!'\n    );\n  });\n\n  it('should return null if it cant find a match anywhere in the document', function () {\n    fixture.innerHTML = '<div id=\"start\"></div>';\n    var start = document.getElementById('start');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isNull(axe.commons.dom.findUp(start, '.nomatchyplzkthx'));\n  });\n\n  it('should return null if it cant find a match anywhere above the start', function () {\n    fixture.innerHTML = '<div id=\"start\"></div><div class=\"target\"></div>';\n    var start = document.getElementById('start');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isNull(axe.commons.dom.findUp(start, '.target'));\n  });\n\n  (shadowSupport.v0 ? it : xit)(\n    'should walk up the assigned content (v0)',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML =\n          '<div id=\"target\" style=\"display:none;\">Stuff<content></content></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.createShadowRoot();\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n\n      fixture.innerHTML = '<label><div><p><a>hello</a></p></div></label>';\n      makeShadowTree(fixture.querySelector('div'));\n      var tree = axe.testUtils.flatTreeSetup(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.equal(\n        axe.commons.dom.findUp(el.actualNode, 'label'),\n        fixture.firstChild\n      );\n    }\n  );\n\n  (shadowSupport.v0 ? it : xit)(\n    'should walk down the shadow DOM v0',\n    function () {\n      function createContent() {\n        var group = document.createElement('div');\n        group.innerHTML = '<a>thing</a>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.createShadowRoot();\n        var div = document.createElement('div');\n        div.appendChild(createContent());\n        root.appendChild(div);\n      }\n\n      fixture.innerHTML = '<label><div></div></label>';\n      makeShadowTree(fixture.querySelector('div'));\n      var tree = axe.testUtils.flatTreeSetup(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.equal(\n        axe.commons.dom.findUp(el.actualNode, 'label'),\n        fixture.firstChild\n      );\n    }\n  );\n\n  (shadowSupport.v0 ? it : xit)(\n    'should work on shadow root children',\n    function () {\n      fixture.innerHTML = '<div role=\"list\" id=\"something\"></div>';\n      var shadow = fixture.querySelector('#something').createShadowRoot();\n\n      shadow.innerHTML = '<div role=\"listitem\">item 1</div>';\n      var listItem = shadow.querySelector('[role=listitem]');\n\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(\n        axe.commons.dom.findUp(listItem, '[role=list]'),\n        fixture.firstChild\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should walk up the assigned slot',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML =\n          '<div id=\"target\" style=\"display:none;\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n\n      fixture.innerHTML = '<label><div><p><a>hello</a></p></div></label>';\n      makeShadowTree(fixture.querySelector('div'));\n      var tree = axe.testUtils.flatTreeSetup(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.equal(\n        axe.commons.dom.findUp(el.actualNode, 'label'),\n        fixture.firstChild\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find element in assigned slot, not in the host document',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.className = 'target';\n        var slot = document.createElement('slot');\n        group.appendChild(slot);\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n\n      fixture.innerHTML = '<label><div><p><a>hello</a></p></div></label>';\n      makeShadowTree(fixture.querySelector('div'));\n      var tree = axe.testUtils.flatTreeSetup(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      var target = axe.utils.querySelectorAll(tree, '.target')[0];\n      assert.equal(\n        axe.commons.dom.findUp(el.actualNode, 'div'),\n        target.actualNode\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)('should walk up the shadow DOM', function () {\n    function createContent() {\n      var group = document.createElement('div');\n      group.innerHTML = '<a>thing</a>';\n      return group;\n    }\n    function makeShadowTree(node) {\n      var root = node.attachShadow({ mode: 'open' });\n      var div = document.createElement('div');\n      root.appendChild(div);\n      div.appendChild(createContent());\n    }\n\n    fixture.innerHTML = '<label><div></div></label>';\n    makeShadowTree(fixture.querySelector('div'));\n    var tree = axe.testUtils.flatTreeSetup(fixture.firstChild);\n    var el = axe.utils.querySelectorAll(tree, 'a')[0];\n    assert.equal(\n      axe.commons.dom.findUp(el.actualNode, 'label'),\n      fixture.firstChild\n    );\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should work on shadow root children',\n    function () {\n      fixture.innerHTML = '<div role=\"list\" id=\"something\"></div>';\n      var shadow = fixture\n        .querySelector('#something')\n        .attachShadow({ mode: 'open' });\n\n      shadow.innerHTML = '<div role=\"listitem\">item 1</div>';\n      var listItem = shadow.querySelector('[role=listitem]');\n\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(\n        axe.commons.dom.findUp(listItem, '[role=list]'),\n        fixture.firstChild\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/focus-disabled.js",
    "content": "describe('dom.focus-disabled', () => {\n  const focusDisabled = axe.commons.dom.focusDisabled;\n  const { queryFixture, queryShadowFixture } = axe.testUtils;\n\n  it('returns false for non-disabled element', () => {\n    const vNode = queryFixture('<span id=\"target\"></span>');\n\n    assert.isFalse(focusDisabled(vNode));\n  });\n\n  it('returns true for disabled element which is allowed to be disabled', () => {\n    const vNode = queryFixture('<button id=\"target\" disabled></button>');\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  it('returns false for disabled element which is not allowed to be disabled', () => {\n    const vNode = queryFixture('<span id=\"target\" disabled></span>');\n\n    assert.isFalse(focusDisabled(vNode));\n  });\n\n  it('returns true if parent fieldset is disabled', () => {\n    const vNode = queryFixture(\n      '<fieldset disabled><input id=\"target\"/></fieldset>'\n    );\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  it('returns false if element is inside a legend inside a disabled fieldset', () => {\n    const vNode = queryFixture(\n      '<fieldset disabled><legend><input id=\"target\"/></legend></fieldset>'\n    );\n\n    assert.isFalse(focusDisabled(vNode));\n  });\n\n  it('returns false for disabled fieldset outside shadow tree', () => {\n    const vNode = queryShadowFixture(\n      '<fieldset disabled><div id=\"shadow\"></div></fieldset>',\n      '<input id=\"target\"/>'\n    );\n\n    assert.isFalse(focusDisabled(vNode));\n  });\n\n  it('returns true if element is hidden for everyone', () => {\n    const vNode = queryFixture('<button id=\"target\" hidden></button>');\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  it('returns true for element with inert', () => {\n    const vNode = queryFixture('<button id=\"target\" inert></button>');\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  it('returns true for ancestor with inert', () => {\n    const vNode = queryFixture(\n      '<div inert><div><button id=\"target\"></button></div></div>'\n    );\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  it('returns true for ancestor with inert outside shadow tree', () => {\n    const vNode = queryShadowFixture(\n      '<div inert><div id=\"shadow\"></div></div>',\n      '<input id=\"target\"/>'\n    );\n\n    assert.isTrue(focusDisabled(vNode));\n  });\n\n  describe('SerialVirtualNode', () => {\n    it('returns false if element is hidden for everyone', () => {\n      const vNode = new axe.SerialVirtualNode({\n        nodeName: 'button',\n        attributes: {\n          hidden: true\n        }\n      });\n\n      assert.isFalse(focusDisabled(vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-composed-parent.js",
    "content": "/* global xit */\ndescribe('dom.getComposedParent', function () {\n  'use strict';\n  var getComposedParent = axe.commons.dom.getComposedParent;\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns the parentNode normally', function () {\n    fixture.innerHTML = '<div id=\"parent\"><div id=\"target\"></div></div>';\n\n    var actual = getComposedParent(document.getElementById('target'));\n    assert.instanceOf(actual, Node);\n    assert.equal(actual, document.getElementById('parent'));\n  });\n\n  it('returns null from the documentElement', function () {\n    assert.isNull(getComposedParent(document.documentElement));\n  });\n\n  (shadowSupport ? it : xit)(\n    'skip the slot node for slotted content',\n    function () {\n      fixture.innerHTML = '<div id=\"shadow\"><div id=\"target\"></div></div>';\n      var shadowRoot = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"grand-parent\">' + '<slot id=\"parent\"></slot>' + '</div>';\n\n      var actual = getComposedParent(fixture.querySelector('#target'));\n      assert.instanceOf(actual, Node);\n      assert.equal(actual, shadowRoot.querySelector('#grand-parent'));\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'understands explicitly slotted nodes',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"shadow\"><div id=\"target\" slot=\"bar\"></div></div>';\n      var shadowRoot = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"grand-parent\">' +\n        '<slot name=\"foo\"></slot>' +\n        '<div id=\"parent\"><slot name=\"bar\"></slot></div>' +\n        '</div>';\n\n      var actual = getComposedParent(fixture.querySelector('#target'));\n      assert.instanceOf(actual, Node);\n      assert.equal(actual, shadowRoot.querySelector('#parent'));\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'returns elements within a shadow tree',\n    function () {\n      fixture.innerHTML = '<div id=\"shadow\"> content </div>';\n      var shadowRoot = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"parent\">' + '<slot id=\"target\"></slot>' + '</div>';\n\n      var actual = getComposedParent(shadowRoot.querySelector('#target'));\n      assert.instanceOf(actual, Node);\n      assert.equal(actual, shadowRoot.querySelector('#parent'));\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'returns the host when it reaches the shadow root',\n    function () {\n      fixture.innerHTML = '<div id=\"parent\"> content </div>';\n      var shadowRoot = document\n        .getElementById('parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<div id=\"target\"> <slot></slot> </div>';\n\n      var actual = getComposedParent(shadowRoot.querySelector('#target'));\n      assert.instanceOf(actual, Node);\n      assert.equal(actual, fixture.querySelector('#parent'));\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/get-element-by-reference.js",
    "content": "describe('dom.getElementByReference', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return null if the attribute is not found', function () {\n    fixture.innerHTML = '<a id=\"link\" href=\"#target\">Hi</a>';\n    var node = document.getElementById('link'),\n      result = axe.commons.dom.getElementByReference(node, 'usemap');\n\n    assert.isNull(result);\n  });\n\n  it('should return null if the attribute does not start with \"#\"', function () {\n    fixture.innerHTML = '<a id=\"link\" usemap=\"target\">Hi</a>';\n    var node = document.getElementById('link'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.isNull(result);\n  });\n\n  it('should return null if no targets are found', function () {\n    fixture.innerHTML = '<a id=\"link\" href=\"#target\">Hi</a>';\n    var node = document.getElementById('link'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.isNull(result);\n  });\n\n  it('should return node if target is found (href)', function () {\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"#target\">Hi</a>' + '<a id=\"target\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n\n  it('should return node if target is found (usemap)', function () {\n    fixture.innerHTML =\n      '<img id=\"link\" usemap=\"#target\">Hi</a>' + '<map id=\"target\"></map>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target'),\n      result = axe.commons.dom.getElementByReference(node, 'usemap');\n\n    assert.equal(result, expected);\n  });\n\n  it('should prioritize ID', function () {\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"#target\">Hi</a>' +\n      '<a id=\"target\"></a>' +\n      '<a name=\"target\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n\n  it('should fallback to name', function () {\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"#target\">Hi</a>' +\n      '<a name=\"target\" id=\"target0\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target0'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n\n  it('should return the first matching element with name', function () {\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"#target\">Hi</a>' +\n      '<a name=\"target\" id=\"target0\"></a>' +\n      '<a name=\"target\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target0'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n\n  it('returns the first matching element using Angular skiplinks', function () {\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"/#target\">Hi</a>' +\n      '<a name=\"target\" id=\"target0\"></a>' +\n      '<a name=\"target\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target0'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n\n  it('should work with absolute links', function () {\n    var currentPage = window.location.origin + window.location.pathname;\n\n    fixture.innerHTML =\n      '<a id=\"link\" href=\"' +\n      currentPage +\n      '#target\">Hi</a>' +\n      '<a id=\"target\"></a>' +\n      '<a name=\"target\"></a>';\n\n    var node = document.getElementById('link'),\n      expected = document.getElementById('target'),\n      result = axe.commons.dom.getElementByReference(node, 'href');\n\n    assert.equal(result, expected);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-element-coordinates.js",
    "content": "//@todo better coverage\ndescribe('dom.getElementCoordinates', function () {\n  'use strict';\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should calculate bounding box based on element position', function () {\n    var el, coords;\n\n    fixture.innerHTML =\n      '<div id=\"div\" style=\"position: absolute; top: -1px; left: -1px;\">' +\n      '<span id=\"coords0\" style=\"position:absolute; top: -999px; left: -999px; width: 1000px; height: 1000px;\">' +\n      'Absolute</span>' +\n      '</div>';\n\n    el = document.getElementById('coords0');\n    coords = axe.commons.dom.getElementCoordinates(el);\n    assert.closeTo(coords.left, -1000, 0.5);\n    assert.closeTo(coords.top, -1000, 0.5);\n    assert.closeTo(coords.width, 1000, 0.5);\n    assert.closeTo(coords.height, 1000, 0.5);\n    assert.closeTo(coords.right, 0, 0.5);\n    assert.closeTo(coords.bottom, 0, 0.5);\n\n    el = document.getElementById('div');\n    coords = axe.commons.dom.getElementCoordinates(el);\n    assert.equal(Math.round(coords.left), -1);\n    assert.equal(Math.round(coords.top), -1);\n  });\n\n  it('should take into account scroll offsets', function () {\n    var el,\n      coords,\n      offset = axe.commons.dom.getScrollOffset(window.document);\n\n    fixture.innerHTML =\n      '<div id=\"div\" style=\"position: absolute; top: -1px; left: -1px;\">' +\n      '<span id=\"coords0\" style=\"position:absolute; top: -999px; left: -999px; width: 1000px; height: 1000px;\">' +\n      'Absolute</span>' +\n      '</div>';\n\n    el = document.getElementById('coords0');\n    coords = axe.commons.dom.getElementCoordinates(el);\n    assert.closeTo(coords.left, -1000, 0.5);\n    assert.closeTo(coords.top, -1000, 0.5);\n    assert.closeTo(coords.width, 1000, 0.5);\n    assert.closeTo(coords.height, 1000, 0.5);\n    assert.closeTo(coords.right, 0, 0.5);\n    assert.closeTo(coords.bottom, 0, 0.5);\n\n    window.scrollTo(0, 150);\n    coords = axe.commons.dom.getElementCoordinates(el);\n    assert.closeTo(coords.left, -1000, 0.5);\n    assert.closeTo(coords.top, -1000, 0.5);\n    assert.closeTo(coords.width, 1000, 0.5);\n    assert.closeTo(coords.height, 1000, 0.5);\n    assert.closeTo(coords.right, 0, 0.5);\n    assert.closeTo(coords.bottom, 0, 0.5);\n\n    window.scrollTo(offset.left, offset.top);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-element-stack.js",
    "content": "describe('dom.getElementStack', () => {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const getElementStack = axe.commons.dom.getElementStack;\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  function mapToIDs(stack) {\n    return stack\n      .map(function (node) {\n        return node.id;\n      })\n      .filter(function (id) {\n        return !!id;\n      });\n  }\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  describe('stack order', () => {\n    it('should return stack in DOM order of non-positioned elements', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '2', '1', 'fixture']);\n    });\n\n    it('should not return elements outside of the stack', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\">' +\n        '<span style=\"display:block\">Foo</span>' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '2', '1', 'fixture']);\n    });\n\n    it('should return stack in DOM order of non-positioned elements with z-index', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\";width:40px;height:40px;\">' +\n        '<div id=\"target\" style=\"width:40px;height:40px;z-index:100\">hello world</div>' +\n        '</div>' +\n        '<div id=\"2\" style=\"width:40px;height:40px;margin-top:-33px;\">' +\n        '<div id=\"3\" style=\"width:40px;height:40px;\">Some text</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      // Browsers seem to be buggy, which suggest  [3, target, 2, 1, fixture]\n      // We're following the spec in this.\n      // @see https://codepen.io/straker/pen/gOxpJyE\n      assert.deepEqual(stack, ['3', '2', 'target', '1', 'fixture']);\n    });\n\n    it('should should handle positioned elements without z-index', () => {\n      // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_without_z-index\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"width:40px;height:40px;position:absolute;top:0;\">' +\n        'DIV #1<br />position:absolute;</div>' +\n        '<div id=\"2\" style=\"width:40px;height:40px;position:relative;top:0;\">' +\n        'DIV #2<br />position:relative;</div>' +\n        '<div id=\"3\" style=\"width:40px;height:40px;position:relative;top:-40px;\">' +\n        'DIV #3<br />position:relative;</div>' +\n        '<div id=\"4\" style=\"width:40px;height:40px;position:absolute;top:0;\">' +\n        'DIV #4<br />position:absolute;</div>' +\n        '<div id=\"target\" style=\"width:40px;height:40px;margin-top:-80px;\">' +\n        'DIV #5<br />position:static;</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['4', '3', '2', '1', 'target', 'fixture']);\n    });\n\n    it('should handle floating and positioned elements without z-index', () => {\n      // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_and_float\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"width:40px;height:40px;position:absolute;top:0;\">' +\n        'DIV #1<br />position:absolute;</div>' +\n        '<div id=\"2\" style=\"width:40px;height:40px;float:left;\">' +\n        'DIV #2<br />float:left;</div>' +\n        '<div id=\"target\" style=\"width:40px;height:40px;\">' +\n        'DIV #3<br />no positioning</div>' +\n        '<div id=\"4\" style=\"width:40px;height:40px;position:absolute;top:0;\">' +\n        'DIV #4<br />position:absolute;</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['4', '1', '2', 'target', 'fixture']);\n    });\n\n    it('should handle floating parent elements', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"float: left; background: #000000; color: #fff;\">' +\n        '<div id=\"2\"><span id=\"target\">whole picture</span></div>' +\n        '</div>' +\n        '<div id=\"3\">' +\n        '<div id=\"4\" style=\"background: #f2f2f2;\">English</div>' +\n        '</div>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '2', '1', '4', '3', 'fixture']);\n    });\n\n    it('should handle z-index positioned elements in the same stacking context', () => {\n      // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_context_example_1\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"width:40px;height:40px;position:relative;\">' +\n        '<br />DIV #1' +\n        '<br />position:relative;' +\n        '<div id=\"2\" style=\"width:40px;height:40px;position:absolute;top:0;z-index:1;\">' +\n        '<br />DIV #2' +\n        '<br />position:absolute;' +\n        '<br />z-index:1;' +\n        '</div>' +\n        '</div>' +\n        '<br />' +\n        '<div id=\"3\" style=\"width:40px;height:40px;position:relative;    margin-top:-58px;\">' +\n        '<br />DIV #3' +\n        '<br />position:relative;' +\n        '<div id=\"4\" style=\"width:40px;height:40px;position:absolute;top:0;z-index:2;\">' +\n        '<br />DIV #4' +\n        '<br />position:absolute;' +\n        '<br />z-index:2;' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['4', '2', '3', 'target', 'fixture']);\n    });\n\n    it('should handle z-index positioned elements in different stacking contexts', () => {\n      // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/Stacking_context_example_2\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"width:40px;height:40px;position:relative;\">' +\n        '<br />DIV #1' +\n        '<br />position:relative;' +\n        '<div id=\"2\" style=\"width:40px;height:40px;position:absolute;top:0;z-index:2;\">' +\n        '<br />DIV #2' +\n        '<br />position:absolute;' +\n        '<br />z-index:2;' +\n        '</div>' +\n        '</div>' +\n        '<br />' +\n        '<div id=\"3\" style=\"width:40px;height:40px;position:relative;    margin-top:-58px;z-index:1\">' +\n        '<br />DIV #3' +\n        '<br />position:relative;' +\n        '<div id=\"4\" style=\"width:40px;height:40px;position:absolute;top:0;z-index:10;\">' +\n        '<br />DIV #4' +\n        '<br />position:absolute;' +\n        '<br />z-index:10;' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['2', '4', '3', 'target', 'fixture']);\n    });\n\n    it('should handle complex stacking context', () => {\n      // see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:5;\">' +\n        'Division Element #1<br/>' +\n        'position: relative;<br/>' +\n        'z-index: 5;' +\n        '</div>' +\n        '<div id=\"2\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:2;\">' +\n        'Division Element #2<br/>' +\n        'position: relative;<br/>' +\n        'z-index: 2;' +\n        '</div>' +\n        '<div id=\"3\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:4;\">' +\n        '<div id=\"4\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:6;\">' +\n        'Division Element #4<br/>' +\n        'position: relative;<br/>' +\n        'z-index: 6;' +\n        '</div>' +\n        'Division Element #3<br/>' +\n        'position: absolute;<br/>' +\n        'z-index: 4;' +\n        '<div id=\"5\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:1;\">' +\n        'Division Element #5<br/>' +\n        'position: relative;<br/>' +\n        'z-index: 1;' +\n        '</div>' +\n        '' +\n        '<div id=\"target\" style=\"position:absolute;top:0;left:0;width:40px;height:40px;z-index:3;\">' +\n        'Division Element #6<br/>' +\n        'position: absolute;<br/>' +\n        'z-index: 3;' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['1', '4', 'target', '5', '3', '2']);\n    });\n\n    it('should correctly order children of position elements without z-index', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"position:relative;width:40px;height:40px;\">' +\n        '<div id=\"target\" style=\"width:40px;height:40px;\"></div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '1', 'fixture']);\n    });\n\n    it('should correctly order children of position elements with z-index', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"position:relative;width:40px;height:40px;z-index:1\">' +\n        '<div id=\"target\" style=\"width:40px;height:40px;\"></div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '1', 'fixture']);\n    });\n\n    ['flex', 'inline-flex', 'grid', 'inline-grid'].forEach(type => {\n      it(`should correctly order \"${type}\" items with z-index`, () => {\n        fixture.innerHTML = `\n          <div id=\"1\" style=\"position:absolute;width:40px;height:40px;z-index:1\"></div>\n          <div id=\"2\" style=\"display: ${type}\">\n            <div id=\"target\" style=\"width:40px;height:40px;z-index:1\"></div>\n          </div>\n        `;\n        axe.testUtils.flatTreeSetup(fixture);\n        const target = fixture.querySelector('#target');\n        const stack = mapToIDs(getElementStack(target));\n        assert.deepEqual(stack, ['target', '1', '2', 'fixture']);\n      });\n    });\n\n    it('should handle modals on top of the stack', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</main>' +\n        '<div id=\"3\" style=\"position:absolute;top:0;left:0;right:0;height:100px\"></div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['3', 'target', '2', '1', 'fixture']);\n    });\n\n    it('should handle \"pointer-events:none\"', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</main>' +\n        '<div id=\"3\" style=\"position:absolute;top:0;left:0;right:0;height:100px;pointer-events:none\"></div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['3', 'target', '2', '1', 'fixture']);\n    });\n\n    it('should return elements left out by document.elementsFromPoint', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\">' +\n        '<label id=\"3\">Foo<input id=\"target\"></label>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '2', '1', 'fixture']);\n    });\n\n    it('should not return elements that do not fully cover the target', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"position:relative;\">' +\n        '<div id=\"2\" style=\"position:absolute;width:300px;height:19px;\"></div>' +\n        '<p id=\"target\" style=\"position:relative;z-index:1;width:300px;height:40px;\">Text oh heyyyy <a href=\"#\" id=\"target\">and here\\'s <br>a link</a></p>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '1', 'fixture']);\n    });\n\n    it('should not return parent elements that do not fully cover the target', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"height:20px;position:relative;\">' +\n        '<div id=\"target\" style=\"position:absolute;top:21px;\">Text</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target']);\n    });\n\n    it('should return elements that partially cover the target', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"height:40px;position:relative;\">' +\n        '<div id=\"2\" style=\"height:20px;\"></div>' +\n        '<div id=\"target\" style=\"position:absolute;margin-top:-11px;\">Text</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '2', '1', 'fixture']);\n    });\n\n    it('should handle negative z-index', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"position:relative;z-index:-10\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['1', 'fixture', 'target', '2']);\n    });\n\n    it('should not add hidden elements', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"position: absolute; display: none;\">Some text</div>' +\n        '<div id=\"3\" style=\"position: absolute; visibility: hidden\">Some text</div>' +\n        '<div id=\"4\" style=\"position: absolute; opacity: 0\">Some text</div>' +\n        '<span id=\"target\">Hello World</span>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '1', 'fixture']);\n    });\n\n    it('should correctly position children of positioned parents', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"position: relative; padding: 60px 0;\">Some text</div>' +\n        '<section id=\"2\" style=\"position: relative; padding: 60px 0;\">' +\n        '<div id=\"3\" style=\"margin-top: -120px;\">' +\n        '<h3 id=\"target\">Hi, Hello World.</h3>' +\n        '</div>' +\n        '</section>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '1', 'fixture']);\n    });\n\n    it('should correctly position siblings with positioned children correctly', () => {\n      fixture.innerHTML =\n        '<div id=\"1\">Some text</div>' +\n        '<div id=\"2\" style=\"position: absolute; top: 0; left: 0;\">Some text</div>' +\n        '<div id=\"3\" style=\"position: absolute; top: 0; left: 0;\">' +\n        '<div id=\"4\" style=\"position: absolute; top: 0; left: 0;\">Some text</div>' +\n        '<div id=\"5\">' +\n        '<div id=\"target\">Some text</div>' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['4', 'target', '5', '3', '2', '1', 'fixture']);\n    });\n\n    it('should correctly position children of float elements with position elements', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"width: 50px; height: 50px; position: relative;\">' +\n        '<div id=\"2\" style=\"width: 50px; height: 50px; float: left;\">' +\n        '<div id=\"3\" style=\"width: 50px; height: 50px;\">' +\n        '<div id=\"4\" style=\"width: 50px; height: 50px;\">' +\n        '<div id=\"target\" style=\"width: 50px; height: 50px; position: relative;\"></div>' +\n        '</div>' +\n        '</div>' +\n        '<div id=\"5\" style=\"width: 50px; height: 50px; position: absolute; top:0\"></div>' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['5', 'target', '4', '3', '2', '1', 'fixture']);\n    });\n\n    it('should correctly position opacity elements and positioned elements', () => {\n      fixture.innerHTML = `\n        <div id=\"1\" style=\"opacity: 0.9;\">\n          <div id=\"2\" style=\"position: relative; z-index: 2\">\n           <h1 id=\"target\">Hello World</h1>\n          </div>\n        </div>\n        <div id=\"3\" style=\"opacity: 0.8;\">\n          <div id=\"4\" style=\"position: absolute; top: 20px; z-index: -1;\">\n            <div id=\"5\" style=\"height: 40px; width: 100vw; background: red\"></div>\n          </div>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['5', '4', 'target', '2', '1', 'fixture']);\n    });\n\n    it('should correctly order elements outside of the axe tree', () => {\n      fixture.innerHTML = `\n        <div id=\"1\" style=\"opacity: 0.9;\">\n          <div id=\"2\" style=\"position: relative; z-index: 2\">\n           <h1 id=\"target\">Hello World</h1>\n          </div>\n        </div>\n        <div id=\"tree\" style=\"opacity: 0.8;\">\n          <div id=\"4\" style=\"position: absolute; top: 20px; z-index: -1;\">\n            <div id=\"5\" style=\"height: 40px; width: 100vw; background: red\"></div>\n          </div>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture.querySelector('#tree'));\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['5', '4', 'target', '2', '1', 'fixture']);\n    });\n\n    it('should return empty array for hidden elements', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"position: absolute; display: none\">' +\n        '<span id=\"3\">text</span>' +\n        '<span id=\"target\">Hello World</span>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, []);\n    });\n\n    it('should return empty array for children of 0 height scrollable regions', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"overflow: scroll; height: 0\">' +\n        '<span id=\"3\">text</span>' +\n        '<span id=\"target\">Hello World</span>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, []);\n    });\n\n    it('should correctly position children of different stacking contexts', () => {\n      fixture.innerHTML = `\n        <header id=\"1\" style=\"position: absolute; z-index: 999; height: 50px; width: 100%; top: 0\">\n          <div id=\"2\" style=\"display: flex; position: relative; height: 50px;\">\n            <div id=\"3\" style=\"position: relative; height: 50px; width: 100%;\"></div>\n          </div>\n          <div id=\"4\" style=\"display: flex; position: absolute; height: 50px; width: 100%; top: 0;\">\n            <div id=\"5\" style=\"position: absolute; transform: translate(0, -50%);\">\n              <div id=\"6\" style=\"display: flex;\">\n                <span id=\"target\">Hello World</span>\n              </div>\n            </div>\n          </div>\n        </header>\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '6', '5', '4', '3', '2', '1']);\n    });\n\n    it('should throw error if element midpoint-x exceeds the grid', () => {\n      fixture.innerHTML = '<div id=\"target\">Hello World</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const vNode = axe.utils.getNodeFromTree(target);\n      Object.defineProperty(vNode, 'boundingClientRect', {\n        get: () => {\n          return {\n            left: 0,\n            top: 10,\n            width: 10000,\n            height: 10\n          };\n        }\n      });\n      assert.throws(() => {\n        getElementStack(target);\n      }, 'Element midpoint exceeds the grid bounds');\n    });\n\n    it('should throw error if element midpoint-y exceeds the grid', () => {\n      fixture.innerHTML = '<div id=\"target\">Hello World</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const vNode = axe.utils.getNodeFromTree(target);\n      Object.defineProperty(vNode, 'boundingClientRect', {\n        get: () => {\n          return {\n            left: 0,\n            top: 10,\n            width: 10,\n            height: 10000\n          };\n        }\n      });\n      assert.throws(() => {\n        getElementStack(target);\n      }, 'Element midpoint exceeds the grid bounds');\n    });\n\n    it('should ignore element which exactly overlaps midpoint of target element', () => {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"width: 100%; height: 50px;\">' +\n        '<h4 id=\"1\" style=\"margin: 0; width: 100%; height: 25px;\">Foo</h4>' +\n        'Bar' +\n        '</div>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', 'fixture']);\n    });\n\n    // IE11 either only supports clip paths defined by url() or not at all,\n    // MDN and caniuse.com give different results...\n    it('should not add hidden elements using clip-path', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"position: absolute; clip: rect(1px, 1px, 1px, 1px);\">Some text</div>' +\n        '<span id=\"target\">Hello World</span>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '1', 'fixture']);\n    });\n\n    (shadowSupported ? it : xit)(\n      'should sort shadow dom elements correctly',\n      () => {\n        fixture.innerHTML = '<div id=\"container\"></div>';\n        const container = fixture.querySelector('#container');\n        const shadow = container.attachShadow({ mode: 'open' });\n        shadow.innerHTML = '<span id=\"shadowTarget\">Text</span>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = shadow.querySelector('#shadowTarget');\n        const stack = mapToIDs(getElementStack(target));\n        assert.deepEqual(stack, ['shadowTarget', 'container', 'fixture']);\n      }\n    );\n\n    (shadowSupported ? it : xit)(\n      'should sort nested shadow dom elements correctly',\n      () => {\n        fixture.innerHTML = '<div id=\"container\"></div>';\n        const container = fixture.querySelector('#container');\n        const shadow = container.attachShadow({ mode: 'open' });\n\n        shadow.innerHTML = '<div id=\"shadowContainer\"></div>';\n        const nestedContainer = shadow.querySelector('#shadowContainer');\n        const nestedShadow = nestedContainer.attachShadow({ mode: 'open' });\n\n        nestedShadow.innerHTML = '<span id=\"shadowTarget\">Text</span>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = nestedShadow.querySelector('#shadowTarget');\n        const stack = mapToIDs(getElementStack(target));\n        assert.deepEqual(stack, [\n          'shadowTarget',\n          'shadowContainer',\n          'container',\n          'fixture'\n        ]);\n      }\n    );\n\n    (shadowSupported ? it : xit)(\n      'should sort positioned shadow elements correctly',\n      () => {\n        fixture.innerHTML = '<div id=\"container\"></div>';\n        const container = fixture.querySelector('#container');\n        const shadow = container.attachShadow({ mode: 'open' });\n\n        shadow.innerHTML =\n          '<div id=\"shadowContainer\" style=\"position: relative; z-index: -1;\"></div>';\n        const nestedContainer = shadow.querySelector('#shadowContainer');\n        const nestedShadow = nestedContainer.attachShadow({ mode: 'open' });\n\n        nestedShadow.innerHTML = '<span id=\"shadowTarget\">Text</span>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = nestedShadow.querySelector('#shadowTarget');\n        const stack = mapToIDs(getElementStack(target));\n        assert.deepEqual(stack, [\n          'container',\n          'fixture',\n          'shadowTarget',\n          'shadowContainer'\n        ]);\n      }\n    );\n\n    (shadowSupported ? it : xit)(\n      'should sort shadow elements with different trees correctly',\n      () => {\n        fixture.innerHTML =\n          '<div id=\"container1\"></div><div id=\"container2\"  style=\"position: absolute; top: 0;\">';\n        const container1 = fixture.querySelector('#container1');\n        const shadow1 = container1.attachShadow({ mode: 'open' });\n        shadow1.innerHTML = '<span id=\"shadowTarget\">Text</span>';\n\n        const container2 = fixture.querySelector('#container2');\n        const shadow2 = container2.attachShadow({ mode: 'open' });\n        shadow2.innerHTML = '<span id=\"1\">Container 2 text</span>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = shadow1.querySelector('#shadowTarget');\n        const stack = mapToIDs(getElementStack(target));\n        assert.deepEqual(stack, [\n          '1',\n          'container2',\n          'shadowTarget',\n          'container1',\n          'fixture'\n        ]);\n      }\n    );\n\n    (shadowSupported ? it : xit)('should sort <slot> elements', () => {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"height: 100px;\"><p id=\"target\">Slotted text</p></div>';\n      const container = fixture.querySelector('#container');\n      const shadow = container.attachShadow({ mode: 'open' });\n\n      shadow.innerHTML =\n        '<div id=\"shadowContainer\" style=\"position: absolute;\"><slot></slot></div>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, [\n        'target',\n        'shadowContainer',\n        'container',\n        'fixture'\n      ]);\n    });\n  });\n\n  describe('scroll regions', () => {\n    const origHeight = document.documentElement.style.height;\n    const origOverflow = document.documentElement.style.overflowY;\n\n    afterEach(() => {\n      document.documentElement.style.height = origHeight;\n      document.documentElement.style.overflowY = origOverflow;\n    });\n\n    it('should return stack of scroll regions', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"overflow:auto\">' +\n        '<div id=\"3\" style=\"height:100px\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '2', '1', 'fixture']);\n    });\n\n    it('should return stack when scroll region is larger than parent', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"overflow:auto;height:40px\">' +\n        '<div id=\"3\" style=\"height:100px\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '2', '1', 'fixture']);\n    });\n\n    it('should return stack of recursive scroll regions', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"overflow:auto;height:40px\">' +\n        '<div id=\"3\" style=\"height:100px\">' +\n        '<div id=\"4\" style=\"overflow:auto;height:40px\">' +\n        '<div id=\"5\" style=\"overflow:auto;height:100px\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</div>' +\n        '</div>' +\n        '</div>' +\n        '</main>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '5', '4', '3', '2', '1', 'fixture']);\n    });\n\n    it('should handle html as a scroll region', () => {\n      fixture.innerHTML =\n        '<main id=\"1\">' +\n        '<div id=\"2\" style=\"overflow:auto\">' +\n        '<div id=\"3\" style=\"height:100px\">' +\n        '<p id=\"target\">Hello World</p>' +\n        '</div>' +\n        '</div>' +\n        '</main>';\n      document.documentElement.style.height = '5000px';\n      document.documentElement.style.overflowY = 'scroll';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '2', '1', 'fixture']);\n    });\n\n    it('should use correct scroll region parent', () => {\n      fixture.innerHTML =\n        '<div id=\"1\" style=\"overflow: scroll; height: 50px;\">' +\n        '<div id=\"2\" style=\"overflow: scroll; height: 100px;\">' +\n        '<div id=\"3\">' +\n        '<div id=\"target\" style=\"margin-top: 200px;\">text</div>' +\n        '</div>' +\n        '</div>' +\n        '</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#target');\n      const stack = mapToIDs(getElementStack(target));\n      assert.deepEqual(stack, ['target', '3', '2', '1', 'fixture']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-modal-dialog.js",
    "content": "describe('dom.getModalDialog', () => {\n  const fixture = document.querySelector('#fixture');\n  const getModalDialog = axe.commons.dom.getModalDialog;\n  const { flatTreeSetup } = axe.testUtils;\n\n  it('returns a modal dialog', () => {\n    fixture.innerHTML = `\n      <dialog id=\"target\"><span>Hello</span></dialog>\n    `;\n    document.querySelector('#target').showModal();\n    const tree = flatTreeSetup(fixture);\n    const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n    assert.equal(getModalDialog(), vNode);\n  });\n\n  it('returns null for an opened dialog', () => {\n    fixture.innerHTML = `\n      <dialog open><span>Hello</span></dialog>\n    `;\n    flatTreeSetup(fixture);\n\n    assert.isNull(getModalDialog());\n  });\n\n  it('returns null for a closed dialog', () => {\n    fixture.innerHTML = `\n      <dialog><span>Hello</span></dialog>\n    `;\n    flatTreeSetup(fixture);\n\n    assert.isNull(getModalDialog());\n  });\n\n  it('returns null when there is no dialog', () => {\n    fixture.innerHTML = `\n      <div>World</div>\n    `;\n    flatTreeSetup(fixture);\n\n    assert.isNull(getModalDialog());\n  });\n\n  it('returns null if the modal dialog is not visible', () => {\n    fixture.innerHTML = `\n      <style>dialog[open] { display: none }</style>\n      <dialog id=\"target\"><span>Hello</span></dialog>\n    `;\n    document.querySelector('#target').showModal();\n    flatTreeSetup(fixture);\n\n    assert.isNull(getModalDialog());\n  });\n\n  describe('fallback', () => {\n    it('returns true for modal dialog when elementsFromPoint does not return the dialog', () => {\n      fixture.innerHTML = `\n        <style>dialog::backdrop { display: none; }</style>\n        <dialog id=\"target\"><span>Hello</span></dialog>\n        <div>World</div>\n      `;\n      document.querySelector('#target').showModal();\n      const tree = flatTreeSetup(fixture);\n      const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n      assert.equal(getModalDialog(), vNode);\n    });\n\n    it('skips checking elements with pointer-events: none', () => {\n      fixture.innerHTML = `\n        <style>body { pointer-events: none; } dialog::backdrop { display: none; }</style>\n        <dialog id=\"target\"><span>Hello</span></dialog>\n        <div>World</div>\n      `;\n      document.querySelector('#target').showModal();\n      flatTreeSetup(fixture);\n\n      assert.isNull(getModalDialog());\n    });\n\n    it('takes into account a scrolled page', () => {\n      fixture.innerHTML = `\n        <style>\n          dialog::backdrop { display: none; }\n          #large-scroll { height: 200vh; margin-bottom: 100px; }\n        </style>\n        <div id=\"large-scroll\"></div>\n        <dialog id=\"target\"><span>Hello</span></dialog>\n        <div id=\"scroll-target\">World</div>\n      `;\n      document.querySelector('#scroll-target').scrollIntoView();\n      document.querySelector('#target').showModal();\n      const tree = flatTreeSetup(fixture);\n      const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n      assert.equal(getModalDialog(), vNode);\n    });\n\n    it('returns the modal dialog when two dialogs are open', () => {\n      fixture.innerHTML = `\n        <style>dialog::backdrop { display: none; }</style>\n        <dialog id=\"not-modal\" open><span>Hello</span></dialog>\n        <dialog id=\"target\"><span>Hello</span></dialog>\n        <div>World</div>\n      `;\n      document.querySelector('#target').showModal();\n      const tree = flatTreeSetup(fixture);\n      const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n      assert.equal(getModalDialog(), vNode);\n    });\n\n    it('returns the outer modal when a dialog modal contains a non-dialog modal', () => {\n      fixture.innerHTML = `\n        <style>dialog::backdrop { display: none; }</style>\n        <dialog id=\"target\">\n          <span>Hello</span>\n          <dialog open>Open modal</dialog>\n        </dialog>\n        <div>World</div>\n      `;\n      document.querySelector('#target').showModal();\n      const tree = flatTreeSetup(fixture);\n      const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n      assert.equal(getModalDialog(), vNode);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-node-grid.js",
    "content": "describe('dom.getNodeGrid', () => {\n  const queryFixture = axe.testUtils.queryFixture;\n  const getNodeGrid = axe.commons.dom.getNodeGrid;\n\n  it('returns the grid of an vNode', () => {\n    const vNode = queryFixture(`\n      <section id=\"container\" style=\"height: 2em; overflow: auto;\">\n        <p id=\"target\" style=\"height: 2em;\">  text  </p>\n        <p id=\"sibling\" style=\"height: 2em;\">  text  </p>\n      </section>\n    `);\n    const grid = getNodeGrid(vNode);\n    assert.equal(grid.container.props.id, 'container');\n  });\n\n  it('returns the grid of an elm', () => {\n    const vNode = queryFixture(`\n      <section id=\"container\" style=\"height: 2em; overflow: auto;\">\n        <p id=\"target\" style=\"height: 2em;\">  text  </p>\n        <p id=\"sibling\" style=\"height: 2em;\">  text  </p>\n      </section>\n    `);\n    const grid = getNodeGrid(vNode.actualNode);\n    assert.equal(grid.container.props.id, 'container');\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-overflow-hidden-ancestors.js",
    "content": "describe('dom.getOverflowHiddenAncestors', () => {\n  const { getOverflowHiddenAncestors } = axe.commons.dom;\n  const { queryFixture } = axe.testUtils;\n\n  function ids(vNodes) {\n    return vNodes.map(vNode => vNode.props.id);\n  }\n\n  it('returns parent node', () => {\n    const vNode = queryFixture(`\n      <div style=\"overflow: hidden\">\n        <div id=\"target\"></div>\n      </div>\n    `);\n\n    const actual = getOverflowHiddenAncestors(vNode);\n    assert.deepEqual(actual, [vNode.parent]);\n  });\n\n  it('returns ancestor node', () => {\n    const vNode = queryFixture(`\n      <div style=\"overflow: hidden\">\n        <div>\n          <div id=\"target\"></div>\n        </div>\n      </div>\n    `);\n\n    const actual = getOverflowHiddenAncestors(vNode);\n    assert.deepEqual(actual, [vNode.parent.parent]);\n  });\n\n  it('returns itself', () => {\n    const vNode = queryFixture(\n      `<div id=\"target\" style=\"overflow: hidden\"></div>`\n    );\n\n    const actual = getOverflowHiddenAncestors(vNode);\n    assert.deepEqual(actual, [vNode]);\n  });\n\n  it('returns all nodes with overflow:hidden', () => {\n    const vNode = queryFixture(`\n      <div id=\"1\" style=\"overflow: hidden\">\n        <div>\n          <div>\n            <div id=\"2\" style=\"overflow: hidden\">\n              <div id=\"target\" style=\"overflow: hidden\"></div>\n            </div>\n          </div>\n        </div>\n      </div>\n    `);\n    const actual = getOverflowHiddenAncestors(vNode);\n    assert.deepEqual(ids(actual), ['target', '2', '1']);\n  });\n\n  it('ignores other overflow types', () => {\n    const vNode = queryFixture(`\n      <div id=\"1\" style=\"overflow: visible\">\n        <div>\n          <div>\n            <div id=\"2\" style=\"overflow: hidden\">\n              <div id=\"target\" style=\"overflow: auto\"></div>\n            </div>\n          </div>\n        </div>\n      </div>\n    `);\n    const actual = getOverflowHiddenAncestors(vNode);\n    assert.deepEqual(ids(actual), ['2']);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-scroll-offset.js",
    "content": "describe('dom.getScrollOffset', function () {\n  'use strict';\n\n  it('should return scrollTop and scrollLeft for normal nodes', function () {\n    var offset = axe.commons.dom.getScrollOffset({\n      nodeType: 3,\n      scrollTop: 42,\n      scrollLeft: 98\n    });\n\n    assert.equal(offset.left, 98);\n    assert.equal(offset.top, 42);\n  });\n\n  it('should get the scroll from the documentElement if a document is passed in', function () {\n    var offset = axe.commons.dom.getScrollOffset({\n      nodeType: 9,\n      documentElement: {\n        scrollTop: 42,\n        scrollLeft: 98\n      }\n    });\n\n    assert.equal(offset.left, 98);\n    assert.equal(offset.top, 42);\n  });\n\n  it('should get the scroll from the document.body if a document is passed in and it has no documentElement', function () {\n    var offset = axe.commons.dom.getScrollOffset({\n      nodeType: 9,\n      body: {\n        scrollTop: 42,\n        scrollLeft: 98\n      }\n    });\n\n    assert.equal(offset.left, 98);\n    assert.equal(offset.top, 42);\n  });\n\n  it('should work on a window object', function () {\n    var offset = axe.commons.dom.getScrollOffset({\n      document: {\n        nodeType: 9,\n        documentElement: {\n          scrollTop: 42,\n          scrollLeft: 98\n        }\n      }\n    });\n\n    assert.equal(offset.left, 98);\n    assert.equal(offset.top, 42);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-tabbable-elements.js",
    "content": "describe('dom.getTabbableElements', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var injectIntoFixture = axe.testUtils.injectIntoFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var getTabbableElementsFn = axe.commons.dom.getTabbableElements;\n\n  it('returns tabbable elms when node contains tabbable element', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\">' +\n        '<label>Enter description:' +\n        '<textarea></textarea>' +\n        '</label>' +\n        '</div>'\n    );\n    var actual = getTabbableElementsFn(virtualNode);\n    assert.lengthOf(actual, 1);\n    assert.equal(actual[0].actualNode.nodeName.toUpperCase(), 'TEXTAREA');\n  });\n\n  it('returns empty [] when element does not contains tabbable element (using tabindex to take element out of tab-order)', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\">' + '<input tabindex=\"-1\">' + '</div>'\n    );\n    var actual = getTabbableElementsFn(virtualNode);\n    assert.lengthOf(actual, 0);\n  });\n\n  it('returns empty [] when element contains disabled (tabbable) element', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\">' + '<button disabled>Submit Me</button>' + '</div>'\n    );\n    var actual = getTabbableElementsFn(virtualNode);\n    assert.lengthOf(actual, 0);\n  });\n\n  it('returns empty [] when element does not contain tabbable element', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"target\">' + '<p>Some text</p>' + '</div>'\n    );\n    var actual = getTabbableElementsFn(virtualNode);\n    assert.lengthOf(actual, 0);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns tabbable elms when element contains tabbable element inside shadowDOM',\n    function () {\n      var fixture = injectIntoFixture('<div id=\"target\"></div>`');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<button>btn</button>';\n      // re build tree after shadowDOM is constructed\n      axe.setup(fixture);\n      var virtualNode = axe.utils.getNodeFromTree(node);\n      var actual = getTabbableElementsFn(virtualNode);\n      assert.lengthOf(actual, 1);\n      assert.equal(actual[0].actualNode.nodeName.toUpperCase(), 'BUTTON');\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'returns empty [] when element contains disabled (tabbable) element inside shadowDOM',\n    function () {\n      var fixture = injectIntoFixture('<div id=\"target\"></div>`');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<button disabled>btn</button>';\n      // re build tree after shadowDOM is constructed\n      axe.setup(fixture);\n      var virtualNode = axe.utils.getNodeFromTree(node);\n      var actual = getTabbableElementsFn(virtualNode);\n      assert.lengthOf(actual, 0);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'returns empty [] when element does not contain tabbable element inside shadowDOM',\n    function () {\n      var fixture = injectIntoFixture('<div id=\"target\"></div>`');\n      var node = fixture.querySelector('#target');\n      var shadow = node.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<p>I am not tabbable</p>';\n      // re build tree after shadowDOM is constructed\n      axe.setup(fixture);\n      var virtualNode = axe.utils.getNodeFromTree(node);\n      var actual = getTabbableElementsFn(virtualNode);\n      assert.lengthOf(actual, 0);\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/get-target-rects.js",
    "content": "describe('get-target-rects', () => {\n  const getTargetRects = axe.commons.dom.getTargetRects;\n  const { queryFixture } = axe.testUtils;\n  const fixture = document.getElementById('fixture');\n\n  it('returns the bounding rect when unobscured', () => {\n    const vNode = queryFixture('<button id=\"target\">x</button>');\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]);\n  });\n\n  it('returns subset rect when obscured', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px;\"></div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [new DOMRect(10, 5, 20, 40)]);\n  });\n\n  it('ignores elements with \"pointer-events: none\"', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px; pointer-events: none\"></div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]);\n  });\n\n  it(\"ignores elements that don't overlap the target\", () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 60px; top: 0; width: 50px; height: 50px;\"></div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]);\n  });\n\n  it('ignores non-tabbable descendants of the target', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">\n        <div style=\"position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;\"></div>\n      </button>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]);\n  });\n\n  it('returns each unobscured area', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px;\"></div>\n      <div style=\"position: absolute; left: 0px; top: 20px; width: 50px; height: 10px\"></div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [\n      new DOMRect(10, 5, 20, 15),\n      new DOMRect(10, 30, 20, 15)\n    ]);\n  });\n\n  it('returns empty if target is fully obscured', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 0; top: 0; width: 50px; height: 50px;\"></div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.lengthOf(rects, 0);\n  });\n\n  it('returns subset rect of the target with tabbable descendant', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">\n        <div tabindex=\"0\" style=\"position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;\"></div>\n      </button>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [\n      new DOMRect(10, 5, 30, 7),\n      new DOMRect(10, 5, 7, 40)\n    ]);\n  });\n\n  it('ignores non-tabbable descendants of the target that are in shadow dom', () => {\n    fixture.innerHTML =\n      '<button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\"><span id=\"shadow\"></span></button>';\n    const target = fixture.querySelector('#target');\n    const shadow = fixture\n      .querySelector('#shadow')\n      .attachShadow({ mode: 'open' });\n    shadow.innerHTML =\n      '<div style=\"position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;\"></div>';\n\n    axe.setup(fixture);\n    const vNode = axe.utils.getNodeFromTree(target);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]);\n  });\n\n  it('uses client rects if target is inline', () => {\n    const vNode = queryFixture(`\n      <div style=\"width: 20px\">\n        <a href=\"#\" id=\"target\">hello world</a>\n      </div>\n    `);\n    const rects = getTargetRects(vNode);\n    assert.deepEqual(rects, Array.from(vNode.actualNode.getClientRects()));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-target-size.js",
    "content": "describe('get-target-size', () => {\n  const getTargetSize = axe.commons.dom.getTargetSize;\n  const { queryFixture } = axe.testUtils;\n\n  it('returns the bounding rect when unobscured', () => {\n    const vNode = queryFixture('<button id=\"target\">x</button>');\n    const rect = getTargetSize(vNode);\n    assert.deepEqual(rect, vNode.actualNode.getBoundingClientRect());\n  });\n\n  it('returns target size when obscured', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px;\"></div>\n    `);\n    const rect = getTargetSize(vNode);\n    assert.deepEqual(rect, new DOMRect(10, 5, 20, 40));\n  });\n\n  it('ignores elements with \"pointer-events: none\"', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px; pointer-events: none\"></div>\n    `);\n    const rect = getTargetSize(vNode);\n    assert.deepEqual(rect, vNode.actualNode.getBoundingClientRect());\n  });\n\n  it(\"ignores elements that don't overlap the target\", () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 60px; top: 0; width: 50px; height: 50px;\"></div>\n    `);\n    const rect = getTargetSize(vNode);\n    assert.deepEqual(rect, vNode.actualNode.getBoundingClientRect());\n  });\n\n  it('returns the largest unobscured area', () => {\n    const vNode = queryFixture(`\n      <button id=\"target\" style=\"width: 30px; height: 40px; position: absolute; left: 10px; top: 5px\">x</button>\n      <div style=\"position: absolute; left: 30px; top: 0; width: 50px; height: 50px;\"></div>\n      <div style=\"position: absolute; left: 0px; top: 0px; width: 50px; height: 10px\"></div>\n    `);\n    const rect = getTargetSize(vNode);\n    assert.deepEqual(rect, new DOMRect(10, 10, 20, 35));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-text-element-stack.js",
    "content": "describe('dom.getTextElementStack', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var getTextElementStack = axe.commons.dom.getTextElementStack;\n\n  function mapToIDs(stack) {\n    return stack\n      .map(function (node) {\n        return node.id;\n      })\n      .filter(function (id) {\n        return !!id;\n      });\n  }\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return array of client text rects', function () {\n    fixture.innerHTML =\n      '<main id=\"1\">' +\n      '<div id=\"target\">' +\n      '<span id=\"2\">Hello</span><br/>World' +\n      '</div>' +\n      '</main>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var stacks = getTextElementStack(target).map(mapToIDs);\n    assert.deepEqual(stacks, [['target', '1', 'fixture']]);\n  });\n\n  it('should ignore newline characters', function () {\n    fixture.innerHTML =\n      '<main id=\"1\">' +\n      '<div id=\"target\">' +\n      '<span id=\"2\">Hello</span><br/>\\n' +\n      'World' +\n      '</div>' +\n      '</main>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var stacks = getTextElementStack(target).map(mapToIDs);\n    assert.deepEqual(stacks, [['target', '1', 'fixture']]);\n  });\n\n  it('should handle truncated text', function () {\n    fixture.innerHTML =\n      '<main id=\"1\">' +\n      '<div id=\"target\" style=\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100px;\">' +\n      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin quam. Fusce mi odio, egestas pulvinar erat eget, vehicula tempus est. Proin vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim nulla, sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna, mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh, quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim. Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor felis ante non libero. Donec dapibus, diam sit amet posuere commodo, magna orci hendrerit ipsum, eu egestas mauris nulla ut ipsum. Sed luctus, orci in fringilla finibus, odio leo porta dolor, eu dignissim risus eros eget erat.' +\n      'World' +\n      '</div>' +\n      '</main>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var stacks = getTextElementStack(target).map(mapToIDs);\n    assert.deepEqual(stacks, [['target', '1', 'fixture']]);\n  });\n\n  it('should handle text that is too large for the container', function () {\n    fixture.innerHTML =\n      '<pre id=\"1\" style=\"width: 400px; overflow: auto;\">' +\n      '<span id=\"target\" style=\"display: flex; width: 400px;\">\\n\\n' +\n      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin quam. Fusce mi odio, egestas pulvinar erat eget, vehicula tempus est. Proin vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim nulla, sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna, mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh, quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim. Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor felis ante non libero. Donec dapibus, diam sit amet posuere commodo, magna orci hendrerit ipsum, eu egestas mauris nulla ut ipsum. Sed luctus, orci in fringilla finibus, odio leo porta dolor, eu dignissim risus eros eget erat.' +\n      '</span>' +\n      '</pre>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var stacks = getTextElementStack(target).map(mapToIDs);\n    assert.deepEqual(stacks, [['target', '1', 'fixture']]);\n  });\n\n  it('should handle text that overflows outside the parent', function () {\n    fixture.innerHTML =\n      '<div id=\"1\" style=\"width: 250px; overflow: hidden\">' +\n      '<p id=\"target\" style=\"max-height: 80px; overflow: hidden; line-height: 20px; font-size: 13px;\">The Chandni Chowk (Moonlight Square) is one of the oldest and busiest markets in Old Delhi, India. Chandni Chowk is located close to Old Delhi Railway Station. The Red Fort monument is located within the market. It was built in the 17th century by Mughal Emperor of India Shah Jahan and designed by his daughter Jahanara. The market was once divided by canals (now closed) to reflect moonlight and remains one of India\\'s largest wholesale markets.</p>' +\n      '</div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    var target = fixture.querySelector('#target');\n    var stacks = getTextElementStack(target).map(mapToIDs);\n    assert.deepEqual(stacks, [['target', '1', 'fixture']]);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-viewport-size.js",
    "content": "describe('dom.getViewportSize', function () {\n  'use strict';\n\n  it('should return an object with width and height', function () {\n    var result = axe.commons.dom.getViewportSize(window);\n\n    assert.property(result, 'width');\n    assert.property(result, 'height');\n\n    assert.isNumber(result.width);\n    assert.isNumber(result.height);\n  });\n\n  it('should have some fallbacks for old browsers', function () {\n    var result = axe.commons.dom.getViewportSize({\n      document: {},\n      innerWidth: 12,\n      innerHeight: 47\n    });\n\n    assert.equal(result.width, 12);\n    assert.equal(result.height, 47);\n\n    result = axe.commons.dom.getViewportSize({\n      document: {\n        documentElement: {\n          clientWidth: 13,\n          clientHeight: 48\n        }\n      }\n    });\n\n    assert.equal(result.width, 13);\n    assert.equal(result.height, 48);\n\n    result = axe.commons.dom.getViewportSize({\n      document: {\n        body: {\n          clientWidth: 22,\n          clientHeight: 41\n        }\n      }\n    });\n\n    assert.equal(result.width, 22);\n    assert.equal(result.height, 41);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/get-visible-child-text-rects.js",
    "content": "describe('dom.getVisibleChildTextRects', () => {\n  const { getVisibleChildTextRects } = axe.commons.dom;\n  const { fixtureSetup } = axe.testUtils;\n  const fixture = document.querySelector('#fixture');\n\n  /**\n   * Get client rects of a node and filter out newline characters.\n   * @param {Element} node\n   */\n  function getClientRects(node) {\n    return Array.from(node.getClientRects()).filter(\n      rect => rect.width > 1 && rect.height > 1\n    );\n  }\n\n  /**\n   * Assert that two DOMRect arrays are equal.\n   * @param {DOMRect[]} actualRects\n   * @param {DOMRect[]} expectedRects\n   */\n  function assertRectsEqual(actualRects, expectedRects) {\n    assert.equal(actualRects.length, expectedRects.length);\n    actualRects.forEach((rect, index) => {\n      const actual = actualRects[index];\n      const expected = expectedRects[index];\n\n      assert.approximately(actual.left, expected.left, 1, 'left');\n      assert.approximately(actual.top, expected.top, 1, 'top');\n      assert.approximately(actual.width, expected.width, 1, 'width');\n      assert.approximately(actual.height, expected.height, 1, 'height');\n    });\n  }\n\n  it('returns the text rect of a node', () => {\n    fixtureSetup(`<span>Hello</span>`);\n    const node = fixture.firstChild;\n    const actual = getVisibleChildTextRects(node);\n\n    assertRectsEqual(actual, getClientRects(node));\n  });\n\n  it('returns multiple text rects and filters out newlines', () => {\n    fixtureSetup(`<span>Hello<br/>World</span>`);\n    const node = fixture.firstChild;\n    const actual = getVisibleChildTextRects(node);\n\n    assertRectsEqual(actual, getClientRects(node));\n  });\n\n  it('returns the nodes bounding box if text rects escape bounds of node', () => {\n    fixtureSetup(`<div style=\"width: 10px;\">Hello World</div>`);\n    const node = fixture.firstChild;\n    const actual = getVisibleChildTextRects(node);\n\n    assert.deepEqual(actual, [node.getBoundingClientRect()]);\n  });\n\n  it('changes rect size based on overflow of parent', () => {\n    fixtureSetup(`\n      <div style=\"overflow: hidden; width: 10px;\">\n        <span id=\"target\">Hello</span>\n      </div>\n    `);\n    const node = fixture.querySelector('#target');\n    const actual = getVisibleChildTextRects(node);\n    const rect = getClientRects(node)[0];\n    const expected = new DOMRect(rect.left, rect.top, 10, rect.height);\n\n    assertRectsEqual(actual, [expected]);\n  });\n\n  it('changes rect size based on overflow of all ancestors', () => {\n    fixtureSetup(`\n      <div style=\"overflow: hidden; height: 10px;\">\n        <div style=\"overflow: hidden; width: 10px;\">\n          <span id=\"target\">Hello</span>\n        </div>\n      </div>\n    `);\n    const node = fixture.querySelector('#target');\n    const actual = getVisibleChildTextRects(node);\n    const rect = getClientRects(node)[0];\n    const expected = new DOMRect(rect.left, rect.top, 10, 10);\n\n    assertRectsEqual(actual, [expected]);\n  });\n\n  it('changes only the rect size of text rects that go outside ancestor overflow', () => {\n    fixtureSetup(`\n      <div style=\"overflow: hidden; height: 25px\">\n        <span id=\"target\">Hello<br/>World</span>\n      </div>\n    `);\n    const node = fixture.querySelector('#target');\n    const actual = getVisibleChildTextRects(node);\n    const rects = getClientRects(node);\n    const expected = [\n      rects[0],\n      new DOMRect(\n        rects[1].left,\n        rects[1].top,\n        rects[1].width,\n        25 - rects[1].height\n      )\n    ];\n\n    assertRectsEqual(actual, expected);\n  });\n\n  it('does not return rects outside overflows', () => {\n    fixtureSetup(`\n      <div style=\"overflow: hidden; height: 50px;\">\n        <div style=\"overflow: hidden; height: 25px\">\n          <span id=\"target\">Hello<br/>World<br/>Goodbye<br/>World</span>\n        </div>\n      </div>\n    `);\n    const node = fixture.querySelector('#target');\n    const actual = getVisibleChildTextRects(node);\n\n    assert.lengthOf(actual, 2);\n  });\n\n  it('changes nodeRect size if all text rects got outside ancestor overflow', () => {\n    fixtureSetup(`\n      <div style=\"overflow: hidden; width: 50px;\">\n        <div style=\"overflow: hidden; width: 25px\">\n          <div id=\"target\" style=\"padding-left: 65px;\">Hello World</div>\n        </div>\n      </div>\n    `);\n    const node = fixture.querySelector('#target');\n    const actual = getVisibleChildTextRects(node);\n    const rect = getClientRects(node)[0];\n    const expected = new DOMRect(rect.left, rect.top, 25, rect.height);\n\n    assertRectsEqual(actual, [expected]);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/has-content-virtual.js",
    "content": "/* global xit */\ndescribe('dom.hasContentVirtual', function () {\n  'use strict';\n  var hasContentVirtual = axe.commons.dom.hasContentVirtual;\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport.v1;\n  var tree;\n\n  it('returns false if there is no content', function () {\n    fixture.innerHTML = '<div id=\"target\">\t</div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isFalse(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('returns false if there are non-visual elements', function () {\n    fixture.innerHTML = '<div id=\"target\"> <span></span> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isFalse(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is true if the element has non-empty text', function () {\n    fixture.innerHTML = '<div id=\"target\"> text </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isTrue(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is true if the element has an aria label', function () {\n    fixture.innerHTML = '<div id=\"target\" aria-label=\"my-label\">\t</div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isTrue(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is false if the element has an aria label but `ignoreAria=true`', function () {\n    fixture.innerHTML = '<div id=\"target\" aria-label=\"my-label\">\t</div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isFalse(\n      hasContentVirtual(\n        axe.utils.querySelectorAll(tree, '#target')[0],\n        true,\n        true\n      )\n    );\n  });\n\n  it('is true if the element contains visual content', function () {\n    fixture.innerHTML = '<div id=\"target\"> <img src=\"\"> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isTrue(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is true if the element contains a node with a aria-label', function () {\n    fixture.innerHTML =\n      '<div id=\"target\"> <span aria-label=\"my-label\"></span> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isTrue(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is false if the element does not show text', function () {\n    fixture.innerHTML = '<style id=\"target\"> #foo { color: green } </style>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    assert.isFalse(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n    );\n  });\n\n  it('is called through hasContent, with a DOM node', function () {\n    var hasContent = axe.commons.dom.hasContent;\n    fixture.innerHTML = '<div id=\"target\"> text </div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(hasContent(fixture.querySelector('#target')));\n\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(hasContent(fixture.querySelector('#target')));\n  });\n\n  it('is false if noRecursion is true and the content is not in a child', function () {\n    fixture.innerHTML = '<div id=\"target\"><span> text </span></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n\n    assert.isFalse(\n      hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0], true)\n    );\n  });\n\n  (shadowSupport ? it : xit)(\n    'looks at content of shadow dom elements',\n    function () {\n      fixture.innerHTML = '<div id=\"target\"></div>';\n      var shadow = fixture\n        .querySelector('#target')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = 'Some text';\n      tree = axe.utils.getFlattenedTree(fixture);\n\n      assert.isTrue(\n        hasContentVirtual(axe.utils.querySelectorAll(tree, '#target')[0])\n      );\n    }\n  );\n\n  (shadowSupport ? it : xit)(\n    'looks at the slots in a shadow tree',\n    function () {\n      fixture.innerHTML = '<div id=\"shadow\">some text</div>';\n      var shadow = fixture\n        .querySelector('#shadow')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div class=\"target\"><slot></slot></div>';\n      tree = axe.utils.getFlattenedTree(fixture);\n\n      assert.isTrue(\n        hasContentVirtual(axe.utils.querySelectorAll(tree, '.target')[0])\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/has-lang-text.js",
    "content": "describe('dom.hasLangText', function () {\n  'use strict';\n  var hasLangText = axe.commons.dom.hasLangText;\n  var fixture = document.getElementById('fixture');\n  var tree;\n\n  it('returns true when the element has a non-empty text node as its content', function () {\n    fixture.innerHTML = '<div id=\"target\">  text  </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('returns true when the element has nested text node as its content', function () {\n    fixture.innerHTML = '<div id=\"target\"> <span> text </span> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('returns false when the element has nested text is hidden', function () {\n    fixture.innerHTML = '<div id=\"target\"> <span hidden> text </span> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isFalse(hasLangText(target));\n  });\n\n  it('returns true when the element has nested text is aria-hidden', function () {\n    fixture.innerHTML =\n      '<div id=\"target\"> <span aria-hidden=\"true\"> text </span> </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('returns true when the element has nested text is off screen', function () {\n    fixture.innerHTML =\n      '<div id=\"target\">' +\n      '  <span style=\"position: absolute; top:-99em;\"> text </span> ' +\n      '</div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('returns false when the element has an empty text node as its content', function () {\n    fixture.innerHTML = '<div id=\"target\">  <!-- comment -->  </div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isFalse(hasLangText(target));\n  });\n\n  it('returns false if all text is in a child with a lang attribute', function () {\n    fixture.innerHTML = '<div id=\"target\"><span lang=\"en\"> text </span></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isFalse(hasLangText(target));\n  });\n\n  it('does not skip if lang is on the starting node', function () {\n    fixture.innerHTML = '<div id=\"target\" lang=\"en\"><span> text </span></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('ignores empty lang attributes', function () {\n    fixture.innerHTML = '<div id=\"target\"><span lang=\"\"> text </span></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('ignores null lang attributes', function () {\n    fixture.innerHTML = '<div id=\"target\"><span lang> text </span></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('true for non-text content with an accessible name', function () {\n    fixture.innerHTML = '<div id=\"target\"><img alt=\"foo\"></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isTrue(hasLangText(target));\n  });\n\n  it('false for non-text content without accessible name', function () {\n    fixture.innerHTML = '<div id=\"target\"><img alt=\"\"></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isFalse(hasLangText(target));\n  });\n\n  it('returns false for non-text content with a lang attr', function () {\n    fixture.innerHTML = '<div id=\"target\"><img alt=\"foo\" lang=\"en\"></div>';\n    tree = axe.utils.getFlattenedTree(fixture);\n    var target = axe.utils.querySelectorAll(tree, '#target')[0];\n    assert.isFalse(hasLangText(target));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/idrefs.js",
    "content": "function createContentIDR() {\n  'use strict';\n  var group = document.createElement('div');\n  group.id = 'target';\n  return group;\n}\n\nfunction makeShadowTreeIDR(node) {\n  'use strict';\n  var root = node.attachShadow({ mode: 'open' });\n  var div = document.createElement('div');\n  div.className = 'parent';\n  div.setAttribute('target', 'target');\n  root.appendChild(div);\n  div.appendChild(createContentIDR());\n}\n\ndescribe('dom.idrefs', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should find referenced nodes by ID', function () {\n    fixture.innerHTML =\n      '<div aria-cats=\"target1 target2\" id=\"start\"></div>' +\n      '<div id=\"target1\"></div><div id=\"target2\"></div>';\n\n    var start = document.getElementById('start'),\n      expected = [\n        document.getElementById('target1'),\n        document.getElementById('target2')\n      ];\n\n    assert.deepEqual(\n      axe.commons.dom.idrefs(start, 'aria-cats'),\n      expected,\n      'Should find it!'\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'should find only referenced nodes within the current root: shadow DOM',\n    function () {\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div target=\"target\"><div id=\"target\"></div></div>';\n      makeShadowTreeIDR(fixture.firstChild);\n      var start = fixture.firstChild.shadowRoot.querySelector('.parent');\n      var expected = [fixture.firstChild.shadowRoot.getElementById('target')];\n\n      assert.deepEqual(\n        axe.commons.dom.idrefs(start, 'target'),\n        expected,\n        'should only find stuff in the shadow DOM'\n      );\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should find only referenced nodes within the current root: document',\n    function () {\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML =\n        '<div target=\"target\" class=\"parent\"><div id=\"target\"></div></div>';\n      makeShadowTreeIDR(fixture.firstChild);\n      var start = fixture.querySelector('.parent');\n      var expected = [document.getElementById('target')];\n\n      assert.deepEqual(\n        axe.commons.dom.idrefs(start, 'target'),\n        expected,\n        'should only find stuff in the document'\n      );\n    }\n  );\n\n  it('should insert null if a reference is not found', function () {\n    fixture.innerHTML =\n      '<div aria-cats=\"target1 target2 target3\" id=\"start\"></div>' +\n      '<div id=\"target1\"></div><div id=\"target2\"></div>';\n\n    var start = document.getElementById('start'),\n      expected = [\n        document.getElementById('target1'),\n        document.getElementById('target2'),\n        null\n      ];\n\n    assert.deepEqual(\n      axe.commons.dom.idrefs(start, 'aria-cats'),\n      expected,\n      'Should find it!'\n    );\n  });\n\n  it('should not fail when extra whitespace is used', function () {\n    fixture.innerHTML =\n      '<div aria-cats=\"    \\ttarget1 \\n  target2  target3 \\n\\t\" id=\"start\"></div>' +\n      '<div id=\"target1\"></div><div id=\"target2\"></div>';\n\n    var start = document.getElementById('start'),\n      expected = [\n        document.getElementById('target1'),\n        document.getElementById('target2'),\n        null\n      ];\n\n    assert.deepEqual(\n      axe.commons.dom.idrefs(start, 'aria-cats'),\n      expected,\n      'Should find it!'\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/inserted-into-focus-order.js",
    "content": "describe('dom.insertedIntoFocusOrder', () => {\n  const fixture = document.getElementById('fixture');\n  const { fixtureSetup } = axe.testUtils;\n  const insertedIntoFocusOrder = axe.commons.dom.insertedIntoFocusOrder;\n\n  function hideByClipping(el) {\n    el.style.cssText =\n      'position: absolute !important;' +\n      ' clip: rect(0px 0px 0px 0px); /* IE6, IE7 */' +\n      ' clip: rect(0px, 0px, 0px, 0px);';\n  }\n\n  function hideByMovingOffScreen(el) {\n    el.style.cssText =\n      'position:absolute;' +\n      ' left:-10000px;' +\n      ' top:auto;' +\n      ' width:1px;' +\n      ' height:1px;' +\n      ' overflow:hidden;';\n  }\n\n  it('should return true for span with tabindex 0', () => {\n    fixtureSetup('<span id=\"spanTabindex0\" tabindex=\"0\"></span>');\n    const node = fixture.querySelector('#spanTabindex0');\n\n    assert.isTrue(insertedIntoFocusOrder(node));\n  });\n\n  it('should return true for clipped span with tabindex 0', () => {\n    fixtureSetup('<span id=\"clippedSpanTabindex0\" tabindex=\"0\"></span>');\n    const node = fixture.querySelector('#clippedSpanTabindex0');\n    hideByClipping(node);\n\n    assert.isTrue(insertedIntoFocusOrder(node));\n  });\n\n  it('should return true for off screen span with tabindex 0', () => {\n    fixtureSetup('<span id=\"offScreenSpanTabindex0\" tabindex=\"0\"></span>');\n    const node = fixture.querySelector('#offScreenSpanTabindex0');\n    hideByMovingOffScreen(node);\n\n    assert.isTrue(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for span with negative tabindex', () => {\n    fixtureSetup('<span id=\"spanNegativeTabindex\" tabindex=\"-1\"></span>');\n    const node = fixture.querySelector('#spanNegativeTabindex');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for native button with tabindex 0', () => {\n    fixtureSetup('<button id=\"nativeButtonTabindex0\" tabindex=\"0\"></button>');\n    const node = fixture.querySelector('#nativeButtonTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for native button with tabindex implicitly 0', () => {\n    fixtureSetup('<button id=\"nativeButtonTabindexImplicitly0\"></button>');\n    const node = fixture.querySelector('#nativeButtonTabindexImplicitly0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for anchor with href and positive tabindex', () => {\n    fixtureSetup(\n      '<a id=\"anchorWithHrefAndPositiveTabindex\" href=\"javascript:void(0)\" tabindex=\"1\"></a>'\n    );\n    const node = fixture.querySelector('#anchorWithHrefAndPositiveTabindex');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for input with tabindex 0', () => {\n    fixtureSetup('<input id=\"inputWithTabindex0\" tabindex=\"0\">');\n    const node = fixture.querySelector('#inputWithTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for off screen native button with tabindex 0', () => {\n    fixtureSetup(\n      '<button id=\"offScreenNativeButtonTabindex0\" tabindex=\"0\"></button>'\n    );\n    const node = fixture.querySelector('#offScreenNativeButtonTabindex0');\n    hideByMovingOffScreen(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for off screen anchor with href and tabindex 1', () => {\n    fixtureSetup(\n      '<a id=\"offScreenAnchorWithHrefTabindex1\" href=\"javascript:void(0)\" tabindex=\"1\"></a>'\n    );\n    const node = fixture.querySelector('#offScreenAnchorWithHrefTabindex1');\n    hideByMovingOffScreen(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for off screen input with tabindex 0', () => {\n    fixtureSetup('<input id=\"offScreenInputWithTabindex0\" tabindex=\"0\">');\n    const node = fixture.querySelector('#offScreenInputWithTabindex0');\n    hideByMovingOffScreen(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for clipped native button with tabindex 0', () => {\n    fixtureSetup(\n      '<button id=\"clippedNativeButtonTabindex0\" tabindex=\"0\"></button>'\n    );\n    const node = fixture.querySelector('#clippedNativeButtonTabindex0');\n    hideByClipping(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for display none native button with tabindex 0', () => {\n    fixtureSetup(\n      '<button id=\"displayNoneNativeButtonTabindex0\" tabindex=\"0\"></button>'\n    );\n    const node = fixture.querySelector('#displayNoneNativeButtonTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for clipped anchor with href and tabindex 1', () => {\n    fixtureSetup(\n      '<a id=\"clippedAnchorWithHrefTabindex1\" href=\"javascript:void(0)\" tabindex=\"1\"></a>'\n    );\n    const node = fixture.querySelector('#clippedAnchorWithHrefTabindex1');\n    hideByClipping(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for display none anchor with href and tabindex 1', () => {\n    fixtureSetup(\n      '<a id=\"displayNoneAnchorWithHrefTabindex1\" href=\"javascript:void(0)\" tabindex=\"1\"></a>'\n    );\n    const node = fixture.querySelector('#displayNoneAnchorWithHrefTabindex1');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for clipped input with tabindex 0', () => {\n    fixtureSetup('<input id=\"clippedInputWithTabindex0\" tabindex=\"0\">');\n    const node = fixture.querySelector('#clippedInputWithTabindex0');\n    hideByClipping(node);\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for display none input with tabindex 0', () => {\n    fixtureSetup('<input id=\"displayNoneInputWithTabindex0\" tabindex=\"0\">');\n    const node = fixture.querySelector('#displayNoneInputWithTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for hidden native button with tabindex 0', () => {\n    fixtureSetup(\n      '<button id=\"hiddenNativeButtonTabindex0\" tabindex=\"0\"></button>'\n    );\n    const node = fixture.querySelector('#hiddenNativeButtonTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for hidden anchor with href and tabindex 1', () => {\n    fixtureSetup(\n      '<a id=\"hiddenAnchorWithHrefTabindex1\" href=\"javascript:void(0)\" tabindex=\"1\"></a>'\n    );\n    const node = fixture.querySelector('#hiddenAnchorWithHrefTabindex1');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for hidden input with tabindex 0', () => {\n    fixtureSetup('<input id=\"hiddenInputWithTabindex0\" tabindex=\"0\">');\n    const node = fixture.querySelector('#hiddenInputWithTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for disabled native button with tabindex 0', () => {\n    fixtureSetup(\n      '<button id=\"disabledNativeButtonTabindex0\" tabindex=\"0\" disabled></button>'\n    );\n    const node = fixture.querySelector('#disabledNativeButtonTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for disabled input with tabindex 0', () => {\n    fixtureSetup('<input id=\"disabledInputTabindex0\" tabindex=\"0\" disabled>');\n    const node = fixture.querySelector('#disabledInputTabindex0');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n\n  it('should return false for an invalid tabindex', () => {\n    fixtureSetup('<span id=\"spanTabindexInvalid\" tabindex=\"invalid\"></span>');\n    const node = fixture.querySelector('#spanTabindexInvalid');\n\n    assert.isFalse(insertedIntoFocusOrder(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-current-page-link.js",
    "content": "describe('is-current-page-link', function () {\n  var isCurrentPageLink = axe.commons.dom.isCurrentPageLink;\n  var currentPage = window.location.origin + window.location.pathname;\n  var base;\n\n  afterEach(function () {\n    if (base) {\n      document.head.removeChild(base);\n    }\n  });\n\n  it('should return true for hash links', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '#main';\n    document.body.appendChild(anchor);\n    assert.isTrue(isCurrentPageLink(anchor));\n  });\n\n  it('should return true for relative links to the same page', function () {\n    var anchor = document.createElement('a');\n    anchor.href = window.location.pathname;\n    assert.isTrue(isCurrentPageLink(anchor));\n  });\n\n  it('should return true for absolute links to the same page', function () {\n    var anchor = document.createElement('a');\n    anchor.href = currentPage;\n    assert.isTrue(isCurrentPageLink(anchor));\n  });\n\n  it('should return true for angular skip links', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '/#main';\n    assert.isTrue(isCurrentPageLink(anchor));\n  });\n\n  it('should return false for just \"#\"', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '#';\n    assert.isFalse(isCurrentPageLink(anchor));\n  });\n\n  it('should return false for relative links to a different page', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '/foo/bar/index.html';\n    assert.isFalse(isCurrentPageLink(anchor));\n  });\n\n  it('should return false for absolute links to a different page', function () {\n    var anchor = document.createElement('a');\n    anchor.href = 'https://my-page.com/foo/bar/index.html';\n    assert.isFalse(isCurrentPageLink(anchor));\n  });\n\n  it('should return false for angular router links (#!)', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '#!main';\n    assert.isFalse(isCurrentPageLink(anchor));\n  });\n\n  it('should return false for angular router links (#/)', function () {\n    var anchor = document.createElement('a');\n    anchor.href = '#/main';\n    assert.isFalse(isCurrentPageLink(anchor));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-focusable.js",
    "content": "describe('dom.isFocusable', () => {\n  const flatTreeSetup = axe.testUtils.flatTreeSetup;\n  const fixture = document.getElementById('fixture');\n\n  it('should return true for visible, enabled textareas', () => {\n    fixture.innerHTML = '<textarea id=\"target\"></textarea>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for visible, enabled selects', () => {\n    fixture.innerHTML = '<select id=\"target\"></select>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for visible, enabled buttons', () => {\n    fixture.innerHTML = '<button id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for visible, enabled, non-hidden inputs', () => {\n    fixture.innerHTML = '<input type=\"text\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for non-element nodes', () => {\n    fixture.innerHTML = '<span id=\"target\">Hello World</span>';\n    flatTreeSetup(fixture);\n    const el = document.getElementById('target').childNodes[0];\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for disabled elements', () => {\n    fixture.innerHTML = '<input type=\"text\" id=\"target\" disabled>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for hidden inputs', () => {\n    fixture.innerHTML = '<input type=\"hidden\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for hidden inputs with tabindex', () => {\n    fixture.innerHTML = '<input type=\"hidden\" tabindex=\"1\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for hidden buttons with tabindex', () => {\n    fixture.innerHTML =\n      '<button style=\"visibility:hidden\" tabindex=\"0\" id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for disabled buttons with tabindex', () => {\n    fixture.innerHTML = '<button tabindex=\"0\" id=\"target\" disabled></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for non-visible elements', () => {\n    fixture.innerHTML = '<input type=\"text\" id=\"target\" style=\"display: none\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for an anchor with an href', () => {\n    fixture.innerHTML = '<a href=\"something.html\" id=\"target\"></a>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for an anchor with no href', () => {\n    fixture.innerHTML = '<a name=\"anchor\" id=\"target\"></a>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a div with a tabindex with spaces', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"\t  0   \"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a div with a tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"0\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a div with a negative tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"-1\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for a div with a non-numeric tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"x\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a summary element', () => {\n    fixture.innerHTML =\n      '<details><summary id=\"target\">Summary</summary><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a details element without a summary element', () => {\n    fixture.innerHTML = '<details id=\"target\"><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for a details element with a summary element', () => {\n    fixture.innerHTML =\n      '<details id=\"target\"><summary>Summary</summary><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for a div with no tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-hidden-for-everyone.js",
    "content": "describe('dom.isHiddenForEveryone', () => {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n  const isHiddenForEveryone = axe.commons.dom.isHiddenForEveryone;\n  const queryFixture = axe.testUtils.queryFixture;\n  const contentVisibilitySupported = CSS.supports('content-visibility: hidden');\n\n  function createContentSlotted(mainProps, targetProps) {\n    const group = document.createElement('div');\n    group.innerHTML =\n      '<main style=\"' +\n      mainProps +\n      '\"><p style=\"' +\n      targetProps +\n      '\"></p></main>';\n    return group;\n  }\n\n  function makeShadowTree(node, mainProps, targetProps) {\n    const root = node.attachShadow({ mode: 'open' });\n    const newNode = createContentSlotted(mainProps, targetProps);\n    root.appendChild(newNode);\n  }\n\n  it('should return false on static-positioned, visible element', () => {\n    const vNode = queryFixture('<div id=\"target\">I am visible</div>');\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true on static-positioned, hidden element', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"display:none\">I am not visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return false on absolutely positioned elements that are on-screen', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; left: 10px; right: 10px\">I am visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen and aria-hidden element', () => {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-hidden=“true” style=“position:absolute: top:-999em”>I am visible</button>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on fixed position elements that are on-screen', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"position:fixed; bottom: 0; left: 0;\">I am visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen absolutely positioned element', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; left: -9999px\">I am visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen fixed positioned element', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"position: fixed; top: -9999px\">I am visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true on detached elements', () => {\n    const el = document.createElement('div');\n    el.innerHTML = 'I am not visible because I am detached!';\n    axe.testUtils.flatTreeSetup(el);\n    const actual = isHiddenForEveryone(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return false on body', () => {\n    axe.testUtils.flatTreeSetup(document.body);\n    const actual = isHiddenForEveryone(document.body);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on html', () => {\n    axe.testUtils.flatTreeSetup(document.documentElement);\n    const actual = isHiddenForEveryone(document.documentElement);\n    assert.isFalse(actual);\n  });\n\n  it('should return false if static-position but top/left is set', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"top: -9999px; left: -9999px; right: -9999px; bottom: -9999px;\">I am visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false, and not be affected by `aria-hidden`', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" aria-hidden=\"true\">I am visible with css (although hidden to screen readers)</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true for STYLE node', () => {\n    const vNode = queryFixture(\n      \"<style id='target'>body {font-size: 200%}</style>\"\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return true for SCRIPT node', () => {\n    const vNode = queryFixture(\n      \"<script id='target' type='text/javascript' src='temp.js'></script>\"\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return true for if parent of element set to `display:none`', () => {\n    const vNode = queryFixture(\n      '<div style=\"display:none\">' +\n        '<div style=\"display:block\">' +\n        '<p id=\"target\" style=\"display:block\">I am not visible</p>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return false for if parent of element set to `display:block`', () => {\n    const vNode = queryFixture(\n      '<div>' +\n        '<div style=\"display:block\">' +\n        '<p id=\"target\" style=\"display:block\">I am visible</p>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  // `visibility` test\n  it('should return true for element that has `visibility:hidden`', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\" style=\"visibility: hidden;\">I am not visible</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return false and compute how `visibility` of self and parent is configured', () => {\n    const vNode = queryFixture(\n      '<div style=\"visibility:hidden;\">' +\n        '<div style=\"visibility:visible;\">' +\n        '<div id=\"target\">I am visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false and compute how `visibility` of self and parent is configured', () => {\n    const vNode = queryFixture(\n      '<div style=\"visibility:hidden\">' +\n        '<div style=\"visibility:hidden\">' +\n        '<div style=\"visibility:visible\" id=\"target\">I am visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true and as parent is set to `visibility:hidden`', () => {\n    const vNode = queryFixture(\n      '<div style=\"visibility: hidden;\">' +\n        '<div>' +\n        '<div id=\"target\">I am not visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  // mixing display and visibility\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', () => {\n    const vNode = queryFixture(\n      '<div style=\"display:none;\">' +\n        '<div style=\"visibility:visible;\">' +\n        '<div id=\"target\">I am not visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return false and compute using both `display` and `visibility` set on element and parent(s)', () => {\n    const vNode = queryFixture(\n      '<div style=\"display:block;\">' +\n        '<div style=\"visibility:visible;\">' +\n        '<div id=\"target\">I am visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', () => {\n    const vNode = queryFixture(\n      '<div style=\"display:block;\">' +\n        '<div style=\"visibility:visible;\">' +\n        '<div id=\"target\" style=\"visibility:hidden\">I am not visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', () => {\n    const vNode = queryFixture(\n      '<div style=\"visibility:hidden\">' +\n        '<div style=\"display:none;\">' +\n        '<div id=\"target\" style=\"visibility:visible\">I am not visible</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = isHiddenForEveryone(vNode);\n    assert.isTrue(actual);\n  });\n\n  describe('details', () => {\n    it('should return true for element in closed details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary>Hello World</summary>\n          <p id=\"target\">Hidden</p>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    });\n\n    it('should return false for closed details', () => {\n      const vNode = queryFixture(`\n        <details id=\"target\">\n          <summary>Hello World</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false for summary element closed details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary id=\"target\">Hello World</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false for element in open details', () => {\n      const vNode = queryFixture(`\n        <details open>\n          <summary>Hello World</summary>\n          <p id=\"target\">Hidden</p>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return true for grandchild element in closed details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary>Hello World</summary>\n          <div><p id=\"target\">Hidden</p></div>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    });\n\n    it('should return true for grandchild summary in close details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <div><summary id=\"target\">Hello World</summary></div>\n          <div><p>Hidden</p></div>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    });\n\n    it('should return true for not first summary in close details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary>Hello World</summary>\n          <summary id=\"target\">Not summary</summary>\n          <div><p>Hidden</p></div>\n        </details>\n      `);\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    });\n  });\n\n  (shadowSupported ? it : it.skip)(\n    'should return true if `display:none` inside shadowDOM',\n    () => {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(fixture.firstChild, 'display:none;', '');\n      const tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      const vNode = axe.utils.querySelectorAll(tree, 'p')[0];\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return true as parent shadowDOM host is set to `visibility:hidden`',\n    () => {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(fixture.firstChild, 'visibility:hidden', '');\n      const tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      const vNode = axe.utils.querySelectorAll(tree, 'p')[0];\n      const actual = isHiddenForEveryone(vNode);\n      assert.isTrue(actual);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false as parent shadowDOM host  set to `visibility:hidden` is overriden',\n    () => {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(\n        fixture.firstChild,\n        'visibility:hidden',\n        'visibility:visible'\n      );\n      const tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      const vNode = axe.utils.querySelectorAll(tree, 'p')[0];\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    }\n  );\n\n  (contentVisibilitySupported ? it : xit)(\n    'should return true for `content-visibility: hidden` parent',\n    () => {\n      const vNode = queryFixture(\n        '<div style=\"content-visibility: hidden\"><div id=\"target\">Hidden</div></div>'\n      );\n      assert.isTrue(isHiddenForEveryone(vNode));\n    }\n  );\n\n  (contentVisibilitySupported ? it : xit)(\n    'should return false for `content-visibility: hidden`',\n    () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"content-visibility: hidden\"></div>'\n      );\n      assert.isFalse(isHiddenForEveryone(vNode));\n    }\n  );\n\n  describe('SerialVirtualNode', () => {\n    it('should return false on detached virtual nodes', () => {\n      const vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false for element in closed details', () => {\n      const vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      const detailsVNode = new axe.SerialVirtualNode({\n        nodeName: 'details'\n      });\n      vNode.parent = detailsVNode;\n      detailsVNode.children = [vNode];\n\n      const actual = isHiddenForEveryone(vNode);\n      assert.isFalse(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-hidden-with-css.js",
    "content": "describe('dom.isHiddenWithCSS', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var isHiddenWithCSS = axe.commons.dom.isHiddenWithCSS;\n  var origComputedStyle = window.getComputedStyle;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  function createContentSlotted(mainProps, targetProps) {\n    var group = document.createElement('div');\n    group.innerHTML =\n      '<main style=\"' +\n      mainProps +\n      '\"><p style=\"' +\n      targetProps +\n      '\"></p></main>';\n    return group;\n  }\n\n  function makeShadowTree(node, mainProps, targetProps) {\n    var root = node.attachShadow({ mode: 'open' });\n    var node = createContentSlotted(mainProps, targetProps);\n    root.appendChild(node);\n  }\n\n  afterEach(function () {\n    window.getComputedStyle = origComputedStyle;\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  it('should throw an error if computedStyle returns null', function () {\n    window.getComputedStyle = function () {\n      return null;\n    };\n    var fakeNode = {\n      nodeType: Node.ELEMENT_NODE,\n      nodeName: 'div'\n    };\n    assert.throws(function () {\n      isHiddenWithCSS(fakeNode);\n    });\n  });\n\n  it('should return false on static-positioned, visible element', function () {\n    fixture.innerHTML = '<div id=\"target\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return true on static-positioned, hidden element', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"display:none\">I am not visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return false on absolutely positioned elements that are on-screen', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; left: 10px; right: 10px\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen and aria-hidden element', function () {\n    fixture.innerHTML =\n      '<button id=\"target\" aria-hidden=“true” style=“position:absolute: top:-999em”>I am visible</button>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on fixed position elements that are on-screen', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position:fixed; bottom: 0; left: 0;\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen absolutely positioned element', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; left: -9999px\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for off-screen fixed positioned element', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: fixed; top: -9999px\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on detached elements', function () {\n    var el = document.createElement('div');\n    el.innerHTML = 'I am not visible because I am detached!';\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on a document', function () {\n    var actual = isHiddenWithCSS(document);\n    assert.isFalse(actual);\n  });\n\n  it('should return false if static-position but top/left is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"top: -9999px; left: -9999px; right: -9999px; bottom: -9999px;\">I am visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false, and not be affected by `aria-hidden`', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" aria-hidden=\"true\">I am visible with css (although hidden to screen readers)</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for STYLE node', function () {\n    fixture.innerHTML = \"<style id='target'>body {font-size: 200%}</style>\";\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false for SCRIPT node', function () {\n    fixture.innerHTML =\n      \"<script id='target' type='text/javascript' src='temp.js'></script>\";\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  // `display` test\n  it('should return true for if parent of element set to `display:none`', function () {\n    fixture.innerHTML =\n      '<div style=\"display:none\">' +\n      '<div style=\"display:block\">' +\n      '<p id=\"target\">I am not visible</p>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return true for if parent of element set to `display:none`', function () {\n    fixture.innerHTML =\n      '<div style=\"display:none\">' +\n      '<div style=\"display:block\">' +\n      '<p id=\"target\" style=\"display:block\">I am not visible</p>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return false for if parent of element set to `display:block`', function () {\n    fixture.innerHTML =\n      '<div>' +\n      '<div style=\"display:block\">' +\n      '<p id=\"target\" style=\"display:block\">I am visible</p>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  (shadowSupported ? it : it.skip)(\n    'should return true if `display:none` inside shadowDOM',\n    function () {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(fixture.firstChild, 'display:none;', '');\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'p')[0];\n      var actual = isHiddenWithCSS(el.actualNode);\n      assert.isTrue(actual);\n    }\n  );\n\n  // `visibility` test\n  it('should return true for element that has `visibility:hidden`', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"visibility: hidden;\">I am not visible</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return false and compute how `visibility` of self and parent is configured', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility:hidden;\">' +\n      '<div style=\"visibility:visible;\">' +\n      '<div id=\"target\">I am visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return false and compute how `visibility` of self and parent is configured', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility:hidden\">' +\n      '<div style=\"visibility:hidden\">' +\n      '<div style=\"visibility:visible\" id=\"target\">I am visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return true and as parent is set to `visibility:hidden`', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility: hidden;\">' +\n      '<div>' +\n      '<div id=\"target\">I am not visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return true as parent shadowDOM host is set to `visibility:hidden`',\n    function () {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(fixture.firstChild, 'visibility:hidden', '');\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'p')[0];\n      var actual = isHiddenWithCSS(el.actualNode);\n      assert.isTrue(actual);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false as parent shadowDOM host  set to `visibility:hidden` is overriden',\n    function () {\n      fixture.innerHTML = '<div></div>';\n      makeShadowTree(\n        fixture.firstChild,\n        'visibility:hidden',\n        'visibility:visible'\n      );\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'p')[0];\n      var actual = isHiddenWithCSS(el.actualNode);\n      assert.isFalse(actual);\n    }\n  );\n\n  // mixing display and visibility\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', function () {\n    fixture.innerHTML =\n      '<div style=\"display:none;\">' +\n      '<div style=\"visibility:visible;\">' +\n      '<div id=\"target\">I am not visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return false and compute using both `display` and `visibility` set on element and parent(s)', function () {\n    fixture.innerHTML =\n      '<div style=\"display:block;\">' +\n      '<div style=\"visibility:visible;\">' +\n      '<div id=\"target\">I am visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isFalse(actual);\n  });\n\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', function () {\n    fixture.innerHTML =\n      '<div style=\"display:block;\">' +\n      '<div style=\"visibility:visible;\">' +\n      '<div id=\"target\" style=\"visibility:hidden\">I am not visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  it('should return true and compute using both `display` and `visibility` set on element and parent(s)', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility:hidden\">' +\n      '<div style=\"display:none;\">' +\n      '<div id=\"target\" style=\"visibility:visible\">I am not visible</div>' +\n      '</div>' +\n      '</div>';\n    var el = document.getElementById('target');\n    var actual = isHiddenWithCSS(el);\n    assert.isTrue(actual);\n  });\n\n  describe('with virtual nodes', function () {\n    it('returns false when virtual nodes are visible', function () {\n      var vNode = queryFixture('<div id=\"target\"></div>');\n      assert.isFalse(isHiddenWithCSS(vNode));\n    });\n\n    it('returns true when virtual nodes are hidden', function () {\n      var vNode = queryFixture('<div id=\"target\" style=\"display:none\"></div>');\n      assert.isTrue(isHiddenWithCSS(vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-html5.js",
    "content": "describe('dom.isHTML5', function () {\n  'use strict';\n\n  it('should return false on any document that is not HTML5', function () {\n    var doc = document.implementation.createDocument(\n      'http://www.w3.org/1999/xhtml',\n      'html',\n      null\n    );\n    assert.isFalse(axe.commons.dom.isHTML5(doc));\n  });\n\n  it('should return true on any document that is HTML5', function () {\n    var doc = document.implementation.createHTMLDocument('Monkeys');\n    assert.isTrue(axe.commons.dom.isHTML5(doc));\n  });\n\n  it('should return true on any document that is HTML5 - fixture', function () {\n    assert.isTrue(axe.commons.dom.isHTML5(document));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-in-tab-order.js",
    "content": "describe('dom.isInTabOrder', function () {\n  'use strict';\n\n  var queryFixture = axe.testUtils.queryFixture;\n  var isInTabOrder = axe.commons.dom.isInTabOrder;\n\n  it('should return false for presentation element with negative tabindex', function () {\n    var target = queryFixture('<div id=\"target\" tabindex=\"-1\"></div>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return true for presentation element with positive tabindex', function () {\n    var target = queryFixture('<div id=\"target\" tabindex=\"1\"></div>');\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return false for presentation element with tabindex not set', function () {\n    var target = queryFixture('<div id=\"target\"></div>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return false for presentation element with tabindex set to non-parseable value', function () {\n    var target = queryFixture('<div id=\"target\" tabindex=\"foobar\"></div>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return false for presentation element with tabindex not set and role of natively focusable element', function () {\n    var target = queryFixture('<div id=\"target\" role=\"button\"></div>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return true for natively focusable element with tabindex 0', function () {\n    var target = queryFixture('<button id=\"target\" tabindex=\"0\"></button>');\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return true for natively focusable element with tabindex 1', function () {\n    var target = queryFixture('<button id=\"target\" tabindex=\"1\"></button>');\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return false for natively focusable element with tabindex -1', function () {\n    var target = queryFixture('<button id=\"target\" tabindex=\"-1\"></button>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return true for natively focusable element with tabindex not set', function () {\n    var target = queryFixture('<button id=\"target\"></button>');\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return true for natively focusable element with tabindex set to empty string', function () {\n    var target = queryFixture('<button id=\"target\" tabindex=\"\"></button>');\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return true for natively focusable element with tabindex set to non-parseable value', function () {\n    var target = queryFixture(\n      '<button id=\"target\" tabindex=\"foobar\"></button>'\n    );\n    assert.isTrue(isInTabOrder(target));\n  });\n\n  it('should return false for disabled', function () {\n    var target = queryFixture('<button id=\"target\" disabled></button>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return false for disabled natively focusable element with tabindex', function () {\n    var target = queryFixture(\n      '<button id=\"target\" disabled tabindex=\"0\"></button>'\n    );\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return false for hidden inputs', function () {\n    var target = queryFixture('<input type=\"hidden\" id=\"target\"></input>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return false for non-element nodes', function () {\n    var target = queryFixture('<span id=\"target\">Hello World</span>');\n    assert.isFalse(isInTabOrder(target.children[0]));\n  });\n\n  it('should return false for natively focusable hidden element', function () {\n    var target = queryFixture('<button id=\"target\" hidden></button>');\n    assert.isFalse(isInTabOrder(target));\n  });\n\n  it('should return for false hidden element with tabindex 1', function () {\n    var target = queryFixture('<div id=\"target\" tabindex=\"1\" hidden></div>');\n    assert.isFalse(isInTabOrder(target));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-in-text-block.js",
    "content": "describe('dom.isInTextBlock', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true if the element is a node in a block of text', function () {\n    fixtureSetup(\n      '<p>Some paragraph with text ' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isTrue(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('returns false if the element is a block', function () {\n    fixtureSetup(\n      '<p>Some paragraph with text ' +\n        '  <a href=\"\" id=\"link\" style=\"display:block\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('returns false if the element has the only text in the block', function () {\n    fixtureSetup('<p><a href=\"\" id=\"link\">link</a></p>');\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('returns false if there is more text in link(s) than in the rest of the block', function () {\n    fixtureSetup(\n      '<p> short text:' +\n        '  <a href=\"\" id=\"link\">on a link with a very long text</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('return false if there are links along side other links', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '  <a href=\"\">other link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignores hidden content', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '  <span style=\"display:none\">some hidden text</span>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignores floated content', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <span style=\"float: left\">A floating text in the area</span>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignores positioned content', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <span style=\"position:absolute;\">Some absolute potitioned text</span>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignores none-text content', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <img alt=\"Some graphical component\" src=\"img.png\" />' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignore text in the block coming before a br', function () {\n    fixtureSetup(\n      '<p>Some paragraph with text <br>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignore text in the block coming after a br', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <a href=\"\" id=\"link\">link</a> <br>' +\n        '  Some paragraph with text ' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignore text in the block coming before and after a br', function () {\n    fixtureSetup(\n      '<p>Some paragraph with text <br>' +\n        '  <a href=\"\" id=\"link\">link</a> <br>' +\n        '  Some paragraph with text ' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignores text inside inline widgets and components', function () {\n    fixtureSetup(\n      '<p>' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '  <button> My button </button>' +\n        '  <button disabled> My disabled button </button>' +\n        '  <span role=\"searchbox\">My query</span>' +\n        '  <textarea>My text content</textarea>' +\n        '  <select><option>My first choice</option></select>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('treats hr elements the same as br elements', function () {\n    fixtureSetup(\n      '<div>Some paragraph with text <hr>' +\n        '  <a href=\"\" id=\"link\">link</a> <hr>' +\n        '  Some paragraph with text ' +\n        '</div>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  it('ignore comments', function () {\n    fixtureSetup(\n      '<p><!-- Some paragraph with text -->' +\n        '  <a href=\"\" id=\"link\">link</a>' +\n        '</p>'\n    );\n    var link = document.getElementById('link');\n    assert.isFalse(axe.commons.dom.isInTextBlock(link));\n  });\n\n  (shadowSupport.v1 ? it : xit)('can reach outside a shadow tree', function () {\n    var div = document.createElement('div');\n    div.innerHTML = 'Some paragraph with text <span></span> ';\n    var shadow = div.querySelector('span').attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<a href=\"\" id=\"link\">link</a>';\n    fixtureSetup(div);\n\n    var link = shadow.querySelector('#link');\n    assert.isTrue(axe.commons.dom.isInTextBlock(link));\n  });\n\n  (shadowSupport.v1 ? it : xit)('can reach into a shadow tree', function () {\n    var div = document.createElement('div');\n    div.innerHTML = '<a href=\"\" id=\"link\">link</a>';\n    var shadow = div.attachShadow({ mode: 'open' });\n    shadow.innerHTML = '<p>Some paragraph with text <slot></slot> </p>';\n    fixtureSetup(div);\n\n    var link = fixture.querySelector('#link');\n    assert.isTrue(axe.commons.dom.isInTextBlock(link));\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'treats shadow DOM slots as siblings',\n    function () {\n      var div = document.createElement('div');\n      div.innerHTML = '<br>';\n      var shadow = div.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<p>Some paragraph with text ' +\n        '<slot></slot> <a href=\"\" id=\"link\">link</a></p>';\n      fixtureSetup(div);\n\n      var link = shadow.querySelector('#link');\n      assert.isFalse(axe.commons.dom.isInTextBlock(link));\n    }\n  );\n\n  describe('options.noLengthCompare', function () {\n    it('returns true if there is any text outside the link', function () {\n      fixtureSetup('<p>amy <a href=\"\" id=\"link\">link text is longer</a></p>');\n      var link = document.getElementById('link');\n      assert.isTrue(\n        axe.commons.dom.isInTextBlock(link, {\n          noLengthCompare: true\n        })\n      );\n    });\n\n    it('returns false if the non-widget text is only whitespace', function () {\n      fixtureSetup(\n        '<p>' +\n          ' <a href=\"\" id=\"link\">link 1</a>\\t\\n\\r' +\n          ' <a href=\"\">link 2</a>' +\n          ' <a href=\"\">link 3</a>' +\n          ' <a href=\"\">link 4</a>' +\n          '</p>'\n      );\n      var link = document.getElementById('link');\n      assert.isFalse(\n        axe.commons.dom.isInTextBlock(link, {\n          noLengthCompare: true\n        })\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-inert.js",
    "content": "describe('dom.isInert', () => {\n  const fixture = document.querySelector('#fixture');\n  const isInert = axe.commons.dom.isInert;\n  const { queryFixture, flatTreeSetup } = axe.testUtils;\n\n  it('returns true for element with \"inert=false`', () => {\n    const vNode = queryFixture('<div id=\"target\" inert=\"false\"></div>');\n\n    assert.isTrue(isInert(vNode));\n  });\n\n  it('returns true for element with \"inert`', () => {\n    const vNode = queryFixture('<div id=\"target\" inert></div>');\n\n    assert.isTrue(isInert(vNode));\n  });\n\n  it('returns false for element without inert', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n\n    assert.isFalse(isInert(vNode));\n  });\n\n  it('returns true for ancestor with inert', () => {\n    const vNode = queryFixture(\n      '<div inert><div><div id=\"target\"></div></div></div>'\n    );\n\n    assert.isTrue(isInert(vNode));\n  });\n\n  it('returns false for closed dialog', () => {\n    const vNode = queryFixture(`\n      <dialog><span>Hello</span></dialog>\n      <div id=\"target\">World</div>\n    `);\n\n    assert.isFalse(isInert(vNode));\n  });\n\n  it('returns false for non-modal dialog', () => {\n    const vNode = queryFixture(`\n      <dialog open><span>Hello</span></dialog>\n      <div id=\"target\">World</div>\n    `);\n\n    assert.isFalse(isInert(vNode));\n  });\n\n  it('returns true for modal dialog', () => {\n    fixture.innerHTML = `\n      <dialog id=\"modal\"><span>Hello</span></dialog>\n      <div id=\"target\">World</div>\n    `;\n    document.querySelector('#modal').showModal();\n    const tree = flatTreeSetup(fixture);\n    const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n    assert.isTrue(isInert(vNode));\n  });\n\n  it('returns false for the modal dialog element', () => {\n    fixture.innerHTML = `\n      <dialog id=\"target\"><span>Hello</span></dialog>\n    `;\n    document.querySelector('#target').showModal();\n    const tree = flatTreeSetup(fixture);\n    const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n    assert.isFalse(isInert(vNode));\n  });\n\n  it('returns false for a descendant of the modal dialog', () => {\n    fixture.innerHTML = `\n      <dialog id=\"modal\"><span id=\"target\">Hello</span></dialog>\n    `;\n    document.querySelector('#modal').showModal();\n    const tree = flatTreeSetup(fixture);\n    const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n    assert.isFalse(isInert(vNode));\n  });\n\n  describe('options.skipAncestors', () => {\n    it('returns false for ancestor with inert', () => {\n      const vNode = queryFixture(\n        '<div inert><div><div id=\"target\"></div></div></div>'\n      );\n\n      assert.isFalse(isInert(vNode, { skipAncestors: true }));\n    });\n  });\n\n  describe('options.isAncestor', () => {\n    it('return false for modal dialog', () => {\n      fixture.innerHTML = `\n        <dialog id=\"modal\"><span>Hello</span></dialog>\n        <div id=\"target\">World</div>\n      `;\n      document.querySelector('#modal').showModal();\n      const tree = flatTreeSetup(fixture);\n      const vNode = axe.utils.querySelectorAll(tree, '#target')[0];\n\n      assert.isFalse(isInert(vNode, { isAncestor: true }));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-modal-open.js",
    "content": "describe('dom.isModalOpen', function () {\n  'use strict';\n\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var isModalOpen = axe.commons.dom.isModalOpen;\n  var dialogElSupport =\n    typeof document.createElement('dialog').open !== 'undefined';\n\n  it('returns true if there is a visible element with role=dialog', function () {\n    fixtureSetup('<div role=\"dialog\">Modal</div>');\n    assert.isTrue(isModalOpen());\n  });\n\n  it('returns true if there is a visible element with aria-modal=true', function () {\n    fixtureSetup('<div aria-modal=\"true\">Modal</div>');\n    assert.isTrue(isModalOpen());\n  });\n\n  (dialogElSupport ? it : xit)(\n    'returns true if there is a visible dialog element',\n    function () {\n      fixtureSetup('<dialog open><div>Modal</div></dialog>');\n      assert.isTrue(isModalOpen());\n    }\n  );\n\n  it('returns true if there is a visible absolutely positioned element with >= 75% width/height', function () {\n    fixtureSetup(\n      '<div style=\"position: absolute; top: 0; bottom: 0; left: 0; right: 0\">Modal</div>'\n    );\n    assert.isTrue(isModalOpen());\n  });\n\n  it('returns true if there is a visible absolutely positioned element with >= 75% width/height and is not the top most element', function () {\n    fixtureSetup(\n      '<div style=\"position: fixed; top: 0; bottom: 0; width: 100%; height: 100%; z-index: 99999; background: rgba(0,0,0,0.5);\">' +\n        '<div style=\"display: flex; align-items: center; justify-content: center; height: 100%;\">' +\n        '<div style=\"padding: 2rem; border: 1px solid; background: white;\">Modal</div>' +\n        '</div>' +\n        '</div>'\n    );\n    assert.isTrue(isModalOpen());\n  });\n\n  it('returns true if modal opens like a drawer', function () {\n    fixtureSetup(\n      '<div style=\"position: fixed; top: 0; bottom: 0; width: 100%; height: 100%; z-index: 99999; background: rgba(0,0,0,0.5);\">' +\n        '<div style=\"width: 25%; height: 100%;\">' +\n        '<div style=\"padding: 2rem; border-right: 1px solid; background: white; height: 100%;\">Modal</div>' +\n        '</div>' +\n        '</div>'\n    );\n    assert.isTrue(isModalOpen());\n  });\n\n  it('returns undefined if there is no modal', function () {\n    fixtureSetup('<div>Modal</div>');\n    assert.isUndefined(isModalOpen());\n  });\n\n  it('returns undefined if there is a hidden element with role=dialog', function () {\n    fixtureSetup('<div role=\"dialog\" style=\"display: none\">Modal</div>');\n    assert.isUndefined(isModalOpen());\n  });\n\n  it('returns undefined if there is a hidden element with aria-modal=true', function () {\n    fixtureSetup('<div aria-modal=\"true\" style=\"display: none\">Modal</div>');\n    assert.isUndefined(isModalOpen());\n  });\n\n  (dialogElSupport ? it : xit)(\n    'returns undefined if there is a hidden dialog element',\n    function () {\n      fixtureSetup('<dialog><div>Modal</div></dialog>');\n      assert.isUndefined(isModalOpen());\n    }\n  );\n\n  it('returns undefined if there is a visible absolutely positioned element with < 75% width/height', function () {\n    fixtureSetup(\n      '<div style=\"position: fixed; top: 0; bottom: 0; width: 50%; height: 50%; margin: 0 auto; transform: translateY(-50%);>Modal</div>'\n    );\n    assert.isUndefined(isModalOpen());\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-multiline.js",
    "content": "describe('dom.isMultiline', function () {\n  var isMultiline = axe.commons.dom.isMultiline;\n  var fixture = document.querySelector('#fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns false if there is a single line', function () {\n    fixture.innerHTML = '<p>hello</p>';\n    assert.isFalse(isMultiline(fixture.firstChild));\n  });\n\n  it('returns true if there are two lines', function () {\n    fixture.innerHTML = '<p>hello <br> world</p>';\n    assert.isTrue(isMultiline(fixture.firstChild));\n  });\n\n  it('handles single-line texts with varying font-sizes', function () {\n    fixture.innerHTML =\n      '<p>' +\n      '  <span style=\"font-size: 12px\">small</span> ' +\n      '  <span style=\"font-size: 20px\">large</span>' +\n      '  <span style=\"font-size: 16px\">medium</span>' +\n      '</p>';\n    assert.isFalse(isMultiline(fixture.firstChild));\n  });\n\n  describe('with non-text elements', function () {\n    it('is true when on a multiple lines', function () {\n      fixture.innerHTML =\n        '<p>' +\n        '  <input /><br>' +\n        '  <textarea rows=\"5\"></textarea><br>' +\n        '  <button style=\"font-size: 20px\">I like big buttons</button>' +\n        '</p>';\n      assert.isTrue(isMultiline(fixture.firstChild));\n    });\n\n    it('is false when on a single line', function () {\n      fixture.innerHTML =\n        '<p>' +\n        '  <span style=\"font-size: 12px\">Hello</span> ' +\n        '  <input />' +\n        '  <textarea rows=\"5\"></textarea>' +\n        '  <button style=\"font-size: 20px\">I like big buttons</button>' +\n        '</p>';\n      assert.isFalse(isMultiline(fixture.firstChild));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-natively-focusable.js",
    "content": "describe('dom.isNativelyFocusable', () => {\n  const fixture = document.getElementById('fixture');\n  const isNativelyFocusable = axe.commons.dom.isNativelyFocusable;\n  const { flatTreeSetup } = axe.testUtils;\n\n  function hideByClipping(el) {\n    el.style.cssText =\n      'position: absolute !important;' +\n      ' clip: rect(0px 0px 0px 0px); /* IE6, IE7 */' +\n      ' clip: rect(0px, 0px, 0px, 0px);';\n  }\n\n  function hideByMovingOffScreen(el) {\n    el.style.cssText =\n      'position:absolute;' +\n      ' left:-10000px;' +\n      ' top:auto;' +\n      ' width:1px;' +\n      ' height:1px;' +\n      ' overflow:hidden;';\n  }\n\n  it('should return true for buttons with redundant tabindex', () => {\n    fixture.innerHTML = '<button tabindex=\"0\" id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for buttons with tabindex -1', () => {\n    fixture.innerHTML = '<button tabindex=\"-1\" id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for visible, enabled textareas', () => {\n    fixture.innerHTML = '<textarea id=\"target\"></textarea>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for visible, enabled selects', () => {\n    fixture.innerHTML = '<select id=\"target\"></select>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for visible, enabled buttons', () => {\n    fixture.innerHTML = '<button id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for visible, enabled, non-hidden inputs', () => {\n    fixture.innerHTML = '<input type=\"text\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return false for disabled elements', () => {\n    fixture.innerHTML = '<input type=\"text\" id=\"target\" disabled>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for hidden inputs', () => {\n    fixture.innerHTML = '<input type=\"hidden\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements hidden with display:none', () => {\n    fixture.innerHTML =\n      '<button id=\"target\" style=\"display: none\">button</button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements hidden with visibility:hidden', () => {\n    fixture.innerHTML =\n      '<button id=\"target\" style=\"visibility: hidden\">button</button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements collapsed with visibility:collapse', () => {\n    fixture.innerHTML =\n      '<button id=\"target\" style=\"visibility: collapse\">button</button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return true for clipped elements', () => {\n    fixture.innerHTML = '<button id=\"target\">button</button>';\n    const el = document.getElementById('target');\n    hideByClipping(el);\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for elements positioned off screen', () => {\n    fixture.innerHTML = '<button id=\"target\">button</button>';\n    const el = document.getElementById('target');\n    hideByMovingOffScreen(el);\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements hidden with display:none on an ancestor', () => {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"display:none\"><button id=\"target\">button</button></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements hidden with visibility:hidden on an ancestor', () => {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"visibility: hidden\"><button id=\"target\">button</button></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for elements collapsed with visibility:collapse on an ancestor', () => {\n    fixture.innerHTML =\n      '<div id=\"parent\" style=\"visibility: collapse\"><button id=\"target\">button</button></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return true for elements with a clipped ancestor', () => {\n    fixture.innerHTML =\n      '<div id=\"parent\"><button id=\"target\">button</button></div>';\n    hideByClipping(document.getElementById('parent'));\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return true for elements off-screened by an ancestor', () => {\n    fixture.innerHTML =\n      '<div id=\"parent\"><button id=\"target\">button</button></div>';\n    hideByMovingOffScreen(document.getElementById('parent'));\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return false for hidden inputs with tabindex', () => {\n    fixture.innerHTML = '<input type=\"hidden\" tabindex=\"1\" id=\"target\">';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for disabled inputs with tabindex', () => {\n    fixture.innerHTML = '<input tabindex=\"1\" id=\"target\" disabled>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for hidden buttons with tabindex', () => {\n    fixture.innerHTML =\n      '<button style=\"visibility:hidden\" tabindex=\"0\" id=\"target\"></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for disabled buttons with tabindex', () => {\n    fixture.innerHTML = '<button tabindex=\"0\" id=\"target\" disabled></button>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for an anchor with an href', () => {\n    fixture.innerHTML = '<a href=\"something.html\" id=\"target\"></a>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(isNativelyFocusable(el));\n  });\n\n  it('should return false for an anchor with no href', () => {\n    fixture.innerHTML = '<a name=\"anchor\" id=\"target\"></a>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for a div with a tabindex with spaces', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"    0     \"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for a div with a tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"0\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return false for a div with a non-numeric tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\" tabindex=\"x\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n\n  it('should return true for a summary element', () => {\n    fixture.innerHTML =\n      '<details><summary id=\"target\">Summary</summary><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return true for a details element without a summary element', () => {\n    fixture.innerHTML = '<details id=\"target\"><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isTrue(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for a details element with a summary element', () => {\n    fixture.innerHTML =\n      '<details id=\"target\"><summary>Summary</summary><p>Detail</p></details>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(axe.commons.dom.isFocusable(el));\n  });\n\n  it('should return false for a div with no tabindex', () => {\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    const el = document.getElementById('target');\n    flatTreeSetup(fixture);\n\n    assert.isFalse(isNativelyFocusable(el));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-node.js",
    "content": "describe('dom.isNode', function () {\n  'use strict';\n\n  it('nodes', function () {\n    var node;\n    node = document;\n    assert.isTrue(axe.commons.dom.isNode(node), 'Document');\n\n    node = document.body;\n    assert.isTrue(axe.commons.dom.isNode(node), 'Body');\n\n    node = document.documentElement;\n    assert.isTrue(axe.commons.dom.isNode(node), 'Document Element');\n\n    node = document.createTextNode('cats');\n    assert.isTrue(axe.commons.dom.isNode(node), 'Text Nodes');\n\n    node = document.createElement('div');\n    assert.isTrue(axe.commons.dom.isNode(node), 'Elements');\n\n    node = document.createComment('div');\n    assert.isTrue(axe.commons.dom.isNode(node), 'Comment nodes');\n\n    node = document.createDocumentFragment();\n    assert.isTrue(axe.commons.dom.isNode(node), 'Document fragments');\n  });\n\n  it('non-nodes', function () {\n    var node;\n\n    node = {};\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = null;\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = window;\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = [];\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = 'cats';\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = undefined;\n    assert.isFalse(axe.commons.dom.isNode(node));\n\n    node = false;\n    assert.isFalse(axe.commons.dom.isNode(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-offscreen.js",
    "content": "describe('dom.isOffscreen', function () {\n  'use strict';\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    document.body.style.direction = 'ltr';\n  });\n\n  it('should detect elements positioned outside the left edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; left: -51px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isTrue(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned to but not beyond the left edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; left: -50px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isTrue(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should not detect elements at the left edge with a zero width', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"width: 0px; left: 0px;\"></div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned outside the top edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; height: 50px; top: -51px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n    assert.isTrue(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should never detect elements positioned outside the bottom edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; height: 50px; bottom: -501px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned that bleed inside the left edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; left: -49px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned outside the right edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; right: -49px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned outside the top edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; height: 50px; top: -49px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned outside the bottom edge', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; height: 50px; bottom: -49px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements that are made off-screen by a parent', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; left: -51px;\">' +\n      '<div id=\"target\">Offscreen?</div>' +\n      '</div>';\n\n    var el = document.getElementById('target');\n\n    assert.isTrue(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should NOT detect elements positioned outside the right edge on LTR documents', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; right: -51px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should detect elements positioned outside the right edge on RTL documents', function () {\n    document.body.style.direction = 'rtl';\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; right: -151px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isTrue(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should NOT detect elements positioned outside the left edge on RTL documents', function () {\n    document.body.style.direction = 'rtl';\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"position: absolute; width: 50px; left: -51px;\">Offscreen?</div>';\n    var el = document.getElementById('target');\n\n    assert.isFalse(axe.commons.dom.isOffscreen(el));\n  });\n\n  it('should not detect elements positioned because of a scroll', function () {\n    fixture.innerHTML =\n      '<div id=\"scrollable\" style=\"max-height:20px;overflow:scroll\">' +\n      '<div id=\"visible\">goobye</div>' +\n      '<div id=\"high\" style=\"height:50px\">high</div>' +\n      '<div id=\"scrollme\">hello</div>' +\n      '</div>';\n    var viz = document.getElementById('visible');\n    assert.isFalse(axe.commons.dom.isOffscreen(viz));\n    var scrollme = document.getElementById('scrollme');\n    scrollme.scrollIntoView();\n    assert.isFalse(axe.commons.dom.isOffscreen(viz));\n  });\n\n  it('should return undefined if actual ndoe is undefined', function () {\n    assert.isUndefined(axe.commons.dom.isOffscreen());\n  });\n\n  (shadowSupport.v1 ? it : xit)(\n    'should detect on screen shadow nodes',\n    function () {\n      fixture.innerHTML = '<div></div>';\n      var shadow = fixture.querySelector('div').attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<div id=\"target\">Offscreen?</div>';\n\n      var el = shadow.querySelector('#target');\n      assert.isFalse(axe.commons.dom.isOffscreen(el));\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should detect off screen shadow nodes',\n    function () {\n      fixture.innerHTML = '<div></div>';\n      var shadow = fixture.querySelector('div').attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; height: 50px; top: -51px;\">Offscreen?</div>';\n\n      var el = shadow.querySelector('#target');\n      assert.isTrue(axe.commons.dom.isOffscreen(el));\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/is-skip-link.js",
    "content": "describe('dom.isSkipLink', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var baseEl;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n\n    if (baseEl) {\n      baseEl.parentNode.removeChild(baseEl);\n    }\n  });\n\n  it('should return true if the href points to an ID', function () {\n    fixture.innerHTML = '<a href=\"#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false if the href points to another document', function () {\n    fixture.innerHTML = '<a href=\"something.html#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return true if the URI encoded href points to an element with an ID', function () {\n    fixture.innerHTML = '<a href=\"#%3Ctarget%3E\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return true if the URI is an Angular skiplink', function () {\n    fixture.innerHTML = '<a href=\"/#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false if the URI is angular #!', function () {\n    fixture.innerHTML = '<a href=\"#!target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false if the URI is angular #/', function () {\n    fixture.innerHTML = '<a href=\"#/target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return true for multiple skip-links', function () {\n    fixture.innerHTML =\n      '<a id=\"skip-link1\" href=\"#target1\">Click Here></a><a id=\"skip-link2\" href=\"/#target2\">Click Here></a><a id=\"skip-link3\" href=\"#target3\">Click Here></a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var nodes = fixture.querySelectorAll('a');\n    for (var i = 0; i < nodes.length; i++) {\n      assert.isTrue(axe.commons.dom.isSkipLink(nodes[i]));\n    }\n  });\n\n  it('should return true if the element is before a page link', function () {\n    fixture.innerHTML =\n      '<a id=\"skip-link\" href=\"#target\">Click Here></a><a href=\"/page\">New Page</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('#skip-link');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false if the element is after a page link', function () {\n    fixture.innerHTML =\n      '<a href=\"/page\">New Page</a><a id=\"skip-link\" href=\"#target\">Click Here></a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('#skip-link');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should ignore links that start with `href=javascript`', function () {\n    fixture.innerHTML =\n      '<a href=\"javascript:void\">New Page</a><a id=\"skip-link\" href=\"#target\">Click Here></a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('#skip-link');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return true for hash href that resolves to current page', function () {\n    fixture.innerHTML =\n      '<a href=\"' + window.location.pathname + '#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return true for absolute path hash href', function () {\n    var url = window.location.href;\n    fixture.innerHTML = '<a href=\"' + url + '#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isTrue(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false for absolute path href that points to another document', function () {\n    var origin = window.location.origin;\n    fixture.innerHTML =\n      '<a href=\"' + origin + '/something.html#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n\n  it('should return false for href with <base> tag that points to another document', function () {\n    baseEl = document.createElement('base');\n    baseEl.href = 'https://www.google.com/';\n    document.getElementsByTagName('head')[0].appendChild(baseEl);\n\n    fixture.innerHTML =\n      '<a href=\"' + window.location.pathname + '#target\">Click Here</a>';\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n    var node = fixture.querySelector('a');\n    assert.isFalse(axe.commons.dom.isSkipLink(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-visible-on-screen.js",
    "content": "describe('dom.isVisibleOnScreen', function () {\n  'use strict';\n\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var isIE11 = axe.testUtils.isIE11;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var isVisibleOnScreen = axe.commons.dom.isVisibleOnScreen;\n\n  it('should return true on statically-positioned, visible elements', function () {\n    var vNode = queryFixture('<div id=\"target\">Hello!</div>');\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should return true on absolutely positioned elements that are on-screen', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; left: 10px; right: 10px\">hi</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should respect position: fixed', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position:fixed; bottom: 0; left: 0;\">StickySticky</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should properly calculate offsets according the offsetParent', function () {\n    var vNode = queryFixture(\n      '<div style=\"position: absolute; top: 400px; left: 400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: -400px; left: -400px\">Hi</div>' +\n        '</div>'\n    );\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false if moved offscreen with left', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; left: -9999px\">Hi</div>'\n    );\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false if moved offscreen with top', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; top: -9999px\">Hi</div>'\n    );\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false on detached elements', function () {\n    var el = document.createElement('div');\n    el.innerHTML = 'I am not visible because I am detached!';\n    axe.testUtils.flatTreeSetup(el);\n    assert.isFalse(isVisibleOnScreen(el));\n  });\n\n  it('should return true on body', function () {\n    axe.testUtils.flatTreeSetup(document.body);\n    var actual = isVisibleOnScreen(document.body);\n    assert.isTrue(actual);\n  });\n\n  it('should return true on html', function () {\n    axe.testUtils.flatTreeSetup(document.documentElement);\n    var actual = isVisibleOnScreen(document.documentElement);\n    assert.isTrue(actual);\n  });\n\n  it('should return false on STYLE tag', function () {\n    var vNode = queryFixture(\n      '<style id=\"target\"> @import \"https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css\"; .green { background-color: green; } </style>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on NOSCRIPT tag', function () {\n    var vNode = queryFixture(\n      '<noscript id=\"target\"><p class=\"invisible\"><img src=\"/piwik/piwik.php?idsite=1\" alt=\"\" /></p></noscript>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return false on TEMPLATE tag', function () {\n    var vNode = queryFixture(\n      '<template id=\"target\"><div>Name:</div></template>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('should return true if positioned statically but top/left is set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"top: -9999px; left: -9999px;' +\n        'right: -9999px; bottom: -9999px;\">Hi</div>'\n    );\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should not be affected by `aria-hidden`', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-hidden=\"true\">Hidden from screen readers</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should not calculate position on parents', function () {\n    var vNode = queryFixture(\n      '<div style=\"position: absolute; top: -400px; left: -400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: 500px; left: 500px\">Hi</div>' +\n        '</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should know how `visibility` works', function () {\n    var vNode = queryFixture(\n      '<div style=\"visibility: hidden;\">' +\n        '<div id=\"target\" style=\"visibility: visible;\">Hi</div>' +\n        '</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect clip rect hidden text technique', function () {\n    var clip =\n      'clip: rect(1px 1px 1px 1px);' +\n      'clip: rect(1px, 1px, 1px, 1px);' +\n      'width: 1px; height: 1px;' +\n      'position: absolute;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture('<div id=\"target\" style=\"' + clip + '\">Hi</div>');\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect clip rect hidden text technique using position: fixed', function () {\n    var clip =\n      'clip: rect(1px 1px 1px 1px);' +\n      'clip: rect(1px, 1px, 1px, 1px);' +\n      'width: 1px; height: 1px;' +\n      'position: fixed;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture('<div id=\"target\" style=\"' + clip + '\">Hi</div>');\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect when clip is not applied because of positioning', function () {\n    var clip =\n      'clip: rect(1px 1px 1px 1px);' +\n      'clip: rect(1px, 1px, 1px, 1px);' +\n      'position: relative;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture('<div id=\"target\" style=\"' + clip + '\">Hi</div>');\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect clip rect hidden text technique on parent', function () {\n    var clip =\n      'clip: rect(1px 1px 1px 1px);' +\n      'clip: rect(1px, 1px, 1px, 1px);' +\n      'width: 1px; height: 1px;' +\n      'position: absolute;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture(\n      '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect when clip is not applied because of positioning on parent', function () {\n    var clip =\n      'clip: rect(1px 1px 1px 1px);' +\n      'clip: rect(1px, 1px, 1px, 1px);' +\n      'position: relative;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture(\n      '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>'\n    );\n\n    assert.isTrue(isVisibleOnScreen(vNode));\n  });\n\n  it('should detect poorly hidden clip rects', function () {\n    var clip =\n      'clip: rect(5px 1px 1px 5px);' +\n      'clip: rect(5px, 1px, 1px, 5px);' +\n      'width: 1px; height: 1px;' +\n      'position: absolute;' +\n      'overflow: hidden;';\n\n    var vNode = queryFixture('<div id=\"target\" style=\"' + clip + '\">Hi</div>');\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false for display: none', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"display: none\">Hello!</div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false for opacity: 0', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"opacity: 0\">Hello!</div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false for 0 height scrollable region', function () {\n    var vNode = queryFixture(\n      '<div style=\"overflow: scroll; height: 0\"><div id=\"target\">Hello!</div></div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false for 0 width scrollable region', function () {\n    var vNode = queryFixture(\n      '<div style=\"overflow: scroll; width: 0\"><div id=\"target\">Hello!</div></div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('returns false for `AREA` without closest `MAP` element', function () {\n    var vNode = queryFixture(\n      '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for `AREA` with closest `MAP` with no name attribute', function () {\n    var vNode = queryFixture(\n      '<map>' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns false for `AREA` element that is inside shadowDOM',\n    function () {\n      fixture.innerHTML = '<div id=\"container\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      var target = shadow.querySelector('#target');\n      var actual = isVisibleOnScreen(target);\n      assert.isFalse(actual);\n    }\n  );\n\n  it('returns false for `AREA` with closest `MAP` with name but not referred by an `IMG` usemap attribute', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic-wrong-name\" alt=\"MDN infographic\" />'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for `AREA` with `MAP` and used in `IMG` which is not visible', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic\" alt=\"MDN infographic\" style=\"display:none\"/>'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true for `AREA` with `MAP` and used in `IMG` which is visible', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic\" alt=\"MDN infographic\" />'\n    );\n    var actual = isVisibleOnScreen(vNode);\n    assert.isTrue(actual);\n  });\n\n  // IE11 either only supports clip paths defined by url() or not at all,\n  // MDN and caniuse.com give different results...\n  (isIE11 ? it.skip : it)(\n    'should detect clip-path hidden text technique',\n    function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"clip-path: inset(50%);\">Hi</div>'\n      );\n\n      assert.isFalse(isVisibleOnScreen(vNode));\n    }\n  );\n\n  (isIE11 ? it.skip : it)(\n    'should detect clip-path hidden text technique on parent',\n    function () {\n      var vNode = queryFixture(\n        '<div style=\"clip-path: circle(0%);\">' +\n          '<div id=\"target\">Hi</div>' +\n          '</div>'\n      );\n\n      assert.isFalse(isVisibleOnScreen(vNode));\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should correctly handle visible slotted elements',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML = '<div id=\"target\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n      fixture.innerHTML = '<div><a>hello</a></div>';\n      makeShadowTree(fixture.firstChild);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.isTrue(isVisibleOnScreen(el.actualNode));\n    }\n  );\n  (shadowSupported ? it : xit)(\n    'should correctly handle hidden slotted elements',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML =\n          '<div id=\"target\" style=\"display:none;\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n      fixture.innerHTML = '<div><p><a>hello</a></p></div>';\n      makeShadowTree(fixture.firstChild);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.isFalse(isVisibleOnScreen(el.actualNode));\n    }\n  );\n\n  it('should return false for screen reader only technique', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"position: absolute; width: 1px; height: 1x; margin: -1px; padding: 0; border: 0; overflow: hidden;\">Visually Hidden</div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  it('should return false for element outside \"overflow:hidden\"', function () {\n    var vNode = queryFixture(\n      '<div style=\"overflow: hidden; height: 100px;\"><div id=\"target\" style=\"margin-top: 200px;\">Visually Hidden</div></div>'\n    );\n\n    assert.isFalse(isVisibleOnScreen(vNode));\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return true on statically-positioned, visible elements', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      assert.isTrue(isVisibleOnScreen(vNode));\n    });\n\n    it('should return false on STYLE tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'style'\n      });\n      var actual = isVisibleOnScreen(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on NOSCRIPT tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'noscript'\n      });\n      var actual = isVisibleOnScreen(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on TEMPLATE tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'template'\n      });\n      var actual = isVisibleOnScreen(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should not be affected by `aria-hidden`', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-hidden': true\n        }\n      });\n      assert.isTrue(isVisibleOnScreen(vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-visible-to-screenreader.js",
    "content": "describe('dom.isVisibleToScreenReaders', function () {\n  'use strict';\n\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var isVisibleToScreenReaders = axe.commons.dom.isVisibleToScreenReaders;\n\n  function createContentHidden() {\n    var group = document.createElement('div');\n    group.innerHTML =\n      '<label id=\"mylabel\">Label</label><input aria-labelledby=\"mylabel\" type=\"text\" />';\n    return group;\n  }\n\n  function makeShadowTreeHidden(node) {\n    var root = node.attachShadow({ mode: 'open' });\n    var div = document.createElement('div');\n    div.className = 'parent';\n    root.appendChild(div);\n    div.appendChild(createContentHidden());\n  }\n\n  it('should return false on detached elements', function () {\n    var el = document.createElement('div');\n    el.innerHTML = 'I am not visible because I am detached!';\n    axe.testUtils.flatTreeSetup(el);\n    assert.isFalse(isVisibleToScreenReaders(el));\n  });\n\n  it('should return true on body', function () {\n    axe.testUtils.flatTreeSetup(document.body);\n    var actual = isVisibleToScreenReaders(document.body);\n    assert.isTrue(actual);\n  });\n\n  it('should return true on html', function () {\n    axe.testUtils.flatTreeSetup(document.documentElement);\n    var actual = isVisibleToScreenReaders(document.documentElement);\n    assert.isTrue(actual);\n  });\n\n  it('should return true for visible element', function () {\n    var vNode = queryFixture('<div id=\"target\">Visible</div>');\n    assert.isTrue(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should return true for visible area element', function () {\n    var vNode = queryFixture(\n      '<map name=\"map\">' +\n        '<area id=\"target\" href=\"#\" />' +\n        '</map>' +\n        '<img usemap=\"#map\" src=\"img.png\" />'\n    );\n    assert.isTrue(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should return false if `aria-hidden` is set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" aria-hidden=\"true\">Hidden from screen readers</div>'\n    );\n    assert.isFalse(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should return false if `inert` is set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" inert>Hidden from screen readers</div>'\n    );\n    assert.isFalse(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should return false if `display: none` is set', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\" style=\"display: none\">Hidden from screen readers</div>'\n    );\n    assert.isFalse(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should return false if `aria-hidden` is set on parent', function () {\n    var vNode = queryFixture(\n      '<div aria-hidden=\"true\"><div id=\"target\">Hidden from screen readers</div></div>'\n    );\n    assert.isFalse(isVisibleToScreenReaders(vNode));\n  });\n\n  it('should know how `visibility` works', function () {\n    var vNode = queryFixture(\n      '<div style=\"visibility: hidden;\">' +\n        '<div id=\"target\" style=\"visibility: visible;\">Hi</div>' +\n        '</div>'\n    );\n    assert.isTrue(isVisibleToScreenReaders(vNode));\n  });\n\n  it('returns false for `AREA` without closest `MAP` element', function () {\n    var vNode = queryFixture(\n      '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>'\n    );\n    var actual = isVisibleToScreenReaders(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for `AREA` with closest `MAP` with no name attribute', function () {\n    var vNode = queryFixture(\n      '<map>' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>'\n    );\n    var actual = isVisibleToScreenReaders(vNode);\n    assert.isFalse(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns false for `AREA` element that is inside shadowDOM',\n    function () {\n      fixture.innerHTML = '<div id=\"container\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      var target = shadow.querySelector('#target');\n      var actual = isVisibleToScreenReaders(target);\n      assert.isFalse(actual);\n    }\n  );\n\n  it('returns false for `AREA` with closest `MAP` with name but not referred by an `IMG` usemap attribute', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic-wrong-name\" alt=\"MDN infographic\" />'\n    );\n    var actual = isVisibleToScreenReaders(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for `AREA` with `MAP` and used in `IMG` which is not visible', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic\" alt=\"MDN infographic\" style=\"display:none\"/>'\n    );\n    var actual = isVisibleToScreenReaders(vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true for `AREA` with `MAP` and used in `IMG` which is visible', function () {\n    var vNode = queryFixture(\n      '<map name=\"infographic\">' +\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n        '</map>' +\n        '<img usemap=\"#infographic\" alt=\"MDN infographic\" />'\n    );\n    var actual = isVisibleToScreenReaders(vNode);\n    assert.isTrue(actual);\n  });\n\n  (shadowSupported ? it : xit)(\n    'not hidden: should work when the element is inside shadow DOM',\n    function () {\n      var tree, node;\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div></div>';\n      makeShadowTreeHidden(fixture.firstChild);\n      tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      node = axe.utils.querySelectorAll(tree, 'input')[0];\n      assert.isTrue(isVisibleToScreenReaders(node));\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'hidden: should work when the element is inside shadow DOM',\n    function () {\n      var tree, node;\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div style=\"display:none\"></div>';\n      makeShadowTreeHidden(fixture.firstChild);\n      tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      node = axe.utils.querySelectorAll(tree, 'input')[0];\n      assert.isFalse(isVisibleToScreenReaders(node));\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should work with hidden slotted elements',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML =\n          '<div id=\"target\" style=\"display:none;\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n      fixture.innerHTML = '<div><p><a>hello</a></p></div>';\n      makeShadowTree(fixture.firstChild);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var vNode = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.isFalse(isVisibleToScreenReaders(vNode));\n    }\n  );\n\n  describe('SerialVirtualNode', function () {\n    it('should return false if `aria-hidden` is set', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-hidden': true\n        }\n      });\n      assert.isFalse(isVisibleToScreenReaders(vNode));\n    });\n\n    it('should return false if `aria-hidden` is set on parent', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      var parentVNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-hidden': true\n        }\n      });\n      parentVNode.children = [vNode];\n      vNode.parent = parentVNode;\n      assert.isFalse(isVisibleToScreenReaders(vNode));\n    });\n\n    it('should return false if `inert` is set', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          inert: true\n        }\n      });\n      assert.isFalse(isVisibleToScreenReaders(vNode));\n    });\n\n    it('should return false if `inert` is set on parent', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      var parentVNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          inert: true\n        }\n      });\n      parentVNode.children = [vNode];\n      vNode.parent = parentVNode;\n      assert.isFalse(isVisibleToScreenReaders(vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-visible.js",
    "content": "describe('dom.isVisible', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var computedStyleStub;\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n    axe._tree = undefined;\n\n    if (computedStyleStub) {\n      computedStyleStub.restore();\n      computedStyleStub = null;\n    }\n  });\n\n  describe('default usage', function () {\n    // Firefox returns `null` if accessed inside a hidden iframe\n    it('should return false if computedStyle return null for whatever reason', function () {\n      computedStyleStub = sinon.stub(window, 'getComputedStyle').returns(null);\n      var el = document.createElement('div');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return true on statically-positioned, visible elements', function () {\n      fixture.innerHTML = '<div id=\"target\">Hello!</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return true on absolutely positioned elements that are on-screen', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; left: 10px; right: 10px\">hi</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should respect position: fixed', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position:fixed; bottom: 0; left: 0;\">StickySticky</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should properly calculate offsets according the offsetParent', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: 400px; left: 400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: -400px; left: -400px\">Hi</div>' +\n        '</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false if moved offscreen with left', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; left: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false if moved offscreen with top', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; top: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false on detached elements', function () {\n      var el = document.createElement('div');\n      el.innerHTML = 'I am not visible because I am detached!';\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return true on a document', function () {\n      assert.isTrue(axe.commons.dom.isVisible(document));\n    });\n\n    it('should return false on STYLE tag', function () {\n      var vNode = queryFixture(\n        '<style id=\"target\"> @import \"https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css\"; .green { background-color: green; } </style>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on NOSCRIPT tag', function () {\n      var vNode = queryFixture(\n        '<noscript id=\"target\"><p class=\"invisible\"><img src=\"/piwik/piwik.php?idsite=1\" alt=\"\" /></p></noscript>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on TEMPLATE tag', function () {\n      var vNode = queryFixture(\n        '<template id=\"target\"><div>Name:</div></template>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return true if positioned statically but top/left is set', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"top: -9999px; left: -9999px;' +\n        'right: -9999px; bottom: -9999px;\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should not be affected by `aria-hidden`', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" aria-hidden=\"true\">Hidden from screen readers</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should not calculate position on parents', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: -400px; left: -400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: 500px; left: 500px\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should know how `visibility` works', function () {\n      fixture.innerHTML =\n        '<div style=\"visibility: hidden;\">' +\n        '<div id=\"target\" style=\"visibility: visible;\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect clip rect hidden text technique', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect clip rect hidden text technique using position: fixed', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'width: 1px; height: 1px;' +\n          'position: fixed;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect when clip is not applied because of positioning', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'position: relative;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect clip rect hidden text technique on parent', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML =\n        '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>';\n\n      el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect when clip is not applied because of positioning on parent', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'position: relative;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML =\n        '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect poorly hidden clip rects', function () {\n      var el,\n        clip =\n          'clip: rect(5px 1px 1px 5px);' +\n          'clip: rect(5px, 1px, 1px, 5px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false for display: none', function () {\n      fixture.innerHTML = '<div id=\"target\" style=\"display: none\">Hello!</div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false for opacity: 0', function () {\n      fixture.innerHTML = '<div id=\"target\" style=\"opacity: 0\">Hello!</div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false for opacity: 0', function () {\n      fixture.innerHTML = '<div id=\"target\" style=\"opacity: 0\">Hello!</div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false for 0 height scrollable region', function () {\n      fixture.innerHTML =\n        '<div style=\"overflow: scroll; height: 0\"><div id=\"target\">Hello!</div></div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should return false for 0 width scrollable region', function () {\n      fixture.innerHTML =\n        '<div style=\"overflow: scroll; width: 0\"><div id=\"target\">Hello!</div></div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('returns false for `AREA` without closest `MAP` element', function () {\n      var vNode = queryFixture(\n        '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('returns false for `AREA` with closest `MAP` with no name attribute', function () {\n      var vNode = queryFixture(\n        '<map>' +\n          '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n          '</map>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    (shadowSupported ? it : xit)(\n      'returns false for `AREA` element that is inside shadowDOM',\n      function () {\n        fixture.innerHTML = '<div id=\"container\"></div>';\n        var container = fixture.querySelector('#container');\n        var shadow = container.attachShadow({ mode: 'open' });\n        shadow.innerHTML =\n          '<map name=\"infographic\">' +\n          '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n          '</map>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        var target = shadow.querySelector('#target');\n        var actual = axe.commons.dom.isVisible(target);\n        assert.isFalse(actual);\n      }\n    );\n\n    it('returns false for `AREA` with closest `MAP` with name but not referred by an `IMG` usemap attribute', function () {\n      var vNode = queryFixture(\n        '<map name=\"infographic\">' +\n          '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n          '</map>' +\n          '<img usemap=\"#infographic-wrong-name\" alt=\"MDN infographic\" />'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('returns false for `AREA` with `MAP` and used in `IMG` which is not visible', function () {\n      var vNode = queryFixture(\n        '<map name=\"infographic\">' +\n          '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n          '</map>' +\n          '<img usemap=\"#infographic\" alt=\"MDN infographic\" style=\"display:none\"/>'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isFalse(actual);\n    });\n\n    it('returns true for `AREA` with `MAP` and used in `IMG` which is visible', function () {\n      var vNode = queryFixture(\n        '<map name=\"infographic\">' +\n          '<area id=\"target\" role=\"link\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\"/>' +\n          '</map>' +\n          '<img usemap=\"#infographic\" alt=\"MDN infographic\" />'\n      );\n      var actual = axe.commons.dom.isVisible(vNode.actualNode);\n      assert.isTrue(actual);\n    });\n\n    it('should detect clip-path hidden text technique', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"clip-path: inset(50%);\">Hi</div>';\n\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    it('should detect clip-path hidden text technique on parent', function () {\n      fixture.innerHTML =\n        '<div style=\"clip-path: circle(0%);\">' +\n        '<div id=\"target\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n\n    (shadowSupported ? it : xit)(\n      'should correctly handle visible slotted elements',\n      function () {\n        function createContentSlotted() {\n          var group = document.createElement('div');\n          group.innerHTML = '<div id=\"target\">Stuff<slot></slot></div>';\n          return group;\n        }\n        function makeShadowTree(node) {\n          var root = node.attachShadow({ mode: 'open' });\n          var div = document.createElement('div');\n          root.appendChild(div);\n          div.appendChild(createContentSlotted());\n        }\n        fixture.innerHTML = '<div><a>hello</a></div>';\n        makeShadowTree(fixture.firstChild);\n        var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n        var el = axe.utils.querySelectorAll(tree, 'a')[0];\n        assert.isTrue(axe.commons.dom.isVisible(el.actualNode));\n      }\n    );\n    (shadowSupported ? it : xit)(\n      'should correctly handle hidden slotted elements',\n      function () {\n        function createContentSlotted() {\n          var group = document.createElement('div');\n          group.innerHTML =\n            '<div id=\"target\" style=\"display:none;\">Stuff<slot></slot></div>';\n          return group;\n        }\n        function makeShadowTree(node) {\n          var root = node.attachShadow({ mode: 'open' });\n          var div = document.createElement('div');\n          root.appendChild(div);\n          div.appendChild(createContentSlotted());\n        }\n        fixture.innerHTML = '<div><p><a>hello</a></p></div>';\n        makeShadowTree(fixture.firstChild);\n        var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n        var el = axe.utils.querySelectorAll(tree, 'a')[0];\n        assert.isFalse(axe.commons.dom.isVisible(el.actualNode));\n      }\n    );\n    it('should return false if element is visually hidden using position absolute, overflow hidden, and a very small height', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position:absolute; height: 1px; overflow: hidden;\">StickySticky</div>';\n      var el = document.getElementById('target');\n\n      assert.isFalse(axe.commons.dom.isVisible(el));\n    });\n  });\n\n  describe('screen readers', function () {\n    // Firefox returns `null` if accessed inside a hidden iframe\n    it('should return false if computedStyle return null for whatever reason', function () {\n      computedStyleStub = sinon.stub(window, 'getComputedStyle').returns(null);\n      var el = document.createElement('div');\n      assert.isFalse(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true on staticly-positioned, visible elements', function () {\n      fixture.innerHTML = '<div id=\"target\">Hello!</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true on absolutely positioned elements that are on-screen', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; left: 10px; right: 10px\">hi</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should respect position: fixed', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position:fixed; bottom: 0; left: 0;\">StickySticky</div>';\n      var el = document.getElementById('target');\n\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should properly calculate offsets according the offsetParent', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: 400px; left: 400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: -400px; left: -400px\">Hi</div>' +\n        '</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true if moved offscreen with left', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; left: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true if moved offscreen with top', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; top: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true if moved offscreen with right', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; right: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true if moved offscreen with bottom', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"position: absolute; bottom: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true if text is moved offscreen with text-indent', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"text-indent: -9999px\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return false on detached elements', function () {\n      var el = document.createElement('div');\n      el.innerHTML = 'I am not visible because I am detached!';\n\n      assert.isFalse(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return true on a document', function () {\n      assert.isTrue(axe.commons.dom.isVisible(document, true));\n    });\n\n    it('should return true if positioned staticly but top/left is set', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"top: -9999px; left: -9999px;' +\n        'right: -9999px; bottom: -9999px;\">Hi</div>';\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return false if `aria-hidden` is set', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" aria-hidden=\"true\">Hidden from screen readers</div>';\n\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should return false if `aria-hidden` is set on parent', function () {\n      fixture.innerHTML =\n        '<div aria-hidden=\"true\"><div id=\"target\">Hidden from screen readers</div></div>';\n\n      var el = document.getElementById('target');\n      assert.isFalse(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should not calculate position on parents', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: -400px; left: -400px;\">' +\n        '<div id=\"target\" style=\"position: absolute; top: 500px; left: 500px\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should know how `visibility` works', function () {\n      fixture.innerHTML =\n        '<div style=\"visibility: hidden;\">' +\n        '<div id=\"target\" style=\"visibility: visible;\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect clip rect hidden text technique', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect even when clip is not applied because of positioning', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'position: relative;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect clip rect hidden text technique on parent', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML =\n        '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect even when clip is not applied because of positioning on parent', function () {\n      var el,\n        clip =\n          'clip: rect(1px 1px 1px 1px);' +\n          'clip: rect(1px, 1px, 1px, 1px);' +\n          'position: relative;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML =\n        '<div style=\"' + clip + '\">' + '<div id=\"target\">Hi</div>' + '</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect poorly hidden clip rects', function () {\n      var el,\n        clip =\n          'clip: rect(5px 1px 1px 5px);' +\n          'clip: rect(5px, 1px, 1px, 5px);' +\n          'width: 1px; height: 1px;' +\n          'position: absolute;' +\n          'overflow: hidden;';\n\n      fixture.innerHTML = '<div id=\"target\" style=\"' + clip + '\">Hi</div>';\n\n      el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect clip-path hidden text technique', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"clip-path: inset(50%);\">Hi</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n\n    it('should detect clip-path hidden text technique on parent', function () {\n      fixture.innerHTML =\n        '<div style=\"clip-path: circle(0%);\">' +\n        '<div id=\"target\">Hi</div>' +\n        '</div>';\n\n      var el = document.getElementById('target');\n      assert.isTrue(axe.commons.dom.isVisible(el, true));\n    });\n  });\n\n  describe('SerialVirtualNode', function () {\n    it('should return true on statically-positioned, visible elements', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div'\n      });\n      assert.isTrue(axe.commons.dom.isVisible(vNode));\n    });\n\n    it('should return false on STYLE tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'style'\n      });\n      var actual = axe.commons.dom.isVisible(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on NOSCRIPT tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'noscript'\n      });\n      var actual = axe.commons.dom.isVisible(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should return false on TEMPLATE tag', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'template'\n      });\n      var actual = axe.commons.dom.isVisible(vNode);\n      assert.isFalse(actual);\n    });\n\n    it('should not be affected by `aria-hidden`', function () {\n      var vNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          'aria-hidden': true\n        }\n      });\n      assert.isTrue(axe.commons.dom.isVisible(vNode));\n    });\n\n    describe('screen readers', function () {\n      it('should return false if `aria-hidden` is set', function () {\n        var vNode = new axe.SerialVirtualNode({\n          nodeName: 'div',\n          attributes: {\n            'aria-hidden': true\n          }\n        });\n        assert.isFalse(axe.commons.dom.isVisible(vNode, true));\n      });\n\n      it('should return false if `aria-hidden` is set on parent', function () {\n        var vNode = new axe.SerialVirtualNode({\n          nodeName: 'div'\n        });\n        var parentVNode = new axe.SerialVirtualNode({\n          nodeName: 'div',\n          attributes: {\n            'aria-hidden': true\n          }\n        });\n        parentVNode.children = [vNode];\n        vNode.parent = parentVNode;\n        assert.isFalse(axe.commons.dom.isVisible(vNode, true));\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/is-visual-content.js",
    "content": "describe('dom.isVisualContent', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var isVisualContent = axe.commons.dom.isVisualContent;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  describe('isVisualContent', function () {\n    it('should return true for img', function () {\n      var virtualNode = queryFixture('<img src=\"\" id=\"target\">');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for iframe', function () {\n      var virtualNode = queryFixture('<iframe src=\"\" id=\"target\"></iframe>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for object', function () {\n      var virtualNode = queryFixture('<object data=\"\" id=\"target\"></object>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for video', function () {\n      var virtualNode = queryFixture('<video src=\"\" id=\"target\"></video>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for audio', function () {\n      var virtualNode = queryFixture('<audio src=\"\" id=\"target\"></audio>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for canvas', function () {\n      var virtualNode = queryFixture('<canvas id=\"target\"></canvas>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for svg', function () {\n      var virtualNode = queryFixture('<svg id=\"target\"></svg>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for math', function () {\n      var virtualNode = queryFixture('<math id=\"target\"></math>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for button', function () {\n      var virtualNode = queryFixture('<button id=\"target\"></button>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for select', function () {\n      var virtualNode = queryFixture('<select id=\"target\"></select>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for textarea', function () {\n      var virtualNode = queryFixture('<textarea id=\"target\"></textarea>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for keygen', function () {\n      var virtualNode = queryFixture('<keygen id=\"target\"></keygen');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for progress', function () {\n      var virtualNode = queryFixture('<progress id=\"target\"></progress>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for meter', function () {\n      var virtualNode = queryFixture('<meter id=\"target\"></meter>');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for non-hidden input', function () {\n      var virtualNode = queryFixture('<input type=\"text\" id=\"target\">');\n      assert.isTrue(isVisualContent(virtualNode));\n    });\n\n    it('should return true for elements with a visual aria role', function () {\n      var virtualNode = queryFixture(\n        '<div id=\"target\">' +\n          '<span role=\"img\"></span>' +\n          '<span role=\"checkbox\"></span>' +\n          '<span role=\"radio\"></span>' +\n          '<span role=\"meter\"></span>' +\n          '<span role=\"progressbar\"></span>' +\n          '<span role=\"scrollbar\"></span>' +\n          '<span role=\"slider\"></span>' +\n          '<span role=\"spinbutton\"></span>' +\n          '<span role=\"textbox\"></span>' +\n          '</div>'\n      );\n\n      for (var i = 0; i < virtualNode.children.length; i++) {\n        assert.isTrue(\n          isVisualContent(virtualNode.children[i]),\n          'for role ' + virtualNode.children[i].attr('role')\n        );\n      }\n    });\n\n    it('should return false for hidden input', function () {\n      var virtualNode = queryFixture('<input type=\"hidden\" id=\"target\">');\n      assert.isFalse(isVisualContent(virtualNode));\n    });\n\n    it('should return false for p', function () {\n      var virtualNode = queryFixture('<p id=\"target\">Paragraph!</p>');\n      assert.isFalse(isVisualContent(virtualNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/shadow-elements-from-point.js",
    "content": "describe('dom.shadowElementsFromPoint', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return an array from inside and outside of shadow dom',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:#000;position:relative;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow1 = container.attachShadow({ mode: 'open' });\n      shadow1.innerHTML =\n        '<p style=\"background-color:red;\">Text</p>' +\n        '<div id=\"shadowHost2\" style=\"position:absolute;\"></div>';\n      var paragraph = shadow1.querySelector('p');\n      var container2 = shadow1.querySelector('#shadowHost2');\n      var shadow2 = container2.attachShadow({ mode: 'open' });\n      shadow2.innerHTML = '<span>Text</span>';\n      var shadowSpan = shadow2.querySelector('span');\n      axe.testUtils.flatTreeSetup(fixture);\n\n      container.scrollIntoView();\n\n      var spanCoords = shadowSpan.getBoundingClientRect();\n      var result = axe.commons.dom.shadowElementsFromPoint(\n        spanCoords.x,\n        spanCoords.y\n      );\n      var pCoords = paragraph.getBoundingClientRect();\n      var result2 = axe.commons.dom.shadowElementsFromPoint(\n        pCoords.x,\n        pCoords.y\n      );\n\n      assert.includeMembers(result, [shadowSpan, container2]);\n      assert.notInclude(result, paragraph);\n      assert.includeMembers(result2, [paragraph, container]);\n      assert.notInclude(result2, shadowSpan);\n    }\n  );\n\n  it('does not throw when elementsFromPoints returns null', function () {\n    var mockDocument = {\n      elementsFromPoint: function () {\n        return null;\n      }\n    };\n    var out;\n    assert.doesNotThrow(function () {\n      out = axe.commons.dom.shadowElementsFromPoint(10, 10, mockDocument);\n    });\n    assert.deepEqual(out, []);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/url-props-from-attribute.js",
    "content": "describe('dom.urlPropsFromAttribute', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns undefined when given node does not have specified attribute', function () {\n    var vNode = queryFixture(\n      '<button id=\"target\" role=\"link\">Schedule appointment</button>'\n    );\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when `A` has no `HREF` attribute', function () {\n    var vNode = queryFixture('<a id=\"target\">Follow us on Instagram</a>');\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.isUndefined(actual);\n  });\n\n  it('returns URL properties when `A` with `HREF` (has port)', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://localhost:9876/test/playground.html\">Follow us on Instagram</a>'\n    );\n    var expected = {\n      filename: 'playground.html',\n      hash: '',\n      hostname: 'localhost',\n      pathname: '/test/',\n      port: '9876',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties when `A` with empty `HREF`', function () {\n    var vNode = queryFixture('<a id=\"target\" href=\"\">See commons tests</a>');\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.equal(actual.protocol, location.protocol);\n    assert.equal(actual.hostname, location.hostname);\n    assert.equal(actual.port, location.port);\n  });\n\n  it('returns URL properties for `A` with `HREF` (having HTTPS protocol)', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"https://facebook.com\">follow us on Facebook</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'facebook.com',\n      pathname: '/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` (having FTP protocol)', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"ftp://mywebsite.org\">Navigate to My Website</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'mywebsite.org',\n      pathname: '/',\n      port: '',\n      protocol: 'ftp:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has subdirectory and inline link', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/directory/#anchor\">Go to Issues</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/directory/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has subdirectory and hashbang', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/directory/#!foo\">See our services</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '#!foo',\n      hostname: 'mysite.com',\n      pathname: '/directory/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has search query', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/search/?q=foo#bar\">Get list of foo bars</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/search/',\n      port: '',\n      protocol: 'http:',\n      search: {\n        q: 'foo'\n      }\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has multiple search query parameters', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/search/?a=123&z=XYZ&name=Axe&branch=&version=1.2.3&values=[1,2,3]\">Get list of foo bars</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/search/',\n      port: '',\n      protocol: 'http:',\n      search: {\n        a: '123',\n        z: 'XYZ',\n        name: 'Axe',\n        branch: '',\n        values: '[1,2,3]',\n        version: '1.2.3'\n      }\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has filename', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/directory/widgets/calendar.html\">Book tour</a>'\n    );\n    var expected = {\n      filename: 'calendar.html',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/directory/widgets/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` which has filename as `index` (ignores index.*)', function () {\n    var vNode = queryFixture(\n      '<a id=\"target\" href=\"http://mysite.com/directory/index.html\">Book tour</a>'\n    );\n    var expected = {\n      filename: '',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/directory/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns URL properties for `A` with `HREF` that is contained in SVG document', function () {\n    var vNode = queryFixture(\n      '<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">' +\n        '<a id=\"target\" href=\"http://mysite.com/directory/widgets/calendar.html\" aria-label=\"Book tour\"><circle cx=\"50\" cy=\"40\" r=\"35\" /></a>' +\n        '</svg>'\n    );\n    var expected = {\n      filename: 'calendar.html',\n      hash: '',\n      hostname: 'mysite.com',\n      pathname: '/directory/widgets/',\n      port: '',\n      protocol: 'http:',\n      search: {}\n    };\n    var actual = axe.commons.dom.urlPropsFromAttribute(\n      vNode.actualNode,\n      'href'\n    );\n    assert.deepEqual(actual, expected);\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/visibility-methods.js",
    "content": "describe('dom.visibility-methods', () => {\n  const { queryFixture, shadowCheckSetup } = axe.testUtils;\n  const {\n    nativelyHidden,\n    displayHidden,\n    visibilityHidden,\n    contentVisibiltyHidden,\n    ariaHidden,\n    opacityHidden,\n    scrollHidden,\n    overflowHidden,\n    clipHidden,\n    areaHidden,\n    detailsHidden\n  } = axe._thisWillBeDeletedDoNotUse.commons.dom;\n  const contentVisibilitySupported = CSS.supports('content-visibility: hidden');\n\n  describe('nativelyHidden', () => {\n    const nativelyHiddenElements = ['style', 'script', 'noscript', 'template'];\n\n    it('should return true for hidden elements', () => {\n      nativelyHiddenElements.forEach(nodeName => {\n        const vNode = new axe.VirtualNode(document.createElement(nodeName));\n        assert.isTrue(nativelyHidden(vNode), (nodeName = ' is not hidden'));\n      });\n    });\n\n    it('should return false for visible elements', () => {\n      Object.keys(axe._audit.standards.htmlElms)\n        .filter(nodeName => {\n          return !nativelyHiddenElements.includes(nodeName);\n        })\n        .forEach(nodeName => {\n          const vNode = new axe.VirtualNode(document.createElement(nodeName));\n          assert.isFalse(nativelyHidden(vNode), nodeName + ' is not visible');\n        });\n    });\n  });\n\n  describe('displayHidden', () => {\n    it('should return true for element with \"display:none`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"display: none;\"></div>'\n      );\n      assert.isTrue(displayHidden(vNode));\n    });\n\n    it('should return false for element with \"display:block`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"display: block;\"></div>'\n      );\n      assert.isFalse(displayHidden(vNode));\n    });\n\n    it('should return false for element without display', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      assert.isFalse(displayHidden(vNode));\n    });\n\n    it('should return false for area element', () => {\n      const vNode = queryFixture(\n        '<map name=\"test\">' +\n          '<area id=\"target\" href=\"#\"/>' +\n          '</map>' +\n          '<img usemap=\"#test\" src=\"img.png\"/>'\n      );\n      assert.isFalse(displayHidden(vNode));\n    });\n  });\n\n  describe('visibilityHidden', () => {\n    it('should return true for element with \"visibility:hidden`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"visibility: hidden;\"></div>'\n      );\n      assert.isTrue(visibilityHidden(vNode));\n    });\n\n    it('should return true for element with \"visibility:collapse`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"visibility: collapse;\"></div>'\n      );\n      assert.isTrue(visibilityHidden(vNode));\n    });\n\n    it('should return false for element with \"visibility:visible`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"visibility: visible;\"></div>'\n      );\n      assert.isFalse(visibilityHidden(vNode));\n    });\n\n    it('should return false for element without visibility', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      assert.isFalse(visibilityHidden(vNode));\n    });\n\n    it('should return false for if passed \"isAncestor:true`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"visibility: hidden;\"></div>'\n      );\n      assert.isFalse(visibilityHidden(vNode, { isAncestor: true }));\n    });\n  });\n\n  (contentVisibilitySupported ? describe : describe.skip)(\n    'contentVisibiltyHidden',\n    () => {\n      it('should return true for element with \"content-visibility:hidden` ancestor', () => {\n        const vNode = queryFixture(\n          '<div id=\"target\" style=\"content-visibility: hidden;\"></div>'\n        );\n        assert.isTrue(contentVisibiltyHidden(vNode, { isAncestor: true }));\n      });\n\n      it('should return false for element with \"content-visibility: visible` ancestor', () => {\n        const vNode = queryFixture(\n          '<div id=\"target\" style=\"content-visibility: visible;\"></div>'\n        );\n        assert.isFalse(contentVisibiltyHidden(vNode, { isAncestor: true }));\n      });\n\n      it('should return false for element without content-visibility', () => {\n        const vNode = queryFixture('<div id=\"target\"></div>');\n        assert.isFalse(contentVisibiltyHidden(vNode));\n      });\n\n      it('should return false for element with \"content-visibility: hidden\"', () => {\n        const vNode = queryFixture(\n          '<div id=\"target\" style=\"content-visibility: hidden;\"></div>'\n        );\n        assert.isFalse(contentVisibiltyHidden(vNode, { isAncestor: false }));\n      });\n    }\n  );\n\n  describe('ariaHidden', () => {\n    it('should return true for element with \"aria-hidden=true`', () => {\n      const vNode = queryFixture('<div id=\"target\" aria-hidden=\"true\"></div>');\n      assert.isTrue(ariaHidden(vNode));\n    });\n\n    it('should return false for element with \"aria-hidden=false`', () => {\n      const vNode = queryFixture('<div id=\"target\" aria-hidden=\"false\"></div>');\n      assert.isFalse(ariaHidden(vNode));\n    });\n\n    it('should return false for element without aria-hidden', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      assert.isFalse(ariaHidden(vNode));\n    });\n  });\n\n  describe('opacityHidden', () => {\n    it('should return true for element with \"opacity:0`', () => {\n      const vNode = queryFixture('<div id=\"target\" style=\"opacity: 0;\"></div>');\n      assert.isTrue(opacityHidden(vNode));\n    });\n\n    it('should return false for element with \"opacity:0.1`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"opacity: 0.1;\"></div>'\n      );\n      assert.isFalse(opacityHidden(vNode));\n    });\n\n    it('should return false for element without opacity', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      assert.isFalse(opacityHidden(vNode));\n    });\n  });\n\n  describe('scrollHidden', () => {\n    it('should return true for element with scroll and \"width:0`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow-x: scroll; width: 0\">scroll hidden</div>'\n      );\n      assert.isTrue(scrollHidden(vNode));\n    });\n\n    it('should return true for element with scroll and \"height:0`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow-y: scroll; height: 0\">scroll hidden</div>'\n      );\n      assert.isTrue(scrollHidden(vNode));\n    });\n\n    it('should return false for element with scroll and width > 0', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow-x: scroll;\">scroll hidden</div>'\n      );\n      assert.isFalse(scrollHidden(vNode));\n    });\n\n    it('should return false for element with scroll and height > 0', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow-y: scroll;\">scroll hidden</div>'\n      );\n      assert.isFalse(scrollHidden(vNode));\n    });\n\n    it('should return false for element without scroll', () => {\n      const vNode = queryFixture('<div id=\"target\">scroll hidden</div>');\n      assert.isFalse(scrollHidden(vNode));\n    });\n  });\n\n  describe('overflowHidden', function () {\n    it('should return true for element with \"overflow:hidden\" and small width', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow: hidden; width: 1px;\">Hello world</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for element with \"overflow:hidden\" and small height', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow: hidden; height: 1px;\">Hello world</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for parent with \"overflow: hidden\" and element outside parent rect', function () {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div id=\"target\" style=\"margin-left: 100px;\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for ancestor with \"overflow: hidden\" and element outside ancestor rect', function () {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div>' +\n          '<div>' +\n          '<div id=\"target\" style=\"margin-left: 100px;\">Hello world</div>' +\n          '</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for multiple ancestors with \"overflow: hidden\" and element outside one ancestor rect', function () {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div>' +\n          '<div style=\"overflow: hidden; width: 500px\">' +\n          '<div id=\"target\" style=\"margin-left: 100px;\">Hello world</div>' +\n          '</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for element barely inside \"overflow: hidden\" parent using floating point', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50.5px;\">' +\n          '<div id=\"target\" style=\"margin-left: 50px;\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return false for element with \"overflow:hidden\" and width larger than 1px', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow: hidden; width: 2px;\">Hello world</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for element with \"overflow:hidden\" and height larger than 1px', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow: hidden; height: 2px;\">Hello world</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for element with just \"overflow:hidden\"', function () {\n      var vNode = queryFixture(\n        '<div id=\"target\" style=\"overflow: hidden;\">Hello world</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for element without \"overflow:hidden\"', function () {\n      var vNode = queryFixture('<div id=\"target\">Hello world</div>');\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for multiple ancestors with \"overflow: hidden\" and element is inside all ancestor rects', function () {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 101px;\">' +\n          '<div>' +\n          '<div style=\"overflow: hidden; width: 500px\">' +\n          '<div id=\"target\" style=\"margin-left: 100px;\">Hello world</div>' +\n          '</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden\" and element with \"position:fixed\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: fixed\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden; position:relative\" and element with \"position:fixed\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px; position: relative\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: fixed\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden\" and element with \"position:absolute\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return true for ancestor with \"overflow:hidden; position:relative\" and element with \"position:absolute\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px; position: relative\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for ancestor with \"overflow:hidden\" and element with \"position:absolute\" if ancestor in-between uses \"position:relative\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div style=\"position: relative\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return true for ancestor with \"overflow:hidden\" and element with \"position:absolute\" if ancestor in-between uses \"position:sticky\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div style=\"position: sticky\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isTrue(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden\" and element with \"position:absolute\" if ancestor in-between uses \"position:absolute\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div style=\"position: absolute\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden\" and element with \"position:absolute\" if ancestor in-between uses \"position:fixed\"', () => {\n      var vNode = queryFixture(\n        '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div style=\"position: fixed\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n\n    it('should return false for ancestor with \"overflow:hidden\" and element with \"position:absolute\" if ancestor of overflow node uses position other than static', () => {\n      var vNode = queryFixture(\n        '<div style=\"position: relative\">' +\n          '<div style=\"overflow: hidden; width: 50px;\">' +\n          '<div id=\"target\" style=\"margin-left: 100px; position: absolute\">Hello world</div>' +\n          '</div>' +\n          '</div>'\n      );\n      assert.isFalse(overflowHidden(vNode));\n    });\n  });\n\n  describe('clipHidden', () => {\n    it('should return true for element with clip-path and inset >= 50%', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip-path: inset(50%);\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return false for element with clip-path and inset < 50%', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip-path: inset(49%);\">Hello world</div>'\n      );\n      assert.isFalse(clipHidden(vNode));\n    });\n\n    it('should return true for element with clip-path and circle=0', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip-path: circle(0);\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return false for element with clip-path and circle > 0', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip-path: circle(1%);\">Hello world</div>'\n      );\n      assert.isFalse(clipHidden(vNode));\n    });\n\n    it('should return true for element with clip and \"position:absolute`', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(1px 1px 1px 1px); position: absolute\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return true for element with clip and \"position:fixed\"', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(1px 1px 1px 1px); position: fixed\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return true for element with clip using commas', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(1px, 1px, 1px, 1px); position: absolute\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return true for poorly hidden clip rects', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(5px 1px 1px 5px); position: absolute\">Hello world</div>'\n      );\n      assert.isTrue(clipHidden(vNode));\n    });\n\n    it('should return false for element with clip and \"position:relative\"', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(1px 1px 1px 1px); position: relative\">Hello world</div>'\n      );\n      assert.isFalse(clipHidden(vNode));\n    });\n\n    it('should return false for element with clip and without position', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(1px 1px 1px 1px);\">Hello world</div>'\n      );\n      assert.isFalse(clipHidden(vNode));\n    });\n\n    it('should return false for element without clip or clip-path', () => {\n      const vNode = queryFixture('<div id=\"target\">Hello world</div>');\n      assert.isFalse(clipHidden(vNode));\n    });\n\n    it('should return false for element with visible clip', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" style=\"clip: rect(5px 6px 7px 8px); position: relative\">Hello world</div>'\n      );\n      assert.isFalse(clipHidden(vNode));\n    });\n  });\n\n  describe('areaHidden', () => {\n    it('should return true for element without map parent', () => {\n      const vNode = queryFixture('<area id=\"target\" />');\n      assert.isTrue(areaHidden(vNode));\n    });\n\n    it('should return true for map without a name', () => {\n      const vNode = queryFixture('<map><area id=\"target\" /></map>');\n      assert.isTrue(areaHidden(vNode));\n    });\n\n    it('should return true if map is in shadowDOM', () => {\n      const vNode = shadowCheckSetup(\n        '<map name=\"foo\"><div id=\"shadow\"></div></map>',\n        '<area id=\"target\" />'\n      )[2];\n      assert.isTrue(areaHidden(vNode));\n    });\n\n    it('should return true if img does not use map', () => {\n      const vNode = queryFixture('<map name=\"foo\"><area id=\"target\" /></map>');\n      assert.isTrue(areaHidden(vNode));\n    });\n\n    it('should return true if img uses map but is hidden', () => {\n      const vNode = queryFixture(\n        '<map name=\"foo\"><area id=\"target\" /></map><img usemap=\"#foo\" />'\n      );\n      assert.isTrue(\n        areaHidden(vNode, () => {\n          return false;\n        })\n      );\n    });\n\n    it('should return false if img uses map and is visible', () => {\n      const vNode = queryFixture(\n        '<map name=\"foo\"><area id=\"target\" /></map><img usemap=\"#foo\" />'\n      );\n      assert.isFalse(\n        areaHidden(vNode, () => {\n          return true;\n        })\n      );\n    });\n  });\n\n  describe('detailsHidden', () => {\n    it('should return true for closed details parent', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary>Hello World</summary>\n          <p id=\"target\">Hidden</p>\n        </details>\n      `);\n\n      assert.isTrue(detailsHidden(vNode));\n    });\n\n    it('should return false for closed details', () => {\n      const vNode = queryFixture(`\n        <details id=\"target\">\n          <summary>Hello World</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n\n      assert.isFalse(detailsHidden(vNode, { isAncestor: false }));\n    });\n\n    it('should return false for summary in closed details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary id=\"target\">Hello World</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n\n      assert.isFalse(detailsHidden(vNode, { isAncestor: false }));\n    });\n\n    it('should return false for summary in open details', () => {\n      const vNode = queryFixture(`\n        <details open>\n          <summary id=\"target\">Hello World</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n\n      assert.isFalse(detailsHidden(vNode, { isAncestor: false }));\n    });\n\n    it('should return false for open details parent', () => {\n      const vNode = queryFixture(`\n        <details open>\n          <summary>Hello World</summary>\n          <p id=\"target\">Hidden</p>\n        </details>\n      `);\n\n      assert.isFalse(detailsHidden(vNode));\n    });\n\n    it('should return true for not first summary in closed details', () => {\n      const vNode = queryFixture(`\n        <details>\n          <summary>Hello World</summary>\n          <summary id=\"target\">Not a summary</summary>\n          <p>Hidden</p>\n        </details>\n      `);\n\n      assert.isTrue(detailsHidden(vNode));\n    });\n\n    it('should return false for element not in details', () => {\n      const vNode = queryFixture(`\n        <div>\n          <div id=\"target\">Hello World</div>\n        </div>\n      `);\n\n      assert.isFalse(detailsHidden(vNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/visually-contains.js",
    "content": "describe('dom.visuallyContains', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  it('should return true when element is trivially contained', function () {\n    var target = queryFixture(\n      '<div style=\"height: 40px; width: 30px; background-color: red;\">' +\n        '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: green;\">' +\n        '</div></div>'\n    );\n    assert.isTrue(\n      axe.commons.dom.visuallyContains(\n        target.actualNode,\n        target.parent.actualNode\n      )\n    );\n  });\n\n  it('should return false when overflow is hidden', function () {\n    var target = queryFixture(\n      '<div style=\"height: 20px; width: 30px; background-color: red; overflow: hidden;\">' +\n        '<div id=\"target\" style=\"height:20px; top: 25px; width: 45px; background-color: green; position:absolute;\">' +\n        '</div></div>'\n    );\n    var result = axe.commons.dom.visuallyContains(\n      target.actualNode,\n      target.parent.actualNode\n    );\n    assert.isFalse(result);\n  });\n\n  it('should return false when absolutely positioned content does not overlap', function () {\n    var target = queryFixture(\n      '<div style=\"height:20px; width:30px; background-color:red;\">' +\n        '<div id=\"target\" style=\"height:20px; top:25px; width:45px; background-color:green; position:absolute;\">Text' +\n        '</div></div>'\n    );\n    var result = axe.commons.dom.visuallyContains(\n      target.actualNode,\n      target.parent.actualNode\n    );\n    assert.isFalse(result);\n  });\n\n  it('should return false when element is outside of margin', function () {\n    var target = queryFixture(\n      '<div style=\"height: 40px; width: 30px; margin-left: 30px; background-color: red;\">' +\n        '<div id=\"target\" style=\"height: 20px; width: 45px; margin-left: -20px; background-color: green;\">' +\n        '</div></div>'\n    );\n    assert.isFalse(\n      axe.commons.dom.visuallyContains(\n        target.actualNode,\n        target.parent.actualNode\n      )\n    );\n  });\n\n  it('should return false when overflow is visible', function () {\n    var target = queryFixture(\n      '<div style=\"height: 40px; width: 30px; background-color: red; overflow: visible;\">' +\n        '<div id=\"target\" style=\"height: 20px; width: 45px; background-color: green;\">' +\n        '</div></div>'\n    );\n    assert.isFalse(\n      axe.commons.dom.visuallyContains(\n        target.actualNode,\n        target.parent.actualNode\n      )\n    );\n  });\n\n  it('should return true when element is scrollable', function () {\n    var target = queryFixture(\n      '<div style=\"height: 40px; width: 30px; background-color: red; overflow: scroll;\">' +\n        '<div id=\"target\" style=\"height: 20px; width: 45px; background-color: green;\">' +\n        '</div></div>'\n    );\n    assert.isTrue(\n      axe.commons.dom.visuallyContains(\n        target.actualNode,\n        target.parent.actualNode\n      )\n    );\n  });\n\n  it('should return true when element is inline', function () {\n    // result depends on the display property of the element\n    var target = queryFixture(\n      '<label>' + 'My label <input type=\"text\" id=\"target\">' + '</label>'\n    );\n    assert.isTrue(\n      axe.commons.dom.visuallyContains(\n        target.actualNode,\n        target.parent.actualNode\n      )\n    );\n  });\n\n  it('should return false when element is partially contained', function () {\n    var target = queryFixture(\n      '<div style=\"background-color:red; height:20px;\">' +\n        '<p id=\"target\" style=\"margin:0; position:absolute;\">Text<br>more text</p></div>'\n    );\n    var result = axe.commons.dom.visuallyContains(\n      target.actualNode,\n      target.parent.actualNode\n    );\n    assert.isFalse(result);\n  });\n\n  it('should return true when element is contained by scroll region', function () {\n    var target = queryFixture(\n      '<div id=\"parent\">' +\n        '<div style=\"overflow: scroll; height: 200px;\">' +\n        '<div style=\"margin-top: 400px; height: 10px;\"></div>' +\n        '<p id=\"target\">Text</p></div></div>'\n    );\n    var parent = fixture.querySelector('#parent');\n    var result = axe.commons.dom.visuallyContains(target.actualNode, parent);\n    assert.isTrue(result);\n  });\n\n  it('should return true for child with truncated text', function () {\n    var target = queryFixture(\n      '<p style=\"max-width: 200px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;\">' +\n        '<span id=\"target\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin quam. Fuscemi odio, egestas pulvinar erat eget, vehicula tempus est. Proin vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim nulla,sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna, mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh, quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim. Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor felis ante non libero.</span>' +\n        '</p>'\n    );\n    var result = axe.commons.dom.visuallyContains(\n      target.actualNode,\n      target.parent.actualNode\n    );\n    assert.isTrue(result);\n  });\n\n  it('should return false if element is outside overflow hidden', function () {\n    var target = queryFixture(\n      '<div id=\"parent\" style=\"width: 200px; height: 200px; overflow: hidden;\">' +\n        '<div id=\"target\" style=\"margin-top: 300px;\">Some text</div>' +\n        '</div>'\n    );\n\n    var parent = fixture.querySelector('#parent');\n    var result = axe.commons.dom.visuallyContains(target.actualNode, parent);\n    assert.isFalse(result);\n  });\n\n  it('should allow subpixel contains due to rounding', function () {\n    var target = queryFixture(\n      '<div id=\"parent\" style=\"width: 200px; height: 200px;\">' +\n        '<div id=\"target\" style=\"margin-left: -0.1px; margin-top: -0.9px; width: 200.5px; height: 200.9px\">Some text</div>' +\n        '</div>'\n    );\n\n    var parent = fixture.querySelector('#parent');\n    var result = axe.commons.dom.visuallyContains(target.actualNode, parent);\n    assert.isTrue(result);\n  });\n\n  (shadowSupported ? it : xit)(\n    'should return true when element is visually contained across shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div style=\"height:40px; background-color:red;\" id=\"container\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"target\" style=\"height: 20px; width: 45px; background-color: green;\"></div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#target');\n      var result = axe.commons.dom.visuallyContains(target, container);\n      assert.isTrue(result);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return true when element is contained by scroll region across shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"parent\">' +\n        '<div style=\"overflow: scroll; height: 200px;\">' +\n        '<div style=\"margin-top: 400px; height: 10px;\"></div>' +\n        '<div id=\"container\"></div></div></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<p id=\"target\">Text</p>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#target');\n      var parent = fixture.querySelector('#parent');\n      var result = axe.commons.dom.visuallyContains(target, parent);\n      assert.isTrue(result);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false when element is not visually contained across shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"height:20px;background-color:red;overflow:hidden;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"target\" style=\"top:20px;height:20px;background-color:green;position:absolute;\"><div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#target');\n      var result = axe.commons.dom.visuallyContains(target, container);\n      assert.isFalse(result);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false when element is absolutely positioned off of background across shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black; height:20px;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color:#333; height:20px; position:absolute; top:20px;\">Text</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#shadowTarget');\n      var result = axe.commons.dom.visuallyContains(target, container);\n      assert.isFalse(result);\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should return false when element is partially positioned off of background across shadow boundary',\n    function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"background-color:black; height:20px; position:relative;\"></div>';\n      var container = fixture.querySelector('#container');\n      var shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color:#333; height:20px; position:absolute; top:10px;\">Text</div>';\n      axe.testUtils.flatTreeSetup(fixture);\n      var target = shadow.querySelector('#shadowTarget');\n      var result = axe.commons.dom.visuallyContains(target, container);\n      assert.isFalse(result);\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/dom/visually-overlaps.js",
    "content": "describe('dom.visuallyOverlaps', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  it('should return true when element is trivially contained', function () {\n    fixture.innerHTML =\n      '<div style=\"height: 40px; width: 30px; background-color: red;\">' +\n      '<div id=\"target\" style=\"height: 20px; width: 15px; background-color: green;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var targetRect = target.getBoundingClientRect();\n    assert.isTrue(\n      axe.commons.dom.visuallyOverlaps(targetRect, target.parentNode)\n    );\n  });\n\n  it('should return false when rect has no overlap', function () {\n    fixture.innerHTML =\n      '<div style=\"position: absolute; top: 0px; left: 0px; height: 40px;' +\n      ' width: 30px; background-color: red;\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 50px; left: 0px; height: 20px;' +\n      ' width: 45px; background-color: green;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var targetRect = target.getBoundingClientRect();\n\n    assert.isFalse(\n      axe.commons.dom.visuallyOverlaps(targetRect, target.parentNode)\n    );\n  });\n\n  it('should return true when rect has overlap', function () {\n    fixture.innerHTML =\n      '<div style=\"position: absolute; top: 0px; left: 0px;; height: 40px; width: 30px;' +\n      ' background-color: red;\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 0px; left: 0px; height: 20px;' +\n      ' width: 45px; background-color: green;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var targetRect = target.getBoundingClientRect();\n    assert.isTrue(\n      axe.commons.dom.visuallyOverlaps(targetRect, target.parentNode)\n    );\n  });\n\n  it('should return true when container is scrollable and rect is in the scroll area', function () {\n    fixture.innerHTML =\n      '<div style=\"position: relative; height: 40px; width: 30px; overflow: scroll;\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 60px; height: 20px; width: 45px;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var targetRect = target.getBoundingClientRect();\n    assert.isTrue(\n      axe.commons.dom.visuallyOverlaps(targetRect, target.parentNode)\n    );\n  });\n\n  it('should return false when container has overflow hidden and rect is in the scroll area', function () {\n    fixture.innerHTML =\n      '<div style=\"position: relative; height: 40px; width: 30px; overflow: hidden;\">' +\n      '<div id=\"target\" style=\"position: absolute; top: 60px; height: 20px; width: 45px;\">' +\n      '</div></div>';\n    var target = fixture.querySelector('#target');\n    var targetRect = target.getBoundingClientRect();\n    assert.isFalse(\n      axe.commons.dom.visuallyOverlaps(targetRect, target.parentNode)\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/dom/visually-sort.js",
    "content": "// This method is mostly tested through color-contrast integrations\ndescribe('visually-sort', () => {\n  'use strict';\n\n  const fixture = document.querySelector('#fixture');\n  const visuallySort = axe.commons.dom.visuallySort;\n  const querySelectorAll = axe.utils.querySelectorAll;\n  let root;\n\n  beforeEach(() => {\n    fixture.innerHTML = `\n      <header id=\"1\" style=\"position: absolute; z-index: 999; height: 50px; width: 100%;\">\n        <div id=\"2\" style=\"display: flex; position: relative; height: 50px;\">\n          <div id=\"3\" style=\"position: relative; height: 50px; width: 100%;\"></div>\n        </div>\n      </header>\n      <div id=\"4\" style=\"position: absolute; z-index: 10; height: 50px; width: 100%;\">\n        <div id=\"5\" style=\"position: absolute; transform: translate(0, -50%);\">\n          <div id=\"6\">\n            <div id=\"7\" style=\"float: left\">Text</div>\n            <div id=\"8\">Text</div>\n            <span id=\"9\">Text</span>\n            <div id=\"10\">Text</div>\n            <div id=\"shadow-host\"></div>\n          </div>\n        </div>\n      </div>\n    `;\n    const shadowRoot = fixture\n      .querySelector('#shadow-host')\n      .attachShadow({ mode: 'open' });\n    shadowRoot.innerHTML = '<div id=\"shadow-child\">Text</div>';\n    root = axe.setup(fixture);\n  });\n\n  /*\n    Array.sort() return meanings:\n\n    compareFn(a, b) | return value sort order\n    ----------------|-----------------------\n    > 0             | sort a after b, e.g. [b, a]\n    < 0             | sort a before b, e.g. [a, b]\n    === 0           | keep original order of a and b\n  */\n\n  it('sorts a higher stack before a lower stack', () => {\n    const vNodeA = querySelectorAll(root, '#1')[0];\n    const vNodeB = querySelectorAll(root, '#4')[0];\n\n    assert.isBelow(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts a lower stack after a higher stack', () => {\n    const vNodeA = querySelectorAll(root, '#4')[0];\n    const vNodeB = querySelectorAll(root, '#1')[0];\n\n    assert.isAbove(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts a child stack before a parent stack', () => {\n    const vNodeA = querySelectorAll(root, '#6')[0];\n    const vNodeB = querySelectorAll(root, '#4')[0];\n\n    assert.isBelow(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts a parent stack after a child stack', () => {\n    const vNodeA = querySelectorAll(root, '#4')[0];\n    const vNodeB = querySelectorAll(root, '#6')[0];\n\n    assert.isAbove(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts a child of a higher stack before a child of a lower stack', () => {\n    const vNodeA = querySelectorAll(root, '#3')[0];\n    const vNodeB = querySelectorAll(root, '#7')[0];\n\n    assert.isBelow(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts a child of a lower stack after a child of a higher stack', () => {\n    const vNodeA = querySelectorAll(root, '#7')[0];\n    const vNodeB = querySelectorAll(root, '#3')[0];\n\n    assert.isAbove(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts elements by tree order when in the same stack', () => {\n    const vNodeA = querySelectorAll(root, '#8')[0];\n    const vNodeB = querySelectorAll(root, '#10')[0];\n\n    assert.isAbove(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts floated elements before other elements of the same stack', () => {\n    const vNodeA = querySelectorAll(root, '#7')[0];\n    const vNodeB = querySelectorAll(root, '#8')[0];\n\n    assert.isBelow(visuallySort(vNodeA, vNodeB), 0);\n  });\n\n  it('sorts shadow DOM elements by tree order when in the same stack', () => {\n    const vNodeA = querySelectorAll(root, '#8')[0];\n    const vNodeB = querySelectorAll(root, '#shadow-host')[0].children[0];\n\n    assert.isAbove(visuallySort(vNodeA, vNodeB), 0);\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-aria-combobox.js",
    "content": "describe('forms.isAriaCombobox', function () {\n  'use strict';\n  var isAriaCombobox = axe.commons.forms.isAriaCombobox;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns true for an element with role=combobox', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'combobox');\n    flatTreeSetup(node);\n    assert.isTrue(isAriaCombobox(node));\n  });\n\n  it('returns false for elements without role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaCombobox(node));\n  });\n\n  it('returns false for elements with incorrect role', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'main');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaCombobox(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-aria-listbox.js",
    "content": "describe('forms.isAriaListbox', function () {\n  'use strict';\n  var isAriaListbox = axe.commons.forms.isAriaListbox;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns true for an element with role=listbox', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'listbox');\n    flatTreeSetup(node);\n    assert.isTrue(isAriaListbox(node));\n  });\n\n  it('returns false for elements without role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaListbox(node));\n  });\n\n  it('returns false for elements with incorrect role', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'main');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaListbox(node));\n  });\n\n  it('returns false for native select', function () {\n    var node = document.createElement('select');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaListbox(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-aria-range.js",
    "content": "describe('forms.isAriaRange', function () {\n  'use strict';\n  var isAriaRange = axe.commons.forms.isAriaRange;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns true for an element with range roles', function () {\n    var rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton'];\n    rangeRoles.forEach(function (role) {\n      var node = document.createElement('div');\n      node.setAttribute('role', role);\n      node.setAttribute('aria-valuenow', '0');\n      flatTreeSetup(node);\n      assert.isTrue(\n        isAriaRange(node),\n        'role=\"' + role + '\" is not an aria range role'\n      );\n    });\n  });\n\n  it('returns false for elements without role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaRange(node));\n  });\n\n  it('returns false for elements with incorrect role', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'main');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaRange(node));\n  });\n\n  it('returns false for native range elements', function () {\n    var nativeRangeElements = [\n      {\n        nodeName: 'progress'\n      },\n      {\n        nodeName: 'input',\n        type: 'range'\n      },\n      {\n        nodeName: 'input',\n        type: 'number'\n      }\n    ];\n    nativeRangeElements.forEach(function (elm) {\n      var node = document.createElement(elm.nodeName);\n      if (elm.type) {\n        node.setAttribute('type', elm.type);\n      }\n      flatTreeSetup(node);\n      assert.isFalse(\n        isAriaRange(node),\n        node.outterHTML + ' is not an aria range element'\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-aria-textbox.js",
    "content": "describe('forms.isAriaTextbox', function () {\n  'use strict';\n  var isAriaTextbox = axe.commons.forms.isAriaTextbox;\n  var flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('returns true for an element with role=textbox', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'textbox');\n    flatTreeSetup(node);\n    assert.isTrue(isAriaTextbox(node));\n  });\n\n  it('returns false for elements without role', function () {\n    var node = document.createElement('div');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaTextbox(node));\n  });\n\n  it('returns false for elements with incorrect role', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'main');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaTextbox(node));\n  });\n\n  it('returns false for native textbox inputs', function () {\n    var node = document.createElement('input');\n    node.setAttribute('type', 'text');\n    flatTreeSetup(node);\n    assert.isFalse(isAriaTextbox(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-disabled.js",
    "content": "describe('forms.isDisabled', function () {\n  'use strict';\n  var isDisabled = axe.commons.forms.isDisabled;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var fixture = document.getElementById('fixture');\n  var shadowSupport = axe.testUtils.shadowSupport;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  describe('with disabled attr', function () {\n    it('returns false when not set', function () {\n      fixtureSetup('<input type=\"text\" />');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n\n      assert.isFalse(isDisabled(node));\n    });\n\n    it('returns true for an element that can be disabled', function () {\n      fixtureSetup('<input type=\"text\" disabled />');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    it('returns false for an element that can not be disabled', function () {\n      fixtureSetup('<span disabled>Hello</span>');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isFalse(isDisabled(node));\n    });\n\n    it('returns true when disabled has a value', function () {\n      fixtureSetup('<input type=\"text\" disabled=\"yes\" />');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    it('returns true when in a disabled fieldset', function () {\n      fixtureSetup(\n        '<fieldset disabled>' + '<span>Hello world</span>' + '</fieldset>'\n      );\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    it('returns true when in a disabled button', function () {\n      fixtureSetup(\n        '<button disabled>' + '<span>Hello world</span>' + '</button>'\n      );\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    (shadowSupport.v1 ? it : xit)(\n      'returns true for an ancestor in the flat tree that can be disabled',\n      function () {\n        fixture.innerHTML = '<fieldset disabled><section></section</fieldset>';\n        var shadowRoot = fixture\n          .querySelector('section')\n          .attachShadow({ mode: 'open' });\n        shadowRoot.innerHTML = '<input type=\"text\" />';\n        axe._tree = axe.utils.getFlattenedTree(fixture);\n\n        var node = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n        assert.isTrue(isDisabled(node));\n      }\n    );\n  });\n\n  describe('with aria-disabled attr', function () {\n    it('returns true for an element with aria-disabled=true', function () {\n      fixtureSetup('<span aria-disabled=\"true\">hello</span>');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    it('returns false for an element when aria-disabled is not true', function () {\n      fixtureSetup('<span aria-disabled=\"not true\">hello</span>');\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isFalse(isDisabled(node));\n    });\n\n    it('returns true if the closest ancestor with aria-disabled is set to true', function () {\n      fixtureSetup(\n        '<section aria-disabled=\"false\">' +\n          '<div aria-disabled=\"true\"><span>hello</span></div>' +\n          '</section>'\n      );\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n\n    it('returns false if the closest ancestor with aria-disabled is set to false', function () {\n      fixtureSetup(\n        '<section aria-disabled=\"true\">' +\n          '<div aria-disabled=\"false\"><span>hello</span></div>' +\n          '</section>'\n      );\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isFalse(isDisabled(node));\n    });\n  });\n\n  describe('with both disabled and aria-disabled', function () {\n    it('returns true when aria-disabled=false and disabled=disabled', function () {\n      fixtureSetup(\n        '<fieldset aria-disabled=\"false\" disabled>' +\n          '<span>hello</span>' +\n          '</fieldset>'\n      );\n      var node = axe.utils.querySelectorAll(axe._tree[0], 'span')[0];\n\n      assert.isTrue(isDisabled(node));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-native-select.js",
    "content": "describe('forms.isNativeSelect', function () {\n  'use strict';\n  var isNativeSelect = axe.commons.forms.isNativeSelect;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  it('returns true for a select element', function () {\n    var node = queryFixture('<select id=\"target\"></select>');\n    assert.isTrue(isNativeSelect(node));\n  });\n\n  it('returns false for non-select elements', function () {\n    var nonSelectElements = ['a', 'h1', 'div', 'span', 'main'];\n    nonSelectElements.forEach(function (nodeName) {\n      var node = queryFixture(\n        '<' + nodeName + ' id=\"target\"></' + nodeName + '>'\n      );\n      assert.isFalse(\n        isNativeSelect(node),\n        '<' + nodeName + '> is not a native select element'\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/forms/is-native-textbox.js",
    "content": "describe('forms.isNativeTextbox', function () {\n  'use strict';\n  var isNativeTextbox = axe.commons.forms.isNativeTextbox;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  it('returns true for a text inputs', function () {\n    var textInputs = [\n      'date',\n      'datetime',\n      'datetime-local',\n      'email',\n      'month',\n      'number',\n      'range',\n      'search',\n      'tel',\n      'text',\n      'time',\n      'url',\n      'week'\n    ];\n    textInputs.forEach(function (type) {\n      var node = queryFixture('<input id=\"target\" type=\"' + type + '\"/>');\n      assert.isTrue(\n        isNativeTextbox(node),\n        '<input type=\"' + type + '\"> is a native text input'\n      );\n    });\n  });\n\n  it('returns true for a textarea element', function () {\n    var node = queryFixture('<textarea id=\"target\"/>');\n    assert.isTrue(isNativeTextbox(node));\n  });\n\n  it('returns false for non-text inputs', function () {\n    var nonTextInputs = [\n      'button',\n      'checkbox',\n      'file',\n      'hidden',\n      'image',\n      'password',\n      'radio',\n      'reset',\n      'submit',\n      'color'\n    ];\n    nonTextInputs.forEach(function (type) {\n      var node = queryFixture('<input id=\"target\" type=\"' + type + '\"/>');\n\n      assert.isFalse(\n        isNativeTextbox(node),\n        '<input type=\"' + type + '\"> is not a native text input'\n      );\n    });\n  });\n\n  it('return false for aria textbox elements', function () {\n    var node = queryFixture('<div id=\"target\" role=\"textbox\"></div>');\n    assert.isFalse(isNativeTextbox(node));\n  });\n\n  it('should ignore type case', function () {\n    var node = queryFixture('<input id=\"target\" type=\"TEXT\"/>');\n    assert.isTrue(isNativeTextbox(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/index.js",
    "content": "describe('axe.commons', function () {\n  'use strict';\n\n  it('should export public api', function () {\n    assert.exists(axe.commons.aria);\n    assert.exists(axe.commons.color);\n    assert.exists(axe.commons.dom);\n    assert.exists(axe.commons.forms);\n    assert.exists(axe.commons.matches);\n    assert.exists(axe.commons.standards);\n    assert.exists(axe.commons.table);\n    assert.exists(axe.commons.text);\n    assert.exists(axe.commons.utils);\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/attributes.js",
    "content": "describe('matches.attributes', function () {\n  var attributes = axe.commons.matches.attributes;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true if all attributes match', function () {\n    var virtualNode = queryFixture(\n      '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n    );\n    assert.isTrue(\n      attributes(virtualNode, {\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'bar'\n      })\n    );\n  });\n\n  it('returns false if some attributes do not match', function () {\n    var virtualNode = queryFixture(\n      '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n    );\n    assert.isFalse(\n      attributes(virtualNode, {\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'baz'\n      })\n    );\n  });\n\n  it('returns false if any attributes are missing', function () {\n    var virtualNode = queryFixture(\n      '<span id=\"target\" foo=\"baz\" baz=\"bar\"></span>'\n    );\n    assert.isFalse(\n      attributes(virtualNode, {\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'bar'\n      })\n    );\n  });\n\n  it('works with actual nodes', function () {\n    var virtualNode = queryFixture(\n      '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n    );\n    assert.isTrue(\n      attributes(virtualNode.actualNode, {\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'bar'\n      })\n    );\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        id: 'target',\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'bar'\n      }\n    });\n    assert.isTrue(\n      attributes(serialNode, {\n        foo: 'baz',\n        bar: 'foo',\n        baz: 'bar'\n      })\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/condition.js",
    "content": "describe('matches.condition', function () {\n  var condition = axe.commons.matches.condition;\n\n  it('passes the first argument to the condition', function () {\n    var count = 0;\n    condition('foo', function (foo) {\n      assert.equal('foo', foo);\n      count++;\n    });\n    assert.equal(count, 1);\n  });\n\n  it('returns true if the condition returns a truthy value', function () {\n    assert.isTrue(\n      condition('foo', function () {\n        return 123;\n      })\n    );\n  });\n\n  it('returns false if the condition returns a falsey value', function () {\n    assert.isFalse(\n      condition('foo', function () {\n        return 0;\n      })\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/explicit-role.js",
    "content": "describe('matches.explicitRole', function () {\n  var explicitRole = axe.commons.matches.explicitRole;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if explicit role matches', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"textbox\"></span>');\n    assert.isTrue(explicitRole(virtualNode, 'textbox'));\n  });\n\n  it('should return true if explicit role matches array', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"textbox\"></span>');\n    assert.isTrue(explicitRole(virtualNode, ['combobox', 'textbox']));\n  });\n\n  it('should return false if explicit role does not match', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"main\"></span>');\n    assert.isFalse(explicitRole(virtualNode, 'textbox'));\n  });\n\n  it('should return false if matching implicit role', function () {\n    var virtualNode = queryFixture('<ul><li id=\"target\"></li></ul>');\n    assert.isFalse(explicitRole(virtualNode, 'listitem'));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        role: 'textbox'\n      }\n    });\n    assert.isTrue(explicitRole(serialNode, 'textbox'));\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/from-definition.js",
    "content": "describe('matches.fromDefinition', function () {\n  var fromDefinition = axe.commons.matches.fromDefinition;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('applies a css selector when the matcher is a string', function () {\n    var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n    assert.isTrue(fromDefinition(virtualNode, '#fixture > div'));\n    assert.isFalse(fromDefinition(virtualNode, '#fixture > span'));\n  });\n\n  it('matches a definition with a `nodeName` property', function () {\n    var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n    var matchers = [\n      'div',\n      ['div', 'span'],\n      /div/,\n      function (nodeName) {\n        return nodeName === 'div';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          nodeName: matcher\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        nodeName: 'span'\n      })\n    );\n  });\n\n  it('matches a definition with an `attributes` property', function () {\n    var virtualNode = queryFixture('<div id=\"target\" foo=\"bar\">foo</div>');\n    var matchers = [\n      'bar',\n      ['bar', 'baz'],\n      /bar/,\n      function (attributeName) {\n        return attributeName === 'bar';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          attributes: {\n            foo: matcher\n          }\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        attributes: {\n          foo: 'baz'\n        }\n      })\n    );\n  });\n\n  it('matches a definition with a `properties` property', function () {\n    var virtualNode = queryFixture('<input id=\"target\" />');\n    var matchers = [\n      'text',\n      ['text', 'password'],\n      /text/,\n      function (type) {\n        return type === 'text';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          properties: {\n            type: matcher\n          }\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        properties: {\n          type: 'password'\n        }\n      })\n    );\n  });\n\n  it('matches a definition with an `explicitRole` property', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"textbox\"></span>');\n    var matchers = [\n      'textbox',\n      ['textbox', 'combobox'],\n      /textbox/,\n      function (attributeName) {\n        return attributeName === 'textbox';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          explicitRole: matcher\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        explicitRole: 'main'\n      })\n    );\n  });\n\n  it('matches a definition with an `implicitRole` property', function () {\n    var virtualNode = queryFixture('<input id=\"target\">');\n    var matchers = [\n      'textbox',\n      ['textbox', 'combobox'],\n      /textbox/,\n      function (attributeName) {\n        return attributeName === 'textbox';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          implicitRole: matcher\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        implicitRole: 'main'\n      })\n    );\n  });\n\n  it('matches a definition with an `semanticRole` property', function () {\n    var virtualNode = queryFixture('<input id=\"target\">');\n    var matchers = [\n      'textbox',\n      ['textbox', 'combobox'],\n      /textbox/,\n      function (attributeName) {\n        return attributeName === 'textbox';\n      }\n    ];\n    matchers.forEach(function (matcher) {\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          semanticRole: matcher\n        })\n      );\n    });\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        semanticRole: 'main'\n      })\n    );\n  });\n\n  it('matches a definition with an `accessibleName` property', function () {\n    var virtualNode = queryFixture('<input id=\"target\" aria-label=\"foo\">');\n    assert.isTrue(\n      fromDefinition(virtualNode, {\n        hasAccessibleName: true\n      })\n    );\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        hasAccessibleName: false\n      })\n    );\n  });\n\n  it('returns true when all matching properties return true', function () {\n    var virtualNode = queryFixture(\n      '<input id=\"target\" value=\"bar\" aria-disabled=\"true\" />'\n    );\n    assert.isTrue(\n      fromDefinition(virtualNode, {\n        nodeName: 'input',\n        properties: {\n          type: 'text',\n          id: 'target'\n        },\n        attributes: {\n          'aria-disabled': 'true'\n        }\n      })\n    );\n  });\n\n  it('returns false when some matching properties return false', function () {\n    var virtualNode = queryFixture(\n      '<input id=\"target\" value=\"bar\" aria-disabled=\"true\" />'\n    );\n    assert.isFalse(\n      fromDefinition(virtualNode, {\n        nodeName: 'input',\n        attributes: {\n          'aria-disabled': 'false'\n        }\n      })\n    );\n  });\n\n  describe('with actual nodes', function () {\n    it('matches using a string', function () {\n      var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n      assert.isTrue(fromDefinition(virtualNode.actualNode, 'div'));\n      assert.isFalse(fromDefinition(virtualNode.actualNode, 'span'));\n    });\n\n    it('matches nodeName', function () {\n      var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n      assert.isTrue(\n        fromDefinition(virtualNode.actualNode, {\n          nodeName: 'div'\n        })\n      );\n      assert.isFalse(\n        fromDefinition(virtualNode.actualNode, {\n          nodeName: 'span'\n        })\n      );\n    });\n\n    it('matches attributes', function () {\n      var virtualNode = queryFixture('<div id=\"target\" foo=\"bar\">foo</div>');\n      assert.isTrue(\n        fromDefinition(virtualNode.actualNode, {\n          attributes: {\n            foo: 'bar'\n          }\n        })\n      );\n      assert.isFalse(\n        fromDefinition(virtualNode.actualNode, {\n          attributes: {\n            foo: 'baz'\n          }\n        })\n      );\n    });\n\n    it('matches properties', function () {\n      var virtualNode = queryFixture('<input id=\"target\" value=\"foo\" />');\n      assert.isTrue(\n        fromDefinition(virtualNode.actualNode, {\n          properties: {\n            id: 'target'\n          }\n        })\n      );\n      assert.isFalse(\n        fromDefinition(virtualNode.actualNode, {\n          properties: {\n            id: 'bar'\n          }\n        })\n      );\n    });\n  });\n\n  describe('with SerialVirtualNode', function () {\n    it('matches using a string', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          id: 'target'\n        }\n      });\n      assert.isTrue(fromDefinition(serialNode, 'div'));\n      assert.isFalse(fromDefinition(serialNode, 'span'));\n    });\n\n    it('matches nodeName', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          id: 'target'\n        }\n      });\n      assert.isTrue(\n        fromDefinition(serialNode, {\n          nodeName: 'div'\n        })\n      );\n      assert.isFalse(\n        fromDefinition(serialNode, {\n          nodeName: 'span'\n        })\n      );\n    });\n\n    it('matches attributes', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          id: 'target',\n          foo: 'bar'\n        }\n      });\n      assert.isTrue(\n        fromDefinition(serialNode, {\n          attributes: {\n            foo: 'bar'\n          }\n        })\n      );\n      assert.isFalse(\n        fromDefinition(serialNode, {\n          attributes: {\n            foo: 'baz'\n          }\n        })\n      );\n    });\n\n    it('matches properties', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'input',\n        id: 'target'\n      });\n      assert.isTrue(\n        fromDefinition(serialNode, {\n          properties: {\n            id: 'target'\n          }\n        })\n      );\n      assert.isFalse(\n        fromDefinition(serialNode, {\n          properties: {\n            id: 'bar'\n          }\n        })\n      );\n    });\n  });\n\n  describe('with a `condition` property', function () {\n    it('calls condition and uses its return value as a matcher', function () {\n      var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n      var called = false;\n      assert.isTrue(\n        fromDefinition(virtualNode, {\n          condition: function (node) {\n            assert.deepEqual(node, virtualNode);\n            called = true;\n            return true;\n          }\n        })\n      );\n      assert.isFalse(\n        fromDefinition(virtualNode, {\n          condition: function () {\n            return false;\n          }\n        })\n      );\n      assert.isTrue(called);\n    });\n\n    it('uses the return value as a matcher', function () {\n      var returnVal = 'true';\n      function condition() {\n        return returnVal;\n      }\n      assert.isTrue(\n        fromDefinition(fixture, {\n          condition: condition // Truthy test\n        })\n      );\n\n      returnVal = 0; // Falsey test\n      assert.isFalse(\n        fromDefinition(fixture, {\n          condition: condition\n        })\n      );\n    });\n  });\n\n  describe('with an `array` of definitions', function () {\n    it('returns true if any definition in the array matches', function () {\n      var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n      assert.isTrue(\n        fromDefinition(virtualNode, [\n          { nodeName: 'span' },\n          { nodeName: 'div' },\n          { nodeName: 'h1' }\n        ])\n      );\n    });\n\n    it('returns false if none definition in the array matches', function () {\n      var virtualNode = queryFixture('<input id=\"target\" />');\n      assert.isFalse(\n        fromDefinition(virtualNode, [\n          { nodeName: 'span' },\n          { nodeName: 'div' },\n          { nodeName: 'h1' }\n        ])\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/from-function.js",
    "content": "describe('matches.fromFunction', () => {\n  const fromFunction = axe.commons.matches.fromFunction;\n  function noop() {}\n\n  it('throws an error when the matcher is a number', () => {\n    assert.throws(() => {\n      fromFunction(noop, 123);\n    });\n  });\n\n  it('throws an error when the matcher is a string', () => {\n    assert.throws(() => {\n      fromFunction(noop, 'foo');\n    });\n  });\n\n  it('throws an error when the matcher is an array', () => {\n    assert.throws(() => {\n      fromFunction(noop, ['foo']);\n    });\n  });\n\n  it('throws an error when the matcher is a RegExp', () => {\n    assert.throws(() => {\n      fromFunction(noop, /foo/);\n    });\n  });\n\n  describe('with object matches', () => {\n    let keyMap = {};\n    function getValue(key) {\n      return key;\n    }\n\n    it('passes every object key to the getValue function once', () => {\n      const keys = ['foo', 'bar', 'baz'];\n      function getValueOnce(key) {\n        const index = keys.indexOf(key);\n        assert.notEqual(index, -1);\n        keys.splice(index, 1);\n        return key;\n      }\n\n      fromFunction(getValueOnce, {\n        foo: 'foo',\n        bar: 'bar',\n        baz: 'baz'\n      });\n      assert.lengthOf(keys, 0);\n    });\n\n    it('returns true if every value is matched', () => {\n      keyMap = {\n        foo: 'foo',\n        bar: 'bar',\n        baz: 'baz'\n      };\n      assert.isTrue(fromFunction(getValue, keyMap));\n    });\n\n    it('returns false if any value is not matched', () => {\n      keyMap = {\n        foo: 'foo',\n        bar: 'bar',\n        baz: 'baz'\n      };\n      assert.isFalse(\n        fromFunction(function (key) {\n          if (key === 'bar') {\n            return 'mismatch';\n          }\n          return key;\n        }, keyMap)\n      );\n    });\n\n    it('returns true if there are no keys', () => {\n      assert.isTrue(fromFunction(getValue, {}));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/from-primative.js",
    "content": "describe('matches.fromPrimative', function () {\n  var fromPrimative = axe.commons.matches.fromPrimative;\n\n  it('returns true when strictly equal', function () {\n    assert.isTrue(fromPrimative('foo', 'foo'));\n    assert.isTrue(fromPrimative(null, null));\n    assert.isTrue(fromPrimative(true, true));\n    assert.isTrue(fromPrimative(123, 123));\n    assert.isTrue(fromPrimative(undefined, undefined));\n  });\n\n  it('returns false when not strictly equal', function () {\n    assert.isFalse(fromPrimative('foo', 'bar'));\n    assert.isFalse(fromPrimative(null, undefined));\n    assert.isFalse(fromPrimative(false, null));\n    assert.isFalse(fromPrimative(true, false));\n    assert.isFalse(fromPrimative(123, 456));\n    assert.isFalse(fromPrimative(undefined, null));\n  });\n\n  describe('with array matchers', function () {\n    it('returns true if the string is included', function () {\n      assert.isTrue(fromPrimative('bar', ['foo', 'bar', 'baz']));\n    });\n    it('returns false if the string is not included', function () {\n      assert.isFalse(fromPrimative('foo bar', ['foo', 'bar', 'baz']));\n    });\n    it('returns false when passed `undefined`', function () {\n      assert.isFalse(fromPrimative(undefined, ['foo', 'bar', 'baz']));\n    });\n  });\n\n  describe('with function matchers', function () {\n    it('returns true if the function returns a truthy value', function () {\n      assert.isTrue(\n        fromPrimative('foo', function (val) {\n          assert.equal(val, 'foo');\n          return true;\n        })\n      );\n      assert.isTrue(\n        fromPrimative('foo', function () {\n          return 123;\n        })\n      );\n      assert.isTrue(\n        fromPrimative('foo', function () {\n          return {};\n        })\n      );\n    });\n    it('returns false if the function returns a falsey value', function () {\n      assert.isFalse(\n        fromPrimative('foo', function (val) {\n          assert.equal(val, 'foo');\n          return false;\n        })\n      );\n      assert.isFalse(\n        fromPrimative('foo', function () {\n          return 0;\n        })\n      );\n      assert.isFalse(\n        fromPrimative('foo', function () {\n          return undefined;\n        })\n      );\n    });\n  });\n\n  describe('with RegExp matchers', function () {\n    it('returns true if the regexp matches', function () {\n      assert.isTrue(fromPrimative('bar', /^(foo|bar|baz)$/));\n    });\n    it('returns false if the regexp does not match', function () {\n      assert.isFalse(fromPrimative('foobar', /^(foo|bar|baz)$/));\n    });\n    it('returns false for null value', function () {\n      assert.isFalse(fromPrimative(null, /.*/));\n    });\n  });\n\n  describe('with RegExp string', function () {\n    it('returns true if the regexp matches', function () {\n      assert.isTrue(fromPrimative('bar', '/^(foo|bar|baz)$/'));\n    });\n    it('returns false if the regexp does not match', function () {\n      assert.isFalse(fromPrimative('foobar', '/^(foo|bar|baz)$/'));\n    });\n    it('returns false for null value', function () {\n      assert.isFalse(fromPrimative(null, '/.*/'));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/has-accessible-name.js",
    "content": "describe('matches.accessibleName', function () {\n  var hasAccessibleName = axe.commons.matches.hasAccessibleName;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true when text has an accessible name', function () {\n    var virtualNode = queryFixture('<button id=\"target\">hello world</button>');\n    assert.isTrue(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return true when aria-label has an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-label=\"hello world\"></button>'\n    );\n    assert.isTrue(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return true when aria-labelledby has an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-labelledby=\"foo\"></button><div id=\"foo\">hello world</div'\n    );\n    assert.isTrue(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return true when title has an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" title=\"hello world\"></button>'\n    );\n    assert.isTrue(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return true when label has an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<label>hello world<input id=\"target\"></label>'\n    );\n    assert.isTrue(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when text does not have an accessible name', function () {\n    var virtualNode = queryFixture('<button id=\"target\"></button>');\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when aria-label does not have an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-label=\"\"></button>'\n    );\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when aria-labelledby does not have an accessible name', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-labelledby=\"\"></button><div id=\"foo\">hello world</div'\n    );\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when aria-labelledby references an element that does not exist', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-labelledby=\"bar\"></button><div id=\"foo\">hello world</div'\n    );\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when aria-labelledby references an elm that does not exist', function () {\n    var virtualNode = queryFixture(\n      '<button id=\"target\" aria-labelledby=\"bar\"></button><div id=\"foo\">hello world</div'\n    );\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when title does not have an accessible name', function () {\n    var virtualNode = queryFixture('<button id=\"target\" title=\"\"></button>');\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('should return false when label does not have an accessible name', function () {\n    var virtualNode = queryFixture('<label><input id=\"target\"></label>');\n    assert.isFalse(hasAccessibleName(virtualNode, true));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        role: 'button',\n        'aria-label': 'hello world'\n      }\n    });\n    assert.isTrue(hasAccessibleName(serialNode, true));\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/implicit-role.js",
    "content": "describe('matches.implicitRole', function () {\n  var implicitRole = axe.commons.matches.implicitRole;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if implicit role matches', function () {\n    var virtualNode = queryFixture('<ul><li id=\"target\"></li></ul>');\n    assert.isTrue(implicitRole(virtualNode, 'listitem'));\n  });\n\n  it('should return true if implicit role matches array', function () {\n    var virtualNode = queryFixture('<ul><li id=\"target\"></li></ul>');\n    assert.isTrue(implicitRole(virtualNode, ['textbox', 'listitem']));\n  });\n\n  it('should return false if implicit role does not match', function () {\n    var virtualNode = queryFixture('<ul><li id=\"target\"></li></ul>');\n    assert.isFalse(implicitRole(virtualNode, 'textbox'));\n  });\n\n  it('should return false if matching explicit role', function () {\n    var virtualNode = queryFixture(\n      '<ul role=\"menu\"><li id=\"target\" role=\"menuitem\"></li></ul>'\n    );\n    assert.isFalse(implicitRole(virtualNode, 'menuitem'));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'li'\n    });\n    assert.isTrue(implicitRole(serialNode, 'listitem'));\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/node-name.js",
    "content": "describe('matches.nodeName', function () {\n  var matchNodeName = axe.commons.matches.nodeName;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true if the nodeName is the same as the matcher', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n    assert.isTrue(matchNodeName(virtualNode, 'h1'));\n  });\n\n  it('returns true if the nodename is included in an array', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n    assert.isTrue(matchNodeName(virtualNode, ['h3', 'h2', 'h1']));\n  });\n\n  it('returns true if the nodeName matches a regexp', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n    assert.isTrue(matchNodeName(virtualNode, /^h[0-6]$/));\n  });\n\n  it('returns true if the nodeName matches with a function', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n    assert.isTrue(\n      matchNodeName(virtualNode, function (nodeName) {\n        return nodeName === 'h1';\n      })\n    );\n  });\n\n  it('returns false if the nodeName does not match', function () {\n    var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n    assert.isFalse(matchNodeName(virtualNode, 'h1'));\n    assert.isFalse(matchNodeName(virtualNode, ['h3', 'h2', 'h1']));\n    assert.isFalse(matchNodeName(virtualNode, /^h[0-6]$/));\n    assert.isFalse(\n      matchNodeName(virtualNode, function (nodeName) {\n        return nodeName === 'h1';\n      })\n    );\n  });\n\n  it('is case sensitive for XHTML', function () {\n    var virtualNode = queryFixture('<H1 id=\"target\">foo</H1>');\n    virtualNode._isXHTML = true;\n    delete virtualNode._cache.props;\n    assert.isFalse(matchNodeName(virtualNode, 'h1'));\n  });\n\n  it('is case insensitive for HTML, but not for XHTML', function () {\n    var virtualNode = queryFixture('<H1 id=\"target\">foo</H1>');\n    virtualNode._isXHTML = false;\n    assert.isTrue(matchNodeName(virtualNode, 'h1'));\n  });\n\n  it('works with actual nodes', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n    assert.isTrue(matchNodeName(virtualNode.actualNode, 'h1'));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'h1',\n      attributes: {\n        id: 'target'\n      }\n    });\n    assert.isTrue(matchNodeName(serialNode, 'h1'));\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/properties.js",
    "content": "describe('matches.properties', function () {\n  var properties = axe.commons.matches.properties;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true if all properties match', function () {\n    var virtualNode = queryFixture('<input type=\"text\" id=\"target\"></input>');\n\n    assert.isTrue(\n      properties(virtualNode, {\n        nodeName: 'input',\n        id: 'target',\n        type: 'text'\n      })\n    );\n  });\n\n  it('returns false if some properties do not match', function () {\n    var virtualNode = queryFixture('<input type=\"text\" id=\"target\"></input>');\n\n    assert.isFalse(\n      properties(virtualNode, {\n        nodeName: 'input',\n        id: 'target',\n        type: 'num'\n      })\n    );\n  });\n\n  it('returns false if any properties are missing', function () {\n    var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n\n    assert.isFalse(\n      properties(virtualNode, {\n        nodeName: 'h1',\n        id: 'target',\n        type: 'text'\n      })\n    );\n  });\n\n  it('works with actual nodes', function () {\n    var virtualNode = queryFixture('<input type=\"text\" id=\"target\"></input>');\n\n    assert.isTrue(\n      properties(virtualNode.actualNode, {\n        nodeName: 'input',\n        id: 'target',\n        type: 'text'\n      })\n    );\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      type: 'text',\n      id: 'target'\n    });\n\n    assert.isTrue(\n      properties(serialNode, {\n        nodeName: 'input',\n        id: 'target',\n        type: 'text'\n      })\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/matches/semantic-role.js",
    "content": "describe('matches.semanticRole', function () {\n  var semanticRole = axe.commons.matches.semanticRole;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if explicit role matches', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"textbox\"></span>');\n    assert.isTrue(semanticRole(virtualNode, 'textbox'));\n  });\n\n  it('should return true if explicit role matches array', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"textbox\"></span>');\n    assert.isTrue(semanticRole(virtualNode, ['combobox', 'textbox']));\n  });\n\n  it('should return true if implicit role matches', function () {\n    var virtualNode = queryFixture('<ul><li id=\"target\"></li></ul>');\n    assert.isTrue(semanticRole(virtualNode, 'listitem'));\n  });\n\n  it('should return false if semantic role does not match', function () {\n    var virtualNode = queryFixture('<span id=\"target\" role=\"main\"></span>');\n    assert.isFalse(semanticRole(virtualNode, 'textbox'));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var serialNode = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        role: 'textbox'\n      }\n    });\n    assert.isTrue(semanticRole(serialNode, 'textbox'));\n  });\n});\n"
  },
  {
    "path": "test/commons/math/get-bounding-rect.js",
    "content": "describe('getBoundingRect', () => {\n  const getBoundingRect = axe.commons.math.getBoundingRect;\n\n  it('returns a rect that bounds both rects', () => {\n    const rectA = new DOMRect(10, 10, 5, 5);\n    const rectB = new DOMRect(25, 25, 5, 5);\n    const rectC = new DOMRect(10, 10, 20, 20);\n    assert.deepEqual(getBoundingRect(rectA, rectB), rectC);\n  });\n});\n"
  },
  {
    "path": "test/commons/math/get-intersection-rect.js",
    "content": "describe('math.getIntersectionRect', () => {\n  const { getIntersectionRect } = axe.commons.math;\n\n  it('returns the intersection of two rects', () => {\n    const rect1 = new DOMRect(10, 10, 20, 30);\n    const rect2 = new DOMRect(15, 10, 20, 30);\n    const actual = getIntersectionRect(rect1, rect2);\n    const expected = new DOMRect(15, 10, 15, 30);\n\n    assert.deepEqual(actual, expected);\n  });\n\n  it('should not matter on order', () => {\n    const rect1 = new DOMRect(10, 10, 20, 30);\n    const rect2 = new DOMRect(15, 10, 20, 30);\n    const actual = getIntersectionRect(rect2, rect1);\n    const expected = new DOMRect(15, 10, 15, 30);\n\n    assert.deepEqual(actual, expected);\n  });\n\n  it('returns null when rects do not intersect (horizontally)', () => {\n    const rect1 = new DOMRect(10, 10, 20, 30);\n    const rect2 = new DOMRect(50, 10, 20, 30);\n    const actual = getIntersectionRect(rect1, rect2);\n\n    assert.isNull(actual);\n  });\n\n  it('returns null when rects do not intersect (vertically)', () => {\n    const rect1 = new DOMRect(10, 10, 20, 30);\n    const rect2 = new DOMRect(15, 60, 20, 30);\n    const actual = getIntersectionRect(rect1, rect2);\n\n    assert.isNull(actual);\n  });\n\n  it('returns null when rects are next to one another', () => {\n    const rect1 = new DOMRect(10, 10, 20, 30);\n    const rect2 = new DOMRect(30, 10, 20, 30);\n    const actual = getIntersectionRect(rect1, rect2);\n\n    assert.isNull(actual);\n  });\n});\n"
  },
  {
    "path": "test/commons/math/get-offset.js",
    "content": "describe('getOffset', () => {\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const getOffset = axe.commons.math.getOffset;\n  const round = 0.2;\n\n  it('returns center to edge of circle when both are undersized', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 50px; left: 0\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.closeTo(getOffset(nodeA, nodeB), 38, round);\n  });\n\n  it('returns center to edge of square when one is undersized', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: 50px; left: 0\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.closeTo(getOffset(nodeA, nodeB), 45, round);\n  });\n\n  it('returns center to corner of square when at a diagonal', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: 50px; left: 50px\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.closeTo(getOffset(nodeA, nodeB), 63.6, round);\n  });\n\n  it('returns null if nodeA is overlapped by nodeB', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: -10px; left: -10px\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.isNull(getOffset(nodeA, nodeB));\n  });\n\n  it('returns null if nodeB is overlapped by nodeA', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: -10px; left: -10px\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[3];\n    const nodeB = fixture.children[1];\n    assert.isNull(getOffset(nodeA, nodeB));\n  });\n\n  it('returns null if nodeA is overlapped by another node', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: 0; left: 20px\">&nbsp;</button>\n      <div style=\"background: white; width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: -10px; left: -10px\">&nbsp;</div>\n    `);\n    const nodeB = fixture.children[1];\n    const nodeA = fixture.children[3];\n    assert.isNull(getOffset(nodeA, nodeB));\n  });\n\n  it('returns null if nodeB is overlapped by another node', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: 0; left: 20px\">&nbsp;</button>\n      <div style=\"background: white; width: 30px; height: 30px; margin: 0; padding: 0; position: absolute; top: -10px; left: -10px\">&nbsp;</div>\n    `);\n    const nodeA = fixture.children[3];\n    const nodeB = fixture.children[1];\n    assert.isNull(getOffset(nodeA, nodeB));\n  });\n\n  it('subtracts minNeighbourRadius from center-to-center calculations', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 50px; left: 0\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.closeTo(getOffset(nodeA, nodeB, 30), 20, round);\n  });\n\n  it('returns 0 if center of nodeA is enclosed by nodeB', () => {\n    const fixture = fixtureSetup(`\n      <button style=\"width: 50px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 0\">&nbsp;</button>\n      <button style=\"width: 10px; height: 10px; margin: 0; padding: 0; position: absolute; top: 0; left: 20px;\">&nbsp;</button>\n    `);\n    const nodeA = fixture.children[1];\n    const nodeB = fixture.children[3];\n    assert.equal(getOffset(nodeA, nodeB, 30), 0);\n  });\n});\n"
  },
  {
    "path": "test/commons/math/get-rect-center.js",
    "content": "describe('getRectCenter', () => {\n  const getRectCenter = axe.commons.math.getRectCenter;\n\n  it('returns the center point of a rect', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    const center = new DOMPoint(15, 30);\n    assert.deepEqual(getRectCenter(rect), center);\n  });\n});\n"
  },
  {
    "path": "test/commons/math/has-visual-overlap.js",
    "content": "describe('hasVisualOverlap', function () {\n  'use strict';\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var hasVisualOverlap = axe.commons.math.hasVisualOverlap;\n\n  it('returns false if there is no overlap', function () {\n    var rootNode = fixtureSetup('<a>foo</a><b>bar</b>');\n    var vNodeA = rootNode.children[0];\n    var vNodeB = rootNode.children[1];\n    assert.isFalse(hasVisualOverlap(vNodeA, vNodeB));\n  });\n\n  it('returns true if B overlaps A', function () {\n    var rootNode = fixtureSetup('<a><b>bar</b></a>');\n    var vNodeA = rootNode.children[0];\n    var vNodeB = vNodeA.children[0];\n    assert.isTrue(hasVisualOverlap(vNodeA, vNodeB));\n  });\n\n  it('returns true A overlaps B', function () {\n    var rootNode = fixtureSetup('<b><a>bar</a></b>');\n    var vNodeB = rootNode.children[0];\n    var vNodeA = vNodeB.children[0];\n    assert.isFalse(hasVisualOverlap(vNodeA, vNodeB));\n  });\n});\n"
  },
  {
    "path": "test/commons/math/is-point-in-rect.js",
    "content": "describe('isPointInRect', () => {\n  const isPointInRect = axe.commons.math.isPointInRect;\n\n  it('returns true when the point is inside the rect', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    const point = new DOMPoint(15, 30);\n    assert.isTrue(isPointInRect(point, rect));\n  });\n\n  it('returns true when the point is on the edge', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    assert.isTrue(isPointInRect(new DOMPoint(10, 20), rect));\n    assert.isTrue(isPointInRect(new DOMPoint(20, 40), rect));\n  });\n\n  it('returns false when the point is vertically outside the rect', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    const point = new DOMPoint(15, 50);\n    assert.isFalse(isPointInRect(point, rect));\n  });\n\n  it('returns false when the point is horizontally outside the rect', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    const point = new DOMPoint(25, 30);\n    assert.isFalse(isPointInRect(point, rect));\n  });\n});\n"
  },
  {
    "path": "test/commons/math/rect-has-minimum-size.js",
    "content": "describe('rectHasMinimumSize', () => {\n  const rectHasMinimumSize = axe.commons.math.rectHasMinimumSize;\n\n  it('returns true if rect is large enough', () => {\n    const rect = new DOMRect(10, 20, 10, 20);\n    assert.isTrue(rectHasMinimumSize(10, rect));\n  });\n\n  it('returns true for rounding margin', () => {\n    const rect = new DOMRect(10, 20, 9.95, 20);\n    assert.isTrue(rectHasMinimumSize(10, rect));\n  });\n\n  it('returns false if width is too small', () => {\n    const rect = new DOMRect(10, 20, 5, 20);\n    assert.isFalse(rectHasMinimumSize(10, rect));\n  });\n\n  it('returns false if height is too small', () => {\n    const rect = new DOMRect(10, 20, 10, 5);\n    assert.isFalse(rectHasMinimumSize(10, rect));\n  });\n\n  it('returns false when below rounding margin', () => {\n    const rect = new DOMRect(10, 20, 9.94, 20);\n    assert.isFalse(rectHasMinimumSize(10, rect));\n  });\n});\n"
  },
  {
    "path": "test/commons/math/rects-overlap.js",
    "content": "describe('math.rects-overlap', function () {\n  const rectsOverlap = axe.commons.math.rectsOverlap;\n\n  let rectA, rectB;\n  beforeEach(() => {\n    rectA = {\n      left: 0,\n      right: 10,\n      top: 0,\n      bottom: 10\n    };\n    rectB = {\n      left: 5,\n      right: 20,\n      top: 0,\n      bottom: 5\n    };\n  });\n\n  it('returns true when rects overlap', () => {\n    assert.isTrue(rectsOverlap(rectA, rectB));\n  });\n\n  it('should not matter on order', () => {\n    assert.isTrue(rectsOverlap(rectB, rectA));\n  });\n\n  it('returns false when rects do not overlap (horizontally)', () => {\n    rectA.left = 25;\n    rectA.right = 40;\n\n    assert.isFalse(rectsOverlap(rectA, rectB));\n  });\n\n  it('returns false when rects do not overlap (vertically)', () => {\n    rectA.top = 15;\n    rectA.bottom = 25;\n\n    assert.isFalse(rectsOverlap(rectA, rectB));\n  });\n\n  it('returns false when rects are next to one another', () => {\n    rectA.left = 20;\n    rectA.right = 25;\n\n    assert.isFalse(rectsOverlap(rectA, rectB));\n  });\n\n  it('returns false when rects barely overlap due to floating point', () => {\n    rectA.left = 20.5;\n    rectA.right = 25.9;\n    rectB.left = 10.9;\n    rectB.right = 20.9;\n\n    assert.isFalse(rectsOverlap(rectA, rectB));\n  });\n});\n"
  },
  {
    "path": "test/commons/math/split-rects.js",
    "content": "describe('splitRects', () => {\n  const splitRects = axe.commons.math.splitRects;\n\n  it('returns the original rect if there is no clipping rect', () => {\n    const rectA = new DOMRect(0, 0, 100, 50);\n    const rects = splitRects(rectA, []);\n    assert.lengthOf(rects, 1);\n    assert.deepEqual(rects[0], rectA);\n  });\n\n  it('returns the original rect if there is no overlap', () => {\n    const rectA = new DOMRect(0, 0, 100, 50);\n    const rectB = new DOMRect(0, 50, 50, 50);\n    const rects = splitRects(rectA, [rectB]);\n    assert.lengthOf(rects, 1);\n    assert.deepEqual(rects[0], rectA);\n  });\n\n  it('throws if there are too many overlapping rects', () => {\n    const rects = [];\n    for (let i = 0; i < 100; i++) {\n      rects.push(new DOMRect(i, i, 50, 50));\n    }\n    const rectA = new DOMRect(0, 0, 1000, 1000);\n\n    assert.throws(() => {\n      splitRects(rectA, rects);\n    }, 'splitRects: Too many rects');\n  });\n\n  it('accepts an array of rects', () => {\n    const rectA = new DOMRect(0, 0, 100, 50);\n    const rectB = new DOMRect(0, 50, 50, 50);\n    const rects = splitRects([rectA], [rectB]);\n    assert.lengthOf(rects, 1);\n    assert.deepEqual(rects[0], rectA);\n  });\n\n  describe('with one overlapping rect', () => {\n    it('returns one rect if overlaps covers two corners', () => {\n      const rectA = new DOMRect(0, 0, 100, 50);\n      const rectB = new DOMRect(40, 0, 100, 50);\n      const rects = splitRects(rectA, [rectB]);\n      assert.lengthOf(rects, 1);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 40, 50));\n    });\n\n    it('returns two rects if overlap covers one corner', () => {\n      const rectA = new DOMRect(0, 0, 100, 100);\n      const rectB = new DOMRect(50, 50, 50, 50);\n      const rects = splitRects(rectA, [rectB]);\n      assert.lengthOf(rects, 2);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 100, 50));\n      assert.deepEqual(rects[1], new DOMRect(0, 0, 50, 100));\n    });\n\n    it('returns three rects if overlap covers an edge, but no corner', () => {\n      const rectA = new DOMRect(0, 0, 100, 150);\n      const rectB = new DOMRect(50, 50, 50, 50);\n      const rects = splitRects(rectA, [rectB]);\n      assert.lengthOf(rects, 3);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 100, 50));\n      assert.deepEqual(rects[1], new DOMRect(0, 100, 100, 50));\n      assert.deepEqual(rects[2], new DOMRect(0, 0, 50, 150));\n    });\n\n    it('returns four rects if overlap sits in the middle, touching no corner', () => {\n      const rectA = new DOMRect(0, 0, 150, 150);\n      const rectB = new DOMRect(50, 50, 50, 50);\n      const rects = splitRects(rectA, [rectB]);\n      assert.lengthOf(rects, 4);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 150, 50));\n      assert.deepEqual(rects[1], new DOMRect(100, 0, 50, 150));\n      assert.deepEqual(rects[2], new DOMRect(0, 100, 150, 50));\n      assert.deepEqual(rects[3], new DOMRect(0, 0, 50, 150));\n    });\n\n    it('returns no rects if overlap covers the entire input rect', () => {\n      const rectA = new DOMRect(0, 0, 100, 50);\n      const rectB = new DOMRect(-50, -50, 400, 400);\n      const rects = splitRects(rectA, [rectB]);\n      assert.lengthOf(rects, 0);\n    });\n\n    it('accepts an array of rects', () => {\n      const rectA = new DOMRect(0, 0, 100, 50);\n      const rectB = new DOMRect(40, 0, 100, 50);\n      const rects = splitRects([rectA], [rectB]);\n      assert.lengthOf(rects, 1);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 40, 50));\n    });\n  });\n\n  describe('with multiple overlaps', () => {\n    it('can return a single rect two overlaps each cover an edge', () => {\n      const rectA = new DOMRect(0, 0, 150, 50);\n      const rectB = new DOMRect(0, 0, 50, 50);\n      const rectC = new DOMRect(100, 0, 50, 50);\n      const rects = splitRects(rectA, [rectB, rectC]);\n      assert.lengthOf(rects, 1);\n      assert.deepEqual(rects[0], new DOMRect(50, 0, 50, 50));\n    });\n\n    it('can recursively clips regions', () => {\n      const rectA = new DOMRect(0, 0, 150, 100);\n      const rectB = new DOMRect(0, 50, 50, 50);\n      const rectC = new DOMRect(100, 50, 50, 50);\n      const rects = splitRects(rectA, [rectB, rectC]);\n      assert.lengthOf(rects, 3);\n      assert.deepEqual(rects[0], new DOMRect(0, 0, 150, 50));\n      assert.deepEqual(rects[1], new DOMRect(50, 0, 100, 50));\n      assert.deepEqual(rects[2], new DOMRect(50, 0, 50, 100));\n    });\n\n    it('returns no rects if overlap covers the entire input rect', () => {\n      const rectA = new DOMRect(0, 0, 100, 50);\n      const rectB = new DOMRect(50, 50, 200, 200);\n      const rectC = new DOMRect(-50, -50, 200, 200);\n      const rects = splitRects(rectA, [rectB, rectC]);\n      assert.lengthOf(rects, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/standards/get-aria-roles-by-type.js",
    "content": "describe('standards.getAriaRolesByType', function () {\n  var getAriaRolesByType = axe.commons.standards.getAriaRolesByType;\n\n  it('should return a list of role names by type', function () {\n    // first remove all role types\n    var roleNames = Object.keys(axe._audit.standards.ariaRoles);\n    var ariaRoles = {};\n    for (var i = 0; i < roleNames.length; i++) {\n      ariaRoles[roleNames[i]] = { type: 'off' };\n    }\n\n    // then turn on a few specific roles\n    ariaRoles.article = { type: 'structure' };\n    ariaRoles.blockquote = { type: 'structure' };\n    ariaRoles.caption = { type: 'structure' };\n    ariaRoles.cell = { type: 'structure' };\n\n    axe.configure({\n      standards: {\n        ariaRoles: ariaRoles\n      }\n    });\n\n    var structureRoles = getAriaRolesByType('structure');\n    assert.deepEqual(structureRoles, [\n      'article',\n      'blockquote',\n      'caption',\n      'cell'\n    ]);\n  });\n\n  it('should return configured roles', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          myRole: {\n            type: 'structure'\n          }\n        }\n      }\n    });\n\n    var structureRoles = getAriaRolesByType('structure');\n    assert.include(structureRoles, 'myRole');\n  });\n\n  it('should not return role that is configured to not be of the type', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          article: {\n            type: 'notstructure'\n          }\n        }\n      }\n    });\n\n    var structureRoles = getAriaRolesByType('structure');\n    assert.notInclude(structureRoles, 'article');\n  });\n});\n"
  },
  {
    "path": "test/commons/standards/get-aria-roles-supporting-name-from-content.js",
    "content": "describe('standards.getAriaRolesSupportingNameFromContent', function () {\n  var getAriaRolesSupportingNameFromContent =\n    axe.commons.standards.getAriaRolesSupportingNameFromContent;\n\n  it('should return a list of role names which are named from content', function () {\n    // first remove all namedFromContent\n    var roleNames = Object.keys(axe._audit.standards.ariaRoles);\n    var ariaRoles = {};\n    for (var i = 0; i < roleNames.length; i++) {\n      ariaRoles[roleNames[i]] = { nameFromContent: false };\n    }\n\n    // then turn on a few specific roles\n    ariaRoles.button = { nameFromContent: true };\n    ariaRoles.cell = { nameFromContent: true };\n    ariaRoles.checkbox = { nameFromContent: true };\n    ariaRoles.columnheader = { nameFromContent: true };\n\n    axe.configure({\n      standards: {\n        ariaRoles: ariaRoles\n      }\n    });\n\n    var contentRoles = getAriaRolesSupportingNameFromContent();\n    assert.deepEqual(contentRoles, [\n      'button',\n      'cell',\n      'checkbox',\n      'columnheader'\n    ]);\n  });\n\n  it('should return configured roles', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          myRole: {\n            nameFromContent: true\n          }\n        }\n      }\n    });\n\n    var contentRoles = getAriaRolesSupportingNameFromContent();\n    assert.include(contentRoles, 'myRole');\n  });\n\n  it('should not return role that is configured to not be of the type', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          button: {\n            nameFromContent: false\n          }\n        }\n      }\n    });\n\n    var contentRoles = getAriaRolesSupportingNameFromContent();\n    assert.notInclude(contentRoles, 'button');\n  });\n});\n"
  },
  {
    "path": "test/commons/standards/get-element-spec.js",
    "content": "describe('standards.getElementSpec', function () {\n  var getElementSpec = axe.commons.standards.getElementSpec;\n  var queryFixture = axe.testUtils.queryFixture;\n  var fixture = document.querySelector('#fixture');\n\n  before(function () {\n    axe._load({});\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  after(function () {\n    axe.reset();\n  });\n\n  it('should return a spec for an element without variants', function () {\n    axe.configure({\n      standards: {\n        htmlElms: {\n          abbr: {\n            contentTypes: ['phrasing', 'flow'],\n            allowedRoles: true\n          }\n        }\n      }\n    });\n\n    var vNode = queryFixture('<abbr id=\"target\"></abbr>');\n    assert.deepEqual(getElementSpec(vNode), {\n      contentTypes: ['phrasing', 'flow'],\n      allowedRoles: true\n    });\n  });\n\n  it('should return empty object if passed an invalid element', function () {\n    var vNode = queryFixture('<foo-bar id=\"target\"></foo-bar>');\n    assert.deepEqual(getElementSpec(vNode), {});\n  });\n\n  describe('variants', function () {\n    before(function () {\n      axe.configure({\n        standards: {\n          htmlElms: {\n            abbr: {\n              variant: {\n                controls: {\n                  matches: '[controls]',\n                  customProp: 'controls'\n                },\n                label: {\n                  matches: '[aria-label]',\n                  anotherProp: 'label'\n                },\n                default: {\n                  customProp: 'default',\n                  anotherProp: 'default'\n                }\n              },\n              allowedRoles: false\n            }\n          }\n        }\n      });\n    });\n\n    it('should return top level properties', function () {\n      var vNode = queryFixture('<abbr id=\"target\" controls></abbr>');\n      var spec = getElementSpec(vNode);\n      assert.equal(spec.allowedRoles, false);\n    });\n\n    it('should return properties from matching variant', function () {\n      var vNode = queryFixture('<abbr id=\"target\" controls></abbr>');\n      var spec = getElementSpec(vNode);\n      assert.equal(spec.customProp, 'controls');\n    });\n\n    it('should return all properties from matching variants', function () {\n      var vNode = queryFixture(\n        '<abbr id=\"target\" controls aria-label=\"foo\"></abbr>'\n      );\n      var spec = getElementSpec(vNode);\n      assert.equal(spec.customProp, 'controls');\n      assert.equal(spec.anotherProp, 'label');\n    });\n\n    it('should return default props in no variants match', function () {\n      var vNode = queryFixture('<abbr id=\"target\"></abbr>');\n      var spec = getElementSpec(vNode);\n      assert.equal(spec.customProp, 'default');\n      assert.equal(spec.anotherProp, 'default');\n    });\n\n    it('should return default props that were not part of other matches', function () {\n      var vNode = queryFixture('<abbr id=\"target\" controls></abbr>');\n      var spec = getElementSpec(vNode);\n      assert.equal(spec.customProp, 'controls');\n      assert.equal(spec.anotherProp, 'default');\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/standards/get-elements-by-content-type.js",
    "content": "describe('standards.getElementsByContentType', function () {\n  var getElementsByContentType = axe.commons.standards.getElementsByContentType;\n\n  before(function () {\n    axe._load({});\n  });\n\n  after(function () {\n    axe.reset();\n  });\n\n  it('should return a list of node names by content type', function () {\n    // Source: https://html.spec.whatwg.org/multipage/dom.html#sectioning-content\n    var sectioningContent = getElementsByContentType('sectioning');\n    assert.deepEqual(sectioningContent, ['article', 'aside', 'nav', 'section']);\n  });\n\n  it('should return a default variants', function () {\n    // Source: https://html.spec.whatwg.org/multipage/dom.html#embedded-content-2\n    var sectioningContent = getElementsByContentType('embedded');\n    assert.deepEqual(sectioningContent, [\n      'audio',\n      'canvas',\n      'embed',\n      'iframe',\n      'img',\n      'math',\n      'object',\n      'svg',\n      'video'\n    ]);\n  });\n\n  it('should return configured roles', function () {\n    axe.configure({\n      standards: {\n        htmlElms: {\n          myElm: {\n            contentTypes: ['flow', 'sectioning']\n          }\n        }\n      }\n    });\n\n    var structureRoles = getElementsByContentType('sectioning');\n    assert.include(structureRoles, 'myElm');\n  });\n\n  it('should not return role that is configured to not be of the type', function () {\n    axe.configure({\n      standards: {\n        htmlElms: {\n          article: {\n            contentTypes: ['flow']\n          }\n        }\n      }\n    });\n\n    var structureRoles = getElementsByContentType('sectioning');\n    assert.notInclude(structureRoles, 'article');\n  });\n});\n"
  },
  {
    "path": "test/commons/standards/get-global-aria-attrs.js",
    "content": "describe('standards.getGlobalAriaAttrs', function () {\n  var getGlobalAriaAttrs = axe.commons.standards.getGlobalAriaAttrs;\n\n  before(function () {\n    axe._load({});\n  });\n\n  after(function () {\n    axe.reset();\n  });\n\n  it('should return global attrs', function () {\n    // Source: https://www.w3.org/TR/wai-aria-1.1/#global_states\n    var globalAttrs = getGlobalAriaAttrs();\n    assert.deepEqual(globalAttrs, [\n      'aria-atomic',\n      'aria-braillelabel',\n      'aria-brailleroledescription',\n      'aria-busy',\n      'aria-controls',\n      'aria-current',\n      'aria-describedby',\n      'aria-description',\n      'aria-details',\n      'aria-disabled',\n      'aria-dropeffect',\n      'aria-errormessage',\n      'aria-flowto',\n      'aria-grabbed',\n      'aria-haspopup',\n      'aria-hidden',\n      'aria-invalid',\n      'aria-keyshortcuts',\n      'aria-label',\n      'aria-labelledby',\n      'aria-live',\n      'aria-owns',\n      'aria-relevant',\n      'aria-roledescription'\n    ]);\n  });\n\n  it('should return configured global attrs', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          myAttr: {\n            global: true\n          }\n        }\n      }\n    });\n\n    var globalAttrs = getGlobalAriaAttrs();\n    assert.include(globalAttrs, 'myAttr');\n  });\n\n  it('should not return global attr that is configured to not be global', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-atomic': {\n            global: false\n          }\n        }\n      }\n    });\n\n    var globalAttrs = getGlobalAriaAttrs();\n    assert.notInclude(globalAttrs, 'aria-atomic');\n  });\n});\n"
  },
  {
    "path": "test/commons/table/get-cell-position.js",
    "content": "describe('table.getCellPosition', function () {\n  'use strict';\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should get x, y coordinates given a cell', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '<tr><td></td><td id=\"target\"></td><td></td></tr>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '</table>';\n\n    var target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getCellPosition(target), {\n      x: 1,\n      y: 1\n    });\n  });\n\n  it('should handle colspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '<tr><td colspan=\"2\"></td><td id=\"target\"></td></tr>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '</table>';\n\n    var target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getCellPosition(target), {\n      x: 2,\n      y: 1\n    });\n  });\n\n  it('should handle rowspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td rowspan=\"2\"></td><td></td><td></td></tr>' +\n      '<tr><td></td><td id=\"target\"></td></tr>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '</table>';\n\n    var target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getCellPosition(target), {\n      x: 2,\n      y: 1\n    });\n  });\n\n  it('should handle rowspans and colspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td rowspan=\"2\" colspan=\"2\"></td><td></td></tr>' +\n      '<tr><td id=\"target\"></td></tr>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '</table>';\n\n    var target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getCellPosition(target), {\n      x: 2,\n      y: 1\n    });\n  });\n\n  it('should handle intermittent empty rows', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '<tr></tr>' +\n      '<tr><td></td><td id=\"target\"></td><td></td></tr>' +\n      '<tr><td></td><td></td><td></td></tr>' +\n      '<tr></tr>' +\n      '</table>';\n\n    var target = document.getElementById('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getCellPosition(target), {\n      x: 1,\n      y: 2\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/table/get-headers.js",
    "content": "describe('table.getHeaders', function () {\n  'use strict';\n  function $id(id) {\n    return document.getElementById(id);\n  }\n\n  var fixture = $id('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('should work with scope=auto', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><th id=\"t1\">1</th><th>2</th></tr>' +\n      '<tr><th id=\"t2\">2</th><td id=\"target\">ok</td><td></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should work with scope set', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><th scope=\"col\" id=\"t1\">1</th><th scope=\"col\">2</th></tr>' +\n      '<tr><th scope=\"row\" id=\"t2\">1</th><td id=\"target\">ok</td><td></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should find multiple column headers', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><th scope=\"col\" id=\"t1\">1</th><th scope=\"col\">2</th></tr>' +\n      '<tr><td></td><th scope=\"col\" id=\"t2\">1</th><th scope=\"col\">2</th></tr>' +\n      '<tr><th scope=\"row\" id=\"t3\">1</th><td id=\"target\">ok</td><td></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2'),\n      $id('t3')\n    ]);\n  });\n\n  it('should find multiple row headers', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><th scope=\"col\">1</th><th scope=\"col\" id=\"t1\">2</th></tr>' +\n      '<tr><th scope=\"row\" id=\"t2\">1</th><th scope=\"row\" id=\"t3\">2</th><td id=\"target\">ok</td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2'),\n      $id('t3')\n    ]);\n  });\n\n  it('should handle colspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><th scope=\"col\">1</th><th scope=\"col\" id=\"t1\">2</th></tr>' +\n      '<tr><td colspan=\"2\"></td><td id=\"target\"></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]);\n  });\n\n  it('should handle rowspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td rowspan=\"2\"></td><th scope=\"col\">1</th><th scope=\"col\" id=\"t1\">2</th></tr>' +\n      '<tr><th scope=\"row\" id=\"t2\"></th><td id=\"target\"></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should handle rowspan=0', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td rowspan=\"0\"></td><th scope=\"col\">1</th><th scope=\"col\" id=\"t1\">2</th></tr>' +\n      '<tr><th scope=\"row\" id=\"t2\"></th><td id=\"target\"></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should handle headers attribute', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\">t1</td><td id=\"t2\">t2</td></tr>' +\n      '<tr><td id=\"t3\">t3</td><td id=\"target\" headers=\"t1 t2 t3\"></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2'),\n      $id('t3')\n    ]);\n  });\n\n  it('should handle empty headers attribute', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr>' +\n      '<th scope=\"col\" id=\"t1\">Projects</th>' +\n      '<th scope=\"col\" id=\"t2\">Progress</th>' +\n      '</tr>' +\n      '<tr>' +\n      '<td headers=\"\" id=\"target\">My Project</td>' +\n      '<td>15%</td>' +\n      '</tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]);\n  });\n\n  it('should handle non-existent headers attribute', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr>' +\n      '<th scope=\"col\" id=\"t1\">Projects</th>' +\n      '<th scope=\"col\" id=\"t2\">Progress</th>' +\n      '</tr>' +\n      '<tr>' +\n      '<td headers=\"non-existent\" id=\"target\">My Project</td>' +\n      '<td>15%</td>' +\n      '</tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]);\n  });\n\n  it('should work with tables that have inconsistent columns', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\">t1</td><td id=\"t2\">t2</td></tr>' +\n      '<tr><td id=\"t3\">t3</td><td headers=\"t1 t2 t3\"></td><td id=\"target\"></td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), []);\n  });\n\n  it('should handle multiple headers with colspan', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<thead>' +\n      '<tr>' +\n      '<th id=\"t1\">Product</th>' +\n      '<th id=\"t2\">Amount</th>' +\n      '<th id=\"t3\">Price</th>' +\n      '</tr>' +\n      '</thead>' +\n      '<tbody>' +\n      '<tr><td colspan=\"2\" id=\"target\">No available products to display</td></tr>' +\n      '</thead>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should handle multiple headers with rowspan', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tbody>' +\n      '<tr>' +\n      '<th id=\"t1\"><td>Projects</td><td rowspan=\"2\" id=\"target\">Foo</td>' +\n      '</tr>' +\n      '<tr><th id=\"t2\"><td>Projects</td></tr>' +\n      '<tr><th id=\"t3\"><td>Projects</td></tr>' +\n      '</thead>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [\n      $id('t1'),\n      $id('t2')\n    ]);\n  });\n\n  it('should handle negative colspan', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<thead>' +\n      '<tr>' +\n      '<th id=\"t1\">Product</th>' +\n      '<th id=\"t2\">Amount</th>' +\n      '<th id=\"t3\">Price</th>' +\n      '</tr>' +\n      '</thead>' +\n      '<tbody>' +\n      '<tr><td colspan=\"-3\" id=\"target\">No available products to display</td></tr>' +\n      '</thead>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]);\n  });\n\n  it('should handle negative rowspan', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tbody>' +\n      '<tr>' +\n      '<th id=\"t1\"><td>Projects</td><td rowspan=\"-3\" id=\"target\">Foo</td>' +\n      '</tr>' +\n      '<tr><th id=\"t2\"><td>Projects</td></tr>' +\n      '<tr><th id=\"t3\"><td>Projects</td></tr>' +\n      '</thead>' +\n      '</table>';\n\n    var target = $id('target');\n\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.deepEqual(axe.commons.table.getHeaders(target), [$id('t1')]);\n  });\n});\n"
  },
  {
    "path": "test/commons/table/get-scope.js",
    "content": "describe('table.getScope', function () {\n  'use strict';\n\n  function $id(id) {\n    return document.getElementById(id);\n  }\n\n  var fixture = $id('fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  it('returns false for TD without scope attribute', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td id=\"target\">1</td></tr>' +\n      '<tr><th>2</th><td>ok</td></tr>' +\n      '</table>';\n\n    var target = $id('target');\n    fixtureSetup();\n    assert.equal(axe.commons.table.getScope(target), false);\n  });\n\n  it('throws if it is not passed a data cell', function () {\n    assert.throws(function () {\n      axe.commons.table.getScope();\n    }, TypeError);\n\n    var node = document.createElement('tr');\n    axe.utils.getFlattenedTree(node);\n\n    assert.throws(function () {\n      axe.commons.table.getScope(node);\n    }, TypeError);\n  });\n\n  it('does not throw if it is passed a data cell', function () {\n    var node = document.createElement('td');\n    axe.utils.getFlattenedTree(node);\n    assert.doesNotThrow(function () {\n      axe.commons.table.getScope(node);\n    });\n  });\n\n  describe('auto scope', function () {\n    it('return `auto` with implicit row and col scope', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><th id=\"target\">1</th><td>ok</td></tr>' +\n        '<tr><td>ok</td><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'auto');\n    });\n\n    it('return `auto` with implicit row and col scope, not in the first column', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><th id=\"target\">1</th></tr>' +\n        '<tr><th>2</th><td>ok</td><td></td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'auto');\n    });\n\n    it('return `auto` with implicit row and col scope, not in the first row', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><th>1</th></tr>' +\n        '<tr><th id=\"target\">2</th><td>ok</td><td></td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'auto');\n    });\n\n    it('return `auto` without an actualNode or in the tree', function () {\n      var serialNode = new axe.SerialVirtualNode({\n        nodeName: 'th'\n      });\n\n      assert.equal(axe.commons.table.getScope(serialNode), 'auto');\n    });\n  });\n\n  describe('col scope', function () {\n    it('returns `col` with explicit col scope on TH', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><th id=\"target\" scope=\"col\">1</th></tr>' +\n        '<tr><th>2</th><td>ok</td><td></td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('returns `col` with explicit col scope on TD', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><td id=\"target\" scope=\"col\">1</td></tr>' +\n        '<tr><th>2</th><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('returns `col` with role = columnheader on TD', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><td id=\"target\" scope=\"row\" role=\"columnheader\">1</td></tr>' +\n        '<tr><th>2</th><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('returns `col` when part of a row of all TH elements', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><th></th><th id=\"target\">1</th></tr>' +\n        '<tr><td>2</td><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('returns `col` when part of both a row and a column of all TH elements', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><th id=\"target\">1</th><th></th></tr>' +\n        '<tr><th>2</th><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('understands colspan on the table', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr> <th colspan=\"2\"></th> </tr>' +\n        '<tr> <th id=\"target\"></th> <th></th> </tr>' +\n        '<tr> <td></td> <td></td> </tr>' +\n        '</table>';\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n\n    it('understands colspan on the cell', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr> <th id=\"target\" colspan=\"2\"></th> </tr>' +\n        '<tr> <td></td> <td></td> </tr>' +\n        '</table>';\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'col');\n    });\n  });\n\n  describe('row scope', function () {\n    it('returns `row` with explicit row scope on TH', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><th>1</th></tr>' +\n        '<tr><th id=\"target\" scope=\"row\">2</th><td>ok</td><td></td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n\n    it('returns `row` with explicit row scope on TD', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><td>1</td></tr>' +\n        '<tr><td id=\"target\" scope=\"row\">2</td><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n\n    it('returns `row` with role = rowheader on TD', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><td></td><td>1</td></tr>' +\n        '<tr><td id=\"target\" scope=\"col\" role=\"rowheader\">2</td><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      fixtureSetup();\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n\n    it('returns `row` when part of a column of all TH elements', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr><th></th><td>1</td></tr>' +\n        '<tr><th id=\"target\">2</th><td>ok</td></tr>' +\n        '</table>';\n\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n\n    it('understands rowspan in the table', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr> <th rowspan=\"2\"></th> <th></th> <th></th> </tr>' +\n        '<tr> <th id=\"target\"></th> <td></td> </tr>' +\n        '</table>';\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n\n    it('understands rowspan on the cell', function () {\n      fixture.innerHTML =\n        '<table>' +\n        '<tr> <th></th> <th></th> </tr>' +\n        '<tr> <th id=\"target\" rowspan=\"2\"></th> <td></td> </tr>' +\n        '<tr> <td></td> </tr>' +\n        '</table>';\n      var target = $id('target');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.equal(axe.commons.table.getScope(target), 'row');\n    });\n  });\n\n  it('does not throw on empty rows', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr> </tr>' +\n      '<tr> <th id=\"target\">foo</th> <td>bar</td> </tr>' +\n      '</table>';\n    var target = $id('target');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.equal(axe.commons.table.getScope(target), 'auto');\n  });\n});\n"
  },
  {
    "path": "test/commons/table/is-column-header.js",
    "content": "describe('table.isColumnHeader', function () {\n  'use strict';\n\n  var table = axe.commons.table;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  beforeEach(function () {\n    fixtureSetup(\n      '<table>' +\n        '<tr>' +\n        '<th id=\"ch1\">column header 1</th>' +\n        '<th scope=\"col\" id=\"ch2\">column header 2</th>' +\n        '</tr>' +\n        '<tr>' +\n        '<th id=\"rh1\">row header 1</th>' +\n        '<td id=\"cell1\">cell 1</td>' +\n        '</tr>' +\n        '<tr>' +\n        '<th scope=\"row\" id=\"rh2\">row header 2</th>' +\n        '<td id=\"cell2\">cell 2</td>' +\n        '</tr>' +\n        '</table>'\n    );\n  });\n\n  it('returns false if not a column header', function () {\n    var cell = document.querySelector('#cell1');\n    assert.isFalse(table.isColumnHeader(cell));\n  });\n\n  it('returns true if scope=\"auto\"', function () {\n    var cell = document.querySelector('#ch1');\n    assert.isTrue(table.isColumnHeader(cell));\n  });\n\n  it('returns true if scope=\"col\"', function () {\n    var cell = document.querySelector('#ch2');\n    assert.isTrue(table.isColumnHeader(cell));\n  });\n\n  it('returns false if scope=\"row\"', function () {\n    var cell = document.querySelector('#rh2');\n    assert.isFalse(table.isColumnHeader(cell));\n  });\n});\n"
  },
  {
    "path": "test/commons/table/is-data-cell.js",
    "content": "describe('table.isDataCell', () => {\n  const fixture = document.getElementById('fixture');\n  const flatTreeSetup = axe.testUtils.flatTreeSetup;\n\n  it('should work with TH', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><th id=\"target\">1</th></tr>' + '</table>';\n    flatTreeSetup(fixture);\n\n    const target = document.getElementById('target');\n\n    assert.isFalse(axe.commons.table.isDataCell(target));\n  });\n\n  it('should work with TD', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td id=\"target\">1</td></tr>' + '</table>';\n    flatTreeSetup(fixture);\n\n    const target = document.getElementById('target');\n\n    assert.isTrue(axe.commons.table.isDataCell(target));\n  });\n\n  it('should work with empty TD', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td id=\"target\"></td></tr>' + '</table>';\n    flatTreeSetup(fixture);\n\n    const target = document.getElementById('target');\n\n    assert.isFalse(axe.commons.table.isDataCell(target));\n  });\n\n  it('should ignore TDs with a valid role other than (grid)cell', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"target1\" role=\"columnheader\">heading</td></tr>' +\n      '<tr><td id=\"target2\" role=\"rowheader\">heading</td></tr>' +\n      '<tr><td id=\"target3\" role=\"presentation\">heading</td></tr>' +\n      '</table>';\n    flatTreeSetup(fixture);\n\n    const target1 = document.getElementById('target1');\n    const target2 = document.getElementById('target2');\n    const target3 = document.getElementById('target3');\n    assert.isFalse(axe.commons.table.isDataCell(target1));\n    assert.isFalse(axe.commons.table.isDataCell(target2));\n    assert.isFalse(axe.commons.table.isDataCell(target3));\n  });\n\n  it('should return true for elements with role=\"(grid)cell\"', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><th id=\"target1\" role=\"cell\">heading</th></tr>' +\n      '<tr><th id=\"target2\" role=\"gridcell\">heading</th></tr>' +\n      '</table>';\n    flatTreeSetup(fixture);\n\n    const target1 = document.getElementById('target1');\n    const target2 = document.getElementById('target2');\n    assert.isTrue(axe.commons.table.isDataCell(target1));\n    assert.isTrue(axe.commons.table.isDataCell(target2));\n  });\n\n  it('should ignore invalid roles', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"target1\" role=\"foobar\">heading</td></tr>' +\n      '<tr><th id=\"target2\" role=\"foobar\">heading</th></tr>' +\n      '</table>';\n    flatTreeSetup(fixture);\n\n    const target1 = document.getElementById('target1');\n    const target2 = document.getElementById('target2');\n    assert.isTrue(axe.commons.table.isDataCell(target1));\n    assert.isFalse(axe.commons.table.isDataCell(target2));\n  });\n\n  it('should ignore abstract roles', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"target1\" role=\"section\">heading</td></tr>' +\n      '<tr><th id=\"target2\" role=\"section\">heading</th></tr>' +\n      '</table>';\n    flatTreeSetup(fixture);\n\n    const target1 = document.getElementById('target1');\n    const target2 = document.getElementById('target2');\n    assert.isTrue(axe.commons.table.isDataCell(target1));\n    assert.isFalse(axe.commons.table.isDataCell(target2));\n  });\n});\n"
  },
  {
    "path": "test/commons/table/is-data-table.js",
    "content": "describe('table.isDataTable', () => {\n  const fixture = document.getElementById('fixture');\n\n  it('should be false if the table has role=presentation', () => {\n    fixture.innerHTML =\n      '<table role=\"presentation\">' +\n      '<thead><tr><th>1</th><th>2</th></tr></thead>' +\n      '<tbody><tr><td>One</td><td>Two</td></tr></tbody>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if the table has role=none', () => {\n    fixture.innerHTML =\n      '<table role=\"none\">' +\n      '<thead><tr><th>1</th><th>2</th></tr></thead>' +\n      '<tbody><tr><td>One</td><td>Two</td></tr></tbody>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table is inside an editable area', () => {\n    fixture.innerHTML =\n      '<div contenteditable=\"true\">' +\n      '<table>' +\n      '<thead><tr><th>1</th><th>2</th></tr></thead>' +\n      '<tbody><tr><td>One</td><td>Two</td></tr></tbody>' +\n      '</table>' +\n      '</div>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a role of grid', () => {\n    fixture.innerHTML = '<table role=\"grid\"></table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a role of treegrid', () => {\n    fixture.innerHTML = '<table role=\"treegrid\"></table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the element has a role of table', () => {\n    fixture.innerHTML = '<div role=\"table\"></div>';\n\n    const node = fixture.querySelector('[role=\"table\"]');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  describe('should be true if the table has a landmark role', () => {\n    it('application', () => {\n      fixture.innerHTML = '<table role=\"application\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('banner', () => {\n      fixture.innerHTML = '<table role=\"banner\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('complementary', () => {\n      fixture.innerHTML = '<table role=\"complementary\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('contentinfo', () => {\n      fixture.innerHTML = '<table role=\"contentinfo\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('form', () => {\n      fixture.innerHTML = '<table role=\"form\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('main', () => {\n      fixture.innerHTML = '<table role=\"main\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('navigation', () => {\n      fixture.innerHTML = '<table role=\"navigation\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n    it('search', () => {\n      fixture.innerHTML = '<table role=\"search\"></table>';\n\n      const node = fixture.querySelector('table');\n      axe.testUtils.flatTreeSetup(fixture.firstChild);\n      assert.isTrue(axe.commons.table.isDataTable(node));\n    });\n  });\n\n  it('should be false if the table has datatable=0', () => {\n    fixture.innerHTML =\n      '<table datatable=\"0\">' +\n      '<thead><tr><th>1</th><th>2</th></tr></thead>' +\n      '<tbody><tr><td>One</td><td>Two</td></tr></tbody>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a summary attribute', () => {\n    fixture.innerHTML = '<table summary=\"Hello\">' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a caption element', () => {\n    fixture.innerHTML = '<table>' + '<caption>Hello</caption>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a col element', () => {\n    fixture.innerHTML = '<table>' + '<col>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a colgroup element', () => {\n    fixture.innerHTML = '<table>' + '<colgroup></colgroup>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a thead element', () => {\n    fixture.innerHTML = '<table>' + '<thead></thead>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a tfoot element', () => {\n    fixture.innerHTML = '<table>' + '<tfoot></tfoot>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a th element', () => {\n    fixture.innerHTML = '<table>' + '<tr><th></th></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a rowheader', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td role=\"rowheader\"></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a columnheader', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td role=\"columnheader\"></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a cell with headers attribute', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td headers=\"yes\"></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a cell with scope attribute', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td scope=\"col\"></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a cell with abbr attribute', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td abbr=\"yes\"></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if the table has a cell with an abbr element as a single child', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td><div><abbr>ok</abbr></div></td></tr>' + '</table>';\n\n    let node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n\n    fixture.innerHTML =\n      '<table>' + '<tr><td><abbr>ok</abbr><div></div></td></tr>' + '</table>';\n\n    node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n\n    fixture.innerHTML =\n      '<table>' + '<tr><td><abbr>ok</abbr></td></tr>' + '</table>';\n\n    node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if it has a nested table', () => {\n    fixture.innerHTML =\n      '<table id=\"out\"><tr><td>' +\n      '<table><tr><td></td></tr></table>' +\n      '</td></tr></table>';\n\n    const node = fixture.querySelector('#out');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if it has only one column', () => {\n    fixture.innerHTML =\n      '<table>' + '<tr><td></td></tr>' + '<tr><td></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if it has only one row', () => {\n    fixture.innerHTML = '<table>' + '<tr><td></td><td></td></tr>' + '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if it has 5 or more columns', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td><td></td><td></td><td></td></tr>' +\n      '<tr><td></td><td></td><td></td><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if it has borders around cells', () => {\n    fixture.innerHTML =\n      '<table border=\"1\">' +\n      '<tr><td></td><td></td></tr>' +\n      '<tr><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if it has zebra rows', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td></tr>' +\n      '<tr style=\"background: #fc0\"><td></td><td></td></tr>' +\n      '<tr><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be true if it has zebra rows - background image', () => {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td></td><td></td></tr>' +\n      '<tr style=\"background-image: url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOy' +\n      'DZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +\n      'AAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQ' +\n      'KGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7)\"><td></td><td></td></tr>' +\n      '<tr><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n  it('should be true if it has 20 or more rows', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(21).join('<tr><td></td><td></td><td></td></tr>') +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n  it('should be false if its width is 95% of the document width', () => {\n    fixture.innerHTML =\n      '<table style=\"width: 95.5%\">' +\n      new Array(3).join('<tr><td></td><td></td><td></td></tr>') +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if it has less than 10 cells', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td></tr>') +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if has an iframe element descendent', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td></tr>') +\n      '<tr><td><iframe src=\"javascript: void 0;\"></iframe></td><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if has an object element descendent', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td></tr>') +\n      '<tr><td><object></object></td><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should be false if has an embed element descendent', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td></tr>') +\n      '<tr><td><embed></td><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  // Causing sauce labs tests to fail & don't really care about applets\n  it.skip('should be false if has an applet element descendent', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td></tr>') +\n      '<tr><td><applet></applet></td><td></td><td></td></tr>' +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isFalse(axe.commons.table.isDataTable(node));\n  });\n\n  it('should otherwise be true', () => {\n    fixture.innerHTML =\n      '<table>' +\n      new Array(4).join('<tr><td></td><td></td><td></td><td></td></tr>') +\n      '</table>';\n\n    const node = fixture.querySelector('table');\n    axe.testUtils.flatTreeSetup(fixture.firstChild);\n    assert.isTrue(axe.commons.table.isDataTable(node));\n  });\n});\n"
  },
  {
    "path": "test/commons/table/is-header.js",
    "content": "describe('table.isHeader', function () {\n  'use strict';\n\n  var table = axe.commons.table;\n  var fixture = document.querySelector('#fixture');\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  var tableFixture =\n    '<table>' +\n    '<tr>' +\n    '<th id=\"ch1\">column header 1</th>' +\n    '<th scope=\"col\" id=\"ch2\">column header 2</th>' +\n    '</tr>' +\n    '<tr>' +\n    '<th id=\"rh1\">row header 1</th>' +\n    '<td id=\"cell1\">cell 1</td>' +\n    '</tr>' +\n    '<tr>' +\n    '<th scope=\"row\" id=\"rh2\">row header 2</th>' +\n    '<td id=\"cell2\">cell 2</td>' +\n    '</tr>' +\n    '</table>';\n\n  it('should return true for column headers', function () {\n    fixtureSetup(tableFixture);\n    var cell = document.querySelector('#ch1');\n    assert.isTrue(table.isHeader(cell));\n  });\n\n  it('should return true if cell is a row header', function () {\n    fixtureSetup(tableFixture);\n    var cell = document.querySelector('#rh1');\n    assert.isTrue(table.isHeader(cell));\n  });\n\n  it('should return false if cell is not a column or row header', function () {\n    fixtureSetup(tableFixture);\n    var cell = document.querySelector('#cell1');\n    assert.isFalse(table.isHeader(cell));\n  });\n\n  it('should return true if referenced by another cells headers attr', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"target\">1</td><td headers=\"bar target foo\"></tr>' +\n      '</table>';\n\n    var target = document.querySelector('#target');\n    fixtureSetup();\n    assert.isTrue(table.isHeader(target));\n  });\n\n  it('should return false otherwise', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"target\">1</td><td headers=\"bar monkeys foo\"></tr>' +\n      '</table>';\n\n    var target = document.querySelector('#target');\n    fixtureSetup();\n    assert.isFalse(table.isHeader(target));\n  });\n});\n"
  },
  {
    "path": "test/commons/table/is-row-header.js",
    "content": "describe('table.isRowHeader', function () {\n  'use strict';\n\n  var table = axe.commons.table;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  beforeEach(function () {\n    fixtureSetup(\n      '<table>' +\n        '<tr>' +\n        '<th id=\"ch1\">column header 1</th>' +\n        '<th scope=\"col\" id=\"ch2\">column header 2</th>' +\n        '</tr>' +\n        '<tr>' +\n        '<th id=\"rh1\">row header 1</th>' +\n        '<td id=\"cell1\">cell 1</td>' +\n        '</tr>' +\n        '<tr>' +\n        '<th scope=\"row\" id=\"rh2\">row header 2</th>' +\n        '<td id=\"cell2\">cell 2</td>' +\n        '</tr>' +\n        '</table>'\n    );\n  });\n\n  it('returns false if not a row header', function () {\n    var cell = document.querySelector('#cell1');\n    assert.isFalse(table.isRowHeader(cell));\n  });\n\n  it('returns true if scope=\"auto\"', function () {\n    var cell = document.querySelector('#rh1');\n    assert.isTrue(table.isRowHeader(cell));\n  });\n\n  it('returns false if scope=\"col\"', function () {\n    var cell = document.querySelector('#ch1');\n    assert.isFalse(table.isRowHeader(cell));\n  });\n\n  it('returns true if scope=\"row\"', function () {\n    var cell = document.querySelector('#rh2');\n    assert.isTrue(table.isRowHeader(cell));\n  });\n});\n"
  },
  {
    "path": "test/commons/table/to-grid.js",
    "content": "describe('table.toGrid', function () {\n  'use strict';\n  function $id(id) {\n    return document.getElementById(id);\n  }\n\n  var fixture = $id('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should work', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\">2</td><td id=\"t2\">ok</td></tr>' +\n      '<tr><td id=\"t3\">2</td><td id=\"t4\">ok</td></tr>' +\n      '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [\n      [$id('t1'), $id('t2')],\n      [$id('t3'), $id('t4')]\n    ]);\n  });\n\n  it('should have cells with a width > 1 span more than one position', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\" colspan=\"2\">2</td><td id=\"t2\">ok</td></tr>' +\n      '<tr><td id=\"t3\" colspan=\"3\">2</td></tr>' +\n      '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [\n      [$id('t1'), $id('t1'), $id('t2')],\n      [$id('t3'), $id('t3'), $id('t3')]\n    ]);\n  });\n\n  it('should have cells with height > 1 occupy more than one row', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\">2</td><td rowspan=\"2\" id=\"t2\">ok</td><td id=\"t3\"></td></tr>' +\n      '<tr><td id=\"t4\">4</td><td id=\"t5\">5</td></tr>' +\n      '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [\n      [$id('t1'), $id('t2'), $id('t3')],\n      [$id('t4'), $id('t2'), $id('t5')]\n    ]);\n  });\n\n  it('should work with both col and rowspans', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\" rowspan=\"2\" colspan=\"2\">2</td><td id=\"t2\">ok</td></tr>' +\n      '<tr><td id=\"t3\">ok</td></tr>' +\n      '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [\n      [$id('t1'), $id('t1'), $id('t2')],\n      [$id('t1'), $id('t1'), $id('t3')]\n    ]);\n  });\n\n  it('should handle rowspan=0', function () {\n    fixture.innerHTML =\n      '<table>' +\n      '<tr><td id=\"t1\">2</td><td rowspan=\"0\" id=\"t2\">ok</td><td id=\"t3\"></td></tr>' +\n      '<tr><td id=\"t4\">4</td><td id=\"t5\">5</td></tr>' +\n      '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [\n      [$id('t1'), $id('t2'), $id('t3')],\n      [$id('t4'), $id('t2'), $id('t5')]\n    ]);\n  });\n\n  it('should insert an empty array for empty rows', function () {\n    fixture.innerHTML =\n      '<table>' + '<tr></tr>' + '<tr><td id=\"t1\">ok</td></tr>' + '</table>';\n\n    var target = fixture.querySelector('table');\n\n    assert.deepEqual(axe.commons.table.toGrid(target), [[], [$id('t1')]]);\n  });\n});\n"
  },
  {
    "path": "test/commons/table/traverse.js",
    "content": "/* global fixture */\ndescribe('table.traverse', function () {\n  var table, dummyTable, topRight, bottomLeft;\n  beforeEach(function () {\n    table = axe.commons.table;\n    dummyTable = [\n      ['1a', '1b', '1c'],\n      ['2a', '2b', '2c'],\n      ['3a', '3b', '3c']\n    ];\n    topRight = { x: 0, y: 0 };\n    bottomLeft = { x: 2, y: 2 };\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('traverses in the `right` direction', function () {\n    var iterations = 0;\n    var expect = ['1b', '1c'];\n\n    table.traverse({ x: 1, y: 0 }, topRight, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n    assert.equal(iterations, expect.length);\n  });\n\n  it('returns an array of traversed cells', function () {\n    var result = table.traverse({ x: 1, y: 0 }, topRight, dummyTable);\n    assert.deepEqual(result, ['1b', '1c']);\n  });\n\n  it('traverses in the `down` direction', function () {\n    var iterations = 0;\n    var expect = ['2a', '3a'];\n\n    table.traverse({ x: 0, y: 1 }, topRight, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n    assert.equal(iterations, expect.length);\n  });\n\n  it('traverses in the `left` direction', function () {\n    var iterations = 0;\n    var expect = ['3b', '3a'];\n\n    table.traverse({ x: -1, y: 0 }, bottomLeft, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n    assert.equal(iterations, expect.length);\n  });\n\n  it('traverses in the `up` direction', function () {\n    var iterations = 0;\n    var expect = ['2c', '1c'];\n\n    table.traverse({ x: 0, y: -1 }, bottomLeft, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n    assert.equal(iterations, expect.length);\n  });\n\n  it('takes string values as directions', function () {\n    var iterations = 0;\n    var expect = ['1b', '1c'];\n    table.traverse('right', topRight, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n\n    iterations = 0;\n    expect = ['2a', '3a'];\n    table.traverse('down', topRight, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n\n    iterations = 0;\n    expect = ['3b', '3a'];\n    table.traverse('left', bottomLeft, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n\n    iterations = 0;\n    expect = ['2c', '1c'];\n    table.traverse('up', bottomLeft, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n  });\n\n  it('stops when the callback returned true', function () {\n    var iterations = 0;\n    table.traverse({ x: 1, y: 1 }, topRight, dummyTable, function (cell) {\n      assert.equal(cell, '2b'); // or not, to be?\n      iterations += 1;\n      return true;\n    });\n    assert.equal(iterations, 1);\n  });\n\n  it('starts at top-right of no position is given', function () {\n    var iterations = 0;\n    var expect = ['1b', '1c'];\n\n    table.traverse({ x: 1, y: 0 }, dummyTable, function (cell) {\n      assert.equal(cell, expect[iterations]);\n      iterations += 1;\n    });\n    assert.equal(iterations, expect.length);\n  });\n});\n"
  },
  {
    "path": "test/commons/text/accessible-text.js",
    "content": "describe('text.accessibleTextVirtual', () => {\n  const fixture = document.getElementById('fixture');\n  const { html, shadowSupport } = axe.testUtils;\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    axe._tree = null;\n  });\n\n  it('is called through accessibleText with a DOM node', () => {\n    const accessibleText = axe.commons.text.accessibleText;\n    fixture.innerHTML = html` <label><input type=\"button\" /></label> `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = fixture.querySelector('input');\n    assert.equal(accessibleText(target), '');\n  });\n\n  it('should match the first example from the ARIA spec', () => {\n    fixture.innerHTML = html`\n      <ul role=\"menubar\">\n        <!-- Rule 2A: \"File\" label via aria-labelledby -->\n        <li\n          role=\"menuitem\"\n          aria-haspopup=\"true\"\n          aria-labelledby=\"fileLabel\"\n          id=\"rule2a\"\n        >\n          <span id=\"fileLabel\">File</span>\n          <ul role=\"menu\">\n            <!-- Rule 2C: \"New\" label via Namefrom:contents -->\n            <li role=\"menuitem\" id=\"rule2c\">New</li>\n            <li role=\"menuitem\">Open…</li>\n            …\n          </ul>\n        </li>\n      </ul>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const rule2a = axe.utils.querySelectorAll(axe._tree, '#rule2a')[0];\n    const rule2c = axe.utils.querySelectorAll(axe._tree, '#rule2c')[0];\n\n    assert.equal(axe.commons.text.accessibleTextVirtual(rule2a), 'File');\n    assert.equal(axe.commons.text.accessibleTextVirtual(rule2c), 'New');\n  });\n\n  it('should match the second example from the ARIA spec', () => {\n    fixture.innerHTML = html`\n      <fieldset>\n        <legend>Meeting alarms</legend>\n        <!-- Rule 2A: \"Beep\" label given by native HTML label element -->\n        <input type=\"checkbox\" id=\"beep\" /> <label for=\"beep\">Beep</label>\n        <br />\n        <input type=\"checkbox\" id=\"mtgTitle\" />\n        <label for=\"mtgTitle\">Display the meeting title</label> <br />\n        <!-- Rule 2B -->\n        <input type=\"checkbox\" id=\"flash\" />\n        <label for=\"flash\">\n          Flash the screen\n          <!-- Rule 2A: label of text input given by aria-label, \"Number of times to flash screen\" -->\n          <input\n            type=\"text\"\n            value=\"3\"\n            size=\"2\"\n            id=\"numTimes\"\n            title=\"Number of times to flash screen\"\n          />\n          times\n        </label>\n      </fieldset>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const rule2a = axe.utils.querySelectorAll(axe._tree, '#beep')[0];\n    const rule2b = axe.utils.querySelectorAll(axe._tree, '#flash')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(rule2a), 'Beep');\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(rule2b),\n      'Flash the screen 3 times'\n    );\n  });\n\n  it('should use aria-labelledby if present', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is a label'\n    );\n  });\n\n  it('should use recusive aria-labelledby properly', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1 t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'ARIA Label This is a label'\n    );\n  });\n\n  it('should include hidden text referred to with aria-labelledby', () => {\n    fixture.innerHTML = html`\n      <div id=\"t1label\" style=\"display:none\">\n        This is a\n        <span style=\"visibility:hidden\">hidden </span>\n        <span aria-hidden=\"true\">secret</span>\n      </div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t1\" aria-labelledby=\"t1label\" />'\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is a hidden secret'\n    );\n  });\n\n  it('should allow setting the initial includeHidden value', () => {\n    fixture.innerHTML = html`\n      <label id=\"lbl1\" style=\"display:none;\">hidden label</label>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#lbl1')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target, {\n        includeHidden: false\n      }),\n      ''\n    );\n\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target, {\n        includeHidden: true\n      }),\n      'hidden label'\n    );\n  });\n\n  it('should use aria-label if present with no labelledby', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input type=\"text\" value=\"the value\" aria-label=\"ARIA Label\" id=\"t1\" />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'ARIA Label');\n  });\n\n  it('should use alt on imgs with no ARIA', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is <input type=\"text\" value=\"the value\" id=\"t1\" /> of\n        <i>everything</i>\n      </div>\n      <img alt=\"Alt text goes here\" id=\"target\" />\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'Alt text goes here'\n    );\n  });\n\n  it('should use alt on image inputs with no ARIA', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is <input type=\"text\" value=\"the value\" id=\"t1\" /> of\n        <i>everything</i>\n      </div>\n      <input type=\"image\" alt=\"Alt text goes here\" id=\"target\" />\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />'\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'Alt text goes here'\n    );\n  });\n\n  it('should use not use alt on text inputs with no ARIA', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is <input type=\"text\" value=\"the value\" id=\"t1\" /> of\n        <i>everything</i>\n      </div>\n      <input type=\"text\" alt=\"Alt text goes here\" id=\"target\" />\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n  });\n\n  it('should use HTML label if no ARIA information', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is <input type=\"text\" value=\"the value\" id=\"t1\" /> of\n        <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'HTML Label');\n  });\n\n  it('should handle last ditch title attribute', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i title=\"italics\"></i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is This is a label of italics'\n    );\n  });\n\n  it('should handle totally empty elements', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i></i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is This is a label of'\n    );\n  });\n\n  it('should handle author name-from roles properly', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\" role=\"heading\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i role=\"alert\">everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0];\n    // Chrome 114: \"This is the value of \"\n    // Firefox 115: \"This is ARIA Label the value everything\"\n    // Safari 16.5: This is the value This is a label of everything\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is This is a label of everything'\n    );\n  });\n\n  it('should only show each node once when label is before input', () => {\n    fixture.innerHTML = html`\n      <div id=\"target\">\n        <label for=\"tb1\">My form input</label> <input type=\"text\" id=\"tb1\" />\n      </div>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'My form input'\n    );\n  });\n\n  it('should only show each node once when label follows input', () => {\n    fixture.innerHTML = html`\n      <div id=\"target\">\n        <input type=\"text\" id=\"tb1\" />\n      </div>\n      <label for=\"tb1\">My form input</label>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'My form input'\n    );\n  });\n\n  it('should handle nested inputs in normal context', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2label')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is This is a label of everything'\n    );\n  });\n\n  it('should use handle nested inputs properly in labelledby context', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          aria-label=\"ARIA Label\"\n          id=\"t1\"\n        />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    // Chrome 114: This is the value of everything\n    // Firefox 115: This is ARIA Label the value of everything\n    // Safari 16.5: THis is This is a label of everything\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is ARIA Label of everything'\n    );\n  });\n\n  it('should use ignore hidden inputs', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is <input type=\"hidden\" value=\"the value\" Label\" id=\"t1\"> of\n        <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is of everything'\n    );\n  });\n\n  it('should use handle inputs with no type as if they were text inputs', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <input value=\"the value\" aria-labelledby=\"t1label\" id=\"t1\" />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    // Chrome 114: \"This is the value of everything\"\n    // Firefox 115: \"This is the value of everything\"\n    // Safari 16.5: \"This is This is a label of everything\"\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is the value of everything'\n    );\n  });\n\n  it('should use handle nested selects properly in labelledby context', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <select multiple aria-labelledby=\"t1label\" id=\"t1\">\n          <option selected>first</option>\n          <option>second</option>\n          <option selected>third</option>\n        </select>\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    // Chrome 114: \"This is first third of everything\"\n    // Firefox 115: \"This is of everything\"\n    // Safari 16.5: \"This is first third of everything\"\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is first third of everything'\n    );\n  });\n\n  it('should use handle nested textareas properly in labelledby context', () => {\n    fixture.innerHTML = html`\n      <div id=\"t2label\">\n        This is\n        <textarea aria-labelledby=\"t1label\" id=\"t1\">the value</textarea>\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    // Chrome 114: \"This is the value of everything\"\n    // Firefox 115: \"This is the value of everything\"\n    // Safari 16.5: \"This is This is a label of everything\"\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This is the value of everything'\n    );\n  });\n\n  it('should use handle ARIA labels properly in labelledby context', () => {\n    fixture.innerHTML = html` <div id=\"t2label\">\n        This\n        <span aria-label=\"not a span\">span</span>\n        is\n        <input\n          type=\"text\"\n          value=\"the value\"\n          aria-labelledby=\"t1label\"\n          id=\"t1\"\n        />\n        of <i>everything</i>\n      </div>\n      <div id=\"t1label\">This is a <b>label</b></div>\n      <label for=\"t1\">HTML Label</label>\n      <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />`;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, '#t2')[0];\n    assert.equal(\n      axe.commons.text.accessibleTextVirtual(target),\n      'This not a span is the value of everything'\n    );\n  });\n\n  it('should come up empty if input is labeled only by select options', () => {\n    fixture.innerHTML = html`\n      <label for=\"target\">\n        <select id=\"select\">\n          <option selected=\"selected\">Chosen</option>\n          <option>Not Selected</option>\n        </select>\n      </label>\n      <input id=\"target\" type=\"text\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    // Chrome 114: \"Chosen\"\n    // Firefox 115: \"Chosen\"\n    // Safari 16.5: \"Chosen\"\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Chosen');\n  });\n\n  it(\"should be empty if input is labeled by labeled select (ref'd string labels have spotty support)\", () => {\n    fixture.innerHTML = html`\n      <label for=\"select\">My Select</label>\n      <label for=\"target\">\n        <select id=\"select\">\n          <option selected=\"selected\">Chosen</option>\n          <option>Not Selected</option>\n        </select>\n      </label>\n      <input id=\"target\" type=\"text\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    // Chrome 114: \"Chosen\"\n    // Firefox 115: \"Chosen\"\n    // Safari 16.5: \"Chosen\"\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Chosen');\n  });\n\n  it('should be empty for an empty label wrapping a select', () => {\n    fixture.innerHTML = html`\n      <label>\n        <span class=\"label\"></span>\n        <select id=\"target\">\n          <option value=\"1\" selected=\"selected\">Please choose a region</option>\n          <option value=\"2\">Coastal</option>\n          <option value=\"3\">Forest</option>\n          <option value=\"4\">Grasslands</option>\n          <option value=\"5\">Mountains</option>\n        </select>\n      </label>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n  });\n\n  it('should not return select options if input is aria-labelled by a select', () => {\n    fixture.innerHTML = html`\n      <label>\n        <select id=\"select\">\n          <option selected=\"selected\">Chosen</option>\n          <option>Not Selected</option>\n        </select>\n      </label>\n      <input aria-labelledby=\"select\" type=\"text\" id=\"target\" />\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, '#target')[0];\n    // Chrome 114: \"Chosen\"\n    // Firefox 115: \"Chosen\"\n    // Safari 16.5: \"Chosen\"\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Chosen');\n  });\n\n  it('shoud properly fall back to title', () => {\n    fixture.innerHTML = html`\n      <a href=\"#\" role=\"presentation\" title=\"Hello\"></a>\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=presentation on anchors', () => {\n    fixture.innerHTML = html` <a href=\"#\" role=\"presentation\">Hello</a> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=presentation on buttons', () => {\n    fixture.innerHTML = html` <button role=\"presentation\">Hello</button> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=presentation on summary', () => {\n    fixture.innerHTML = html` <summary role=\"presentation\">Hello</summary> `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, 'summary')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('shoud properly fall back to title', () => {\n    fixture.innerHTML = html` <a href=\"#\" role=\"none\" title=\"Hello\"></a> `;\n    axe.testUtils.flatTreeSetup(fixture);\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=none on anchors', () => {\n    fixture.innerHTML = html` <a href=\"#\" role=\"none\">Hello</a> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=none on buttons', () => {\n    fixture.innerHTML = html` <button role=\"none\">Hello</button> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should give text even for role=none on summary', () => {\n    fixture.innerHTML = html` <summary role=\"none\">Hello</summary> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'summary')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n  });\n\n  it('should not add extra spaces around phrasing elements', () => {\n    fixture.innerHTML = html` <a href=\"#\">Hello<span>World</span></a> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'HelloWorld');\n  });\n\n  it('should add spaces around non-phrasing elements', () => {\n    fixture.innerHTML = html`\n      <a href=\"#\"\n        >Hello\n        <div>World</div></a\n      >\n    `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello World');\n  });\n\n  it('should not look at scripts', () => {\n    fixture.innerHTML =\n      '<a href=\"#\"><script> const ajiasdf = true; </script></a>';\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n  });\n\n  it('should use <label> for input buttons', () => {\n    fixture.innerHTML = html` <label><input type=\"button\" /></label> `;\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n  });\n\n  it('should not stop when attributes contain whitespace', () => {\n    fixture.innerHTML =\n      '<button aria-label=\" \" aria-labelledby=\" \">Hello World</button>';\n    axe.testUtils.flatTreeSetup(fixture);\n\n    const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n    assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello World');\n  });\n\n  (!!document.fonts ? it : xit)(\n    'should allow ignoring icon ligatures',\n    done => {\n      const materialFont = new FontFace(\n        'Material Icons',\n        'url(https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2)'\n      );\n      materialFont.load().then(() => {\n        document.fonts.add(materialFont);\n\n        fixture.innerHTML =\n          '<button id=\"target\">Hello World<span style=\"font-family: \\'Material Icons\\'\">delete</span></button>';\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n        try {\n          assert.equal(\n            axe.commons.text.accessibleTextVirtual(target, {\n              ignoreIconLigature: true\n            }),\n            'Hello World'\n          );\n          done();\n        } catch (err) {\n          done(err);\n        }\n      });\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should only find aria-labelledby element in the same context ',\n    () => {\n      fixture.innerHTML = html`\n        <div id=\"t2label\">\n          This is\n          <input\n            type=\"text\"\n            value=\"the value\"\n            aria-labelledby=\"t1label\"\n            aria-label=\"ARIA Label\"\n            id=\"t1\"\n          />\n          of <i>everything</i>\n        </div>\n        <div id=\"shadow\"></div>\n      `;\n\n      const shadow = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = html`\n        <div id=\"t1label\">This is a <b>label</b></div>\n        <label for=\"t1\">HTML Label</label>\n        <input type=\"text\" id=\"t2\" aria-labelledby=\"t2label\" />\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = axe.utils.querySelectorAll(axe._tree, '#t1')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'ARIA Label'\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find attributes within a shadow tree',\n    () => {\n      fixture.innerHTML = html` <div id=\"shadow\"></div> `;\n\n      const shadow = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<input type=\"text\" id=\"t1\" title=\"I will be king\">';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'I will be king'\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find attributes within a slot on the shadow tree',\n    () => {\n      fixture.innerHTML =\n        '<div id=\"shadow\"><input type=\"text\" id=\"t1\" title=\"you will be queen\"></div>';\n\n      const shadow = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<slot></slot>';\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'you will be queen'\n      );\n    }\n  );\n\n  (shadowSupport.v1 ? it : xit)(\n    'should find fallback content for shadow DOM',\n    () => {\n      fixture.innerHTML = html` <div id=\"shadow\"></div> `;\n\n      const shadow = document\n        .getElementById('shadow')\n        .attachShadow({ mode: 'open' });\n      shadow.innerHTML = html`\n        <input type=\"text\" id=\"t1\" />\n        <label for=\"t1\"><slot>Fallback content heroes</slot></label>\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Fallback content heroes'\n      );\n    }\n  );\n\n  describe('figure', () => {\n    it('should check aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <figure aria-labelledby=\"t1\">\n          Not part of a11yName\n          <figcaption>Fail</figcaption>\n        </figure>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should check aria-label', () => {\n      fixture.innerHTML = html`\n        <figure aria-label=\"Hello\">\n          Not part of a11yName\n          <figcaption>Fail</figcaption>\n        </figure>\n        ';\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should check the figures figcaption', () => {\n      fixture.innerHTML =\n        '<figure>Not part of a11yName <figcaption>Hello</figcaption></figure>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should check title on figure', () => {\n      fixture.innerHTML =\n        '<figure title=\"Hello\">Not part of a11yName <figcaption></figcaption></figure>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should fall back to innerText of figure', () => {\n      fixture.innerHTML = html`\n        <figure>\n          Hello\n          <figcaption></figcaption>\n        </figure>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    (shadowSupport.v1 ? it : xit)(\n      'should check within the composed (shadow) tree',\n      () => {\n        const node = document.createElement('div');\n        node.innerHTML = 'Hello';\n        const shadowRoot = node.attachShadow({ mode: 'open' });\n        shadowRoot.innerHTML =\n          '<figure>Not part of a11yName <figcaption><slot></slot></figcaption></figure>';\n        fixture.appendChild(node);\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'figure')[0];\n        assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n      }\n    );\n  });\n\n  describe('img', () => {\n    it('should work with aria-labelledby attribute', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <img aria-labelledby=\"t1 t2\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'img')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with aria-label attribute', () => {\n      fixture.innerHTML = html` <img aria-label=\"Hello World\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'img')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with alt attribute', () => {\n      fixture.innerHTML = html` <img alt=\"Hello World\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'img')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with title attribute', () => {\n      fixture.innerHTML = html` <img title=\"Hello World\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'img')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n  });\n\n  describe('input buttons', () => {\n    it('should find value for input type=button', () => {\n      fixture.innerHTML = html` <input type=\"button\" value=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should find value for input type=reset', () => {\n      fixture.innerHTML = html` <input type=\"reset\" value=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should find value for input type=submit', () => {\n      fixture.innerHTML = html` <input type=\"submit\" value=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should provide a default value for input type=\"submit\"', () => {\n      fixture.innerHTML = html` <input type=\"submit\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        target.actualNode.value || 'Submit'\n      );\n    });\n\n    it('should provide a default value for input type=\"reset\"', () => {\n      fixture.innerHTML = html` <input type=\"reset\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      const defaultText = axe.commons.text.accessibleTextVirtual(target);\n      assert.isString(defaultText);\n      assert.equal(defaultText, target.actualNode.value || 'Reset');\n    });\n\n    it('should find title for input type=button', () => {\n      fixture.innerHTML = html` <input type=\"button\" title=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Hello');\n    });\n\n    it('should find title for input type=reset', () => {\n      fixture.innerHTML = html` <input type=\"reset\" title=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      // IE does not use title; but will use default value instead\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        target.actualNode.value || 'Hello'\n      );\n    });\n\n    it('should find title for input type=submit', () => {\n      fixture.innerHTML = html` <input type=\"submit\" title=\"Hello\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      // Again, default value takes precedence over title\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        target.actualNode.value || 'Hello'\n      );\n    });\n  });\n\n  describe('tables', () => {\n    it('should work with aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <table aria-labelledby=\"t1 t2\"></table>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with aria-label', () => {\n      fixture.innerHTML = html` <table aria-label=\"Hello World\"></table> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with the caption element', () => {\n      fixture.innerHTML =\n        '<table><caption>Hello World</caption><tr><td>Stuff</td></tr></table>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with the title attribute', () => {\n      fixture.innerHTML = html` <table title=\"Hello World\"></table> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should work with the summary attribute', () => {\n      fixture.innerHTML = html` <table summary=\"Hello World\"></table> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should prefer summary attribute over title attribute', () => {\n      // Chrome 114: \"Hello world\"\n      // Firefox 115: \"Hello world\"\n      // Safari 16.5: \"Hello world\"\n      fixture.innerHTML = html`\n        <table summary=\"Hello World\" title=\"FAIL\"></table>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'table')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n  });\n\n  describe('text inputs', () => {\n    const types = ['text', 'password', 'search', 'tel', 'email', 'url', null];\n\n    it('should find aria-labelledby', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = html`\n          <div id=\"t1\">Hello</div>\n          <div id=\"t2\">World</div>\n          <input ${t} aria-labelledby=\"t1 t2\" />\n        `;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find aria-label', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<input ${t} aria-label=\"Hello World\">`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find an implicit label', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<label for=\"t1\">Hello World<input ${t}></label>`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find an explicit label', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<label for=\"t1\">Hello World</label><input ${t} id=\"t1\">`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find implicit labels with id that does not match to a label', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<label for=\"t1\">Hello World<input ${t} id=\"foo\"></label>`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find a placeholder attribute', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<input ${t} placeholder=\"Hello World\">`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should find a title attribute', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<input ${t} title=\"Hello World\">`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(\n          axe.commons.text.accessibleTextVirtual(target),\n          'Hello World',\n          type\n        );\n      });\n    });\n\n    it('should otherwise be empty string', () => {\n      types.forEach(function (type) {\n        const t = type ? ' type=\"' + type + '\"' : '';\n        fixture.innerHTML = `<input ${t}>`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n        assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n      });\n    });\n  });\n\n  describe('textarea', () => {\n    it('should find aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <textarea aria-labelledby=\"t1 t2\"></textarea>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find aria-label', () => {\n      fixture.innerHTML = html`\n        <textarea aria-label=\"Hello World\"></textarea>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find an implicit label', () => {\n      fixture.innerHTML = `<label for=\"t1\">Hello World<textarea></textarea></label>`;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find an explicit label', () => {\n      fixture.innerHTML = `<label for=\"t1\">Hello World</label><textarea id=\"t1\"></textarea>`;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find a placeholder attribute', () => {\n      fixture.innerHTML = html`\n        <textarea placeholder=\"Hello World\"></textarea>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find a title attribute', () => {\n      fixture.innerHTML = html` <textarea title=\"Hello World\"></textarea> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should otherwise be empty string', () => {\n      fixture.innerHTML = html` <textarea></textarea> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'textarea')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n    });\n  });\n\n  describe('image inputs', () => {\n    it('should find aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <input type=\"image\" aria-labelledby=\"t1 t2\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find aria-label', () => {\n      fixture.innerHTML = html`\n        <input type=\"image\" aria-label=\"Hello World\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find an alt attribute', () => {\n      fixture.innerHTML = html` <input type=\"image\" alt=\"Hello World\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find a title attribute', () => {\n      fixture.innerHTML = html` <input type=\"image\" title=\"Hello World\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should otherwise be \"Submit\" string', () => {\n      fixture.innerHTML = html` <input type=\"image\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'input')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), 'Submit');\n    });\n  });\n\n  describe('a', () => {\n    it('should find aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <a aria-labelledby=\"t1 t2\"></a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find aria-label', () => {\n      fixture.innerHTML = html` <a aria-label=\"Hello World\"></a> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should check subtree', () => {\n      fixture.innerHTML = html`\n        <a\n          ><span>Hello<span> World</span></span></a\n        >\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find a title attribute', () => {\n      fixture.innerHTML = html` <a title=\"Hello World\"></a> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should otherwise be empty string', () => {\n      fixture.innerHTML = html` <a></a> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n    });\n\n    it('should use text from a table with a single cell and role=presentation', () => {\n      fixture.innerHTML = html`\n        <a href=\"example.html\">\n          <table role=\"presentation\">\n            <tr>\n              <td>Descriptive Link Text</td>\n            </tr>\n          </table>\n        </a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'a')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Descriptive Link Text'\n      );\n    });\n  });\n\n  describe('button', () => {\n    it('should find aria-labelledby', () => {\n      fixture.innerHTML = html`\n        <div id=\"t1\">Hello</div>\n        <div id=\"t2\">World</div>\n        <button aria-labelledby=\"t1 t2\"></button>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find aria-label', () => {\n      fixture.innerHTML = html` <button aria-label=\"Hello World\"></button> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should check subtree', () => {\n      fixture.innerHTML =\n        '<button><span>Hello<span> World</span></span></button>';\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should find a title attribute', () => {\n      fixture.innerHTML = html` <button title=\"Hello World\"></button> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n      assert.equal(\n        axe.commons.text.accessibleTextVirtual(target),\n        'Hello World'\n      );\n    });\n\n    it('should otherwise be empty string', () => {\n      fixture.innerHTML = html` <button></button> `;\n      axe.testUtils.flatTreeSetup(fixture);\n\n      const target = axe.utils.querySelectorAll(axe._tree, 'button')[0];\n      assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n    });\n  });\n\n  describe('text level semantics', () => {\n    const tags = [\n      'em',\n      'strong',\n      'small',\n      's',\n      'cite',\n      'q',\n      'dfn',\n      'abbr',\n      'time',\n      'code',\n      'var',\n      'samp',\n      'kbd',\n      'sub',\n      'sup',\n      'i',\n      'b',\n      'u',\n      'mark',\n      'ruby',\n      'rt',\n      'rp',\n      'bdi',\n      'bdo',\n      'br',\n      'wbr'\n    ];\n\n    it('should find aria-labelledby', () => {\n      tags.forEach(function (tag) {\n        fixture.innerHTML = html`\n          <div id=\"t1\">Hello</div>\n          <div id=\"t2\">World</div>\n        `;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const elm = document.createElement(tag);\n        elm.setAttribute('aria-labelledby', 't1 t2');\n        elm.style.display = 'inline'; // Firefox hides some of these elements because reasons...\n        fixture.appendChild(elm);\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.getNodeFromTree(elm);\n        const result = axe.commons.text.accessibleTextVirtual(target);\n        assert.equal(result, 'Hello World', tag);\n      });\n    });\n\n    it('should find aria-label', () => {\n      tags.forEach(function (tag) {\n        const elm = document.createElement(tag);\n        elm.setAttribute('aria-label', 'Hello World');\n        elm.style.display = 'inline'; // Firefox hack, see above\n        fixture.appendChild(elm);\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.getNodeFromTree(elm);\n        const result = axe.commons.text.accessibleTextVirtual(target);\n        assert.equal(result, 'Hello World', tag);\n      });\n    });\n\n    it('should find a title attribute', () => {\n      tags.forEach(function (tag) {\n        const elm = document.createElement(tag);\n        elm.setAttribute('title', 'Hello World');\n        elm.style.display = 'inline'; // Firefox hack, see above\n        fixture.appendChild(elm);\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.getNodeFromTree(elm);\n        const result = axe.commons.text.accessibleTextVirtual(target);\n        assert.equal(result, 'Hello World', tag);\n      });\n    });\n\n    it('should otherwise be empty string', () => {\n      tags.forEach(function (tag) {\n        fixture.innerHTML = `<${tag}></${tag}>`;\n        axe.testUtils.flatTreeSetup(fixture);\n\n        const target = axe.utils.querySelectorAll(axe._tree, tag)[0];\n        assert.equal(axe.commons.text.accessibleTextVirtual(target), '');\n      });\n    });\n\n    it('inserts a space before img alt text', () => {\n      const accessibleText = axe.commons.text.accessibleText;\n      fixture.innerHTML =\n        '<a id=\"test\" href=\"../images/index.html\">Images tool test page<img id=\"img18\" aria-label=\"aria-label text\" alt=\"logo\" src=\"../images/Accessibility.jpg\" width=\"50\" height=\"50\"></a>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(\n        accessibleText(target),\n        'Images tool test page aria-label text'\n      );\n    });\n  });\n\n  describe('text.accessibleText acceptance tests', () => {\n    'use strict';\n    // Tests borrowed from the AccName 1.1 testing docs\n    // https://www.w3.org/wiki/AccName_1.1_Testable_Statements#Name_test_case_539\n\n    const ariaValuetext = xit; // Not acc supported\n    const pseudoText = xit; // Not acc supported\n\n    const accessibleText = axe.commons.text.accessibleText;\n    let _unsupported;\n\n    before(() => {\n      _unsupported = axe.commons.text.unsupported.accessibleNameFromFieldValue;\n      axe.commons.text.unsupported.accessibleNameFromFieldValue = [];\n    });\n    after(() => {\n      axe.commons.text.unsupported.accessibleNameFromFieldValue = _unsupported;\n    });\n\n    afterEach(() => {\n      fixture.innerHTML = html``;\n      axe._tree = null;\n    });\n\n    it('passes test 1', () => {\n      fixture.innerHTML = html`\n        <input type=\"button\" aria-label=\"Rich\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Rich');\n    });\n\n    it('passes test 2', () => {\n      fixture.innerHTML = html`\n        <div id=\"ID1\">Rich's button</div>\n        <input type=\"button\" aria-labelledby=\"ID1\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), \"Rich's button\");\n    });\n\n    it('passes test 3', () => {\n      fixture.innerHTML = html`\n        <div id=\"ID1\">Rich's button</div>\n        <input type=\"button\" aria-label=\"bar\" aria-labelledby=\"ID1\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), \"Rich's button\");\n    });\n\n    it('passes test 4', () => {\n      fixture.innerHTML = html` <input type=\"reset\" id=\"test\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Reset');\n    });\n\n    it('passes test 5', () => {\n      fixture.innerHTML = html` <input type=\"button\" id=\"test\" value=\"foo\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 6', () => {\n      fixture.innerHTML =\n        '<input src=\"baz.html\" type=\"image\" id=\"test\" alt=\"foo\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 7', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">States:</label>\n        <input type=\"text\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'States:');\n    });\n\n    it('passes test 8', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"text\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 9', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"text\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    ariaValuetext('passes test 10', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"text\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    it('passes test 11', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"text\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 12', () => {\n      fixture.innerHTML =\n        '<input type=\"text\" id=\"test\" title=\"crazy\" value=\"baz\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    pseudoText('passes test 13', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"text\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 14', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          [data-after]:after {\n            content: attr(data-after);\n          }\n        </style>\n        <label for=\"test\" data-after=\"test content\"></label>\n        <input type=\"text\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'test content');\n    });\n\n    it('passes test 15', () => {\n      fixture.innerHTML = html`\n        <img id=\"test\" src=\"foo.jpg\" aria-label=\"1\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '1');\n    });\n\n    it('passes test 16', () => {\n      fixture.innerHTML =\n        '<img id=\"test\" src=\"foo.jpg\" aria-label=\"1\" alt=\"a\" title=\"t\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '1');\n    });\n\n    // To the best of my knowledge, this test is incorrect\n    // Chrome and Firefox seem to return \"peanuts\", so does axe-core.\n    xit('passes test 17', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"test\" />\n        <img aria-labelledby=\"test\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '');\n    });\n\n    it('passes test 18', () => {\n      fixture.innerHTML =\n        '<img id=\"test\" aria-labelledby=\"test\" src=\"foo.jpg\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '');\n    });\n\n    // To the best of my knowledge, this test is incorrect\n    // Chrome and Firefox seem to return \"peanuts\", so does axe-core.\n    xit('passes test 19', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"test\" />\n        <img aria-labelledby=\"test\" aria-label=\"1\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '');\n    });\n\n    it('passes test 20', () => {\n      fixture.innerHTML =\n        '<img id=\"test\" aria-labelledby=\"test\" aria-label=\"1\" src=\"foo.jpg\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '1');\n    });\n\n    it('passes test 21', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"ID1\" />\n        <input type=\"text\" value=\"popcorn\" id=\"ID2\" />\n        <input type=\"text\" value=\"apple jacks\" id=\"ID3\" />\n        <img aria-labelledby=\"ID1 ID2 ID3\" id=\"test\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'peanuts popcorn apple jacks');\n    });\n\n    it('passes test 22', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"ID1\" />\n        <img\n          id=\"test\"\n          aria-label=\"l\"\n          aria-labelledby=\"test ID1\"\n          src=\"foo.jpg\"\n        />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'l peanuts');\n    });\n\n    it('passes test 23', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"ID1\" />\n        <input type=\"text\" value=\"popcorn\" id=\"ID2\" />\n        <img\n          id=\"test\"\n          aria-label=\"l\"\n          aria-labelledby=\"test ID1 ID2\"\n          src=\"foo.jpg\"\n        />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'l peanuts popcorn');\n    });\n\n    it('passes test 24', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"ID1\" />\n        <input type=\"text\" value=\"popcorn\" id=\"ID2\" />\n        <input type=\"text\" value=\"apple jacks\" id=\"ID3\" />\n        <img\n          id=\"test\"\n          aria-label=\"l\"\n          aria-labelledby=\"test ID1 ID2 ID3\"\n          alt=\"a\"\n          title=\"t\"\n          src=\"foo.jpg\"\n        />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'l peanuts popcorn apple jacks');\n    });\n\n    it('passes test 25', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" value=\"peanuts\" id=\"ID1\" />\n        <input type=\"text\" value=\"popcorn\" id=\"ID2\" />\n        <input type=\"text\" value=\"apple jacks\" id=\"ID3\" />\n        <img\n          id=\"test\"\n          aria-label=\"\"\n          aria-labelledby=\"test ID1 ID2 ID3\"\n          alt=\"\"\n          title=\"t\"\n          src=\"foo.jpg\"\n        />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 't peanuts popcorn apple jacks');\n    });\n\n    it('passes test 26', () => {\n      fixture.innerHTML = html`\n        <div id=\"test\" aria-labelledby=\"ID1\">foo</div>\n        <span id=\"ID1\">bar</span>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'bar');\n    });\n\n    it('passes test 27', () => {\n      fixture.innerHTML = html` <div id=\"test\" aria-label=\"Tag\">foo</div> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Tag');\n    });\n\n    it('passes test 28', () => {\n      fixture.innerHTML = html`\n        <div id=\"test\" aria-labelledby=\"ID1\" aria-label=\"Tag\">foo</div>\n        <span id=\"ID1\">bar</span>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'bar');\n    });\n\n    it('passes test 29', () => {\n      fixture.innerHTML = html`\n        <div id=\"test\" aria-labelledby=\"ID0 ID1\" aria-label=\"Tag\">foo</div>\n        <span id=\"ID0\">bar</span>\n        <span id=\"ID1\">baz</span>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'bar baz');\n    });\n\n    // Should only pass in strict mode\n    it('passes test 30', () => {\n      fixture.innerHTML = html` <div id=\"test\">Div with text</div> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target, { strict: true }), '');\n    });\n\n    it('passes test 31', () => {\n      fixture.innerHTML = html` <div id=\"test\" role=\"button\">foo</div> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 32', () => {\n      fixture.innerHTML = html`\n        <div\n          id=\"test\"\n          role=\"button\"\n          title=\"Tag\"\n          style=\"outline:medium solid black; width:2em; height:1em;\"\n        ></div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Tag');\n    });\n\n    it('passes test 33', () => {\n      fixture.innerHTML = html`\n        <div id=\"ID1\">foo</div>\n        <a id=\"test\" href=\"test.html\" aria-labelledby=\"ID1\">bar</a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 34', () => {\n      fixture.innerHTML = html`\n        <a id=\"test\" href=\"test.html\" aria-label=\"Tag\">ABC</a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Tag');\n    });\n\n    it('passes test 35', () => {\n      fixture.innerHTML = html`\n        <a href=\"test.html\" id=\"test\" aria-labelledby=\"ID1\" aria-label=\"Tag\"\n          >foo</a\n        >\n        <p id=\"ID1\">bar</p>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'bar');\n    });\n\n    it('passes test 36', () => {\n      fixture.innerHTML = html`\n        <a\n          href=\"test.html\"\n          id=\"test\"\n          aria-labelledby=\"test ID1\"\n          aria-label=\"Tag\"\n        ></a>\n        <p id=\"ID1\">foo</p>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Tag foo');\n    });\n\n    it('passes test 37', () => {\n      fixture.innerHTML = html` <a href=\"test.html\" id=\"test\">ABC</a> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'ABC');\n    });\n\n    it('passes test 38', () => {\n      fixture.innerHTML = html`\n        <a href=\"test.html\" id=\"test\" title=\"Tag\"></a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Tag');\n    });\n\n    it('passes test 39', () => {\n      fixture.innerHTML = html`\n        <input id=\"test\" type=\"text\" aria-labelledby=\"ID1 ID2 ID3\" />\n        <p id=\"ID1\">foo</p>\n        <p id=\"ID2\">bar</p>\n        <p id=\"ID3\">baz</p>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    it('passes test 40', () => {\n      fixture.innerHTML = html`\n        <input id=\"test\" type=\"text\" aria-label=\"bar\" aria-labelledby=\"ID1 test\">\n        <div id=\"ID1\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar');\n    });\n\n    it('passes test 41', () => {\n      fixture.innerHTML = html`\n        <input id=\"test\" type=\"text\" />\n        <label for=\"test\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 42', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 43', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\">\n        <label for=\"test\">foo</label></body>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 44', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 45', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 46', () => {\n      fixture.innerHTML = html`\n        <input type=\"image\" id=\"test\" />\n        <label for=\"test\">foo</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 47', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\">foo<input type=\"text\" value=\"bar\" />baz</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    it('passes test 48', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\">foo<input type=\"text\" value=\"bar\" />baz</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    it('passes test 49', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\">foo<input type=\"text\" value=\"bar\" />baz</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    it('passes test 50', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\">foo<input type=\"text\" value=\"bar\" />baz</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    it('passes test 51', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\">foo <input type=\"text\" value=\"bar\" /> baz</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    pseudoText('passes test 52', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\" title=\"bar\"\n            ><input id=\"test\" type=\"text\" name=\"test\" title=\"buz\"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    pseudoText('passes test 53', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\" title=\"bar\"\n            ><input id=\"test\" type=\"password\" name=\"test\" title=\"buz\"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo bar baz');\n    });\n\n    pseudoText('passes test 54', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\"\n            ><input id=\"test\" type=\"checkbox\" name=\"test\" title=\" bar \"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo baz');\n    });\n\n    pseudoText('passes test 55', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\"\n            ><input id=\"test\" type=\"radio\" name=\"test\" title=\" bar \"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo baz');\n    });\n\n    pseudoText('passes test 56', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\"\n            ><input id=\"test\" type=\"file\" name=\"test\" title=\"bar\"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo baz');\n    });\n\n    pseudoText('passes test 57', () => {\n      fixture.innerHTML = html`\n        <style type=\"text/css\">\n          label:before {\n            content: 'foo';\n          }\n          label:after {\n            content: 'baz';\n          }\n        </style>\n        <form>\n          <label for=\"test\"\n            ><input\n              id=\"test\"\n              type=\"image\"\n              src=\"foo.jpg\"\n              name=\"test\"\n              title=\"bar\"\n          /></label>\n        </form>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo baz');\n    });\n\n    it('passes test 58', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">States:</label>\n        <input type=\"password\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'States:');\n    });\n\n    it('passes test 59', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">States:</label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'States:');\n    });\n\n    it('passes test 60', () => {\n      fixture.innerHTML =\n        '<label for=\"test\">States:</label><input type=\"radio\" id=\"test\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'States:');\n    });\n\n    it('passes test 61', () => {\n      fixture.innerHTML =\n        '<label for=\"test\">File:</label><input type=\"file\" id=\"test\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'File:');\n    });\n\n    it('passes test 62', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">States:</label>\n        <input type=\"image\" id=\"test\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'States:');\n    });\n\n    it('passes test 63', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"password\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 64', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 65', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 66', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 67', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          foo\n          <input type=\"text\" value=\"David\" />\n        </label>\n        <input type=\"image\" id=\"test\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo David');\n    });\n\n    it('passes test 68', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"password\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 69', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 70', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 71', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 72', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <select name=\"member\" size=\"1\" role=\"menu\" tabindex=\"0\">\n            <option role=\"menuitem\" value=\"beard\" selected=\"true\">clown</option>\n            <option role=\"menuitem\" value=\"scuba\">rich</option>\n          </select>\n        </label>\n        <input type=\"image\" id=\"test\" src=\"foo.jpg\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    ariaValuetext('passes test 73', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"password\" value=\"baz\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    ariaValuetext('passes test 74', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    ariaValuetext('passes test 75', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    ariaValuetext('passes test 76', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    ariaValuetext('passes test 77', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuetext=\"Monday\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"image\" src=\"foo.jpg\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy Monday');\n    });\n\n    it('passes test 78', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"password\" id=\"test\" value=\"baz\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 79', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 80', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 81', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 82', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">\n          crazy\n          <div\n            role=\"spinbutton\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"7\"\n            aria-valuenow=\"4\"\n          ></div>\n        </label>\n        <input type=\"image\" src=\"foo.jpg\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy 4');\n    });\n\n    it('passes test 83', () => {\n      fixture.innerHTML =\n        '<input type=\"password\" id=\"test\" title=\"crazy\" value=\"baz\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 84', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" title=\"crazy\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 85', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" title=\"crazy\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 86', () => {\n      fixture.innerHTML = html` <input type=\"file\" id=\"test\" title=\"crazy\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    it('passes test 87', () => {\n      fixture.innerHTML =\n        '<input type=\"image\" src=\"foo.jpg\" id=\"test\" title=\"crazy\"/>';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'crazy');\n    });\n\n    pseudoText('passes test 88', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"password\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 89', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 90', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 91', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 92', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'fancy ';\n          }\n        </style>\n        <label for=\"test\">fruit</label>\n        <input type=\"image\" src=\"foo.jpg\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 93', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:after {\n            content: ' fruit';\n          }\n        </style>\n        <label for=\"test\">fancy</label>\n        <input type=\"password\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 94', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:after {\n            content: ' fruit';\n          }\n        </style>\n        <label for=\"test\">fancy</label>\n        <input type=\"checkbox\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 95', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:after {\n            content: ' fruit';\n          }\n        </style>\n        <label for=\"test\">fancy</label>\n        <input type=\"radio\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 96', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:after {\n            content: ' fruit';\n          }\n        </style>\n        <label for=\"test\">fancy</label>\n        <input type=\"file\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    pseudoText('passes test 97', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:after {\n            content: ' fruit';\n          }\n        </style>\n        <label for=\"test\">fancy</label>\n        <input type=\"image\" src=\"foo.jpg\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'fancy fruit');\n    });\n\n    it('passes test 98', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 99', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <span role=\"menu\">\n            <span role=\"menuitem\" aria-selected=\"true\">1</span>\n            <span role=\"menuitem\" hidden>2</span>\n            <span role=\"menuitem\" hidden>3</span>\n          </span>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen times.');\n    });\n\n    it('passes test 100', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <select size=\"1\">\n            <option selected=\"selected\">1</option>\n            <option>2</option>\n            <option>3</option>\n          </select>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 101', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"slider\"\n            type=\"range\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 102', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"spinbutton\"\n            type=\"number\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 103', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" title=\"foo\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 104', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 105', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <span role=\"menu\">\n            <span role=\"menuitem\" aria-selected=\"true\">1</span>\n            <span role=\"menuitem\" hidden>2</span>\n            <span role=\"menuitem\" hidden>3</span>\n          </span>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen times.');\n    });\n\n    it('passes test 106', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <select size=\"1\">\n            <option selected=\"selected\">1</option>\n            <option>2</option>\n            <option>3</option>\n          </select>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 107', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"slider\"\n            type=\"range\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 108', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"spinbutton\"\n            type=\"number\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 109', () => {\n      fixture.innerHTML = html` <input type=\"file\" id=\"test\" title=\"foo\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 110', () => {\n      fixture.innerHTML =\n        '<input type=\"image\" src=\"test.png\" id=\"test\" title=\"foo\" />';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 111', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 112', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <span role=\"menu\">\n            <span role=\"menuitem\" aria-selected=\"true\">1</span>\n            <span role=\"menuitem\" hidden>2</span>\n            <span role=\"menuitem\" hidden>3</span>\n          </span>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen times.');\n    });\n\n    it('passes test 113', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <select size=\"1\">\n            <option selected=\"selected\">1</option>\n            <option>2</option>\n            <option>3</option>\n          </select>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 114', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"slider\"\n            type=\"range\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 115', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"spinbutton\"\n            type=\"number\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 116', () => {\n      fixture.innerHTML = html`\n        <input type=\"password\" id=\"test\" title=\"foo\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 117', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 118', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <span role=\"menu\">\n            <span role=\"menuitem\" aria-selected=\"true\">1</span>\n            <span role=\"menuitem\" hidden>2</span>\n            <span role=\"menuitem\" hidden>3</span>\n          </span>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen times.');\n    });\n\n    it('passes test 119', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <select size=\"1\">\n            <option selected=\"selected\">1</option>\n            <option>2</option>\n            <option>3</option>\n          </select>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 120', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"slider\"\n            type=\"range\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 121', () => {\n      fixture.innerHTML = html`\n        <input type=\"radio\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"spinbutton\"\n            type=\"number\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 122', () => {\n      fixture.innerHTML = html` <input type=\"radio\" id=\"test\" title=\"foo\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    it('passes test 123', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 124', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <span role=\"menu\">\n            <span role=\"menuitem\" aria-selected=\"true\">1</span>\n            <span role=\"menuitem\" hidden>2</span>\n            <span role=\"menuitem\" hidden>3</span>\n          </span>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen times.');\n    });\n\n    it('passes test 125', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <select size=\"1\">\n            <option selected=\"selected\">1</option>\n            <option>2</option>\n            <option>3</option>\n          </select>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 126', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"slider\"\n            type=\"range\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 127', () => {\n      fixture.innerHTML = html`\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\"\n          >foo\n          <input\n            role=\"spinbutton\"\n            type=\"number\"\n            value=\"5\"\n            min=\"1\"\n            max=\"10\"\n            aria-valuenow=\"5\"\n            aria-valuemin=\"1\"\n            aria-valuemax=\"10\"\n          />\n          baz\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo 5 baz');\n    });\n\n    it('passes test 128', () => {\n      fixture.innerHTML = html` <input type=\"text\" id=\"test\" title=\"foo\" /> `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'foo');\n    });\n\n    // Skip from 128 - 138 as those are name description cases\n\n    it('passes test 139', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <div id=\"test\" role=\"link\" tabindex=\"0\">\n          <span aria-hidden=\"true\"><i> Hello, </i></span>\n          <span>My</span> name is\n          <div>\n            <img src=\"file.jpg\" title=\"Bryan\" alt=\"\" role=\"presentation\" />\n          </div>\n          <span role=\"presentation\" aria-label=\"Eli\">\n            <span aria-label=\"Garaventa\">Zambino</span>\n          </span>\n          <span>the weird.</span>\n          (QED)\n          <span class=\"hidden\"\n            ><i><b>and don't you forget it.</b></i></span\n          >\n          <table>\n            <tr>\n              <td>Where</td>\n              <td style=\"visibility:hidden;\"><div>in</div></td>\n              <td><div style=\"display:none;\">the world</div></td>\n              <td>are my marbles?</td>\n            </tr>\n          </table>\n        </div>\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      // Chrome 114: \"My name is Garaventa the weird. (QED) Where are my marbles?\"\"\n      // Firefox 115: \"My name is Bryan Eli the weird. (QED) Where are my marbles?\"\n      // Safari 16.5: \"My name is Eli the weird. (QED) Where are my marbles?\"\n      assert.equal(\n        accessibleText(target),\n        'My name is Eli the weird. (QED) Where are my marbles?'\n      );\n    });\n\n    it('passes test 140', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <input id=\"test\" type=\"text\" aria-labelledby=\"lblId\" />\n        <div id=\"lblId\">\n          <span aria-hidden=\"true\"><i> Hello, </i></span>\n          <span>My</span> name is\n          <div>\n            <img src=\"file.jpg\" title=\"Bryan\" alt=\"\" role=\"presentation\" />\n          </div>\n          <span role=\"presentation\" aria-label=\"Eli\">\n            <span aria-label=\"Garaventa\">Zambino</span>\n          </span>\n          <span>the weird.</span>\n          (QED)\n          <span class=\"hidden\"\n            ><i><b>and don't you forget it.</b></i></span\n          >\n          <table>\n            <tr>\n              <td>Where</td>\n              <td style=\"visibility:hidden;\"><div>in</div></td>\n              <td><div style=\"display:none;\">the world</div></td>\n              <td>are my marbles?</td>\n            </tr>\n          </table>\n        </div>\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(\n        accessibleText(target),\n        'My name is Eli the weird. (QED) Where are my marbles?'\n      );\n    });\n\n    // Disabling this, axe has a buggy implicitName computation\n    //  shouldn't be a big deal\n    xit('passes test 141', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <input type=\"text\" id=\"test\" />\n        <label for=\"test\" id=\"label\">\n          <span aria-hidden=\"true\"><i> Hello, </i></span>\n          <span>My</span> name is\n          <div>\n            <img src=\"file.jpg\" title=\"Bryan\" alt=\"\" role=\"presentation\" />\n          </div>\n          <span role=\"presentation\" aria-label=\"Eli\">\n            <span aria-label=\"Garaventa\">Zambino</span>\n          </span>\n          <span>the weird.</span>\n          (QED)\n          <span class=\"hidden\"\n            ><i><b>and don't you forget it.</b></i></span\n          >\n          <table>\n            <tr>\n              <td>Where</td>\n              <td style=\"visibility:hidden;\"><div>in</div></td>\n              <td><div style=\"display:none;\">the world</div></td>\n              <td>are my marbles?</td>\n            </tr>\n          </table>\n        </label>\n      `;\n\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(\n        accessibleText(target),\n        'My name is Eli the weird. (QED) Where are my marbles?'\n      );\n    });\n\n    it('passes test 143', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <div>\n          <input\n            id=\"test\"\n            type=\"text\"\n            aria-labelledby=\"lbl1 lbl2\"\n            aria-describedby=\"descId\"\n          />\n          <span>\n            <span aria-hidden=\"true\" id=\"lbl1\">Important</span>\n            <span class=\"hidden\">\n              <span aria-hidden=\"true\" id=\"lbl2\">stuff</span>\n            </span>\n          </span>\n        </div>\n        <div class=\"hidden\">\n          <div id=\"descId\">\n            <span aria-hidden=\"true\"><i> Hello, </i></span>\n            <span>My</span> name is\n            <div>\n              <img src=\"file.jpg\" title=\"Bryan\" alt=\"\" role=\"presentation\" />\n            </div>\n            <span role=\"presentation\" aria-label=\"Eli\">\n              <span aria-label=\"Garaventa\">Zambino</span>\n            </span>\n            <span>the weird.</span>\n            (QED)\n            <span class=\"hidden\"\n              ><i><b>and don't you forget it.</b></i></span\n            >\n            <table>\n              <tr>\n                <td>Where</td>\n                <td style=\"visibility:hidden;\"><div>in</div></td>\n                <td><div style=\"display:none;\">the world</div></td>\n                <td>are my marbles?</td>\n              </tr>\n            </table>\n          </div>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Important stuff');\n    });\n\n    it('passes test 144', () => {\n      fixture.innerHTML =\n        '<input id=\"test\" role=\"combobox\" type=\"text\" title=\"Choose your language\" value=\"English\">';\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Choose your language');\n    });\n\n    it('passes test 145', () => {\n      fixture.innerHTML = html`\n        <div\n          id=\"test\"\n          role=\"combobox\"\n          tabindex=\"0\"\n          title=\"Choose your language.\"\n        >\n          <span> English </span>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Choose your language.');\n    });\n\n    it('passes test 147', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <ul role=\"listbox\" style=\"list-style-type: none;\">\n            <li role=\"option\" aria-selected=\"true\">1</li>\n            <li role=\"option\">2</li>\n            <li role=\"option\">3</li>\n          </ul>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    pseudoText('passes test 148', () => {\n      fixture.innerHTML = html`\n        <input type=\"checkbox\" id=\"test\" />\n        <label for=\"test\"\n          >Flash the screen\n          <div role=\"textbox\" contenteditable>1</div>\n          times.\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 149', () => {\n      fixture.innerHTML = html`\n        <label for=\"test\">a test</label>\n        <label>This <input type=\"checkbox\" id=\"test\" /> is</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'a test This is');\n    });\n\n    it('passes test 150', () => {\n      fixture.innerHTML = html`\n        <label>This <input type=\"checkbox\" id=\"test\" /> is</label>\n        <label for=\"test\">a test</label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'This is a test');\n    });\n\n    it('passes test 151', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >W<i>h<b>a</b></i\n          >t<br />is\n          <div>\n            your\n            <div>name<b>?</b></div>\n          </div></label\n        >\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'What is your name?');\n    });\n\n    pseudoText('passes test 152', () => {\n      fixture.innerHTML = html`\n        <style>\n          label:before {\n            content: 'This';\n            display: block;\n          }\n          label:after {\n            content: '.';\n          }\n        </style>\n        <label for=\"test\">is a test</label>\n        <input type=\"text\" id=\"test\" />\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'This is a test.');\n    });\n\n    it('passes test 153', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\">\n          <span class=\"hidden\">1</span><span>2</span>\n          <span style=\"visibility: hidden;\">3</span><span>4</span>\n          <span hidden>5</span><span>6</span> <span aria-hidden=\"true\">7</span\n          ><span>8</span> <span aria-hidden=\"false\" class=\"hidden\">9</span\n          ><span>10</span>\n        </label>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), '2 4 6 8 10');\n    });\n\n    it('passes test 154', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >Flash <span aria-owns=\"id1\">the screen</span> times.</label\n        >\n        <div>\n          <div id=\"id1\" role=\"combobox\" aria-owns=\"id2\">\n            <div role=\"textbox\"></div>\n          </div>\n        </div>\n        <div>\n          <ul id=\"id2\" role=\"listbox\" style=\"list-style-type: none;\">\n            <li role=\"option\">1</li>\n            <li role=\"option\" aria-selected=\"true\">2</li>\n            <li role=\"option\">3</li>\n          </ul>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 2 times.');\n    });\n\n    it('passes test 155', () => {\n      fixture.innerHTML = html`\n        <input type=\"file\" id=\"test\" />\n        <label for=\"test\"\n          >Flash <span aria-owns=\"id1\">the screen</span> times.</label\n        >\n        <div id=\"id1\">\n          <div role=\"combobox\">\n            <div role=\"textbox\"></div>\n            <ul role=\"listbox\" style=\"list-style-type: none;\">\n              <li role=\"option\" aria-selected=\"true\">1</li>\n              <li role=\"option\">2</li>\n              <li role=\"option\">3</li>\n            </ul>\n          </div>\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Flash the screen 1 times.');\n    });\n\n    it('passes test 156', () => {\n      fixture.innerHTML = html`\n        <style>\n          .hidden {\n            display: none;\n          }\n        </style>\n        <div id=\"test\" role=\"link\" tabindex=\"0\">\n          <span aria-hidden=\"true\"><i> Hello, </i></span>\n          <span>My</span> name is\n          <div>\n            <img src=\"file.jpg\" title=\"Bryan\" alt=\"\" role=\"presentation\" />\n          </div>\n          <span role=\"presentation\" aria-label=\"Eli\"\n            ><span aria-label=\"Garaventa\">Zambino</span></span\n          >\n          <span>the weird.</span>\n          (QED)\n          <span class=\"hidden\"\n            ><i><b>and don't you forget it.</b></i></span\n          >\n        </div>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'My name is Eli the weird. (QED)');\n    });\n\n    it('passes test 158', () => {\n      fixture.innerHTML = html`\n        <a id=\"test\" href=\"#\" aria-label=\"California\" title=\"San Francisco\">\n          United States\n        </a>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'California');\n    });\n\n    it('passes test 159', () => {\n      fixture.innerHTML = html`\n        <h2 id=\"test\">\n          Country of origin:\n          <input\n            role=\"combobox\"\n            type=\"text\"\n            title=\"Choose your country.\"\n            value=\"United States\"\n          />\n        </h2>\n      `;\n      axe.testUtils.flatTreeSetup(fixture);\n      const target = fixture.querySelector('#test');\n      assert.equal(accessibleText(target), 'Country of origin: United States');\n    });\n\n    /**\n\t// In case anyone even wants it, here's the script used to generate these test cases\n\tfunction getTestCase(content, index = 0) {\n\t\tconst regex = new RegExp('if given\\n([^]*)\\nthen the accessible name of the element with id of \"(.*)\" is \"(.*)\"')\n\t\tconst out = content.match(regex)\n\t\tif (!out || out.length !== 4) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst [, html, id, expected] = out;\n\t\tconst strings = html.split(/\\n/g).map(\n\t\t\tline => `'${line.substr(2)}'`\n\t\t).join(' +\\n      ') + ';'\n\n\t\treturn `\n\t\tit('passes test ${index + 1}', () => {\n\t\t\tfixture.innerHTML = ${strings}\n\t\t\taxe.testUtils.flatTreeSetup(fixture);\n\t\t\tconst target = fixture.querySelector('#${id}');\n\t\t\tassert.equal(accessibleText(target), '${expected}');\n\t\t});`\n\t}*/\n  });\n});\n"
  },
  {
    "path": "test/commons/text/form-control-value.js",
    "content": "describe('text.formControlValue', () => {\n  const formControlValue = axe.commons.text.formControlValue;\n  const { queryFixture, fixtureSetup, injectIntoFixture, html } = axe.testUtils;\n  const fixture = document.querySelector('#fixture');\n\n  function getNodeType(node) {\n    // Note: Inconsistent response for `node.type` across browsers, hence resolving and sanitizing using getAttribute\n    let nodeType = node.hasAttribute('type')\n      ? axe.commons.text.sanitize(node.getAttribute('type')).toLowerCase()\n      : 'text';\n    nodeType = axe.utils.validInputTypes().includes(nodeType)\n      ? nodeType\n      : 'text';\n    return nodeType;\n  }\n\n  it('returns the first truthy result from text.formControlValueMethods', () => {\n    const target = queryFixture(html`\n      <div id=\"target\" role=\"textbox\" value=\"foo\">bar</div>\n    `);\n    const vNode = axe.utils.querySelectorAll(axe._tree, '#fixture')[0];\n    assert.equal(formControlValue(target, { startNode: vNode }), 'bar');\n  });\n\n  it('returns `` when the node equals context.startNode', () => {\n    const target = queryFixture('<input id=\"target\" value=\"foo\" />');\n    assert.equal(formControlValue(target, { startNode: target }), '');\n  });\n\n  describe('unsupported accessibleNameFromFieldValue', () => {\n    it('returns `` for unsupported progressbar role', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"progressbar\" aria-value=\"foo\"></div>\n      `);\n      assert.equal(formControlValue(target), '');\n    });\n\n    it('returns the value for supported textbox role', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"textbox\">bar</div>\n      `);\n      assert.equal(formControlValue(target), 'bar');\n    });\n\n    it('returns the value for supported listbox role', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\">\n          <div role=\"option\">foo</div>\n          <div role=\"option\" aria-selected=\"true\">bar</div>\n          <div role=\"option\">baz</div>\n        </div>\n      `);\n      assert.equal(formControlValue(target), 'bar');\n    });\n\n    it('returns the value for supported combobox role', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"combobox\">\n          <div role=\"textbox\" id=\"text\">nope</div>\n          <div role=\"listbox\" id=\"list\">\n            <div role=\"option\">foo</div>\n            <div role=\"option\" aria-selected=\"true\">bar</div>\n          </div>\n        </div>\n      `);\n      assert.equal(formControlValue(target), 'bar');\n    });\n  });\n\n  describe('nativeTextboxValue', () => {\n    const { nativeTextboxValue } = axe.commons.text.formControlValueMethods;\n\n    it('returns the value of textarea elements', () => {\n      const target = queryFixture('<textarea>foo</textarea>', 'textarea');\n      assert.equal(nativeTextboxValue(target), 'foo');\n    });\n\n    it('returns the value of text field input elements', () => {\n      const formData = {\n        text: 'foo',\n        date: '2018-12-12',\n        'datetime-local': '2018-12-12T12:34',\n        email: 'foo@bar.baz',\n        month: '2018-11',\n        number: '123',\n        search: 'foo',\n        tel: '123456',\n        time: '12:34',\n        url: 'http://foo.bar.baz',\n        week: '2018-W46'\n      };\n      fixtureSetup(\n        Object.keys(formData).reduce((code, fieldType) => {\n          return `${code}<input type=\"${fieldType}\" value=\"${formData[fieldType]}\">`;\n        }, '')\n      );\n      axe.utils\n        .querySelectorAll(axe._tree[0], '#fixture input')\n        .forEach(function (target) {\n          const expected = formData[getNodeType(target.actualNode)];\n          assert.isDefined(expected);\n          const actual = nativeTextboxValue(target);\n          assert.equal(\n            actual,\n            expected,\n            'Expected value for ' + target.actualNode.outerHTML\n          );\n        });\n    });\n\n    it('returns `` for non-text input elements', () => {\n      fixtureSetup(html`\n        <input type=\"button\" value=\"foo\" />\n        <input type=\"checkbox\" value=\"foo\" />\n        <input type=\"file\" value=\"foo\" />\n        <input type=\"hidden\" value=\"foo\" />\n        <input type=\"image\" value=\"foo\" />\n        <input type=\"password\" value=\"foo\" />\n        <input type=\"radio\" value=\"foo\" />\n        <input type=\"reset\" value=\"foo\" />\n        <input type=\"submit\" value=\"foo\" />\n        <input type=\"color\" value=\"#000000\" />\n      `);\n      axe.utils\n        .querySelectorAll(axe._tree[0], '#fixture input')\n        .forEach(function (target) {\n          // Safari and IE11 do not support the color input type\n          // and thus treat them as text inputs. ignore fallback\n          // inputs\n          if (target.actualNode.type === 'text') {\n            return;\n          }\n\n          assert.equal(\n            nativeTextboxValue(target),\n            '',\n            'Expected no value for ' + target.actualNode.outerHTML\n          );\n        });\n    });\n\n    it('returns the value of DOM nodes', () => {\n      fixture.innerHTML = '<input value=\"foo\">';\n      axe.utils.getFlattenedTree(fixture);\n      assert.equal(nativeTextboxValue(fixture.querySelector('input')), 'foo');\n    });\n\n    it('returns `` for other elements', () => {\n      // some random elements:\n      ['div', 'span', 'h1', 'output', 'summary', 'style', 'template'].forEach(\n        function (nodeName) {\n          const target = document.createElement(nodeName);\n          target.value = 'foo'; // That shouldn't do anything\n          fixture.appendChild(target);\n          axe.utils.getFlattenedTree(fixture);\n          assert.equal(nativeTextboxValue(target), '');\n        }\n      );\n    });\n  });\n\n  describe('nativeSelectValue', () => {\n    const { nativeSelectValue } = axe.commons.text.formControlValueMethods;\n\n    it('returns the selected option text', () => {\n      const target = queryFixture(html`\n        <select id=\"target\">\n          <option>foo</option>\n          <option value=\"bar\" selected>baz</option>\n        </select>\n      `);\n      assert.equal(nativeSelectValue(target), 'baz');\n    });\n\n    it('returns the selected option text after selection', () => {\n      injectIntoFixture(html`\n        <select id=\"target\">\n          <option value=\"foo\" selected>foo</option>\n          <option value=\"bar\">baz</option>\n        </select>\n      `);\n      fixture.querySelector('#target').value = 'bar';\n      const rootNode = axe.setup(fixture);\n      const target = axe.utils.querySelectorAll(rootNode, '#target')[0];\n      assert.equal(nativeSelectValue(target), 'baz');\n    });\n\n    it('returns multiple options, space separated', () => {\n      // Can't apply multiple \"selected\" props without setting \"multiple\"\n      const target = queryFixture(html`\n        <select id=\"target\" multiple>\n          <option>oof</option>\n          <option selected>foo</option>\n          <option>rab</option>\n          <option selected>bar</option>\n          <option>zab</option>\n          <option selected>baz</option>\n        </select>\n      `);\n      assert.equal(nativeSelectValue(target), 'foo bar baz');\n    });\n\n    it('returns options from within optgroup elements', () => {\n      const target = queryFixture(html`\n        <select id=\"target\" multiple>\n          <option>oof</option>\n          <option selected>foo</option>\n          <optgroup>\n            <option>rab</option>\n            <option selected>bar</option>\n          </optgroup>\n          <optgroup>\n            <option>zab</option>\n            <option selected>baz</option>\n          </optgroup>\n        </select>\n      `);\n      assert.equal(nativeSelectValue(target), 'foo bar baz');\n    });\n\n    it('returns the first option when there are no selected options', () => {\n      // Browser automatically selectes the first option\n      const target = queryFixture(html`\n        <select id=\"target\">\n          <option>foo</option>\n          <option>baz</option>\n        </select>\n      `);\n      assert.equal(nativeSelectValue(target), 'foo');\n    });\n\n    it('returns `` for other elements', () => {\n      // some random elements:\n      ['div', 'span', 'h1', 'output', 'summary', 'style', 'template'].forEach(\n        function (nodeName) {\n          const target = document.createElement(nodeName);\n          target.value = 'foo'; // That shouldn't do anything\n          fixture.appendChild(target);\n          axe.utils.getFlattenedTree(fixture);\n          assert.equal(nativeSelectValue(target), '');\n        }\n      );\n    });\n  });\n\n  describe('ariaTextboxValue', () => {\n    const ariaTextboxValue =\n      axe.commons.text.formControlValueMethods.ariaTextboxValue;\n\n    it('returns the text of role=textbox elements', () => {\n      const target = queryFixture('<div id=\"target\" role=\"textbox\">foo</div>');\n      assert.equal(ariaTextboxValue(target), 'foo');\n    });\n\n    it('returns `` for elements without role=textbox', () => {\n      const target = queryFixture('<div id=\"target\" role=\"combobox\">foo</div>');\n      assert.equal(ariaTextboxValue(target), '');\n    });\n\n    it('ignores text hidden with CSS', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"textbox\">\n          <span>foo</span>\n          <span style=\"display: none;\">bar</span>\n          <span style=\"visibility: hidden;\">baz</span>\n        </div>\n      `);\n      assert.equal(ariaTextboxValue(target), 'foo');\n    });\n\n    it('ignores elements with hidden content', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"textbox\">\n          <span>span</span>\n          <style>\n            style {\n            }\n          </style>\n          <template>template</template>\n          <script>\n            script;\n          </script>\n          <!-- comment -->\n          <h1>h1</h1>\n        </div>\n      `);\n      assert.equal(ariaTextboxValue(target), 'span h1');\n    });\n\n    it('does not return HTML or comments', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"textbox\">\n          <i>foo</i>\n          <!-- comment -->\n        </div>\n      `);\n      assert.equal(ariaTextboxValue(target), 'foo');\n    });\n\n    it('returns the entire text content if the textbox is hidden', () => {\n      // Yes, this is how it works in browsers :-(\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"textbox\" style=\"display:none\">\n          <style>\n            [role='texbox'] {\n              display: none;\n            }\n          </style>\n        </div>\n      `);\n      const text = ariaTextboxValue(target).replace(/\\s+/g, ' ').trim();\n      assert.equal(text, \"[role='texbox'] { display: none; }\");\n    });\n  });\n\n  describe('ariaListboxValue', () => {\n    const ariaListboxValue =\n      axe.commons.text.formControlValueMethods.ariaListboxValue;\n\n    it('returns the selected option when the element is a listbox', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\">\n          <div role=\"option\">foo</div>\n          <div role=\"option\" aria-selected=\"true\">bar</div>\n          <div role=\"option\">baz</div>\n        </div>\n      `);\n      assert.equal(ariaListboxValue(target), 'bar');\n    });\n\n    it('returns `` when the element is not a listbox', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"combobox\">\n          <div role=\"option\">foo</div>\n          <div role=\"option\" aria-selected=\"true\">bar</div>\n          <div role=\"option\">baz</div>\n        </div>\n      `);\n      assert.equal(ariaListboxValue(target), '');\n    });\n\n    it('returns `` when there is no selected option', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\">\n          <div role=\"option\">foo</div>\n          <div role=\"option\">bar</div>\n          <div role=\"option\">baz</div>\n        </div>\n      `);\n      assert.equal(ariaListboxValue(target), '');\n    });\n\n    it('returns `` when aria-selected is not true option', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\">\n          <div role=\"option\" aria-selected=\"false\">foo</div>\n          <div role=\"option\" aria-selected=\"TRUE\">bar</div>\n          <div role=\"option\" aria-selected=\"yes\">baz</div>\n          <div role=\"option\" aria-selected=\"selected\">fiz</div>\n        </div>\n      `);\n      assert.equal(ariaListboxValue(target), '');\n    });\n\n    it('returns selected options from aria-owned', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\" aria-owns=\"opt1 opt2 opt3\"></div>\n        <div role=\"option\" id=\"opt1\">foo</div>\n        <div role=\"option\" id=\"opt2\" aria-selected=\"true\">bar</div>\n        <div role=\"option\" id=\"opt3\">baz</div>\n      `);\n      assert.equal(ariaListboxValue(target), 'bar');\n    });\n\n    it('ignores aria-selected for elements that are not options', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"listbox\" aria-owns=\"opt1 opt2 opt3\"></div>\n        <div id=\"opt1\">foo</div>\n        <div id=\"opt2\" aria-selected=\"true\">bar</div>\n        <div id=\"opt3\">baz</div>\n      `);\n      assert.equal(ariaListboxValue(target), '');\n    });\n\n    describe('with multiple aria-selected', () => {\n      it('returns the first selected option from children', () => {\n        const target = queryFixture(html`\n          <div id=\"target\" role=\"listbox\">\n            <div role=\"option\">foo</div>\n            <div role=\"option\" aria-selected=\"true\">bar</div>\n            <div role=\"option\" aria-selected=\"true\">baz</div>\n          </div>\n        `);\n        assert.equal(ariaListboxValue(target), 'bar');\n      });\n\n      it('returns the first selected option in aria-owned (as opposed to in the DOM order)', () => {\n        const target = queryFixture(html`\n          <div id=\"target\" role=\"listbox\" aria-owns=\"opt3 opt2 opt1\"></div>\n          <div role=\"option\" id=\"opt1\" aria-selected=\"true\">foo</div>\n          <div role=\"option\" id=\"opt2\" aria-selected=\"true\">bar</div>\n          <div role=\"option\" id=\"opt3\">baz</div>\n        `);\n        assert.equal(ariaListboxValue(target), 'bar');\n      });\n\n      it('returns the a selected child before a selected aria-owned element', () => {\n        const target = queryFixture(html`\n          <div id=\"target\" role=\"listbox\" aria-owns=\"opt2 opt3\">\n            <div role=\"option\" aria-selected=\"true\">foo</div>\n          </div>\n          <div role=\"option\" id=\"opt2\" aria-selected=\"true\">bar</div>\n          <div role=\"option\" id=\"opt3\">baz</div>\n        `);\n        assert.equal(ariaListboxValue(target), 'foo');\n      });\n\n      it('ignores aria-multiselectable=true', () => {\n        // aria-multiselectable doesn't add additional content to the accessible name\n        const target = queryFixture(html`\n          <div\n            id=\"target\"\n            role=\"listbox\"\n            aria-owns=\"opt2 opt3\"\n            aria-multiselectable=\"true\"\n          >\n            <div role=\"option\" aria-selected=\"true\">foo</div>\n          </div>\n          <div role=\"option\" id=\"opt2\" aria-selected=\"true\">bar</div>\n          <div role=\"option\" id=\"opt3\" aria-selected=\"true\">baz</div>\n        `);\n        assert.equal(ariaListboxValue(target), 'foo');\n      });\n    });\n  });\n\n  describe('ariaComboboxValue', () => {\n    const { ariaComboboxValue } = axe.commons.text.formControlValueMethods;\n\n    const comboboxContent = html`\n      <div role=\"textbox\" id=\"text\">nope</div>\n      <div role=\"listbox\" id=\"list\">\n        <div role=\"option\">foo</div>\n        <div role=\"option\" aria-selected=\"true\">bar</div>\n      </div>\n    `;\n\n    it('returns the text of role=combobox elements', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"combobox\">${comboboxContent}</div>\n      `);\n      assert.equal(ariaComboboxValue(target), 'bar');\n    });\n\n    it('returns `` for elements without role=combobox', () => {\n      const target = queryFixture(\n        `<div role=\"combobox\">${comboboxContent}</div>`,\n        '[role=listbox]'\n      );\n      assert.equal(ariaComboboxValue(target), '');\n    });\n\n    it('passes child listbox to `ariaListboxValue` and returns its result', () => {\n      const target = queryFixture(html`\n        <div id=\"target\" role=\"combobox\">${comboboxContent}</div>\n      `);\n      assert.equal(ariaComboboxValue(target), 'bar');\n    });\n\n    it('passes aria-owned listbox to `ariaListboxValue` and returns its result', () => {\n      const target = queryFixture(\n        '<div id=\"target\" role=\"combobox\" aria-owns=\"text list\"></div>' +\n          comboboxContent\n      );\n      assert.equal(ariaComboboxValue(target), 'bar');\n    });\n  });\n\n  describe('ariaRangeValue', () => {\n    const rangeRoles = ['progressbar', 'scrollbar', 'slider', 'spinbutton'];\n    const ariaRangeValue =\n      axe.commons.text.formControlValueMethods.ariaRangeValue;\n\n    it('returns `` for roles that are not ranges', () => {\n      const target = queryFixture('<div id=\"target\" role=\"textbox\">foo</div>');\n      assert.equal(ariaRangeValue(target), '');\n    });\n\n    rangeRoles.forEach(function (role) {\n      describe('with ' + role, () => {\n        it('returns the result of aria-valuenow', () => {\n          const target = queryFixture(html`\n            <div id=\"target\" role=\"${role}\" aria-valuenow=\"+123\">foo</div>\n          `);\n          assert.equal(ariaRangeValue(target), '123');\n        });\n\n        it('returns `0` if aria-valuenow is not a number', () => {\n          const target = queryFixture(html`\n            <div id=\"target\" role=\"${role}\" aria-valuenow=\"abc\">foo</div>\n          `);\n          assert.equal(ariaRangeValue(target), '0');\n        });\n\n        it('returns decimal numbers', () => {\n          const target = queryFixture(html`\n            <div id=\"target\" role=\"${role}\" aria-valuenow=\"1.5678\">foo</div>\n          `);\n          assert.equal(ariaRangeValue(target), '1.5678');\n        });\n\n        it('returns negative numbers', () => {\n          const target = queryFixture(html`\n            <div id=\"target\" role=\"${role}\" aria-valuenow=\"-1.0\">foo</div>\n          `);\n          assert.equal(ariaRangeValue(target), '-1');\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/is-human-interpretable.js",
    "content": "describe('text.isHumanInterpretable', function () {\n  it('returns 0 when given string is empty', function () {\n    const actual = axe.commons.text.isHumanInterpretable('');\n    assert.equal(actual, 0);\n  });\n\n  it('returns 0 when given string is a single alpha character', function () {\n    const singleCharacterExamples = ['i', 'x', 'X', '×', ''];\n    singleCharacterExamples.forEach(function (characterExample) {\n      const actual = axe.commons.text.isHumanInterpretable(characterExample);\n      assert.equal(actual, 0);\n    });\n  });\n\n  it('returns 0 when given string is in the symbolic text characters set (blocklist)', function () {\n    const blocklistedSymbols = ['aA', 'Aa', 'abc', 'ABC'];\n    blocklistedSymbols.forEach(function (symbolicText) {\n      const actual = axe.commons.text.isHumanInterpretable(symbolicText);\n      assert.equal(actual, 0);\n    });\n  });\n\n  it('returns 0 when given string is only punctuations', function () {\n    const actual = axe.commons.text.isHumanInterpretable('?!!!,.');\n    assert.equal(actual, 0);\n  });\n\n  it('returns 1 when given string that has a number', function () {\n    const actual = axe.commons.text.isHumanInterpretable('7');\n    assert.equal(actual, 1);\n  });\n\n  it('returns 1 when given string has emoji as a part of the sentence', function () {\n    const actual = axe.commons.text.isHumanInterpretable('I like 🏀');\n    assert.equal(actual, 1);\n  });\n\n  it('returns 1 when given string has non BMP character (eg: windings font) as part of the sentence', function () {\n    const actual = axe.commons.text.isHumanInterpretable('I ✂ my hair');\n    assert.equal(actual, 1);\n  });\n\n  it('returns 1 when given string has both non BMP character, and emoji as part of the sentence', function () {\n    const actual = axe.commons.text.isHumanInterpretable(\n      'I ✂ my hair, and I like 🏀'\n    );\n    assert.equal(actual, 1);\n  });\n\n  it('returns 0 when given string has only emoji', function () {\n    const actual = axe.commons.text.isHumanInterpretable('🏀🍔🍉🎅');\n    assert.equal(actual, 0);\n  });\n\n  it('returns 0 when given string has only non BNP characters', function () {\n    const actual = axe.commons.text.isHumanInterpretable('⌛👓');\n    assert.equal(actual, 0);\n  });\n\n  it('returns 0 when given string has combination of only non BNP characters and emojis', function () {\n    const actual = axe.commons.text.isHumanInterpretable('⌛👓🏀🍔🍉🎅');\n    assert.equal(actual, 0);\n  });\n\n  it('returns 1 when given string is a punctuated sentence', function () {\n    const actual = axe.commons.text.isHumanInterpretable(\n      \"I like football, but I prefer basketball; although I can't play either very well.\"\n    );\n    assert.equal(actual, 1);\n  });\n\n  it('returns 1 for a sentence without emoji or punctuations', function () {\n    const actual = axe.commons.text.isHumanInterpretable('Earth is round');\n    assert.equal(actual, 1);\n  });\n});\n"
  },
  {
    "path": "test/commons/text/is-icon-ligature.js",
    "content": "describe('text.isIconLigature', () => {\n  'use strict';\n\n  const isIconLigature = axe.commons.text.isIconLigature;\n  const queryFixture = axe.testUtils.queryFixture;\n  const fontApiSupport = !!document.fonts;\n\n  before(done => {\n    if (!fontApiSupport) {\n      done();\n    }\n\n    const firaFont = new FontFace(\n      'Fira Code',\n      'url(/test/assets/FiraCode-Regular.woff)'\n    );\n    const ligatureFont = new FontFace(\n      'LigatureSymbols',\n      'url(/test/assets/LigatureSymbols.woff)'\n    );\n    const materialFont = new FontFace(\n      'Material Icons',\n      'url(/test/assets/MaterialIcons.woff2)'\n    );\n    const robotoFont = new FontFace('Roboto', 'url(/test/assets/Roboto.woff2)');\n    const zeroWidth0CharFont = new FontFace(\n      'ZeroWidth0Char',\n      'url(/test/assets/ZeroWidth0Char.woff)'\n    );\n\n    window.Promise.all([\n      firaFont.load(),\n      ligatureFont.load(),\n      materialFont.load(),\n      robotoFont.load(),\n      zeroWidth0CharFont.load()\n    ]).then(() => {\n      document.fonts.add(firaFont);\n      document.fonts.add(ligatureFont);\n      document.fonts.add(materialFont);\n      document.fonts.add(robotoFont);\n      document.fonts.add(zeroWidth0CharFont);\n      done();\n    });\n  });\n\n  it('should return false for normal text', () => {\n    const target = queryFixture('<div id=\"target\">Normal text</div>');\n    assert.isFalse(isIconLigature(target.children[0]));\n  });\n\n  it('should return false for emoji', () => {\n    const target = queryFixture('<div id=\"target\">🌎</div>');\n    assert.isFalse(isIconLigature(target.children[0]));\n  });\n\n  it('should return false for non-bmp unicode', () => {\n    const target = queryFixture('<div id=\"target\">◓</div>');\n    assert.isFalse(isIconLigature(target.children[0]));\n  });\n\n  it('should return false for whitespace strings', () => {\n    const target = queryFixture('<div id=\"target\">     </div>');\n    assert.isFalse(isIconLigature(target.children[0]));\n  });\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for common ligatures (fi)',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Roboto\">figure</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for common ligatures (ff)',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Roboto\">ffugative</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for common ligatures (fl)',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Roboto\">flu shot</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for common ligatures (ffi)',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Roboto\">ffigure</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for common ligatures (ffl)',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Roboto\">fflu shot</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return true for an icon ligature',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'Material Icons\\'\">delete</div>'\n      );\n      assert.isTrue(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)('should trim the string', () => {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"font-family: Roboto\">  fflu shot  </div>'\n    );\n    assert.isFalse(isIconLigature(target.children[0]));\n  });\n\n  (fontApiSupport ? it : it.skip)(\n    'should return true for a font that has no character data',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'Material Icons\\'\">f</div>'\n      );\n      assert.isTrue(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return true for a font that has zero width characters',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'ZeroWidth0Char\\'\">0</div>'\n      );\n      assert.isTrue(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false for a programming text ligature',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: Fira Code\">!==</div>'\n      );\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return true for an icon ligature with low pixel difference',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'Material Icons\\'\">keyboard_arrow_left</div>'\n      );\n      assert.isTrue(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return true after the 3rd time the font is an icon',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'LigatureSymbols\\'\">delete</div>'\n      );\n\n      isIconLigature(target.children[0]);\n      isIconLigature(target.children[0]);\n      isIconLigature(target.children[0]);\n\n      // change text to non-icon\n      target.children[0].actualNode.textContent = '__non-icon text__';\n      assert.isTrue(isIconLigature(target.children[0]));\n    }\n  );\n\n  (fontApiSupport ? it : it.skip)(\n    'should return false after the 3rd time the font is not an icon',\n    () => {\n      const target = queryFixture(\n        '<div id=\"target\" style=\"font-family: \\'Roboto\\'\">__non-icon text__</div>'\n      );\n\n      isIconLigature(target.children[0]);\n      isIconLigature(target.children[0]);\n      isIconLigature(target.children[0]);\n\n      // change text to icon\n      target.children[0].actualNode.textContent = 'delete';\n      assert.isFalse(isIconLigature(target.children[0]));\n    }\n  );\n\n  describe('pixelThreshold', () => {\n    (fontApiSupport ? it : it.skip)(\n      'should allow higher percent (will not flag icon ligatures)',\n      () => {\n        const target = queryFixture(\n          '<div id=\"target\" style=\"font-family: \\'LigatureSymbols\\'\">delete</div>'\n        );\n\n        // every pixel must be different to flag as icon\n        assert.isFalse(isIconLigature(target.children[0], 1));\n      }\n    );\n\n    (fontApiSupport ? it : it.skip)(\n      'should allow lower percent (will flag text ligatures)',\n      () => {\n        const target = queryFixture(\n          '<div id=\"target\" style=\"font-family: Roboto\">figure</div>'\n        );\n        assert.isTrue(isIconLigature(target.children[0], 0));\n      }\n    );\n  });\n\n  describe('occurrenceThreshold', () => {\n    (fontApiSupport ? it : it.skip)(\n      'should change the number of times a font is seen before returning',\n      () => {\n        const target = queryFixture(\n          '<div id=\"target\" style=\"font-family: \\'LigatureSymbols\\'\">delete</div>'\n        );\n\n        isIconLigature(target.children[0]);\n\n        // change text to non-icon\n        target.children[0].actualNode.textContent = '__non-icon text__';\n        assert.isTrue(isIconLigature(target.children[0], 0.1, 1));\n        assert.isFalse(isIconLigature(target.children[0]));\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/commons/text/is-valid-autocomplete.js",
    "content": "describe('text.isValidAutocomplete', () => {\n  const isValidAutocomplete = axe.commons.text.isValidAutocomplete;\n  const options = {\n    standaloneTerms: ['standalone-term'],\n    qualifiedTerms: ['qualified-term']\n  };\n\n  it('is true when empty', () => {\n    assert.isTrue(isValidAutocomplete('', options));\n  });\n\n  it('is true when there is a stateTerm', () => {\n    assert.isTrue(isValidAutocomplete('on', options));\n  });\n\n  it('is true when there is a standaloneTerms', () => {\n    assert.isTrue(isValidAutocomplete('standalone-term', options));\n  });\n\n  it('is true when there is a qualifiedTerms', () => {\n    assert.isTrue(isValidAutocomplete('qualified-term', options));\n  });\n\n  it('is false when there is no stateTerm, standaloneTerms, or qualifiedTerms', () => {\n    assert.isFalse(isValidAutocomplete('bad-term', options));\n  });\n\n  describe('section-* grouping', () => {\n    it('is false when used by itself', () => {\n      assert.isFalse(isValidAutocomplete('section-foo', options));\n    });\n\n    it('is true when used before a standaloneTerm', () => {\n      assert.isTrue(\n        isValidAutocomplete('section-foo standalone-term', options)\n      );\n    });\n\n    it('is true when used before a qualifiedTerm', () => {\n      assert.isTrue(isValidAutocomplete('section-foo qualified-term', options));\n    });\n\n    it('is false when used with a stateTerm', () => {\n      assert.isFalse(isValidAutocomplete('section-foo off', options));\n      assert.isFalse(isValidAutocomplete('section-foo on', options));\n    });\n\n    it('is false when used out of order', () => {\n      assert.isFalse(\n        isValidAutocomplete('qualified-term section-foo', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('standalone-term section-foo', options)\n      );\n    });\n  });\n\n  describe('locations', () => {\n    it('is false when used by itself', () => {\n      assert.isFalse(isValidAutocomplete('shipping', options));\n    });\n\n    it('is true when in order', () => {\n      assert.isTrue(isValidAutocomplete('shipping standalone-term', options));\n      assert.isTrue(isValidAutocomplete('shipping qualified-term', options));\n      assert.isTrue(\n        isValidAutocomplete('section-foo shipping standalone-term', options)\n      );\n      assert.isTrue(\n        isValidAutocomplete('section-foo shipping qualified-term', options)\n      );\n    });\n\n    it('is false when used out of order', () => {\n      assert.isFalse(isValidAutocomplete('standalone-term shipping', options));\n      assert.isFalse(isValidAutocomplete('qualified-term shipping', options));\n      assert.isFalse(\n        isValidAutocomplete('shipping section-foo standalone-term', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('shipping section-foo qualified-term', options)\n      );\n    });\n\n    it('is false when used with a stateTerm', () => {\n      assert.isFalse(isValidAutocomplete('shipping off', options));\n      assert.isFalse(isValidAutocomplete('shipping on', options));\n    });\n  });\n\n  describe('qualifiers', () => {\n    it('is true when used before a qualifiedTerm', () => {\n      assert.isTrue(isValidAutocomplete('home qualified-term', options));\n      assert.isTrue(\n        isValidAutocomplete('shipping home qualified-term', options)\n      );\n    });\n\n    it('is false when used before a standaloneTerm', () => {\n      assert.isFalse(isValidAutocomplete('home standalone-term', options));\n    });\n\n    it('is false when used with a stateTerm', () => {\n      assert.isFalse(isValidAutocomplete('home off', options));\n      assert.isFalse(isValidAutocomplete('home on', options));\n    });\n\n    it('is false when used out of order', () => {\n      assert.isFalse(isValidAutocomplete('qualified-term home', options));\n      assert.isFalse(\n        isValidAutocomplete('home section-foo qualified-term', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('home shipping qualified-term', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('section-foo home shipping qualified-term', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('home section-foo shipping qualified-term', options)\n      );\n    });\n  });\n\n  describe('webauthn', () => {\n    it('returns false if used as the only term', () => {\n      assert.isFalse(isValidAutocomplete('webauthn', options));\n    });\n\n    it('returns false if used with a state term', () => {\n      assert.isFalse(isValidAutocomplete('on webauthn', options));\n      assert.isFalse(isValidAutocomplete('off webauthn', options));\n    });\n\n    it('returns true if used after a standalone term', () => {\n      assert.isTrue(isValidAutocomplete('standalone-term webauthn', options));\n      assert.isTrue(\n        isValidAutocomplete('billing standalone-term webauthn', options)\n      );\n      assert.isTrue(\n        isValidAutocomplete('section-foo standalone-term webauthn', options)\n      );\n      assert.isTrue(\n        isValidAutocomplete(\n          'section-foo billing standalone-term webauthn',\n          options\n        )\n      );\n    });\n\n    it('returns false if used before a standalone term', () => {\n      assert.isFalse(isValidAutocomplete('webauthn standalone-term', options));\n      assert.isFalse(\n        isValidAutocomplete('webauthn section-foo standalone-term', options)\n      );\n      assert.isFalse(\n        isValidAutocomplete('section-foo webauthn standalone-term', options)\n      );\n    });\n\n    it('returns true if used after a qualified term', () => {\n      assert.isTrue(isValidAutocomplete('qualified-term webauthn', options));\n      assert.isTrue(\n        isValidAutocomplete('section-foo qualified-term webauthn', options)\n      );\n      assert.isTrue(\n        isValidAutocomplete('home qualified-term webauthn', options)\n      );\n      assert.isTrue(\n        isValidAutocomplete('section-foo home qualified-term webauthn', options)\n      );\n    });\n\n    it('returns false when used with only optional tokens', () => {\n      assert.isFalse(isValidAutocomplete('home webauthn', options));\n      assert.isFalse(isValidAutocomplete('section-foo webauthn', options));\n      assert.isFalse(isValidAutocomplete('section-foo home webauthn', options));\n    });\n  });\n\n  describe('options.strictMode:false', () => {\n    it('returns true if the last term is a valid autocomplete term', () => {\n      assert.isTrue(\n        isValidAutocomplete('do not care! valid-term', {\n          looseTyped: true,\n          standaloneTerms: ['valid-term']\n        })\n      );\n    });\n\n    it('returns true if the last term is webauthn, and the term before is valid', () => {\n      assert.isTrue(\n        isValidAutocomplete('do not care! valid-term webauthn', {\n          looseTyped: true,\n          standaloneTerms: ['valid-term']\n        })\n      );\n    });\n\n    it('returns false if the last term is an invalid autocomplete term', () => {\n      assert.isFalse(\n        isValidAutocomplete('shipping invalid', {\n          looseTyped: true,\n          standaloneTerms: ['valid-term']\n        })\n      );\n    });\n  });\n\n  describe('options.ignoredValues', () => {\n    it('returns undefined if value is invalid and ignored', () => {\n      assert.isUndefined(\n        isValidAutocomplete('bad-term', {\n          ignoredValues: ['bad-term']\n        })\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/label-text.js",
    "content": "describe('text.labelText', function () {\n  var labelText = axe.commons.text.labelText;\n  var queryFixture = axe.testUtils.queryFixture;\n\n  it('returns the text of an implicit label', function () {\n    var target = queryFixture(\n      '<label>' + 'My implicit label<input id=\"target\" />' + '</label>'\n    );\n    assert.equal(labelText(target), 'My implicit label');\n  });\n\n  it('returns the text of an explicit label', function () {\n    var target = queryFixture(\n      '<label for=\"target\">My explicit label</label>' + '<input id=\"target\" />'\n    );\n    assert.equal(labelText(target), 'My explicit label');\n  });\n\n  it('ignores the text of nested implicit labels', function () {\n    var target = queryFixture(\n      '<label>My outer label' +\n        '<label>My inner label' +\n        '<input id=\"target\" />' +\n        '</label>' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My inner label');\n  });\n\n  it('concatinates multiple explicit labels', function () {\n    var target = queryFixture(\n      '<label for=\"target\">My label 1</label>' +\n        '<label for=\"target\">My label 2</label>' +\n        '<input id=\"target\" />'\n    );\n    assert.equal(labelText(target), 'My label 1 My label 2');\n  });\n\n  it('concatinates explicit and implicit labels', function () {\n    var target = queryFixture(\n      '<label for=\"target\">My explicit label</label>' +\n        '<label for=\"target\">My implicit label' +\n        '<input id=\"target\" />' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My explicit label My implicit label');\n  });\n\n  it('returns label text in the DOM order', function () {\n    var target = queryFixture(\n      '<label for=\"target\">Label 1</label>' +\n        '<label for=\"target\">My implicit ' +\n        '<label for=\"target\">Label 2</label>' +\n        '<input id=\"target\" />' +\n        '</label>' +\n        '<label for=\"target\">Label 3</label>'\n    );\n    assert.equal(labelText(target), 'Label 1 My implicit Label 2 Label 3');\n  });\n\n  it('does not return the same label twice', function () {\n    var target = queryFixture(\n      '<label for=\"target\">' +\n        'My implicit and explicit label' +\n        '<input id=\"target\" />' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My implicit and explicit label');\n  });\n\n  it('ignores the value of a textbox', function () {\n    var target = queryFixture(\n      '<label>My label' +\n        '<input value=\"without text\" id=\"target\" />' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My label');\n  });\n\n  it('ignores the content of a textarea', function () {\n    var target = queryFixture(\n      '<label>My label' +\n        '<textarea id=\"target\">Without text</textarea' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My label');\n  });\n\n  it('ignores the options of a select element', function () {\n    var target = queryFixture(\n      '<label>My label' +\n        '<select id=\"target\">' +\n        '<option selected>Without</option>' +\n        '<option>text</option>' +\n        '</select>' +\n        '</label>'\n    );\n    assert.equal(labelText(target), 'My label');\n  });\n\n  describe('with context = { inControlContext: true }', function () {\n    it('returns `` ', function () {\n      var target = queryFixture(\n        '<label for=\"target\">My explicit label</label>' +\n          '<input id=\"target\" />'\n      );\n      assert.equal(labelText(target, { inControlContext: true }), '');\n    });\n  });\n\n  describe('with context = { inLabelledByContext: true }', function () {\n    it('returns `` ', function () {\n      var target = queryFixture(\n        '<label for=\"target\">My explicit label</label>' +\n          '<input id=\"target\" />'\n      );\n      assert.equal(labelText(target, { inLabelledByContext: true }), '');\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/label-virtual.js",
    "content": "describe('text.labelVirtual', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is called from text.label', function () {\n    fixture.innerHTML =\n      '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n      '<input id=\"target\" aria-labelledby=\"monkeys bananas\">';\n\n    axe.testUtils.flatTreeSetup(document.body);\n    var target = fixture.querySelector('#target');\n    assert.equal(axe.commons.text.label(target), 'monkeys bananas');\n  });\n\n  describe('aria-labelledby', function () {\n    it('should join text with a single space', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should filter invisible elements', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\" style=\"display: none\">bananas</div>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n\n    it('should take precedence over aria-label', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\" aria-label=\"nope\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should take precedence over explicit labels', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n        '<label for=\"target\">nope</label>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should take precedence over implicit labels', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">monkeys</div><div id=\"bananas\">bananas</div>' +\n        '<label>nope' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\"></label>';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys bananas');\n    });\n\n    it('should ignore whitespace only labels', function () {\n      fixture.innerHTML =\n        '<div id=\"monkeys\">\t\\n  </div><div id=\"bananas\"></div>' +\n        '<input id=\"target\" aria-labelledby=\"monkeys bananas\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.isNull(axe.commons.text.labelVirtual(target));\n    });\n  });\n\n  describe('aria-label', function () {\n    it('should detect it', function () {\n      fixture.innerHTML = '<input id=\"target\" aria-label=\"monkeys\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n\n    it('should ignore whitespace only labels', function () {\n      fixture.innerHTML = '<input id=\"target\" aria-label=\"   \\n\t\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.isNull(axe.commons.text.labelVirtual(target));\n    });\n\n    it('should take precedence over explicit labels', function () {\n      fixture.innerHTML =\n        '<label for=\"target\">nope</label>' +\n        '<input id=\"target\" aria-label=\"monkeys\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n\n    it('should take precedence over implicit labels', function () {\n      fixture.innerHTML =\n        '<label>nope' + '<input id=\"target\" aria-label=\"monkeys\"></label>';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n  });\n\n  describe('explicit label', function () {\n    it('should detect it', function () {\n      fixture.innerHTML =\n        '<label for=\"target\">monkeys</label>' + '<input id=\"target\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n\n    it('should ignore whitespace only or empty labels', function () {\n      fixture.innerHTML =\n        '<label for=\"target\">\t\\n\\r  </label>' + '<input id=\"target\">';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.isNull(axe.commons.text.labelVirtual(target));\n    });\n\n    it('should take precedence over implicit labels', function () {\n      fixture.innerHTML =\n        '<label for=\"target\">monkeys</label>' +\n        '<label>nope' +\n        '<input id=\"target\"></label>';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n  });\n\n  describe('implicit label', function () {\n    it('should detect it', function () {\n      fixture.innerHTML = '<label>monkeys' + '<input id=\"target\"><label>';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.equal(axe.commons.text.labelVirtual(target), 'monkeys');\n    });\n\n    it('should ignore whitespace only or empty labels', function () {\n      fixture.innerHTML = '<label> ' + '<input id=\"target\"><label>';\n\n      var tree = axe.testUtils.flatTreeSetup(document.body);\n      var target = axe.utils.querySelectorAll(tree, '#target')[0];\n      assert.isNull(axe.commons.text.labelVirtual(target));\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/native-text-alternative.js",
    "content": "describe('text.nativeTextAlternative', function () {\n  var text = axe.commons.text;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n  var queryFixture = axe.testUtils.queryFixture;\n  var nativeTextAlternative = text.nativeTextAlternative;\n\n  it('runs accessible text methods specified for the native element', function () {\n    var vNode = queryFixture('<button id=\"target\">foo</button>');\n    assert.equal(nativeTextAlternative(vNode), 'foo');\n  });\n\n  it('returns the accessible text of the first method that returns something', function () {\n    var vNode = queryFixture(\n      '<input id=\"target\" type=\"image\" alt=\"foo\" value=\"bar\" title=\"baz\">'\n    );\n    assert.equal(nativeTextAlternative(vNode), 'foo');\n  });\n\n  it('returns `` when no method matches', function () {\n    var vNode = queryFixture('<div id=\"target\">baz</div>');\n    assert.equal(nativeTextAlternative(vNode), '');\n  });\n\n  it('returns `` when no accessible text method returned something', function () {\n    var div = queryFixture('<div id=\"target\">baz</div>');\n    assert.equal(nativeTextAlternative(div), '');\n  });\n\n  it('returns `` when the node is not an element', function () {\n    fixtureSetup('foo bar baz');\n    var fixture = axe.utils.querySelectorAll(axe._tree[0], '#fixture')[0];\n    assert.equal(fixture.children[0].actualNode.nodeType, 3);\n    assert.equal(nativeTextAlternative(fixture.children[0]), '');\n  });\n\n  it('returns `` when the element has role=presentation', function () {\n    var vNode = queryFixture(\n      '<img id=\"target\" alt=\"foo\" role=\"presentation\" />'\n    );\n    assert.equal(nativeTextAlternative(vNode), '');\n  });\n\n  it('returns `` when the element has role=none', function () {\n    var vNode = queryFixture('<img id=\"target\" alt=\"foo\" role=\"none\" />');\n    assert.equal(nativeTextAlternative(vNode), '');\n  });\n});\n"
  },
  {
    "path": "test/commons/text/native-text-methods.js",
    "content": "describe('text.nativeTextMethods', () => {\n  const text = axe.commons.text;\n  const nativeTextMethods = text.nativeTextMethods;\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n\n  describe('valueText', () => {\n    const valueText = nativeTextMethods.valueText;\n    it('returns the value of actualNode', () => {\n      fixtureSetup('<input value=\"foo\" />');\n      const input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n      assert.equal(valueText(input), 'foo');\n    });\n\n    it('returns `` when there is no value', () => {\n      fixtureSetup('<input />');\n      const input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n      assert.equal(valueText(input), '');\n    });\n  });\n\n  describe('buttonDefaultText', () => {\n    const buttonDefaultText = nativeTextMethods.buttonDefaultText;\n    it('returns the default button text', () => {\n      fixtureSetup(\n        '<input type=\"submit\" />' +\n          '<input type=\"image\" />' +\n          '<input type=\"reset\" />' +\n          '<input type=\"button\" />'\n      );\n      const inputs = axe.utils.querySelectorAll(axe._tree[0], 'input');\n      assert.equal(buttonDefaultText(inputs[0]), 'Submit');\n      assert.equal(buttonDefaultText(inputs[1]), 'Submit');\n      assert.equal(buttonDefaultText(inputs[2]), 'Reset');\n      assert.equal(buttonDefaultText(inputs[3]), '');\n    });\n\n    it('returns `` when the element is not a button', () => {\n      fixtureSetup('<input type=\"text\">');\n      const input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n      assert.equal(buttonDefaultText(input), '');\n    });\n\n    it('returns the default button text with mixed-case types', () => {\n      fixtureSetup(\n        '<input type=\"SUBMIT\" />' +\n          '<input type=\"ImAGE\" />' +\n          '<input type=\"ResET\" />' +\n          '<input type=\"buTTON\" />'\n      );\n      const inputs = axe.utils.querySelectorAll(axe._tree[0], 'input');\n      assert.equal(buttonDefaultText(inputs[0]), 'Submit');\n      assert.equal(buttonDefaultText(inputs[1]), 'Submit');\n      assert.equal(buttonDefaultText(inputs[2]), 'Reset');\n      assert.equal(buttonDefaultText(inputs[3]), '');\n    });\n  });\n\n  describe('altText', () => {\n    const altText = nativeTextMethods.altText;\n    it('returns the alt attribute of actualNode', () => {\n      fixtureSetup('<img alt=\"foo\" />');\n      const img = axe.utils.querySelectorAll(axe._tree[0], 'img')[0];\n      assert.equal(altText(img), 'foo');\n    });\n\n    it('returns `` when there is no alt', () => {\n      fixtureSetup('<img />');\n      const img = axe.utils.querySelectorAll(axe._tree[0], 'img')[0];\n      assert.equal(altText(img), '');\n    });\n  });\n\n  describe('figureText', () => {\n    it('returns the figcaption text', () => {\n      const figureText = nativeTextMethods.figureText;\n      fixtureSetup(\n        '<figure>' +\n          '  <figcaption>My caption</figcaption>' +\n          '  some content' +\n          '</figure>'\n      );\n      const figure = axe.utils.querySelectorAll(axe._tree[0], 'figure')[0];\n      assert.equal(figureText(figure), 'My caption');\n    });\n\n    it('returns `` when there is no figcaption', () => {\n      const figureText = nativeTextMethods.figureText;\n      fixtureSetup('<figure>' + '  some content' + '</figure>');\n      const figure = axe.utils.querySelectorAll(axe._tree[0], 'figure')[0];\n      assert.equal(figureText(figure), '');\n    });\n\n    it('returns `` when if the figcaption is nested in another figure', () => {\n      const figureText = nativeTextMethods.figureText;\n      fixtureSetup(\n        '<figure id=\"fig1\">' +\n          '  <figure>' +\n          '    <figcaption>No caption</figcaption>' +\n          '    some content' +\n          '  </figure>' +\n          '  some other content' +\n          '</figure>'\n      );\n      const figure = axe.utils.querySelectorAll(axe._tree[0], '#fig1')[0];\n      assert.equal(figureText(figure), '');\n    });\n  });\n\n  describe('tableCaptionText', () => {\n    const tableCaptionText = nativeTextMethods.tableCaptionText;\n    it('returns the table caption text', () => {\n      fixtureSetup(\n        '<table>' +\n          '  <caption>My caption</caption>' +\n          '  <tr><th>heading</th></tr>' +\n          '  <tr><td>data</td></tr>' +\n          '</table>'\n      );\n      const table = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];\n      assert.equal(tableCaptionText(table), 'My caption');\n    });\n\n    it('returns `` when there is no caption', () => {\n      fixtureSetup(\n        '<table>' +\n          '  <tr><th>heading</th></tr>' +\n          '  <tr><td>data</td></tr>' +\n          '</table>'\n      );\n      const table = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];\n      assert.equal(tableCaptionText(table), '');\n    });\n\n    it('returns `` when if the caption is nested in another table', () => {\n      fixtureSetup(\n        '<table id=\"tbl1\">' +\n          '  <tr><td>' +\n          '    <table>' +\n          '      <caption>My caption</caption>' +\n          '      <tr><th>heading</th></tr>' +\n          '      <tr><td>data</td></tr>' +\n          '    </table>' +\n          '  </th></tr>' +\n          '</table>'\n      );\n      const table = axe.utils.querySelectorAll(axe._tree[0], '#tbl1')[0];\n      assert.equal(tableCaptionText(table), '');\n    });\n  });\n\n  describe('fieldsetLegendText', () => {\n    it('returns the legend text', () => {\n      const fieldsetLegendText = nativeTextMethods.fieldsetLegendText;\n      fixtureSetup(\n        '<fieldset>' +\n          '  <legend>My legend</legend>' +\n          '  some content' +\n          '</fieldset>'\n      );\n      const fieldset = axe.utils.querySelectorAll(axe._tree[0], 'fieldset')[0];\n      assert.equal(fieldsetLegendText(fieldset), 'My legend');\n    });\n\n    it('returns `` when there is no legend', () => {\n      const fieldsetLegendText = nativeTextMethods.fieldsetLegendText;\n      fixtureSetup('<fieldset>' + '  some content' + '</fieldset>');\n      const fieldset = axe.utils.querySelectorAll(axe._tree[0], 'fieldset')[0];\n      assert.equal(fieldsetLegendText(fieldset), '');\n    });\n\n    it('returns `` when if the legend is nested in another fieldset', () => {\n      const fieldsetLegendText = nativeTextMethods.fieldsetLegendText;\n      fixtureSetup(\n        '<fieldset id=\"fig1\">' +\n          '  <fieldset>' +\n          '    <legend>No legend</legend>' +\n          '    some content' +\n          '  </fieldset>' +\n          '  some other content' +\n          '</fieldset>'\n      );\n      const fieldset = axe.utils.querySelectorAll(axe._tree[0], '#fig1')[0];\n      assert.equal(fieldsetLegendText(fieldset), '');\n    });\n  });\n\n  describe('svgTitleText', () => {\n    const svgTitleText = nativeTextMethods.svgTitleText;\n    it('returns the title text', () => {\n      fixtureSetup(\n        '<svg>' + '  <title>My title</title>' + '  some content' + '</svg>'\n      );\n      const svg = axe.utils.querySelectorAll(axe._tree[0], 'svg')[0];\n      assert.equal(svgTitleText(svg), 'My title');\n    });\n\n    it('returns `` when there is no title', () => {\n      it('returns the title text', () => {\n        fixtureSetup('<svg>' + '  some content' + '</svg>');\n        const svg = axe.utils.querySelectorAll(axe._tree[0], 'svg')[0];\n        assert.equal(svgTitleText(svg), '');\n      });\n    });\n\n    it('returns `` when if the title is nested in another svg', () => {\n      it('returns the title text', () => {\n        fixtureSetup(\n          '<svg id=\"fig1\">' +\n            '  <svg>' +\n            '    <title>No title</title>' +\n            '    some content' +\n            '  </svg>' +\n            '  some other content' +\n            '</svg>'\n        );\n        const svg = axe.utils.querySelectorAll(axe._tree[0], '#fig1')[0];\n        assert.equal(svgTitleText(svg), '');\n      });\n    });\n  });\n\n  describe('singleSpace', () => {\n    const singleSpace = nativeTextMethods.singleSpace;\n    it('returns a single space', () => {\n      assert.equal(singleSpace(), ' ');\n    });\n  });\n\n  describe('placeholderText', () => {\n    const placeholderText = nativeTextMethods.placeholderText;\n    it('returns the placeholder attribute of actualNode', () => {\n      fixtureSetup('<input placeholder=\"foo\" />');\n      const input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n      assert.equal(placeholderText(input), 'foo');\n    });\n\n    it('returns `` when there is no placeholder', () => {\n      fixtureSetup('<input />');\n      const input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];\n      assert.equal(placeholderText(input), '');\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/sanitize.js",
    "content": "describe('text.sanitize', function () {\n  'use strict';\n\n  it('should collapse whitespace and trim', function () {\n    assert.equal(axe.commons.text.sanitize('\\thi\\t'), 'hi');\n    assert.equal(axe.commons.text.sanitize('\\t\\nhi \\t'), 'hi');\n    assert.equal(axe.commons.text.sanitize('\\thi \\n\\t '), 'hi');\n    assert.equal(axe.commons.text.sanitize(' hi\\r\\nok'), 'hi\\nok');\n    assert.equal(axe.commons.text.sanitize('hello\\u00A0there'), 'hello there');\n  });\n\n  it('should accept null', function () {\n    assert.equal(axe.commons.text.sanitize(null), '');\n  });\n});\n"
  },
  {
    "path": "test/commons/text/subtree-text.js",
    "content": "describe('text.subtreeText', () => {\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const subtreeText = axe.commons.text.subtreeText;\n\n  it('concatinated the accessible name for child elements', () => {\n    fixtureSetup('<span>foo</span> <span>bar</span> <span>baz</span>');\n    const fixture = axe.utils.querySelectorAll(axe._tree[0], '#fixture')[0];\n    assert.equal(subtreeText(fixture), 'foo bar baz');\n  });\n\n  it('returns `` when the element is not named from contents', () => {\n    fixtureSetup('<main>foo bar baz</main>');\n    const main = axe.utils.querySelectorAll(axe._tree[0], 'main')[0];\n    assert.equal(subtreeText(main), '');\n  });\n\n  it('adds spacing around \"block-like\" elements', () => {\n    fixtureSetup(\n      '<div>foo</div>' +\n        '<h1>bar</h1>' +\n        '<p>baz</p>' +\n        '<blockquote>fizz</blockquote>' +\n        '<pre>buzz</pre>'\n    );\n    const fixture = axe.utils.querySelectorAll(axe._tree[0], '#fixture')[0];\n    assert.equal(subtreeText(fixture), 'foo bar baz fizz buzz ');\n  });\n\n  it('does not add spacing around \"inline-like\" elements', () => {\n    fixtureSetup(\n      '<a>foo</a>' + '<b>bar</b>' + '<i>baz</i>' + '<s>fizz</s>' + '<u>buzz</u>'\n    );\n    const fixture = axe.utils.querySelectorAll(axe._tree[0], '#fixture')[0];\n    assert.equal(subtreeText(fixture), 'foobarbazfizzbuzz');\n  });\n\n  it('returns `` for embedded content', () => {\n    fixtureSetup(\n      '<video>foo</video>' +\n        '<audio>foo</audio>' +\n        '<canvas>foo</canvas>' +\n        '<iframe>foo</iframe>' +\n        '<svg>foo</svg>'\n    );\n    const children = axe._tree[0].children;\n    assert.lengthOf(children, 5);\n    children.forEach(function (embeddedContent) {\n      assert.equal(subtreeText(embeddedContent), '');\n    });\n  });\n\n  describe('name from author', () => {\n    it('returns `` with default context', () => {\n      fixtureSetup('<main>foo</main>');\n      const div = axe.utils.querySelectorAll(axe._tree[0], 'main')[0];\n      const context = {};\n      assert.equal(subtreeText(div, context), '');\n    });\n\n    it('returns content with { inLabelledByContext: true }', () => {\n      fixtureSetup('<main>foo</main>');\n      const div = axe.utils.querySelectorAll(axe._tree[0], 'main')[0];\n      const context = { inLabelledByContext: true };\n      assert.equal(subtreeText(div, context), 'foo');\n    });\n\n    it('returns content with { subtreeDescendant: true }', () => {\n      fixtureSetup('<main>foo</main>');\n      const div = axe.utils.querySelectorAll(axe._tree[0], 'main')[0];\n      const context = { subtreeDescendant: true };\n      assert.equal(subtreeText(div, context), 'foo');\n    });\n\n    it('returns `` for roles that have a value, even with inLabelledByContext and subtreeDescendant', () => {\n      fixtureSetup('<select><option>foo</option</select>');\n      const div = axe.utils.querySelectorAll(axe._tree[0], 'select')[0];\n      const context = { inLabelledByContext: true, subtreeDescendant: true };\n      assert.equal(subtreeText(div, context), '');\n    });\n  });\n\n  describe('context.processed', () => {\n    beforeEach(() => {\n      fixtureSetup('<h1>foo</h1>');\n    });\n\n    it('appends the element to context.processed to prevent duplication', () => {\n      const h1 = axe.utils.querySelectorAll(axe._tree[0], 'h1')[0];\n      const text = h1.children[0];\n      const context = { processed: [] };\n      subtreeText(h1, context);\n      assert.deepEqual(context.processed, [h1, text]);\n    });\n\n    it('sets context.processed when it is undefined', () => {\n      const h1 = axe.utils.querySelectorAll(axe._tree[0], 'h1')[0];\n      const text = h1.children[0];\n      const emptyContext = {};\n      subtreeText(h1, emptyContext);\n      assert.deepEqual(emptyContext.processed, [h1, text]);\n    });\n\n    it('returns `` when the element is in the `processed` array', () => {\n      const h1 = axe.utils.querySelectorAll(axe._tree[0], 'h1')[0];\n      const context = {\n        processed: [h1]\n      };\n      assert.equal(subtreeText(h1, context), '');\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/text/unicode.js",
    "content": "describe('text.hasUnicode', () => {\n  describe('text.hasUnicode, characters of type Non Bi Multilingual Plane', () => {\n    it('returns false when given string is alphanumeric', () => {\n      const actual = axe.commons.text.hasUnicode('1 apple', {\n        nonBmp: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns false when given string is number', () => {\n      const actual = axe.commons.text.hasUnicode('100', {\n        nonBmp: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns false when given string is a sentence', () => {\n      const actual = axe.commons.text.hasUnicode('Earth is round', {\n        nonBmp: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns true when given string is a phonetic extension', () => {\n      const actual = axe.commons.text.hasUnicode('ᴁ', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string is a combining diacritical marks supplement', () => {\n      const actual = axe.commons.text.hasUnicode('ᴁ', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string is a currency symbols', () => {\n      const actual = axe.commons.text.hasUnicode('₨ 20000', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has arrows', () => {\n      const actual = axe.commons.text.hasUnicode('← turn left', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has geometric shapes', () => {\n      const actual = axe.commons.text.hasUnicode('◓', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has math operators', () => {\n      const actual = axe.commons.text.hasUnicode('√4 = 2', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has windings font', () => {\n      const actual = axe.commons.text.hasUnicode('▽', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true for a string with characters in supplementary private use area A', () => {\n      const actual = axe.commons.text.hasUnicode('\\uDB80\\uDFFE', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has format unicode', () => {\n      // zero-width spacer character U+200B\n      const actual = axe.commons.text.hasUnicode('\\u200BHello World', {\n        nonBmp: true\n      });\n      assert.isTrue(actual);\n    });\n  });\n\n  describe('text.hasUnicode, characters of type Emoji', () => {\n    it('returns false when given string is alphanumeric', () => {\n      const actual = axe.commons.text.hasUnicode(\n        '1 apple a day, keeps the doctor away',\n        {\n          emoji: true\n        }\n      );\n      assert.isFalse(actual);\n    });\n\n    it('returns false when given string is number', () => {\n      const actual = axe.commons.text.hasUnicode('100', {\n        emoji: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns false when given string is a sentence', () => {\n      const actual = axe.commons.text.hasUnicode('Earth is round', {\n        emoji: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns true when given string has emoji', () => {\n      const actual = axe.commons.text.hasUnicode('🌎 is round', {\n        emoji: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given string has emoji', () => {\n      const actual = axe.commons.text.hasUnicode('plant a 🌱', {\n        emoji: true\n      });\n      assert.isTrue(actual);\n    });\n  });\n\n  describe('text.hasUnicode, characters of type punctuations', () => {\n    it('returns false when given string is number', () => {\n      const actual = axe.commons.text.hasUnicode('100', {\n        punctuations: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns false when given string is a sentence', () => {\n      const actual = axe.commons.text.hasUnicode('Earth is round', {\n        punctuations: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns true when given string has punctuations', () => {\n      const actual = axe.commons.text.hasUnicode(\"What's your name?\", {\n        punctuations: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true for strings with money signs and odd symbols', () => {\n      ['£', '¢', '¥', '€', '§', '±'].forEach(function (str) {\n        const actual = axe.commons.text.hasUnicode(str, {\n          punctuations: true\n        });\n        assert.isTrue(actual);\n      });\n    });\n  });\n\n  describe('text.hasUnicode, has combination of unicode', () => {\n    it('returns false when given string is number', () => {\n      const actual = axe.commons.text.hasUnicode('100', {\n        emoji: true,\n        nonBmp: true,\n        punctuations: true\n      });\n      assert.isFalse(actual);\n    });\n\n    it('returns true when given string has unicode characters', () => {\n      const actual = axe.commons.text.hasUnicode(\n        'The ☀️ is orange, the ◓ is white.',\n        {\n          emoji: true,\n          nonBmp: true,\n          punctuations: true\n        }\n      );\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given format unicode characters', () => {\n      // zero-width spacer character U+200B\n      const actual = axe.commons.text.hasUnicode('\\u200BHello World', {\n        emoji: true,\n        nonBmp: true,\n        punctuations: true\n      });\n      assert.isTrue(actual);\n    });\n\n    it('returns true when given punctuation characters', () => {\n      const actual = axe.commons.text.hasUnicode('Earth!!!', {\n        emoji: true,\n        nonBmp: true,\n        punctuations: true\n      });\n      assert.isTrue(actual);\n    });\n  });\n});\n\ndescribe('text.removeUnicode', () => {\n  it('returns string by removing non BMP unicode ', () => {\n    const actual = axe.commons.text.removeUnicode('₨₨20000₨₨', {\n      nonBmp: true\n    });\n    assert.equal(actual, '20000');\n  });\n\n  it('returns string by removing emoji unicode ', () => {\n    const actual = axe.commons.text.removeUnicode('☀️Sun 🌎Earth', {\n      emoji: true\n    });\n    assert.equal(actual, 'Sun Earth');\n  });\n\n  it('returns string after removing punctuations from word', () => {\n    const actual = axe.commons.text.removeUnicode('Earth!!!', {\n      punctuations: true\n    });\n    assert.equal(actual, 'Earth');\n  });\n\n  it('returns string removing all punctuations', () => {\n    const actual = axe.commons.text.removeUnicode('<!,.\"\\':;!>', {\n      punctuations: true\n    });\n    assert.equal(actual, '');\n  });\n\n  it('returns string removing all private use unicode', () => {\n    const actual = axe.commons.text.removeUnicode('', {\n      nonBmp: true\n    });\n    assert.equal(actual, '');\n  });\n\n  it('returns string removing all supplementary private use unicode', () => {\n    const actual = axe.commons.text.removeUnicode('󰀀󿰀󿿽󰏽', {\n      nonBmp: true\n    });\n    assert.equal(actual, '');\n  });\n\n  it('returns the string with supplementary private use area A characters removed', () => {\n    const actual = axe.commons.text.removeUnicode('\\uDB80\\uDFFE', {\n      nonBmp: true\n    });\n    assert.equal(actual, '');\n  });\n\n  it('returns string removing combination of unicode characters', () => {\n    const actual = axe.commons.text.removeUnicode(\n      'The ☀️ is orange, the ◓ is white.',\n      {\n        emoji: true,\n        nonBmp: true,\n        punctuations: true\n      }\n    );\n    assert.equal(actual, 'The  is orange the  is white');\n  });\n\n  it('returns string removing format unicode', () => {\n    // zero-width spacer character U+200B\n    const actual = axe.commons.text.removeUnicode('\\u200BHello World', {\n      nonBmp: true\n    });\n    assert.equal(actual, 'Hello World');\n  });\n});\n"
  },
  {
    "path": "test/commons/text/visible-text-nodes.js",
    "content": "describe('text.visibleTextNodes', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var visibleTextNodes = axe.commons.text.visibleTextNodes;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should handle multiple text nodes to a single parent', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">Hello<span>Hi</span>Goodbye</div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 3);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n    assert.equal(nodes[1].actualNode.nodeValue, 'Hi');\n    assert.equal(nodes[2].actualNode.nodeValue, 'Goodbye');\n  });\n\n  it('should handle recursive calls', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">Hello<span><span>Hi</span></span></div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 2);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n    assert.equal(nodes[1].actualNode.nodeValue, 'Hi');\n  });\n\n  it('should not return elements with visibility: hidden', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">Hello<span style=\"visibility: hidden;\">Hi</span></div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 1);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n  });\n\n  it('should know how visibility works', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">Hello<span style=\"visibility: hidden;\">' +\n        '<span style=\"visibility: visible;\">Hi</span>' +\n        '</span></div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 2);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n    assert.equal(nodes[1].actualNode.nodeValue, 'Hi');\n  });\n\n  it('should not return elements with display: none', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">Hello<span style=\"display: none;\">Hi</span></div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 1);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n  });\n\n  it('should ignore script and style tags', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\"><script> // hello </script><style> /*hello */</style>' +\n        'Hello</div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 1);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n  });\n\n  it('should not take into account position of parents', function () {\n    var vNode = queryFixture(\n      '<div id=\"target\">' +\n        '<div style=\"position: absolute; top: -9999px;\">' +\n        '<div style=\"position: absolute; top: 10000px;\">Hello</div>' +\n        '</div>' +\n        '</div>'\n    );\n    var nodes = visibleTextNodes(vNode);\n    assert.equal(nodes.length, 1);\n    assert.equal(nodes[0].actualNode.nodeValue, 'Hello');\n  });\n\n  (shadowSupported ? it : xit)(\n    'should correctly handle slotted elements',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML = '<div id=\"target\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n      fixture.innerHTML = '<div><a>hello</a></div>';\n      makeShadowTree(fixture.firstChild);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var nodes = visibleTextNodes(tree[0]);\n      assert.equal(nodes.length, 2);\n      assert.equal(nodes[0].actualNode.nodeValue, 'Stuff');\n      assert.equal(nodes[1].actualNode.nodeValue, 'hello');\n    }\n  );\n});\n"
  },
  {
    "path": "test/commons/text/visible-virtual.js",
    "content": "describe('text.visible', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var visibleVirtual = axe.commons.text.visibleVirtual;\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  describe('non-screen-reader', function () {\n    it('should not return elements with visibility: hidden', function () {\n      fixture.innerHTML = 'Hello<span style=\"visibility: hidden;\">Hi</span>';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello');\n    });\n\n    it('should handle implicitly recursive calls', function () {\n      fixture.innerHTML = 'Hello<span><span>Hi</span></span>';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'HelloHi');\n    });\n\n    it('should handle explicitly recursive calls', function () {\n      fixture.innerHTML = 'Hello<span><span>Hi</span></span>';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], null, false), 'HelloHi');\n    });\n\n    it('should handle non-recursive calls', function () {\n      fixture.innerHTML = 'Hello<span><span>Hi</span></span>';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], null, true), 'Hello');\n    });\n\n    it('should know how visibility works', function () {\n      fixture.innerHTML =\n        'Hello <span style=\"visibility: hidden;\">' +\n        '<span style=\"visibility: visible;\">Hi</span>' +\n        '</span>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello Hi');\n    });\n\n    it('should not return elements with display: none', function () {\n      fixture.innerHTML =\n        'Hello<span style=\"display: none;\"><span>Hi</span></span>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello');\n    });\n\n    it('should trim the result', function () {\n      fixture.innerHTML =\n        '   &nbsp;\\u00A0    Hello  &nbsp;\\r\\n   Hi     \\n \\n &nbsp; \\n   ';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello Hi');\n    });\n\n    it('should ignore script and style tags', function () {\n      fixture.innerHTML =\n        '<script> // hello </script><style> /*hello */</style>' + 'Hello';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello');\n    });\n\n    it('should not take into account position of parents', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: -9999px;\">' +\n        '<div style=\"position: absolute; top: 10000px;\">Hello</div>' +\n        '</div>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0]), 'Hello');\n    });\n\n    (shadowSupported ? it : xit)(\n      'should correctly handle slotted elements',\n      function () {\n        function createContentSlotted() {\n          var group = document.createElement('div');\n          group.innerHTML = '<div id=\"target\">Stuff<slot></slot></div>';\n          return group;\n        }\n        function makeShadowTree(node) {\n          var root = node.attachShadow({ mode: 'open' });\n          var div = document.createElement('div');\n          root.appendChild(div);\n          div.appendChild(createContentSlotted());\n        }\n        fixture.innerHTML = '<div><a>hello</a></div>';\n        makeShadowTree(fixture.firstChild);\n        var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n        assert.equal(visibleVirtual(tree[0]), 'Stuffhello');\n      }\n    );\n  });\n\n  describe('screen reader', function () {\n    it('should not return elements with visibility: hidden', function () {\n      fixture.innerHTML = 'Hello<span style=\"visibility: hidden;\">Hi</span>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello');\n    });\n\n    it('should know how visibility works', function () {\n      fixture.innerHTML =\n        'Hello <span style=\"visibility: hidden;\">' +\n        '<span style=\"visibility: visible;\">Hi</span>' +\n        '</span>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello Hi');\n    });\n\n    it('should not return elements with display: none', function () {\n      fixture.innerHTML =\n        'Hello<span style=\"display: none;\"><span>Hi</span></span>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello');\n    });\n\n    it('should trim the result', function () {\n      fixture.innerHTML =\n        '   &nbsp;\\u00A0    Hello  &nbsp;\\r\\n   Hi     \\n \\n &nbsp; \\n   ';\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello Hi');\n    });\n\n    it('should ignore script and style tags', function () {\n      fixture.innerHTML =\n        '<script> // hello </script><style> /*hello */</style>' + 'Hello';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello');\n    });\n\n    it('should not consider offscreen text as hidden (position)', function () {\n      fixture.innerHTML =\n        '<div style=\"position: absolute; top: -9999px;\">' +\n        '<div>Hello</div>' +\n        '</div>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello');\n    });\n\n    it('should not consider offscreen text as hidden (text-indent)', function () {\n      fixture.innerHTML = '<div style=\"text-indent: -9999px;\">' + 'Hello</div>';\n\n      var tree = axe.utils.getFlattenedTree(fixture);\n      assert.equal(visibleVirtual(tree[0], true), 'Hello');\n    });\n  });\n});\n"
  },
  {
    "path": "test/commons/utils/index.js",
    "content": "describe('utils.escapeSelector', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.commons.utils.escapeSelector);\n  });\n});\ndescribe('utils.matchesSelector', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.commons.utils.matchesSelector);\n  });\n});\ndescribe('utils.clone', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.commons.utils.clone);\n  });\n});\n"
  },
  {
    "path": "test/core/base/audit.js",
    "content": "describe('Audit', () => {\n  const Audit = axe._thisWillBeDeletedDoNotUse.base.Audit;\n  const Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;\n  const ver = axe.version.substring(0, axe.version.lastIndexOf('.'));\n  const { fixtureSetup, captureError } = axe.testUtils;\n  let audit;\n  const isNotCalled = err => {\n    throw err || new Error('Reject should not be called');\n  };\n  const noop = () => {};\n\n  const assertEqualRuleError = (actual, expect) => {\n    assert.include(actual.message, expect.message);\n    assert.equal(actual.stack, expect.stack);\n    assert.equal(actual.name, expect.name);\n  };\n\n  const assertErrorResults = (result, error, selector) => {\n    assert.equal(result.result, 'cantTell');\n    assertEqualRuleError(result.error, error);\n\n    assert.lengthOf(result.nodes, 1);\n    const node1 = result.nodes[0];\n    assert.isEmpty(node1.any);\n    assert.isEmpty(node1.all);\n    assert.include(node1.node.selector, selector);\n\n    assert.lengthOf(node1.none, 1);\n    const none = node1.none[0];\n    assert.equal(none.id, 'error-occurred');\n    assert.equal(none.result, undefined);\n    assert.isDefined(none.data);\n    assertEqualRuleError(none.data, error);\n    assert.lengthOf(none.relatedNodes, 0);\n  };\n\n  const mockChecks = [\n    {\n      id: 'positive1-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'positive2-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'negative1-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'positive3-check1',\n      evaluate: () => true\n    }\n  ];\n\n  const mockRules = [\n    {\n      id: 'positive1',\n      selector: 'input',\n      tags: ['positive'],\n      any: [\n        {\n          id: 'positive1-check1'\n        }\n      ]\n    },\n    {\n      id: 'positive2',\n      selector: '#monkeys',\n      tags: ['positive'],\n      any: ['positive2-check1']\n    },\n    {\n      id: 'negative1',\n      selector: 'div',\n      tags: ['negative'],\n      none: ['negative1-check1']\n    },\n    {\n      id: 'positive3',\n      selector: 'blink',\n      tags: ['positive'],\n      any: ['positive3-check1']\n    }\n  ];\n\n  const fixture = document.getElementById('fixture');\n  let origAuditRun;\n  let origUtils;\n  beforeEach(() => {\n    audit = new Audit();\n    mockRules.forEach(function (r) {\n      audit.addRule(r);\n    });\n    mockChecks.forEach(function (c) {\n      audit.addCheck(c);\n    });\n    origAuditRun = audit.run;\n    origUtils = axe.utils;\n    axe._audit = audit;\n  });\n\n  afterEach(() => {\n    axe.teardown();\n    audit.run = origAuditRun;\n    axe.utils = origUtils;\n  });\n\n  it('should be a function', () => {\n    assert.isFunction(Audit);\n  });\n\n  describe('defaults', () => {\n    it('should set noHtml', () => {\n      audit = new Audit();\n      assert.isFalse(audit.noHtml);\n    });\n\n    it('should set allowedOrigins', () => {\n      audit = new Audit();\n      assert.deepEqual(audit.allowedOrigins, [window.location.origin]);\n    });\n  });\n\n  describe('Audit#_constructHelpUrls', () => {\n    it('should create default help URLS', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit._constructHelpUrls();\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/axe/' +\n          ver +\n          '/target?application=axeAPI'\n      });\n    });\n    it('should use changed branding', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit.brand = 'thing';\n      audit._constructHelpUrls();\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/thing/' +\n          ver +\n          '/target?application=axeAPI'\n      });\n    });\n    it('should use changed application', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit.application = 'thing';\n      audit._constructHelpUrls();\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/axe/' +\n          ver +\n          '/target?application=thing'\n      });\n    });\n\n    it('does not override helpUrls of different products', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target1',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob',\n        metadata: {\n          helpUrl:\n            'https://dequeuniversity.com/rules/myproject/' +\n            ver +\n            '/target1?application=axeAPI'\n        }\n      });\n      audit.addRule({\n        id: 'target2',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n\n      assert.equal(\n        audit.data.rules.target1.helpUrl,\n        'https://dequeuniversity.com/rules/myproject/' +\n          ver +\n          '/target1?application=axeAPI'\n      );\n      assert.isUndefined(audit.data.rules.target2);\n\n      assert.lengthOf(audit.rules, 2);\n      audit.brand = 'thing';\n      audit._constructHelpUrls();\n\n      assert.equal(\n        audit.data.rules.target1.helpUrl,\n        'https://dequeuniversity.com/rules/myproject/' +\n          ver +\n          '/target1?application=axeAPI'\n      );\n      assert.equal(\n        audit.data.rules.target2.helpUrl,\n        'https://dequeuniversity.com/rules/thing/' +\n          ver +\n          '/target2?application=axeAPI'\n      );\n    });\n    it('understands prerelease type version numbers', () => {\n      const tempVersion = axe.version;\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n\n      axe.version = '3.2.1-alpha.0';\n      audit._constructHelpUrls();\n\n      axe.version = tempVersion;\n      assert.equal(\n        audit.data.rules.target.helpUrl,\n        'https://dequeuniversity.com/rules/axe/3.2/target?application=axeAPI'\n      );\n    });\n\n    it('matches major release versions', () => {\n      const tempVersion = axe.version;\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n\n      axe.version = '1.0.0';\n      audit._constructHelpUrls();\n\n      axe.version = tempVersion;\n      assert.equal(\n        audit.data.rules.target.helpUrl,\n        'https://dequeuniversity.com/rules/axe/1.0/target?application=axeAPI'\n      );\n    });\n    it('sets the lang query if locale has been set', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      audit.applyLocale({\n        lang: 'de'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit._constructHelpUrls();\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/axe/' +\n          ver +\n          '/target?application=axeAPI&lang=de'\n      });\n    });\n  });\n\n  describe('Audit#setBranding', () => {\n    it('should change the brand', () => {\n      audit = new Audit();\n      assert.equal(audit.brand, 'axe');\n      assert.equal(audit.application, 'axeAPI');\n      audit.setBranding({\n        brand: 'thing'\n      });\n      assert.equal(audit.brand, 'thing');\n      assert.equal(audit.application, 'axeAPI');\n    });\n    it('should change the application', () => {\n      audit = new Audit();\n      assert.equal(audit.brand, 'axe');\n      assert.equal(audit.application, 'axeAPI');\n      audit.setBranding({\n        application: 'thing'\n      });\n      assert.equal(audit.brand, 'axe');\n      assert.equal(audit.application, 'thing');\n    });\n    it('should change the application when passed a string', () => {\n      audit = new Audit();\n      assert.equal(audit.brand, 'axe');\n      assert.equal(audit.application, 'axeAPI');\n      audit.setBranding('thing');\n      assert.equal(audit.brand, 'axe');\n      assert.equal(audit.application, 'thing');\n    });\n    it('should call _constructHelpUrls', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit.setBranding({\n        application: 'thing'\n      });\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/axe/' +\n          ver +\n          '/target?application=thing'\n      });\n    });\n    it('should call _constructHelpUrls even when nothing changed', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.data.rules.target, undefined);\n      audit.setBranding(undefined);\n      assert.deepEqual(audit.data.rules.target, {\n        helpUrl:\n          'https://dequeuniversity.com/rules/axe/' +\n          ver +\n          '/target?application=axeAPI'\n      });\n    });\n    it('should not replace custom set branding', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob',\n        metadata: {\n          helpUrl:\n            'https://dequeuniversity.com/rules/customer-x/' +\n            ver +\n            '/target?application=axeAPI'\n        }\n      });\n      audit.setBranding({\n        application: 'thing',\n        brand: 'other'\n      });\n      assert.equal(\n        audit.data.rules.target.helpUrl,\n        'https://dequeuniversity.com/rules/customer-x/' +\n          ver +\n          '/target?application=axeAPI'\n      );\n    });\n  });\n\n  describe('Audit#addRule', () => {\n    it('should override existing rule', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        matches: 'function () {return \"hello\";}',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.rules[0].selector, 'bob');\n      assert.equal(audit.rules[0].matches(), 'hello');\n\n      audit.addRule({\n        id: 'target',\n        selector: 'fred'\n      });\n\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.rules[0].selector, 'fred');\n      assert.equal(audit.rules[0].matches(), 'hello');\n    });\n    it('should otherwise push new rule', () => {\n      audit = new Audit();\n      audit.addRule({\n        id: 'target',\n        selector: 'bob'\n      });\n      assert.lengthOf(audit.rules, 1);\n      assert.equal(audit.rules[0].id, 'target');\n      assert.equal(audit.rules[0].selector, 'bob');\n\n      audit.addRule({\n        id: 'target2',\n        selector: 'fred'\n      });\n\n      assert.lengthOf(audit.rules, 2);\n      assert.equal(audit.rules[1].id, 'target2');\n      assert.equal(audit.rules[1].selector, 'fred');\n    });\n  });\n\n  describe('Audit#resetRulesAndChecks', () => {\n    it('should override newly created check', () => {\n      audit = new Audit();\n      assert.equal(audit.checks.target, undefined);\n      audit.addCheck({\n        id: 'target',\n        options: { value: 'jane' }\n      });\n      assert.ok(audit.checks.target);\n      assert.deepEqual(audit.checks.target.options, { value: 'jane' });\n      audit.resetRulesAndChecks();\n      assert.equal(audit.checks.target, undefined);\n    });\n    it('should reset locale', () => {\n      audit = new Audit();\n      assert.equal(audit.lang, 'en');\n      audit.applyLocale({\n        lang: 'de'\n      });\n      assert.equal(audit.lang, 'de');\n      audit.resetRulesAndChecks();\n      assert.equal(audit.lang, 'en');\n    });\n    it('should reset brand', () => {\n      audit = new Audit();\n      assert.equal(audit.brand, 'axe');\n      audit.setBranding({\n        brand: 'test'\n      });\n      assert.equal(audit.brand, 'test');\n      audit.resetRulesAndChecks();\n      assert.equal(audit.brand, 'axe');\n    });\n    it('should reset brand application', () => {\n      audit = new Audit();\n      assert.equal(audit.application, 'axeAPI');\n      audit.setBranding({\n        application: 'test'\n      });\n      assert.equal(audit.application, 'test');\n      audit.resetRulesAndChecks();\n      assert.equal(audit.application, 'axeAPI');\n    });\n    it('should reset brand tagExlcude', () => {\n      axe._load({});\n      assert.deepEqual(axe._audit.tagExclude, ['experimental', 'deprecated']);\n      axe.configure({\n        tagExclude: ['ninjas']\n      });\n      axe._audit.resetRulesAndChecks();\n      assert.deepEqual(axe._audit.tagExclude, ['experimental', 'deprecated']);\n    });\n\n    it('should reset noHtml', () => {\n      audit = new Audit();\n      audit.noHtml = true;\n      audit.resetRulesAndChecks();\n      assert.isFalse(audit.noHtml);\n    });\n\n    it('should reset allowedOrigins', () => {\n      audit = new Audit();\n      audit.allowedOrigins = ['hello'];\n      audit.resetRulesAndChecks();\n      assert.deepEqual(audit.allowedOrigins, [window.location.origin]);\n    });\n  });\n\n  describe('Audit#addCheck', () => {\n    it('should create a new check', () => {\n      audit = new Audit();\n      assert.equal(audit.checks.target, undefined);\n      audit.addCheck({\n        id: 'target',\n        options: { value: 'jane' }\n      });\n      assert.ok(audit.checks.target);\n      assert.deepEqual(audit.checks.target.options, { value: 'jane' });\n    });\n    it('should configure the metadata, if passed', () => {\n      audit = new Audit();\n      assert.equal(audit.checks.target, undefined);\n      audit.addCheck({\n        id: 'target',\n        metadata: { guy: 'bob' }\n      });\n      assert.ok(audit.checks.target);\n      assert.equal(audit.data.checks.target.guy, 'bob');\n    });\n    it('should reconfigure existing check', () => {\n      audit = new Audit();\n      const myTest = () => {};\n      audit.addCheck({\n        id: 'target',\n        evaluate: myTest,\n        options: { value: 'jane' }\n      });\n\n      assert.deepEqual(audit.checks.target.options, { value: 'jane' });\n\n      audit.addCheck({\n        id: 'target',\n        options: { value: 'fred' }\n      });\n\n      assert.equal(audit.checks.target.evaluate, myTest);\n      assert.deepEqual(audit.checks.target.options, { value: 'fred' });\n    });\n    it('should not turn messages into a function', () => {\n      audit = new Audit();\n      const spec = {\n        id: 'target',\n        evaluate: 'function () { return \"blah\";}',\n        metadata: {\n          messages: {\n            fail: 'it failed'\n          }\n        }\n      };\n      audit.addCheck(spec);\n\n      assert.equal(typeof audit.checks.target.evaluate, 'function');\n      assert.equal(typeof audit.data.checks.target.messages.fail, 'string');\n      assert.equal(audit.data.checks.target.messages.fail, 'it failed');\n    });\n\n    it('should turn function strings into a function', () => {\n      audit = new Audit();\n      const spec = {\n        id: 'target',\n        evaluate: 'function () { return \"blah\";}',\n        metadata: {\n          messages: {\n            fail: 'function () {return \"it failed\";}'\n          }\n        }\n      };\n      audit.addCheck(spec);\n\n      assert.equal(typeof audit.checks.target.evaluate, 'function');\n      assert.equal(typeof audit.data.checks.target.messages.fail, 'function');\n      assert.equal(audit.data.checks.target.messages.fail(), 'it failed');\n    });\n  });\n\n  describe('Audit#setAllowedOrigins', () => {\n    it('should set allowedOrigins', () => {\n      audit = new Audit();\n      audit.setAllowedOrigins([\n        'https://deque.com',\n        'https://dequeuniversity.com'\n      ]);\n      assert.deepEqual(audit.allowedOrigins, [\n        'https://deque.com',\n        'https://dequeuniversity.com'\n      ]);\n    });\n\n    it('should normalize <same_origin>', () => {\n      audit = new Audit();\n      audit.setAllowedOrigins(['<same_origin>', 'https://deque.com']);\n      assert.deepEqual(audit.allowedOrigins, [\n        window.location.origin,\n        'https://deque.com'\n      ]);\n    });\n\n    it('should normalize <unsafe_all_origins>', () => {\n      audit = new Audit();\n      audit.setAllowedOrigins([\n        'https://deque.com',\n        '<unsafe_all_origins>',\n        '<same_origin>'\n      ]);\n      assert.deepEqual(audit.allowedOrigins, ['*']);\n    });\n  });\n\n  describe('Audit#run', () => {\n    it('should run all the rules', done => {\n      fixtureSetup(\n        '<input aria-label=\"monkeys\" type=\"text\">' +\n          '<div id=\"monkeys\">bananas</div>' +\n          '<input aria-labelledby=\"monkeys\">' +\n          '<blink>FAIL ME</blink>'\n      );\n\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {},\n        function (results) {\n          const expected = [\n            {\n              id: 'positive1',\n              result: 'inapplicable',\n              pageLevel: false,\n              impact: null,\n              nodes: '...other tests cover this...'\n            },\n            {\n              id: 'positive2',\n              result: 'inapplicable',\n              pageLevel: false,\n              impact: null,\n              nodes: '...other tests cover this...'\n            },\n            {\n              id: 'negative1',\n              result: 'inapplicable',\n              pageLevel: false,\n              impact: null,\n              nodes: '...other tests cover this...'\n            },\n            {\n              id: 'positive3',\n              result: 'inapplicable',\n              pageLevel: false,\n              impact: null,\n              nodes: '...other tests cover this...'\n            }\n          ];\n\n          const out = results[0].nodes[0].node.source;\n          results.forEach(function (res) {\n            // attribute order is a pain in the lower back in IE, so we're not\n            // comparing nodes. Check.run and Rule.run do this.\n            res.nodes = '...other tests cover this...';\n          });\n\n          assert.deepEqual(JSON.parse(JSON.stringify(results)), expected);\n          assert.match(\n            out,\n            /^<input(\\s+type=\"text\"|\\s+aria-label=\"monkeys\"){2,}>/\n          );\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should not run rules disabled by the options', done => {\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        {\n          rules: {\n            positive3: {\n              enabled: false\n            }\n          }\n        },\n        function (results) {\n          assert.equal(results.length, 3);\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should ensure audit.run recieves preload options', done => {\n      fixture.innerHTML = '<input aria-label=\"yo\" type=\"text\">';\n\n      audit = new Audit();\n      audit.addRule({\n        id: 'preload1',\n        selector: '*'\n      });\n      audit.run = function (context, options, resolve, reject) {\n        const randomRule = this.rules[0];\n        randomRule.run(\n          context,\n          options,\n          function (ruleResult) {\n            ruleResult.OPTIONS_PASSED = options;\n            resolve([ruleResult]);\n          },\n          reject\n        );\n      };\n\n      const preloadOptions = {\n        preload: {\n          assets: ['cssom']\n        }\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          preload: preloadOptions\n        },\n        function (res) {\n          assert.isDefined(res);\n\n          assert.lengthOf(res, 1);\n          assert.property(res[0], 'OPTIONS_PASSED');\n\n          const optionsPassed = res[0].OPTIONS_PASSED;\n          assert.property(optionsPassed, 'preload');\n          assert.deepEqual(optionsPassed.preload, preloadOptions);\n\n          // ensure cache is cleared\n          assert.isTrue(typeof axe._selectCache === 'undefined');\n\n          done();\n        },\n        noop\n      );\n    });\n\n    it.skip('should run rules (that do not need preload) and preload assets simultaneously', done => {\n      /**\n       * Note:\n       * overriding and resolving both check and preload with a delay,\n       * but the invoked timestamp should ensure that they were invoked almost immediately\n       */\n\n      fixture.innerHTML = '<div id=\"div1\"></div><div id=\"div2\"></div>';\n\n      const runStartTime = new Date();\n      const preloadInvokedTime = new Date();\n      const noPreloadCheckedInvokedTime = new Date();\n      const noPreloadRuleCheckEvaluateInvoked = false;\n      const preloadOverrideInvoked = false;\n\n      // override preload method\n      axe.utils.preload = function (options) {\n        preloadInvokedTime = new Date();\n        preloadOverrideInvoked = true;\n\n        return new Promise(function (res, rej) {\n          setTimeout(() => {\n            res(true);\n          }, 2000);\n        });\n      };\n\n      audit = new Audit();\n      // add a rule and check that does not need preload\n      audit.addRule({\n        id: 'no-preload',\n        selector: 'div#div1',\n        any: ['no-preload-check'],\n        preload: false\n      });\n      audit.addCheck({\n        id: 'no-preload-check',\n        evaluate: function (node, options, vNode, context) {\n          noPreloadCheckedInvokedTime = new Date();\n          noPreloadRuleCheckEvaluateInvoked = true;\n          const ready = this.async();\n          setTimeout(() => {\n            ready(true);\n          }, 1000);\n        }\n      });\n\n      // add a rule which needs preload\n      audit.addRule({\n        id: 'yes-preload',\n        selector: 'div#div2',\n        preload: true\n      });\n\n      const preloadOptions = {\n        preload: {\n          assets: ['cssom']\n        }\n      };\n\n      const allowedDiff = 50;\n\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          preload: preloadOptions\n        },\n        function (results) {\n          assert.isDefined(results);\n          // assert that check was invoked for rule(s)\n          assert.isTrue(noPreloadRuleCheckEvaluateInvoked);\n          // assert preload was invoked\n          assert.isTrue(preloadOverrideInvoked);\n          // assert that time diff(s)\n          // assert that run check invoked immediately\n          // choosing 5ms as an arbitary number\n          assert.isBelow(\n            noPreloadCheckedInvokedTime - runStartTime,\n            allowedDiff\n          );\n          // assert that preload  invoked immediately\n          assert.isBelow(preloadInvokedTime - runStartTime, allowedDiff);\n          // ensure cache is clear\n          assert.isTrue(typeof axe._selectCache === 'undefined');\n          // done\n          done();\n        },\n        noop\n      );\n    });\n\n    it.skip('should pass assets from preload to rule check that needs assets as context', done => {\n      fixture.innerHTML = '<div id=\"div1\"></div><div id=\"div2\"></div>';\n\n      const yesPreloadRuleCheckEvaluateInvoked = false;\n      const preloadOverrideInvoked = false;\n\n      const preloadData = {\n        data: 'you got it!'\n      };\n      // override preload method\n      axe.utils.preload = function (options) {\n        preloadOverrideInvoked = true;\n        return Promise.resolve({\n          cssom: preloadData\n        });\n      };\n\n      audit = new Audit();\n      // add a rule and check that does not need preload\n      audit.addRule({\n        id: 'no-preload',\n        selector: 'div#div1',\n        preload: false\n      });\n      // add a rule which needs preload\n      audit.addRule({\n        id: 'yes-preload',\n        selector: 'div#div2',\n        preload: true,\n        any: ['yes-preload-check']\n      });\n      audit.addCheck({\n        id: 'yes-preload-check',\n        evaluate: function (node, options, vNode, context) {\n          yesPreloadRuleCheckEvaluateInvoked = true;\n          this.data(context);\n          return true;\n        }\n      });\n\n      const preloadOptions = {\n        preload: {\n          assets: ['cssom']\n        }\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          preload: preloadOptions\n        },\n        function (results) {\n          assert.isDefined(results);\n          // assert that check was invoked for rule(s)\n          assert.isTrue(yesPreloadRuleCheckEvaluateInvoked);\n          // assert preload was invoked\n          assert.isTrue(preloadOverrideInvoked);\n\n          // assert preload data that was passed to check\n          const ruleResult = results.filter(function (r) {\n            return (r.id = 'yes-preload' && r.nodes.length > 0);\n          })[0];\n          const checkResult = ruleResult.nodes[0].any[0];\n          assert.isDefined(checkResult.data);\n          assert.property(checkResult.data, 'cssom');\n          assert.deepEqual(checkResult.data.cssom, preloadData);\n          // ensure cache is clear\n          assert.isTrue(typeof axe._selectCache === 'undefined');\n          // done\n          done();\n        },\n        noop\n      );\n    });\n\n    it.skip('should continue to run rules and return result when preload is rejected', done => {\n      fixture.innerHTML = '<div id=\"div1\"></div><div id=\"div2\"></div>';\n\n      const preloadOverrideInvoked = false;\n      const preloadNeededCheckInvoked = false;\n      const rejectionMsg =\n        'Boom! Things went terribly wrong! (But this was intended in this test)';\n\n      // override preload method\n      axe.utils.preload = function (options) {\n        preloadOverrideInvoked = true;\n        return Promise.reject(rejectionMsg);\n      };\n\n      audit = new Audit();\n      // add a rule and check that does not need preload\n      audit.addRule({\n        id: 'no-preload',\n        selector: 'div#div1',\n        preload: false\n      });\n      // add a rule which needs preload\n      audit.addRule({\n        id: 'yes-preload',\n        selector: 'div#div2',\n        preload: true,\n        any: ['yes-preload-check']\n      });\n      audit.addCheck({\n        id: 'yes-preload-check',\n        evaluate: function (node, options, vNode, context) {\n          preloadNeededCheckInvoked = true;\n          this.data(context);\n          return true;\n        }\n      });\n\n      const preloadOptions = {\n        preload: {\n          assets: ['cssom']\n        }\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          preload: preloadOptions\n        },\n        function (results) {\n          assert.isDefined(results);\n          // assert preload was invoked\n          assert.isTrue(preloadOverrideInvoked);\n\n          // assert that both rules ran, although preload failed\n          assert.lengthOf(results, 2);\n\n          // assert that because preload failed\n          // cssom was not populated on context of repective check\n          assert.isTrue(preloadNeededCheckInvoked);\n          const ruleResult = results.filter(function (r) {\n            return (r.id = 'yes-preload' && r.nodes.length > 0);\n          })[0];\n          const checkResult = ruleResult.nodes[0].any[0];\n          assert.isDefined(checkResult.data);\n          assert.notProperty(checkResult.data, 'cssom');\n          // done\n          done();\n        },\n        noop\n      );\n    });\n\n    it('should continue to run rules and return result when axios time(s)out and rejects preload', done => {\n      fixture.innerHTML = '<div id=\"div1\"></div><div id=\"div2\"></div>';\n\n      // there is no stubbing here,\n      // the actual axios call is invoked, and timedout immediately as timeout is set to 0.1\n\n      let preloadNeededCheckInvoked = false;\n      audit = new Audit();\n      // add a rule and check that does not need preload\n      audit.addRule({\n        id: 'no-preload',\n        selector: 'div#div1',\n        preload: false\n      });\n      // add a rule which needs preload\n      audit.addRule({\n        id: 'yes-preload',\n        selector: 'div#div2',\n        preload: true,\n        any: ['yes-preload-check']\n      });\n      audit.addCheck({\n        id: 'yes-preload-check',\n        evaluate: function (node, options, vNode, context) {\n          preloadNeededCheckInvoked = true;\n          this.data(context);\n          return true;\n        }\n      });\n      axe.setup();\n\n      const preloadOptions = {\n        preload: {\n          assets: ['cssom'],\n          timeout: 0.1\n        }\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          preload: preloadOptions\n        },\n        function (results) {\n          assert.isDefined(results);\n          // assert that both rules ran, although preload failed\n          assert.lengthOf(results, 2);\n\n          // assert that because preload failed\n          // cssom was not populated on context of repective check\n          assert.isTrue(preloadNeededCheckInvoked);\n          const ruleResult = results.filter(function (r) {\n            return (r.id = 'yes-preload' && r.nodes.length > 0);\n          })[0];\n          const checkResult = ruleResult.nodes[0].any[0];\n          assert.isDefined(checkResult.data);\n          assert.notProperty(checkResult.data, 'cssom');\n          // done\n          done();\n        },\n        noop\n      );\n    });\n\n    it.skip('should assign an empty array to axe._selectCache', done => {\n      const saved = axe.utils.ruleShouldRun;\n      axe.utils.ruleShouldRun = () => {\n        assert.equal(axe._selectCache.length, 0);\n        return false;\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        {},\n        () => {\n          axe.utils.ruleShouldRun = saved;\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should clear axe._selectCache', done => {\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        {\n          rules: {}\n        },\n        () => {\n          assert.isTrue(typeof axe._selectCache === 'undefined');\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should not run rules disabled by the configuration', done => {\n      audit = new Audit();\n      const success = true;\n      audit.rules.push(\n        new Rule({\n          id: 'positive1',\n          selector: '*',\n          enabled: false,\n          any: [\n            {\n              id: 'positive1-check1',\n              evaluate: () => {\n                success = false;\n              }\n            }\n          ]\n        })\n      );\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        {},\n        () => {\n          assert.ok(success);\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it(\"should call the rule's run function\", done => {\n      const targetRule = mockRules[mockRules.length - 1];\n      const rule = axe.utils.findBy(audit.rules, 'id', targetRule.id);\n      let called = false;\n      let orig;\n\n      fixture.innerHTML = '<a href=\"#\">link</a>';\n      orig = rule.run;\n      rule.run = function (node, options, callback) {\n        called = true;\n        callback({});\n      };\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        {},\n        () => {\n          assert.isTrue(called);\n          rule.run = orig;\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should pass the option to the run function', done => {\n      const targetRule = mockRules[mockRules.length - 1];\n      const rule = axe.utils.findBy(audit.rules, 'id', targetRule.id);\n      let passed = false;\n      let orig;\n      let options;\n\n      fixture.innerHTML = '<a href=\"#\">link</a>';\n      orig = rule.run;\n      rule.run = function (node, o, callback) {\n        assert.deepEqual(o, options);\n        passed = true;\n        callback({});\n      };\n      options = { rules: {} };\n      (options.rules[targetRule.id] = {}).data = 'monkeys';\n      audit.run(\n        { include: [axe.utils.getFlattenedTree()[0]] },\n        options,\n        () => {\n          assert.ok(passed);\n          rule.run = orig;\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should skip pageLevel rules if context is not set to entire page', () => {\n      audit = new Audit();\n\n      audit.rules.push(\n        new Rule({\n          pageLevel: true,\n          enabled: true,\n          evaluate: () => {\n            assert.ok(false, 'Should not run');\n          }\n        })\n      );\n\n      audit.run(\n        {\n          include: [axe.utils.getFlattenedTree(document.body)[0]],\n          page: false\n        },\n        {},\n        function (results) {\n          assert.deepEqual(results, []);\n        },\n        isNotCalled\n      );\n    });\n\n    it('should not halt if errors occur', done => {\n      axe._audit = audit;\n      audit.addRule({\n        id: 'throw1',\n        selector: '*',\n        any: [\n          {\n            id: 'throw1-check1'\n          }\n        ]\n      });\n      audit.addCheck({\n        id: 'throw1-check1',\n        evaluate: () => {\n          throw new Error('Launch the super sheep!');\n        }\n      });\n      audit.run(\n        { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['throw1', 'positive1']\n          }\n        },\n        () => {\n          done();\n        },\n        isNotCalled\n      );\n    });\n\n    it('should run axe.utils.normalizeRunOptions to ensure valid input', () => {\n      fixture.innerHTML =\n        '<input type=\"text\" aria-label=\"monkeys\">' +\n        '<div id=\"monkeys\">bananas</div>' +\n        '<input aria-labelledby=\"monkeys\" type=\"text\">' +\n        '<blink>FAIL ME</blink>';\n      assert.throws(() => {\n        audit.run(\n          { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n          { runOnly: { type: 'rule', values: ['foo'] } },\n          noop,\n          isNotCalled\n        );\n      }, 'unknown rule `foo` in options.runOnly');\n    });\n\n    it('propagates DqElement options', async () => {\n      fixtureSetup('<input id=\"input\">');\n      const results = await new Promise((resolve, reject) => {\n        audit.run(\n          { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n          { elementRef: true, absolutePaths: true },\n          resolve,\n          reject\n        );\n      });\n      const { node } = results[0].nodes[0];\n      assert.equal(node.element, fixture.firstChild);\n      assert.equal(node.selector, 'html > body > #fixture > #input');\n    });\n\n    describe('when an error occurs', () => {\n      let err;\n      beforeEach(() => {\n        err = new Error('Launch the super sheep!');\n        audit.addRule({\n          id: 'throw1',\n          selector: '#fixture',\n          any: [\n            {\n              id: 'throw1-check1'\n            }\n          ]\n        });\n        audit.addCheck({\n          id: 'throw1-check1',\n          evaluate: () => {\n            throw err;\n          }\n        });\n        axe.setup();\n      });\n\n      it('catches errors and resolves them as a cantTell result', done => {\n        audit.run(\n          { include: [axe._tree[0]] },\n          { runOnly: { type: 'rule', values: ['throw1'] } },\n          captureError(results => {\n            assert.lengthOf(results, 1);\n            assertErrorResults(results[0], err, '#fixture');\n            done();\n          }, done),\n          isNotCalled\n        );\n      });\n\n      it('should halt if an error occurs when debug is set', done => {\n        const context = { include: [axe.utils.getFlattenedTree(fixture)[0]] };\n        const options = {\n          debug: true,\n          runOnly: { type: 'rule', values: ['throw1'] }\n        };\n        audit.run(\n          context,\n          options,\n          noop,\n          captureError(reject => {\n            assert.include(reject.message, err.message);\n            done();\n          }, done)\n        );\n      });\n    });\n  });\n\n  describe('Audit#after', () => {\n    it('should run Rule#after on any rule whose result is passed in', () => {\n      /*eslint no-unused-vars:0*/\n      audit = new Audit();\n      let success = false;\n      const options = { runOnly: 'hehe' };\n      const results = [\n        {\n          id: 'hehe',\n          monkeys: 'bananas'\n        }\n      ];\n      audit.rules.push(\n        new Rule({\n          id: 'hehe',\n          pageLevel: false,\n          enabled: false\n        })\n      );\n\n      audit.rules[0].after = function (res, opts) {\n        assert.equal(res, results[0]);\n        assert.deepEqual(opts, options);\n        success = true;\n      };\n\n      audit.after(results, options);\n      assert.isTrue(success);\n    });\n\n    it('does not run Rule#after if the result has an error', () => {\n      audit = new Audit();\n      const results = [{ id: 'throw1', error: new Error('La la la!') }];\n      let success = true;\n      audit.rules.push(new Rule({ id: 'throw1' }));\n      audit.rules[0].after = () => (success = false);\n      audit.after(results, {});\n      assert.lengthOf(results, 1);\n      assert.equal(results[0].error.message, 'La la la!');\n      assert.isTrue(success, 'Rule#after should not be called');\n    });\n\n    it('catches errors and passes them as a cantTell result', () => {\n      audit = new Audit();\n      const err = new SyntaxError('La la la!');\n      const results = [\n        {\n          id: 'throw1',\n          nodes: [\n            {\n              id: 'throw1-check1-after',\n              node: new axe.utils.DqElement(fixture),\n              any: [{ id: 'throw1-check1-after', result: false }],\n              all: [],\n              none: []\n            }\n          ]\n        }\n      ];\n      audit.addRule({\n        id: 'throw1',\n        selector: '#fixture',\n        any: [{ id: 'throw1-check1-after' }]\n      });\n      audit.addCheck({\n        id: 'throw1-check1-after',\n        after: () => {\n          throw err;\n        }\n      });\n      axe.setup();\n      const result = audit.after(results, {});\n      assert.lengthOf(result, 1);\n      assertErrorResults(result[0], err, '#fixture');\n    });\n\n    it('throws errors when debug is set', () => {\n      audit = new Audit();\n      const err = new SyntaxError('La la la!');\n      const options = { debug: true };\n      const results = [\n        {\n          id: 'throw1',\n          nodes: [\n            {\n              id: 'throw1-check1-after',\n              node: new axe.utils.DqElement(fixture),\n              any: [{ id: 'throw1-check1-after', result: false }],\n              all: [],\n              none: []\n            }\n          ]\n        }\n      ];\n      audit.addRule({\n        id: 'throw1',\n        selector: '#fixture',\n        any: [{ id: 'throw1-check1-after' }]\n      });\n      audit.addCheck({\n        id: 'throw1-check1-after',\n        after: () => {\n          throw err;\n        }\n      });\n      axe.setup();\n      try {\n        audit.after(results, options);\n        assert.fail('Should have thrown');\n      } catch (actual) {\n        assertEqualRuleError(actual, err);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/cache.js",
    "content": "describe('axe._cache', function () {\n  'use strict';\n\n  describe('set', function () {\n    it('should set items without error', function () {\n      function fn() {\n        axe._cache.set('foo', 'bar');\n      }\n      assert.doesNotThrow(fn);\n    });\n\n    it('should set `undefined` without error', function () {\n      function fn() {\n        axe._cache.set('foo', undefined);\n      }\n      assert.doesNotThrow(fn);\n    });\n\n    it('should throw for non-string keys', function () {\n      assert.throws(function () {\n        axe._cache.set(1, 'bar');\n      });\n      assert.throws(function () {\n        axe._cache.set({}, 'bar');\n      });\n      assert.throws(function () {\n        axe._cache.set(null, 'bar');\n      });\n      assert.throws(function () {\n        axe._cache.set(function () {\n          return;\n        }, 'bar');\n      });\n    });\n\n    it('should throw for empty string keys', function () {\n      assert.throws(function () {\n        axe._cache.set('', 'bar');\n      });\n    });\n  });\n\n  describe('get', function () {\n    it('should get an item from the cache', function () {\n      axe._cache.set('foo', 'bar');\n      var value = axe._cache.get('foo');\n      assert.equal(value, 'bar');\n    });\n\n    it('should return `undefined` for key lookup miss', function () {\n      assert.isUndefined(axe._cache.get('foo'));\n    });\n\n    describe('with creator function', function () {\n      it('should set and return primitive types', function () {\n        assert.equal(\n          axe._cache.get('integer', function () {\n            return 1;\n          }),\n          1\n        );\n        assert.equal(\n          axe._cache.get('float', function () {\n            return 1.1;\n          }),\n          1.1\n        );\n        assert.equal(\n          axe._cache.get('string', function () {\n            return 'foo';\n          }),\n          'foo'\n        );\n        assert.isTrue(\n          axe._cache.get('boolean', function () {\n            return true;\n          })\n        );\n        assert.isNull(\n          axe._cache.get('null', function () {\n            return null;\n          })\n        );\n\n        var obj = { foo: 'bar' };\n        assert.equal(\n          axe._cache.get('object', function () {\n            return obj;\n          }),\n          obj\n        );\n\n        var arr = [1, 2, 3];\n        assert.equal(\n          axe._cache.get('array', function () {\n            return arr;\n          }),\n          arr\n        );\n      });\n\n      it('should throw when creator is not a function', function () {\n        function fn() {\n          axe._cache.get('foo', 'bar');\n        }\n        assert.throws(fn);\n      });\n\n      it('should throw when creator function returns `undefined`', function () {\n        function fn() {\n          axe._cache.get('foo', function () {\n            return undefined;\n          });\n        }\n        assert.throws(fn);\n      });\n\n      it('should not evaluate creator if key already exists', function () {\n        var spy = sinon.spy();\n        axe._cache.set('foo', 'bar');\n        axe._cache.get('foo', spy);\n        assert.isTrue(spy.notCalled);\n      });\n\n      it('should evaluate creator once', function () {\n        var spy = sinon.spy(function () {\n          return true;\n        });\n        axe._cache.get('foo', spy);\n        axe._cache.get('foo', spy);\n        axe._cache.get('foo', spy);\n        assert.isTrue(spy.calledOnce);\n      });\n\n      it('should not override a value from `set()`', function () {\n        axe._cache.set('foo', 'bar');\n        axe._cache.get('foo', function () {\n          return 'baz';\n        });\n        assert.equal(axe._cache.get('foo'), 'bar');\n      });\n\n      it('should override a value from `get()` with a `set()` value', function () {\n        axe._cache.get('foo', function () {\n          return 'baz';\n        });\n        axe._cache.set('foo', 'bar');\n        assert.equal(axe._cache.get('foo'), 'bar');\n      });\n\n      it('should set value after calling with undefined', function () {\n        axe._cache.get('foo');\n        axe._cache.get('foo', function () {\n          return 'value';\n        });\n        assert.equal(axe._cache.get('foo'), 'value');\n      });\n    });\n  });\n\n  describe('clear', function () {\n    it('should clear the cache', function () {\n      axe._cache.set('foo', 'bar');\n      axe._cache.clear();\n      var value = axe._cache.get('foo');\n      assert.isUndefined(value);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/check-result.js",
    "content": "describe('CheckResult', function () {\n  'use strict';\n\n  var CheckResult = axe._thisWillBeDeletedDoNotUse.base.CheckResult;\n  it('should be a function', function () {\n    assert.isFunction(CheckResult);\n  });\n\n  it('should have an id', function () {\n    var result = new CheckResult({ id: 'monkeys' });\n    assert.equal(result.id, 'monkeys');\n  });\n\n  it('should set `data` to `null`', function () {\n    var result = new CheckResult({});\n    assert.isNull(result.data);\n  });\n\n  it('should set `relatedNodes` to `[]`', function () {\n    var result = new CheckResult({});\n    assert.deepEqual(result.relatedNodes, []);\n  });\n\n  it('should set `result` to `null`', function () {\n    var result = new CheckResult({});\n    assert.isNull(result.result);\n  });\n});\n"
  },
  {
    "path": "test/core/base/check.js",
    "content": "describe('Check', () => {\n  const Check = axe._thisWillBeDeletedDoNotUse.base.Check;\n  const CheckResult = axe._thisWillBeDeletedDoNotUse.base.CheckResult;\n  const metadataFunctionMap =\n    axe._thisWillBeDeletedDoNotUse.base.metadataFunctionMap;\n  const noop = () => {};\n\n  const fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', () => {\n    assert.isFunction(Check);\n  });\n\n  describe('prototype', () => {\n    describe('enabled', () => {\n      it('should be true by default', () => {\n        const check = new Check({});\n        assert.isTrue(check.enabled);\n      });\n      it('should be set to whatever is passed in', () => {\n        const check = new Check({ enabled: false });\n        assert.isFalse(check.enabled);\n      });\n    });\n\n    describe('configure', () => {\n      it('should accept one parameter', () => {\n        assert.lengthOf(new Check({}).configure, 1);\n      });\n      it('should override options', () => {\n        Check.prototype.test = function () {\n          return this.options;\n        };\n        const check = new Check({\n          options: ['foo']\n        });\n        check.configure({ options: { value: 'fong' } });\n        assert.deepEqual({ value: 'fong' }, check.test());\n        delete Check.prototype.test;\n      });\n      it('should override evaluate', () => {\n        Check.prototype.test = function () {\n          return this.evaluate();\n        };\n        const check = new Check({\n          evaluate: 'function () { return \"foo\"; }'\n        });\n        check.configure({ evaluate: 'function () { return \"fong\"; }' });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n      });\n      it('should override after', () => {\n        Check.prototype.test = function () {\n          return this.after();\n        };\n        const check = new Check({\n          after: 'function () { return \"foo\"; }'\n        });\n        check.configure({ after: 'function () { return \"fong\"; }' });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n      });\n      it('should override evaluate as a function', () => {\n        Check.prototype.test = function () {\n          return this.evaluate();\n        };\n        const check = new Check({\n          evaluate() {\n            return 'foo';\n          }\n        });\n        check.configure({\n          evaluate() {\n            return 'fong';\n          }\n        });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n      });\n      it('should override after as a function', () => {\n        Check.prototype.test = function () {\n          return this.after();\n        };\n        const check = new Check({\n          after() {\n            return 'foo';\n          }\n        });\n        check.configure({\n          after() {\n            return 'fong';\n          }\n        });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n      });\n      it('should override evaluate as ID', () => {\n        metadataFunctionMap['custom-evaluate'] = () => {\n          return 'fong';\n        };\n\n        Check.prototype.test = function () {\n          return this.evaluate();\n        };\n        const check = new Check({\n          evaluate: 'function () { return \"foo\"; }'\n        });\n        check.configure({ evaluate: 'custom-evaluate' });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n        delete metadataFunctionMap['custom-evaluate'];\n      });\n      it('should override after as ID', () => {\n        metadataFunctionMap['custom-after'] = () => {\n          return 'fong';\n        };\n\n        Check.prototype.test = function () {\n          return this.after();\n        };\n        const check = new Check({\n          after: 'function () { return \"foo\"; }'\n        });\n        check.configure({ after: 'custom-after' });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n        delete metadataFunctionMap['custom-after'];\n      });\n      it('should error if evaluate does not match an ID', () => {\n        function fn() {\n          const check = new Check({});\n          check.configure({ evaluate: 'does-not-exist' });\n        }\n\n        assert.throws(\n          fn,\n          'Function ID does not exist in the metadata-function-map: does-not-exist'\n        );\n      });\n      it('should error if after does not match an ID', () => {\n        function fn() {\n          const check = new Check({});\n          check.configure({ after: 'does-not-exist' });\n        }\n\n        assert.throws(\n          fn,\n          'Function ID does not exist in the metadata-function-map: does-not-exist'\n        );\n      });\n      it('should override enabled', () => {\n        Check.prototype.test = function () {\n          return this.enabled;\n        };\n        const check = new Check({\n          enabled: true\n        });\n        check.configure({ enabled: false });\n        assert.equal(false, check.test());\n        delete Check.prototype.test;\n      });\n      it('should NOT override id', () => {\n        Check.prototype.test = function () {\n          return this.id;\n        };\n        const check = new Check({\n          id: 'fong'\n        });\n        check.configure({ id: 'foo' });\n        assert.equal('fong', check.test());\n        delete Check.prototype.test;\n      });\n      it('should NOT override any random property', () => {\n        Check.prototype.test = function () {\n          return this.random;\n        };\n        const check = new Check({});\n        check.configure({ random: 'foo' });\n        assert.equal(undefined, check.test());\n        delete Check.prototype.test;\n      });\n    });\n\n    describe('run', () => {\n      it('should accept 5 parameters', () => {\n        assert.lengthOf(new Check({}).run, 5);\n      });\n\n      it('should pass the node through', done => {\n        new Check({\n          evaluate(node) {\n            assert.equal(node, fixture);\n            done();\n          }\n        }).run(axe.utils.getFlattenedTree(fixture)[0], {}, {}, noop);\n      });\n\n      it('should pass the options through', done => {\n        const expected = { monkeys: 'bananas' };\n\n        new Check({\n          options: expected,\n          evaluate(node, options) {\n            assert.deepEqual(options, expected);\n            done();\n          }\n        }).run(fixture, {}, {}, noop);\n      });\n\n      it('should pass the options through modified by the ones passed into the call', done => {\n        const configured = { monkeys: 'bananas' },\n          expected = { monkeys: 'bananas', dogs: 'cats' };\n\n        new Check({\n          options: configured,\n          evaluate(node, options) {\n            assert.deepEqual(options, expected);\n            done();\n          }\n        }).run(fixture, { options: expected }, {}, noop);\n      });\n\n      it('should normalize non-object options for internal checks', done => {\n        metadataFunctionMap['custom-check'] = function (node, options) {\n          assert.deepEqual(options, { value: 'foo' });\n          done();\n        };\n        new Check({\n          evaluate: 'custom-check'\n        }).run(fixture, { options: 'foo' }, {}, noop);\n        delete metadataFunctionMap['custom-check'];\n      });\n\n      it('should not normalize non-object options for external checks', done => {\n        new Check({\n          evaluate(node, options) {\n            assert.deepEqual(options, 'foo');\n            done();\n          }\n        }).run(fixture, { options: 'foo' }, {}, noop);\n      });\n\n      it('should pass the context through to check evaluate call', done => {\n        const configured = {\n          cssom: 'yay',\n          source: 'this is page source',\n          aom: undefined\n        };\n        new Check({\n          options: configured,\n          evaluate(node, options, virtualNode, context) {\n            assert.property(context, 'cssom');\n            assert.deepEqual(context, configured);\n            done();\n          }\n        }).run(fixture, {}, configured, noop);\n      });\n\n      it('should pass the virtual node through', done => {\n        const tree = axe.utils.getFlattenedTree(fixture);\n        new Check({\n          evaluate(node, options, virtualNode) {\n            assert.equal(virtualNode, tree[0]);\n            done();\n          }\n        }).run(tree[0]);\n      });\n\n      it.skip('should bind context to `bindCheckResult`', done => {\n        const orig = axe.utils.checkHelper,\n          cb = () => {\n            return true;\n          },\n          options = {},\n          context = {},\n          result = { monkeys: 'bananas' };\n\n        axe.utils.checkHelper = function (checkResult, opts, callback) {\n          assert.instanceOf(checkResult, window.CheckResult);\n          assert.equal(callback, cb);\n          return result;\n        };\n\n        new Check({\n          evaluate() {\n            assert.deepEqual(result, this);\n            axe.utils.checkHelper = orig;\n            done();\n          }\n        }).run(fixture, options, context, cb);\n      });\n\n      it('should allow for asynchronous checks', done => {\n        const data = { monkeys: 'bananas' };\n        new Check({\n          evaluate() {\n            const ready = this.async();\n            setTimeout(function () {\n              ready(data);\n            }, 10);\n          }\n        }).run(fixture, {}, {}, function (d) {\n          assert.instanceOf(d, CheckResult);\n          assert.deepEqual(d.result, data);\n          done();\n        });\n      });\n\n      it('should pass `null` as the parameter if not enabled', done => {\n        new Check({\n          evaluate() {},\n          enabled: false\n        }).run(fixture, {}, {}, function (data) {\n          assert.isNull(data);\n          done();\n        });\n      });\n\n      it('should pass `null` as the parameter if options disable', done => {\n        new Check({\n          evaluate() {}\n        }).run(\n          fixture,\n          {\n            enabled: false\n          },\n          {},\n          function (data) {\n            assert.isNull(data);\n            done();\n          }\n        );\n      });\n\n      it('passes a result to the resolve argument', done => {\n        new Check({\n          evaluate() {\n            return true;\n          }\n        }).run(fixture, {}, {}, function (data) {\n          assert.instanceOf(data, CheckResult);\n          assert.isTrue(data.result);\n          done();\n        });\n      });\n\n      it('should pass errors to the reject argument', done => {\n        new Check({\n          evaluate() {\n            throw new Error('Grenade!');\n          }\n        }).run(fixture, {}, {}, noop, function (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(err.message, 'Grenade!');\n          done();\n        });\n      });\n    });\n\n    describe('runSync', () => {\n      it('should accept 3 parameters', () => {\n        assert.lengthOf(new Check({}).runSync, 3);\n      });\n\n      it('should pass the node through', () => {\n        new Check({\n          evaluate(node) {\n            assert.equal(node, fixture);\n          }\n        }).runSync(axe.utils.getFlattenedTree(fixture)[0], {}, {});\n      });\n\n      it('should pass the options through', () => {\n        const expected = { monkeys: 'bananas' };\n\n        new Check({\n          options: expected,\n          evaluate(node, options) {\n            assert.deepEqual(options, expected);\n          }\n        }).runSync(fixture, {}, {});\n      });\n\n      it('should pass the options through modified by the ones passed into the call', () => {\n        const configured = { monkeys: 'bananas' },\n          expected = { monkeys: 'bananas', dogs: 'cats' };\n\n        new Check({\n          options: configured,\n          evaluate(node, options) {\n            assert.deepEqual(options, expected);\n          }\n        }).runSync(fixture, { options: expected }, {});\n      });\n\n      it('should normalize non-object options for internal checks', done => {\n        metadataFunctionMap['custom-check'] = function (node, options) {\n          assert.deepEqual(options, { value: 'foo' });\n          done();\n        };\n        new Check({\n          evaluate: 'custom-check'\n        }).runSync(fixture, { options: 'foo' }, {});\n        delete metadataFunctionMap['custom-check'];\n      });\n\n      it('should not normalize non-object options for external checks', done => {\n        new Check({\n          evaluate(node, options) {\n            assert.deepEqual(options, 'foo');\n            done();\n          }\n        }).runSync(fixture, { options: 'foo' }, {});\n      });\n\n      it('should pass the context through to check evaluate call', () => {\n        const configured = {\n          cssom: 'yay',\n          source: 'this is page source',\n          aom: undefined\n        };\n        new Check({\n          options: configured,\n          evaluate(node, options, virtualNode, context) {\n            assert.property(context, 'cssom');\n            assert.deepEqual(context, configured);\n          }\n        }).runSync(fixture, {}, configured);\n      });\n\n      it('should pass the virtual node through', () => {\n        const tree = axe.utils.getFlattenedTree(fixture);\n        new Check({\n          evaluate(node, options, virtualNode) {\n            assert.equal(virtualNode, tree[0]);\n          }\n        }).runSync(tree[0]);\n      });\n\n      it('should throw error for asynchronous checks', () => {\n        const data = { monkeys: 'bananas' };\n\n        try {\n          new Check({\n            evaluate() {\n              const ready = this.async();\n              setTimeout(function () {\n                ready(data);\n              }, 10);\n            }\n          }).runSync(fixture, {}, {});\n        } catch (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(\n            err.message,\n            'Cannot run async check while in a synchronous run'\n          );\n        }\n      });\n\n      it('should pass `null` as the parameter if not enabled', () => {\n        const data = new Check({\n          evaluate() {},\n          enabled: false\n        }).runSync(fixture, {}, {});\n\n        assert.isNull(data);\n      });\n\n      it('should pass `null` as the parameter if options disable', () => {\n        const data = new Check({\n          evaluate() {}\n        }).runSync(\n          fixture,\n          {\n            enabled: false\n          },\n          {}\n        );\n        assert.isNull(data);\n      });\n\n      it('passes a result to the resolve argument', () => {\n        const data = new Check({\n          evaluate() {\n            return true;\n          }\n        }).runSync(fixture, {}, {});\n        assert.instanceOf(data, CheckResult);\n        assert.isTrue(data.result);\n      });\n\n      it('should throw errors', () => {\n        try {\n          new Check({\n            evaluate() {\n              throw new Error('Grenade!');\n            }\n          }).runSync(fixture, {}, {});\n        } catch (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(err.message, 'Grenade!');\n        }\n      });\n    });\n\n    describe('getOptions', () => {\n      let check;\n      beforeEach(function () {\n        check = new Check({\n          options: {\n            foo: 'bar'\n          }\n        });\n      });\n\n      it('should return default check options', () => {\n        assert.deepEqual(check.getOptions(), { foo: 'bar' });\n      });\n\n      it('should merge options with Check defaults', () => {\n        const options = check.getOptions({ hello: 'world' });\n        assert.deepEqual(options, { foo: 'bar', hello: 'world' });\n      });\n\n      it('should override defaults', () => {\n        const options = check.getOptions({ foo: 'world' });\n        assert.deepEqual(options, { foo: 'world' });\n      });\n\n      it('should normalize passed in options', () => {\n        const options = check.getOptions('world');\n        assert.deepEqual(options, { foo: 'bar', value: 'world' });\n      });\n    });\n  });\n\n  describe('spec object', () => {\n    describe('.id', () => {\n      it('should be set', () => {\n        const spec = {\n          id: 'monkeys'\n        };\n        assert.equal(new Check(spec).id, spec.id);\n      });\n\n      it('should have no default', () => {\n        const spec = {};\n        assert.equal(new Check(spec).id, spec.id);\n      });\n    });\n\n    describe('.after', () => {\n      it('should be set', () => {\n        const spec = {\n          after() {}\n        };\n        assert.equal(new Check(spec).after, spec.after);\n      });\n\n      it('should have no default', () => {\n        const spec = {};\n        assert.equal(new Check(spec).after, spec.after);\n      });\n\n      it('should be able to take a string and turn it into a function', () => {\n        const spec = {\n          after: 'function () {return \"blah\";}'\n        };\n        assert.equal(typeof new Check(spec).after, 'function');\n        assert.equal(new Check(spec).after(), 'blah');\n      });\n    });\n\n    describe('.options', () => {\n      it('should be set', () => {\n        const spec = {\n          options: { value: ['monkeys', 'bananas'] }\n        };\n        assert.equal(new Check(spec).options, spec.options);\n      });\n\n      it('should have no default', () => {\n        const spec = {};\n        assert.equal(new Check(spec).options, spec.options);\n      });\n\n      it('should normalize non-object options for internal checks', () => {\n        const spec = {\n          options: 'foo'\n        };\n        assert.deepEqual(new Check(spec).options, { value: 'foo' });\n      });\n\n      it('should not normalize non-object options for external checks', () => {\n        const spec = {\n          options: 'foo',\n          evaluate() {}\n        };\n        assert.deepEqual(new Check(spec).options, 'foo');\n      });\n    });\n\n    describe('.evaluate', () => {\n      it('should be set', () => {\n        const spec = {\n          evaluate() {}\n        };\n        assert.equal(typeof new Check(spec).evaluate, 'function');\n        assert.equal(new Check(spec).evaluate, spec.evaluate);\n      });\n      it('should turn a string into a function', () => {\n        const spec = {\n          evaluate: 'function () { return \"blah\";}'\n        };\n        assert.equal(typeof new Check(spec).evaluate, 'function');\n        assert.equal(new Check(spec).evaluate(), 'blah');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/context.js",
    "content": "/*eslint no-new:0*/\ndescribe('Context', () => {\n  const { Context } = axe._thisWillBeDeletedDoNotUse.base;\n  const { createNestedShadowDom } = axe.testUtils;\n  const fixture = document.getElementById('fixture');\n\n  function $id(id) {\n    return document.getElementById(id);\n  }\n  const selectors = elms =>\n    elms.map(elm => {\n      const domNode = elm?.actualNode || elm;\n      return domNode.id ? `#${domNode.id}` : domNode.nodeName.toLowerCase();\n    });\n\n  it('should not mutate exclude in input', () => {\n    fixture.innerHTML = '<div id=\"foo\"></div>';\n    const context = { exclude: [['iframe', '#foo']] };\n\n    new Context(context);\n    assert.deepEqual(context, { exclude: [['iframe', '#foo']] });\n  });\n\n  it('should not mutate its include input', () => {\n    fixture.innerHTML = '<div id=\"foo\"></div>';\n    const context = { include: [['#foo']] };\n\n    new Context(context);\n    assert.deepEqual(context, { include: [['#foo']] });\n  });\n\n  it('should not share memory with complex object', () => {\n    fixture.innerHTML = '<div id=\"foo\"><a href=\"\">Click me</a></div>';\n    const spec = {\n      include: [['#foo'], ['a']],\n      exclude: [['iframe', '#foo2']],\n      size: { width: 100, height: 100 }\n    };\n    const context = new Context(spec);\n    assert.notStrictEqual(spec.include, context.include);\n    spec.include.forEach((_, index) => {\n      assert.notStrictEqual(spec.include[index], context.include[index]);\n    });\n    assert.notStrictEqual(spec.exclude, context.exclude);\n    spec.exclude.forEach((_, index) => {\n      assert.notStrictEqual(spec.exclude[index], context.exclude[index]);\n    });\n    assert.notStrictEqual(spec.size, context.size);\n  });\n\n  it('should not share memory with simple array', () => {\n    fixture.innerHTML = '<div id=\"foo\"></div>';\n    const spec = [['#foo']];\n    const context = new Context(spec);\n    assert.notStrictEqual(spec, context.include);\n  });\n\n  describe('include', () => {\n    it('accepts a single selector', () => {\n      fixture.innerHTML = '<div id=\"foo\"></div>';\n      const result = new Context('#foo');\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('accepts frame selectors', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const result = new Context([['#foo'], ['#bar']]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('accepts an array of strings as selectors', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const context = new Context(['#foo', '#bar']);\n      assert.deepEqual(selectors(context.include), ['#foo', '#bar']);\n      assert.isEmpty(context.exclude);\n    });\n\n    it('accepts a node reference', () => {\n      const div = document.createElement('div');\n      fixture.appendChild(div);\n      const result = new Context(div);\n      assert.deepEqual([result.include[0].actualNode], [div]);\n    });\n\n    it('does not match shadow DOM nodes with light DOM selection', () => {\n      createNestedShadowDom(\n        fixture,\n        `<p id=\"p1\">Light DOM</p>\n        <article id=\"shadowHost\">\n          <p id=\"p2\">Slotted light DOM</p>\n        </article>`,\n        `<section id=\"shadowHost\"> <slot /> </section>\n        <p id=\"p3\">Shadow DOM</p>`\n      );\n      const result = new Context([[['p']]]);\n      assert.deepEqual(selectors(result.include), ['#p1', '#p2']);\n    });\n\n    it('accepts shadow DOM selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<h1 id=\"target\">Hello world</h1>'\n      );\n      const result = new Context([[['#fixture > article', 'section', 'h1']]]);\n      assert.equal(result.include[0].props.id, 'target');\n    });\n\n    it('accepts a reference to a ShadowRoot', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        `<h1 id=\"h1\">Heading</h1>\n\t\t\t\t<p id=\"p\">Content</p>`\n      );\n      const shadowHost = fixture.querySelector('#shadowHost');\n      const shadowRoot = shadowHost.shadowRoot;\n      const result = new Context(shadowRoot);\n      assert.deepEqual(selectors(result.include), ['#h1', '#p']);\n    });\n\n    it('accepts a node reference consisting of nested divs', () => {\n      const div1 = document.createElement('div');\n      const div2 = document.createElement('div');\n      div1.appendChild(div2);\n      fixture.appendChild(div1);\n\n      const result = new Context(div1);\n      assert.deepEqual([result.include[0].actualNode], [div1]);\n    });\n\n    it('accepts a node reference consisting of a form with nested controls', () => {\n      const form = document.createElement('form');\n      const input = document.createElement('input');\n\n      form.appendChild(input);\n      fixture.appendChild(form);\n\n      const result = new Context(form);\n      assert.deepEqual(result.include[0].actualNode, form);\n    });\n\n    it('accepts an array of node references', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const result = new Context([$id('foo'), $id('bar')]);\n      assert.deepEqual(\n        [result.include[0].actualNode, result.include[1].actualNode],\n        [$id('foo'), $id('bar')]\n      );\n    });\n\n    it('removes any non-matched reference', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const result = new Context([['#foo'], ['#baz'], ['#bar']]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar']);\n    });\n\n    it('sorts the include nodes in document order', () => {\n      fixture.innerHTML =\n        '<div id=\"foo\"><div id=\"bar\"></div></div><div id=\"baz\"></div>';\n      const result = new Context([['#foo'], ['#baz'], ['#bar']]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar', '#baz']);\n    });\n\n    it('removes any null reference', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const result = new Context([$id('foo'), $id('bar'), null]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar']);\n    });\n\n    it('accepts an array of mixed selectors and nodes', () => {\n      fixture.innerHTML = '<div id=\"foo\"><div id=\"bar\"></div></div>';\n      const div = document.createElement('div');\n      div.id = 'baz';\n      fixture.appendChild(div);\n\n      const result = new Context([['#foo'], '#bar', div]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar', '#baz']);\n    });\n\n    it('accepts jQuery-like objects', () => {\n      fixture.innerHTML =\n        '<div id=\"foo\"></div><div id=\"bar\"></div><div id=\"baz\"></div>';\n      const $test = {\n        0: $id('foo'),\n        1: $id('bar'),\n        2: $id('baz'),\n        length: 3\n      };\n\n      const result = new Context($test);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar', '#baz']);\n    });\n\n    describe('throwing errors', () => {\n      let isInFrame;\n\n      beforeEach(() => {\n        isInFrame = axe.utils.respondable.isInFrame;\n      });\n      afterEach(() => {\n        axe.utils.respondable.isInFrame = isInFrame;\n      });\n\n      it('should throw when no elements match the context', () => {\n        fixture.innerHTML = '<div id=\"foo\"></div>';\n        assert.throws(\n          () => {\n            new Context('#notAnElement');\n          },\n          Error,\n          'No elements found for include in page Context'\n        );\n      });\n\n      it.skip('should throw when no elements match the context inside a frame', () => {\n        axe.utils.respondable.isInFrame = () => {\n          return true;\n        };\n\n        fixture.innerHTML = '<div id=\"foo\"></div>';\n        assert.throws(\n          () => {\n            new Context('#notAnElement');\n          },\n          Error,\n          'No elements found for include in frame Context'\n        );\n      });\n    });\n\n    it('creates a flatTree property', () => {\n      const context = new Context({ include: [document] });\n      assert.isArray(context.flatTree);\n      assert.isAtLeast(context.flatTree.length, 1);\n    });\n  });\n\n  describe('object definition', () => {\n    it('should assign include/exclude', () => {\n      const context = new Context({\n        include: [['#fixture']],\n        exclude: [['#mocha']]\n      });\n      assert.isNotNull(context);\n      assert.hasAllKeys(context, [\n        'include',\n        'exclude',\n        'flatTree',\n        'initiator',\n        'page',\n        'frames',\n        'focusable',\n        'size'\n      ]);\n      assert.isArray(context.flatTree);\n      assert.isAtLeast(context.flatTree.length, 1);\n    });\n\n    it('should disregard bad input, non-matching selectors', () => {\n      const flatTree = axe.utils.getFlattenedTree(document);\n      const context = new Context({\n        include: [['#fixture'], ['#monkeys']],\n        exclude: [['#bananas']]\n      });\n      assert.isNotNull(context);\n      assert.hasAllKeys(context, [\n        'include',\n        'exclude',\n        'flatTree',\n        'initiator',\n        'page',\n        'frames',\n        'focusable',\n        'size'\n      ]);\n      assert.lengthOf(context.include, 1);\n      assert.equal(\n        context.include[0].actualNode.id,\n        axe.utils.querySelectorAll(flatTree, '#fixture')[0].actualNode.id\n      );\n    });\n\n    it('should disregard bad input (null)', () => {\n      const result = new Context();\n\n      assert.lengthOf(result.include, 1);\n      assert.equal(result.include[0].actualNode, document.documentElement);\n\n      assert.isEmpty(result.exclude);\n\n      assert.isTrue(result.initiator);\n      assert.isTrue(result.page);\n\n      assert.isEmpty(result.frames);\n    });\n\n    it('should default include to document', () => {\n      const result = new Context({ exclude: [['#fixture']] });\n      assert.lengthOf(result.include, 1);\n      assert.equal(result.include[0].actualNode, document.documentElement);\n\n      assert.lengthOf(result.exclude, 1);\n      assert.equal(result.exclude[0].actualNode, $id('fixture'));\n\n      assert.isTrue(result.initiator);\n      assert.isTrue(result.page);\n\n      assert.isEmpty(result.frames);\n    });\n\n    it('should default empty include to document', () => {\n      const result = new Context({ include: [], exclude: [] });\n      assert.lengthOf(result.include, 1);\n      assert.equal(result.include[0].actualNode, document.documentElement);\n    });\n\n    it('throws when it has a fromFrames prop', () => {\n      assert.throws(() => {\n        new Context({\n          include: [],\n          fromFrames: ['frame', '#fixture']\n        });\n      });\n    });\n\n    it('throws when it has a fromShadowDom prop', () => {\n      assert.throws(() => {\n        new Context({\n          include: [],\n          fromShadowDom: ['frame', '#fixture']\n        });\n      });\n    });\n  });\n\n  describe('initiator', () => {\n    it('should not be clobbered', () => {\n      const result = new Context({\n        initiator: false\n      });\n      assert.lengthOf(result.include, 1);\n      assert.equal(result.include[0].actualNode, document.documentElement);\n\n      assert.isEmpty(result.exclude);\n      assert.isFalse(result.initiator);\n      assert.isTrue(result.page);\n      assert.isEmpty(result.frames);\n    });\n\n    // document.hasOwnProperty is undefined in Firefox content scripts\n    it('should not throw given really weird circumstances when hasOwnProperty is deleted from a document node?', () => {\n      const spec = document.implementation.createHTMLDocument('ie is dumb');\n      spec.hasOwnProperty = undefined;\n\n      const result = new Context(spec);\n\n      assert.lengthOf(result.include, 1);\n      assert.equal(result.include[0].actualNode, spec.documentElement);\n\n      assert.isEmpty(result.exclude);\n\n      assert.isTrue(result.initiator);\n      assert.isFalse(result.page);\n\n      assert.isEmpty(result.frames);\n    });\n  });\n\n  describe('page', () => {\n    it('takes the page argument as default', () => {\n      assert.isTrue(new Context({ page: true }).page);\n      assert.isFalse(new Context({ page: false }).page);\n    });\n\n    it('is true if the document element is included', () => {\n      assert.isTrue(new Context(document).page);\n      assert.isTrue(new Context(document.documentElement).page);\n      assert.isTrue(new Context('html').page);\n      assert.isTrue(new Context(':root').page);\n    });\n\n    it('is true, with exclude used', () => {\n      // What matters is that the documentElement is included\n      // not that parts within that are excluded\n      assert.isTrue(\n        new Context({\n          include: document,\n          exclude: [['#mocha']]\n        }).page\n      );\n    });\n\n    it('is false if the context does not include documentElement', () => {\n      assert.isFalse(new Context(fixture).page);\n      assert.isFalse(new Context('#fixture').page);\n      assert.isFalse(new Context([['#fixture']]).page);\n      assert.isFalse(new Context({ include: [['#fixture']] }).page);\n    });\n  });\n\n  describe('focusable', () => {\n    it('should default to true', () => {\n      const result = new Context();\n      assert.isTrue(result.focusable);\n    });\n\n    it('should use passed in value', () => {\n      const result = new Context({\n        focusable: false\n      });\n      assert.isFalse(result.focusable);\n    });\n\n    it('should reject bad values', () => {\n      const result = new Context({\n        focusable: 'hello'\n      });\n      assert.isTrue(result.focusable);\n    });\n  });\n\n  describe('size', () => {\n    it('should default to empty object', () => {\n      const result = new Context();\n      assert.deepEqual(result.size, {});\n    });\n\n    it('should use passed in value', () => {\n      const result = new Context({\n        size: {\n          width: 10,\n          height: 20\n        }\n      });\n      assert.deepEqual(result.size, {\n        width: 10,\n        height: 20\n      });\n    });\n\n    it('should reject bad values', () => {\n      const result = new Context({\n        size: 'hello'\n      });\n      assert.deepEqual(result.size, {});\n    });\n  });\n\n  describe('frames', () => {\n    function iframeReady(src, context, id, cb, done) {\n      const iframe = document.createElement('iframe');\n      iframe.addEventListener('load', () => {\n        try {\n          cb(iframe);\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n      iframe.src = src;\n      iframe.id = id;\n      context.appendChild(iframe);\n    }\n\n    it('adds frames that are explicitly included', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context('#target');\n          assert.lengthOf(result.frames, 1);\n          assert.deepEqual(result.frames[0].node, $id('target'));\n        },\n        done\n      );\n    });\n\n    it('adds frames that are implicitly included', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context('#outer');\n          assert.lengthOf(result.frames, 1);\n          assert.deepEqual(result.frames[0].node, $id('target'));\n        },\n        done\n      );\n    });\n\n    it('sets include', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context([['#target', '#foo']]);\n          assert.lengthOf(result.frames, 1);\n\n          assert.deepEqual(result.frames[0].node, $id('target'));\n          assert.deepEqual(result.frames[0].include, [['#foo']]);\n          assert.deepEqual(result.frames[0].exclude, []);\n        },\n        done\n      );\n    });\n\n    it('sets exclude', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context({\n            exclude: [['#target', '#foo']]\n          });\n          assert.lengthOf(result.frames, 1);\n\n          assert.deepEqual(result.frames[0].node, $id('target'));\n          assert.deepEqual(result.frames[0].include, []);\n          assert.deepEqual(result.frames[0].exclude, [['#foo']]);\n        },\n        done\n      );\n    });\n\n    it('sets initiator: false', function (done) {\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('fixture'),\n        'target',\n        () => {\n          const result = new Context();\n          assert.isTrue(result.initiator);\n          assert.lengthOf(result.frames, 1);\n          assert.isFalse(result.frames[0].initiator);\n        },\n        done\n      );\n    });\n\n    it('finds frames inside shadow DOM trees', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<iframe id=\"target\" srcdoc=\"<h1>My iframe page</h1>\"></iframe>'\n      );\n      const result = new Context();\n      assert.equal(result.frames[0].node.id, 'target');\n    });\n\n    it('accepts frames inside shadow DOM selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<iframe id=\"target\" srcdoc=\"<h1>My iframe page</h1>\"></iframe>'\n      );\n      const result = new Context([\n        [['#fixture > article', 'section', 'iframe'], ['h1']]\n      ]);\n      assert.equal(result.frames[0].node.id, 'target');\n    });\n\n    it('skips frames excluded with shadow DOM selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<iframe id=\"target\" srcdoc=\"<h1>My iframe page</h1>\"></iframe>'\n      );\n      const result = new Context({\n        exclude: [[['#fixture > article', 'section', 'iframe']]]\n      });\n      assert.isEmpty(result.frames);\n    });\n\n    it('skips frames in excluded shadow trees', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<iframe id=\"target\" srcdoc=\"<h1>My iframe page</h1>\"></iframe>'\n      );\n      const result = new Context({\n        exclude: [[['#fixture > article', 'section']]]\n      });\n      assert.isEmpty(result.frames);\n    });\n\n    describe('.page', () => {\n      it('is true if context includes the document element', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          () => {\n            const result = new Context({\n              exclude: [['#mocha']]\n            });\n            assert.lengthOf(result.frames, 1);\n            assert.isTrue(result.frames[0].page);\n          },\n          done\n        );\n      });\n\n      it(\"can be false, even if the frame's documentElement is included\", function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          () => {\n            const result = new Context({\n              include: [['#fixture']]\n            });\n            assert.lengthOf(result.frames, 1);\n            assert.isFalse(result.frames[0].page);\n          },\n          done\n        );\n      });\n    });\n\n    describe('.focusable', () => {\n      it('is true if tabindex is 0', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          function (iframe) {\n            iframe.tabIndex = '0';\n            const result = new Context();\n            assert.lengthOf(result.frames, 1);\n            assert.isTrue(result.frames[0].focusable);\n          },\n          done\n        );\n      });\n\n      it('is false if the context has a negative tabindex', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          function (iframe) {\n            iframe.tabIndex = '-1';\n            const result = new Context('#fixture');\n            assert.lengthOf(result.frames, 1);\n            assert.isFalse(result.frames[0].focusable);\n          },\n          done\n        );\n      });\n\n      it('is false if the parent context is not focusable', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          () => {\n            const result = new Context({\n              include: [['#fixture']],\n              focusable: false\n            });\n            assert.lengthOf(result.frames, 1);\n            assert.isFalse(result.frames[0].focusable);\n          },\n          done\n        );\n      });\n    });\n\n    describe('.size', () => {\n      it('sets width and height of the frame', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          function (iframe) {\n            iframe.width = '100';\n            iframe.height = '200';\n            const result = new Context('#fixture');\n            const size = result.frames[0].size;\n            assert.closeTo(size.width, 105, 10);\n            assert.closeTo(size.height, 205, 10);\n          },\n          done\n        );\n      });\n\n      it('works with CSS width / height', function (done) {\n        iframeReady(\n          '../mock/frames/context.html',\n          $id('fixture'),\n          'target',\n          function (iframe) {\n            iframe.setAttribute('style', 'width: 100px; height: 200px');\n            const result = new Context('#fixture');\n            const size = result.frames[0].size;\n            assert.closeTo(size.width, 105, 10);\n            assert.closeTo(size.height, 205, 10);\n          },\n          done\n        );\n      });\n    });\n\n    it('combines includes', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context([\n            ['#target', '#foo'],\n            ['#target', '#bar']\n          ]);\n\n          assert.lengthOf(result.frames, 1);\n          assert.deepEqual(result.frames[0].node, $id('target'));\n          assert.deepEqual(result.frames[0].include, [['#foo'], ['#bar']]);\n          assert.deepEqual(result.frames[0].exclude, []);\n        },\n        done\n      );\n    });\n\n    it('does not include the same frame twice', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const result = new Context([$id('target'), $id('target')]);\n          assert.lengthOf(result.frames, 1);\n          assert.deepEqual(result.frames[0].node, $id('target'));\n        },\n        done\n      );\n    });\n\n    it('should filter out invisible frames', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          const frame = $id('target');\n          frame.setAttribute('hidden', 'hidden');\n\n          const result = new Context([$id('target')]);\n          assert.deepEqual(result.frames, []);\n        },\n        done\n      );\n    });\n\n    it('should throw when frame could not be found', function (done) {\n      fixture.innerHTML = '<div id=\"outer\"></div>';\n      iframeReady(\n        '../mock/frames/context.html',\n        $id('outer'),\n        'target',\n        () => {\n          assert.throws(function () {\n            new Context(['#notAFrame', '#foo']);\n          });\n        },\n        done\n      );\n    });\n  });\n\n  describe('with labelled fame selectors', () => {\n    it('accepts a single labelled selector', () => {\n      fixture.innerHTML = '<div id=\"foo\"></div>';\n      const result = new Context({\n        fromFrames: ['#foo']\n      });\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('accepts multiple labelled selectors', () => {\n      fixture.innerHTML = '<div id=\"foo\"></div><div id=\"bar\"></div>';\n      const result = new Context([\n        { fromFrames: ['#foo'] },\n        { fromFrames: ['#bar'] }\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('accepts labelled selectors on include & exclude', () => {\n      fixture.innerHTML = '<div id=\"foo\"></div><div id=\"bar\"></div>';\n      const result = new Context({\n        include: [{ fromFrames: ['#foo'] }],\n        exclude: { fromFrames: ['#bar'] }\n      });\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.deepEqual(selectors(result.exclude), ['#bar']);\n    });\n\n    it('throws when not passed an array', () => {\n      assert.throws(() => {\n        new Context({ fromFrames: '#fixture' });\n      });\n    });\n\n    it('throws when fromShadowDom is on the same object', () => {\n      assert.throws(() => {\n        new Context({\n          fromFrames: ['#fixture'],\n          fromShadowDom: ['#fixture']\n        });\n      });\n    });\n\n    it('throws when labelled frames are nested', () => {\n      assert.throws(() => {\n        new Context({\n          fromFrames: ['#fixture', { fromFrames: ['#fixture'] }]\n        });\n      });\n    });\n\n    describe('when the selector has length > 1', () => {\n      it('sets the frame, rather than include / exclude', () => {\n        fixture.innerHTML = `<iframe id=\"foo\" srcdoc=\"\n          <h1>Hello world</h1> <img>\n        \"></iframe>`;\n        const result = new Context({\n          include: { fromFrames: ['#foo', 'h1'] },\n          exclude: { fromFrames: ['iframe', 'img'] }\n        });\n        assert.isEmpty(result.include);\n        assert.isEmpty(result.exclude);\n        assert.lengthOf(result.frames, 1);\n      });\n\n      it('creates a context for the frame', () => {\n        fixture.innerHTML = `<iframe id=\"foo\" srcdoc=\"\n          <h1>Hello world</h1> <img>\n        \"></iframe>`;\n        const result = new Context({\n          include: { fromFrames: ['#foo', 'h1'] },\n          exclude: { fromFrames: ['iframe', 'img'] }\n        });\n        const frameContext = result.frames[0];\n        assert.lengthOf(result.frames, 1);\n        assert.deepEqual(frameContext.include, [['h1']]);\n        assert.deepEqual(frameContext.exclude, [['img']]);\n      });\n    });\n  });\n\n  describe('with labelled shadow DOM selectors', () => {\n    it('accepts a single labelled selector', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<h1 id=\"foo\">Heading</h1>'\n      );\n      const result = new Context({\n        fromShadowDom: ['#fixture > article', 'section', 'h1']\n      });\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n      assert.isEmpty(result.frames);\n    });\n\n    it('accepts multiple labelled selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<div id=\"foo\"></div><div id=\"bar\"></div>'\n      );\n      const result = new Context([\n        { fromShadowDom: ['#fixture > article', 'section', '#foo'] },\n        { fromShadowDom: ['#fixture > article', 'section', '#bar'] }\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo', '#bar']);\n      assert.isEmpty(result.exclude);\n      assert.isEmpty(result.frames);\n    });\n\n    it('accepts labelled selectors on include & exclude', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<h1 id=\"foo\"></h1><h2 id=\"bar\"></h2>'\n      );\n      const result = new Context({\n        include: [{ fromShadowDom: ['#fixture > article', 'h1'] }],\n        exclude: { fromShadowDom: ['#fixture > article', 'h2'] }\n      });\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.deepEqual(selectors(result.exclude), ['#bar']);\n    });\n\n    it('throws when fromShadowDom does not contain an array', () => {\n      fixture.innerHTML = '<h1>Hello World</h1>';\n      assert.throws(() => {\n        new Context({ fromShadowDom: 'h1' });\n      });\n    });\n\n    it('throws when containing a labelled frame selector', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        `<iframe id=\"foo\" srcdoc=\"\n          <h1>Hello World</h1>\n        \"></iframe>`\n      );\n      assert.throws(() => {\n        new Context({\n          fromShadowDom: [\n            '#fixture > article',\n            { fromFrames: ['iframe', 'h1'] }\n          ]\n        });\n      });\n    });\n\n    it('throws when containing another labelled shadow dom selector', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<div><section id=\"shadowHost\"></section></div>',\n        '<h1 id=\"foo\">Heading</h1>'\n      );\n      assert.throws(() => {\n        new Context({\n          fromShadowDom: [\n            '#fixture > article',\n            { fromShadowDom: ['section', 'h1'] }\n          ]\n        });\n      });\n    });\n\n    describe('nested in a frame selector', () => {\n      it('works for unlabelled frame selectors', () => {\n        createNestedShadowDom(\n          fixture,\n          '<article id=\"shadowHost\"></article>',\n          `<iframe id=\"foo\" srcdoc=\"\n            <h1>Hello World</h1>\n          \"></iframe>`\n        );\n        const result = new Context([\n          [\n            {\n              fromShadowDom: ['#fixture > article', 'iframe']\n            },\n            ['h1']\n          ]\n        ]);\n\n        const frameNodes = result.frames.map(({ node }) => node);\n        assert.deepEqual(selectors(frameNodes), ['#foo']);\n      });\n\n      it('works for labelled frame selectors', () => {\n        createNestedShadowDom(\n          fixture,\n          '<article id=\"shadowHost\"></article>',\n          `<iframe id=\"foo\" srcdoc=\"\n            <h1>Hello World</h1>\n          \"></iframe>`\n        );\n        const result = new Context({\n          fromFrames: [\n            {\n              fromShadowDom: ['#fixture > article', 'iframe']\n            },\n            ['h1']\n          ]\n        });\n        const frameNodes = result.frames.map(({ node }) => node);\n        assert.deepEqual(selectors(frameNodes), ['#foo']);\n      });\n    });\n  });\n\n  describe('ignores bad values', () => {\n    it('passed directly into include', () => {\n      const result = new Context([null, fixture, false, {}]);\n      assert.deepEqual(selectors(result.include), ['#fixture']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('in unlabelled frame selectors', () => {\n      fixture.innerHTML = '<article id=\"foo\"></article>';\n      const result = new Context([\n        [null],\n        ['#fixture > article'],\n        [fixture],\n        [false],\n        [{}]\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('in labelled frame selectors', () => {\n      fixture.innerHTML = '<article id=\"foo\"></article>';\n      const result = new Context([\n        { fromFrames: [null] },\n        { fromFrames: ['#fixture > article'] },\n        { fromFrames: [fixture] },\n        { fromFrames: [false] },\n        { fromFrames: [{}] }\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('in unlabelled shadow DOM selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<h1 id=\"foo\"></h1><h2 id=\"bar\"></h2>'\n      );\n      const result = new Context([\n        [['#fixture > article', null]],\n        [['#fixture > article', 'h1']], // Valid\n        [['#fixture > article', ['h2']]],\n        [['#fixture > article', fixture]],\n        [['#fixture > article', false]],\n        [['#fixture > article', {}]]\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n\n    it('in labelled shadow DOM selectors', () => {\n      createNestedShadowDom(\n        fixture,\n        '<article id=\"shadowHost\"></article>',\n        '<h1 id=\"foo\"></h1><h2 id=\"bar\"></h2>'\n      );\n      const result = new Context([\n        { fromShadowDom: ['#fixture > article', null] },\n        { fromShadowDom: ['#fixture > article', 'h1'] }, // valid\n        { fromShadowDom: ['#fixture > article', ['h1']] },\n        { fromShadowDom: ['#fixture > article', fixture] },\n        { fromShadowDom: ['#fixture > article', false] },\n        { fromShadowDom: ['#fixture > article', {}] }\n      ]);\n      assert.deepEqual(selectors(result.include), ['#foo']);\n      assert.isEmpty(result.exclude);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/rule-result.js",
    "content": "describe('RuleResult', function () {\n  'use strict';\n  var RuleResult = axe._thisWillBeDeletedDoNotUse.base.RuleResult;\n\n  it('should be a function', function () {\n    assert.isFunction(RuleResult);\n  });\n\n  it('should have an empty array for nodes', function () {\n    assert.deepEqual(new RuleResult({ id: 'monkeys' }).nodes, []);\n  });\n\n  it('should grab id from passed in rule', function () {\n    var result = new RuleResult({ id: 'monkeys' });\n    assert.equal(result.id, 'monkeys');\n  });\n});\n"
  },
  {
    "path": "test/core/base/rule.js",
    "content": "describe('Rule', () => {\n  const Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;\n  const Check = axe._thisWillBeDeletedDoNotUse.base.Check;\n  const metadataFunctionMap =\n    axe._thisWillBeDeletedDoNotUse.base.metadataFunctionMap;\n  const fixture = document.getElementById('fixture');\n  const { fixtureSetup, captureError } = axe.testUtils;\n  const noop = () => {};\n  const isNotCalled = function (err) {\n    throw err || new Error('Reject should not be called');\n  };\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', () => {\n    assert.isFunction(Rule);\n  });\n\n  it('should accept two parameters', () => {\n    assert.lengthOf(Rule, 2);\n  });\n\n  describe('prototype', () => {\n    describe('gather', () => {\n      it('should gather nodes which match the selector', () => {\n        const node = document.createElement('div');\n        node.id = 'monkeys';\n        fixture.appendChild(node);\n\n        const rule = new Rule({\n          selector: '#monkeys'\n        });\n        let nodes = rule.gather({\n          include: [axe.utils.getFlattenedTree(fixture)[0]],\n          exclude: [],\n          frames: []\n        });\n\n        assert.lengthOf(nodes, 1);\n        assert.equal(nodes[0].actualNode, node);\n\n        node.id = 'bananas';\n        nodes = rule.gather({\n          include: [axe.utils.getFlattenedTree(fixture)[0]],\n          exclude: [],\n          frames: []\n        });\n\n        assert.lengthOf(nodes, 0);\n      });\n\n      it('should return a real array', () => {\n        const rule = new Rule({\n          selector: 'div'\n        });\n        const result = rule.gather({\n          include: [axe.utils.getFlattenedTree(fixture)[0]],\n          exclude: [],\n          frames: []\n        });\n\n        assert.isArray(result);\n      });\n\n      it('should take a context parameter', () => {\n        const node = document.createElement('div');\n        fixture.appendChild(node);\n\n        const rule = new Rule({\n          selector: 'div'\n        });\n        const nodes = rule.gather({\n          include: [\n            axe.utils.getFlattenedTree(\n              document.getElementById('fixture').firstChild\n            )[0]\n          ]\n        });\n\n        assert.deepEqual(\n          nodes.map(function (n) {\n            return n.actualNode;\n          }),\n          [node]\n        );\n      });\n\n      it('should default to all nodes if selector is not specified', () => {\n        const nodes = [fixture];\n        let node = document.createElement('div');\n\n        fixture.appendChild(node);\n        nodes.push(node);\n\n        node = document.createElement('div');\n\n        fixture.appendChild(node);\n        nodes.push(node);\n\n        const rule = new Rule({}),\n          result = rule.gather({\n            include: [\n              axe.utils.getFlattenedTree(document.getElementById('fixture'))[0]\n            ]\n          });\n\n        assert.lengthOf(result, 3);\n        assert.sameMembers(\n          result.map(function (n) {\n            return n.actualNode;\n          }),\n          nodes\n        );\n      });\n      it('should exclude hidden elements', () => {\n        fixtureSetup('<div style=\"display: none\"><span>HEHEHE</span></div>');\n\n        const rule = new Rule({}),\n          result = rule.gather({\n            include: [\n              axe.utils.getFlattenedTree(\n                document.getElementById('fixture').firstChild\n              )[0]\n            ]\n          });\n\n        assert.lengthOf(result, 0);\n      });\n      it('should include hidden elements if excludeHidden is false', () => {\n        fixtureSetup('<div style=\"display: none\"></div>');\n\n        const rule = new Rule({\n          excludeHidden: false\n        });\n        const result = rule.gather({\n          include: [\n            axe.utils.getFlattenedTree(\n              document.getElementById('fixture').firstChild\n            )[0]\n          ]\n        });\n\n        assert.deepEqual(\n          result.map(function (n) {\n            return n.actualNode;\n          }),\n          [fixture.firstChild]\n        );\n      });\n    });\n\n    describe('run', () => {\n      it('should be a function', () => {\n        assert.isFunction(Rule.prototype.run);\n      });\n\n      it('should run #matches', done => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false;\n        const rule = new Rule({\n          matches: function (node) {\n            assert.equal(node, div);\n            success = true;\n            return [];\n          }\n        });\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(div)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should pass a virtualNode to #matches', done => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false,\n          rule = new Rule({\n            matches: function (node, virtualNode) {\n              assert.equal(virtualNode.actualNode, div);\n              success = true;\n              return [];\n            }\n          });\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(div)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should pass a context to #matches', done => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false,\n          rule = new Rule({\n            matches: function (node, virtualNode, context) {\n              assert.isDefined(context);\n              assert.hasAnyKeys(context, ['cssom', 'include', 'exclude']);\n              assert.lengthOf(context.include, 1);\n              success = true;\n              return [];\n            }\n          });\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(div)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should execute Check#run on its child checks - any', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                run: function (node, options, context, resolve) {\n                  success = true;\n                  resolve(true);\n                }\n              }\n            }\n          }\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should execute Check#run on its child checks - all', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            all: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                run: function (node, options, context, resolve) {\n                  success = true;\n                  resolve(true);\n                }\n              }\n            }\n          }\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should execute Check#run on its child checks - none', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                run: function (node, options, context, resolve) {\n                  success = true;\n                  resolve(true);\n                }\n              }\n            }\n          },\n          isNotCalled\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          () => {\n            assert.isTrue(success);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should pass the matching option to check.run', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        const options = {\n          checks: {\n            cats: {\n              enabled: 'bananas',\n              options: 'minkeys'\n            }\n          }\n        };\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                run: function (node, opts, context, resolve) {\n                  assert.equal(opts.enabled, 'bananas');\n                  assert.equal(opts.options, 'minkeys');\n                  resolve(true);\n                }\n              }\n            }\n          }\n        );\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(document)[0]]\n          },\n          options,\n          () => {\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should pass the matching option to check.run defined on the rule over global', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        const options = {\n          rules: {\n            cats: {\n              checks: {\n                cats: {\n                  enabled: 'apples',\n                  options: 'apes'\n                }\n              }\n            }\n          },\n          checks: {\n            cats: {\n              enabled: 'bananas',\n              options: 'minkeys'\n            }\n          }\n        };\n\n        const rule = new Rule(\n          {\n            id: 'cats',\n            any: [\n              {\n                id: 'cats'\n              }\n            ]\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                run: function (node, opts, context, resolve) {\n                  assert.equal(opts.enabled, 'apples');\n                  assert.equal(opts.options, 'apes');\n                  resolve(true);\n                }\n              }\n            }\n          }\n        );\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(document)[0]]\n          },\n          options,\n          () => {\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should filter out null results', () => {\n        const rule = new Rule(\n          {\n            selector: '#fixture',\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                run: () => {}\n              }\n            }\n          }\n        );\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(document)[0]]\n          },\n          {},\n          function (r) {\n            assert.lengthOf(r.nodes, 0);\n          },\n          isNotCalled\n        );\n      });\n\n      describe.skip('DqElement', () => {\n        let origDqElement;\n        let isDqElementCalled;\n\n        beforeEach(() => {\n          isDqElementCalled = false;\n          origDqElement = axe.utils.DqElement;\n          axe.utils.DqElement = () => {\n            isDqElementCalled = true;\n          };\n          fixtureSetup('<blink>Hi</blink>');\n        });\n\n        afterEach(() => {\n          axe.utils.DqElement = origDqElement;\n        });\n\n        it('is created for matching nodes', done => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: true,\n                  evaluate: () => {\n                    return true;\n                  },\n                  matches: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {},\n            () => {\n              assert.isTrue(isDqElementCalled);\n              done();\n            },\n            isNotCalled\n          );\n        });\n\n        it('is not created for disabled checks', done => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: false,\n                  evaluate: () => {},\n                  matches: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {},\n            () => {\n              assert.isFalse(isDqElementCalled);\n              done();\n            },\n            isNotCalled\n          );\n        });\n\n        it('is created for matching nodes', done => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: true,\n                  evaluate: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {},\n            () => {\n              assert.isTrue(isDqElementCalled);\n              done();\n            },\n            isNotCalled\n          );\n        });\n\n        it('is not created for disabled checks', done => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: false,\n                  evaluate: () => {}\n                })\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {},\n            () => {\n              assert.isFalse(isDqElementCalled);\n              done();\n            },\n            isNotCalled\n          );\n        });\n      });\n\n      it('should pass thrown errors to the reject param', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        const rule = new Rule(\n          { none: ['cats'] },\n          {\n            checks: {\n              cats: {\n                run: () => {\n                  throw new Error('Holy hand grenade');\n                }\n              }\n            }\n          }\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          noop,\n          function (err) {\n            assert.equal(err.message, 'Holy hand grenade');\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should pass reject calls to the reject param', done => {\n        fixtureSetup('<blink>Hi</blink>');\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                run: function (nope, options, context, resolve, reject) {\n                  reject(new Error('your reality'));\n                }\n              }\n            }\n          }\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          noop,\n          function (err) {\n            assert.equal(err.message, 'your reality');\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      it('should mark checks as incomplete if reviewOnFail is set to true', done => {\n        axe.setup();\n        const rule = new Rule(\n          {\n            reviewOnFail: true,\n            all: ['cats'],\n            any: ['cats'],\n            none: ['dogs']\n          },\n          {\n            checks: {\n              cats: new Check({\n                id: 'cats',\n                evaluate: () => {\n                  return false;\n                }\n              }),\n              dogs: new Check({\n                id: 'dogs',\n                evaluate: () => {\n                  return true;\n                }\n              })\n            }\n          }\n        );\n\n        rule.run(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {},\n          function (results) {\n            assert.isUndefined(results.nodes[0].all[0].result);\n            assert.isUndefined(results.nodes[0].any[0].result);\n            assert.isUndefined(results.nodes[0].none[0].result);\n            done();\n          },\n          isNotCalled\n        );\n      });\n\n      describe('error handling', () => {\n        it('should return a RuleError if #matches throws', done => {\n          const rule = new Rule({\n            id: 'fizz',\n            matches: () => {\n              throw new Error('this is an error');\n            }\n          });\n          axe.setup();\n          rule.run(\n            { include: [axe.utils.getFlattenedTree(fixture)[0]] },\n            {},\n            isNotCalled,\n            captureError(err => {\n              assert.instanceOf(err, axe.utils.RuleError);\n              assert.include(err.message, 'this is an error');\n              assert.equal(err.ruleId, 'fizz');\n              assert.equal(err.method, '#matches');\n              assert.deepEqual(err.errorNode.selector, ['#fixture']);\n              done();\n            }, done)\n          );\n        });\n\n        it('should return a RuleError if check.evaluate throws', done => {\n          const rule = new Rule(\n            { id: 'garden', any: ['plants'] },\n            {\n              checks: {\n                plants: new Check({\n                  id: 'plants',\n                  enabled: true,\n                  evaluate: () => {\n                    throw new Error('zombies ate my pants');\n                  }\n                })\n              }\n            }\n          );\n          axe.setup();\n          rule.run(\n            { include: axe.utils.getFlattenedTree(fixture) },\n            {},\n            isNotCalled,\n            captureError(err => {\n              assert.instanceOf(err, axe.utils.RuleError);\n              assert.include(err.message, 'zombies ate my pants');\n              assert.equal(err.ruleId, 'garden');\n              assert.equal(err.method, 'plants#evaluate');\n              assert.deepEqual(err.errorNode.selector, ['#fixture']);\n              done();\n            }, done)\n          );\n        });\n      });\n\n      describe('NODE rule', () => {\n        it('should create a RuleResult', () => {\n          axe.setup();\n          const orig = window.RuleResult;\n          let success = false;\n          window.RuleResult = function (r) {\n            this.nodes = [];\n            assert.equal(rule, r);\n            success = true;\n          };\n\n          const rule = new Rule(\n            {\n              any: [\n                {\n                  evaluate: () => {},\n                  id: 'cats'\n                }\n              ]\n            },\n            {\n              checks: {\n                cats: {\n                  run: function (node, options, context, resolve) {\n                    success = true;\n                    resolve(true);\n                  }\n                }\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(document)[0]]\n            },\n            {},\n            noop,\n            isNotCalled\n          );\n          assert.isTrue(success);\n\n          window.RuleResult = orig;\n        });\n\n        it('should execute rule callback', () => {\n          axe.setup();\n          let success = false;\n\n          const rule = new Rule(\n            {\n              any: [\n                {\n                  evaluate: () => {},\n                  id: 'cats'\n                }\n              ]\n            },\n            {\n              checks: {\n                cats: {\n                  run: function (node, options, context, resolve) {\n                    success = true;\n                    resolve(true);\n                  }\n                }\n              }\n            }\n          );\n          rule.run(\n            {\n              include: [axe.utils.getFlattenedTree(document)[0]]\n            },\n            {},\n            () => {\n              success = true;\n            },\n            isNotCalled\n          );\n          assert.isTrue(success);\n        });\n      });\n    });\n\n    describe('runSync', () => {\n      it('should be a function', () => {\n        assert.isFunction(Rule.prototype.runSync);\n      });\n\n      it('should run #matches', () => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false;\n        const rule = new Rule({\n          matches: function (node) {\n            assert.equal(node, div);\n            success = true;\n            return [];\n          }\n        });\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(div)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should pass a virtualNode to #matches', () => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false;\n        const rule = new Rule({\n          matches: function (node, virtualNode) {\n            assert.equal(virtualNode.actualNode, div);\n            success = true;\n            return [];\n          }\n        });\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(div)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should pass a context to #matches', () => {\n        const div = document.createElement('div');\n        fixture.appendChild(div);\n        let success = false;\n        const rule = new Rule({\n          matches: function (node, virtualNode, context) {\n            assert.isDefined(context);\n            assert.hasAnyKeys(context, ['cssom', 'include', 'exclude']);\n            assert.lengthOf(context.include, 1);\n            success = true;\n            return [];\n          }\n        });\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(div)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should handle an error in #matches', () => {\n        const div = document.createElement('div');\n        div.setAttribute('style', '#fff');\n        fixture.appendChild(div);\n        let success = false;\n        const rule = new Rule({\n          matches: () => {\n            throw new Error('this is an error');\n          }\n        });\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(div)[0]]\n            },\n            {}\n          );\n          isNotCalled();\n        } catch {\n          assert.isFalse(success);\n        }\n      });\n\n      it('should execute Check#runSync on its child checks - any', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                runSync: () => {\n                  success = true;\n                }\n              }\n            }\n          }\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should execute Check#runSync on its child checks - all', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            all: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                runSync: () => {\n                  success = true;\n                }\n              }\n            }\n          }\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should execute Check#run on its child checks - none', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        let success = false;\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                runSync: () => {\n                  success = true;\n                }\n              }\n            }\n          },\n          isNotCalled\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {}\n          );\n          assert.isTrue(success);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should pass the matching option to check.runSync', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        const options = {\n          checks: {\n            cats: {\n              enabled: 'bananas',\n              options: 'minkeys'\n            }\n          }\n        };\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                runSync: function (node, opts) {\n                  assert.equal(opts.enabled, 'bananas');\n                  assert.equal(opts.options, 'minkeys');\n                }\n              }\n            }\n          }\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(document)[0]]\n            },\n            options\n          );\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should pass the matching option to check.runSync defined on the rule over global', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        const options = {\n          rules: {\n            cats: {\n              checks: {\n                cats: {\n                  enabled: 'apples',\n                  options: 'apes'\n                }\n              }\n            }\n          },\n          checks: {\n            cats: {\n              enabled: 'bananas',\n              options: 'minkeys'\n            }\n          }\n        };\n\n        const rule = new Rule(\n          {\n            id: 'cats',\n            any: [\n              {\n                id: 'cats'\n              }\n            ]\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                runSync: function (node, opts) {\n                  assert.equal(opts.enabled, 'apples');\n                  assert.equal(opts.options, 'apes');\n                }\n              }\n            }\n          }\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(document)[0]]\n            },\n            options\n          );\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      it('should filter out null results', () => {\n        const rule = new Rule(\n          {\n            selector: '#fixture',\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                runSync: () => {}\n              }\n            }\n          }\n        );\n\n        try {\n          const r = rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(document)[0]]\n            },\n            {}\n          );\n          assert.lengthOf(r.nodes, 0);\n        } catch (err) {\n          isNotCalled(err);\n        }\n      });\n\n      describe.skip('DqElement', () => {\n        let origDqElement;\n        let isDqElementCalled;\n\n        beforeEach(() => {\n          isDqElementCalled = false;\n          origDqElement = axe.utils.DqElement;\n          axe.utils.DqElement = () => {\n            isDqElementCalled = true;\n          };\n          fixtureSetup('<blink>Hi</blink>');\n        });\n\n        afterEach(() => {\n          axe.utils.DqElement = origDqElement;\n        });\n\n        it('is created for matching nodes', () => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: true,\n                  evaluate: () => {\n                    return true;\n                  },\n                  matches: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n\n          try {\n            rule.runSync(\n              {\n                include: [axe.utils.getFlattenedTree(fixture)[0]]\n              },\n              {}\n            );\n            assert.isTrue(isDqElementCalled);\n          } catch (err) {\n            isNotCalled(err);\n          }\n        });\n\n        it('is not created for disabled checks', () => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: false,\n                  evaluate: () => {},\n                  matches: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n\n          try {\n            rule.runSync(\n              {\n                include: [axe.utils.getFlattenedTree(fixture)[0]]\n              },\n              {}\n            );\n            assert.isFalse(isDqElementCalled);\n          } catch (err) {\n            isNotCalled(err);\n          }\n        });\n\n        it('is created for matching nodes', () => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: true,\n                  evaluate: () => {\n                    return true;\n                  }\n                })\n              }\n            }\n          );\n\n          try {\n            rule.runSync(\n              {\n                include: [axe.utils.getFlattenedTree(fixture)[0]]\n              },\n              {}\n            );\n            assert.isTrue(isDqElementCalled);\n          } catch (err) {\n            isNotCalled(err);\n          }\n        });\n\n        it('is not created for disabled checks', () => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  enabled: false,\n                  evaluate: () => {}\n                })\n              }\n            }\n          );\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {},\n            () => {\n              assert.isFalse(isDqElementCalled);\n            },\n            isNotCalled\n          );\n        });\n\n        it('should not be called when there is no actualNode', () => {\n          const rule = new Rule(\n            {\n              all: ['cats']\n            },\n            {\n              checks: {\n                cats: new Check({\n                  id: 'cats',\n                  evaluate: () => {}\n                })\n              }\n            }\n          );\n          rule.excludeHidden = false; // so we don't call utils.isHidden\n          const vNode = {\n            shadowId: undefined,\n            children: [],\n            parent: undefined,\n            _cache: {},\n            _isHidden: null,\n            _attrs: {\n              type: 'text',\n              autocomplete: 'not-on-my-watch'\n            },\n            props: {\n              nodeType: 1,\n              nodeName: 'input',\n              id: null,\n              type: 'text'\n            },\n            hasClass: () => {\n              return false;\n            },\n            attr: function (attrName) {\n              return this._attrs[attrName];\n            },\n            hasAttr: function (attrName) {\n              return !!this._attrs[attrName];\n            }\n          };\n          rule.runSync(\n            {\n              include: [vNode]\n            },\n            {},\n            () => {\n              assert.isFalse(isDqElementCalled);\n            },\n            isNotCalled\n          );\n        });\n      });\n\n      it('should pass thrown errors to the reject param', () => {\n        fixtureSetup('<blink>Hi</blink>');\n        const rule = new Rule(\n          {\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                runSync: () => {\n                  throw new Error('Holy hand grenade');\n                }\n              }\n            }\n          }\n        );\n\n        try {\n          rule.runSync(\n            {\n              include: [axe.utils.getFlattenedTree(fixture)[0]]\n            },\n            {}\n          );\n          isNotCalled();\n        } catch (err) {\n          assert.equal(err.message, 'Holy hand grenade');\n        }\n      });\n\n      it('should mark checks as incomplete if reviewOnFail is set to true', () => {\n        axe.setup();\n        const rule = new Rule(\n          {\n            reviewOnFail: true,\n            all: ['cats'],\n            any: ['cats'],\n            none: ['dogs']\n          },\n          {\n            checks: {\n              cats: new Check({\n                id: 'cats',\n                evaluate: () => {\n                  return false;\n                }\n              }),\n              dogs: new Check({\n                id: 'dogs',\n                evaluate: () => {\n                  return true;\n                }\n              })\n            }\n          }\n        );\n\n        const results = rule.runSync(\n          {\n            include: [axe.utils.getFlattenedTree(fixture)[0]]\n          },\n          {}\n        );\n\n        assert.isUndefined(results.nodes[0].all[0].result);\n        assert.isUndefined(results.nodes[0].any[0].result);\n        assert.isUndefined(results.nodes[0].none[0].result);\n      });\n\n      describe.skip('NODE rule', () => {\n        it('should create a RuleResult', () => {\n          const orig = window.RuleResult;\n          let success = false;\n          window.RuleResult = function (r) {\n            this.nodes = [];\n            assert.equal(rule, r);\n            success = true;\n          };\n\n          const rule = new Rule(\n            {\n              any: [\n                {\n                  evaluate: () => {},\n                  id: 'cats'\n                }\n              ]\n            },\n            {\n              checks: {\n                cats: {\n                  runSync: () => {}\n                }\n              }\n            }\n          );\n\n          try {\n            rule.runSync(\n              {\n                include: [axe.utils.getFlattenedTree(document)[0]]\n              },\n              {}\n            );\n            assert.isTrue(success);\n          } catch (err) {\n            isNotCalled(err);\n          }\n\n          window.RuleResult = orig;\n        });\n\n        it('should execute rule callback', () => {\n          let success = false;\n\n          const rule = new Rule(\n            {\n              any: [\n                {\n                  evaluate: () => {},\n                  id: 'cats'\n                }\n              ]\n            },\n            {\n              checks: {\n                cats: {\n                  runSync: () => {\n                    success = true;\n                  }\n                }\n              }\n            }\n          );\n\n          try {\n            rule.runSync(\n              {\n                include: [axe.utils.getFlattenedTree(document)[0]]\n              },\n              {}\n            );\n          } catch (err) {\n            isNotCalled(err);\n          }\n\n          assert.isTrue(success);\n        });\n      });\n    });\n\n    describe('after', () => {\n      it('should execute Check#after with options', () => {\n        let success = false;\n\n        const rule = new Rule(\n          {\n            id: 'cats',\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                enabled: true,\n                after: function (results, options) {\n                  assert.deepEqual(options, { dogs: true });\n                  success = true;\n                  return results;\n                }\n              }\n            }\n          }\n        );\n\n        rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                all: [],\n                none: [],\n                any: [\n                  {\n                    id: 'cats',\n                    result: true\n                  }\n                ]\n              }\n            ]\n          },\n          { checks: { cats: { options: { dogs: true } } } }\n        );\n\n        assert.isTrue(success);\n      });\n\n      it('should add the check node to the check result', () => {\n        let success = false;\n\n        const rule = new Rule(\n          {\n            id: 'cats',\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                enabled: true,\n                after: function (results) {\n                  assert.equal(results[0].node, 'customNode');\n                  success = true;\n                  return results;\n                }\n              }\n            }\n          }\n        );\n\n        rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                all: [],\n                none: [],\n                any: [\n                  {\n                    id: 'cats',\n                    result: true\n                  }\n                ],\n                node: 'customNode'\n              }\n            ]\n          },\n          { checks: { cats: { options: { dogs: true } } } }\n        );\n\n        assert.isTrue(success);\n      });\n\n      it('should filter removed checks', () => {\n        const rule = new Rule(\n          {\n            id: 'cats',\n            any: ['cats']\n          },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                after: function (results) {\n                  return [results[0]];\n                }\n              }\n            }\n          }\n        );\n\n        const result = rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                any: [],\n                none: [],\n                all: [\n                  {\n                    id: 'cats',\n                    result: true\n                  }\n                ]\n              },\n              {\n                any: [],\n                none: [],\n                all: [\n                  {\n                    id: 'cats',\n                    result: false\n                  }\n                ]\n              }\n            ]\n          },\n          { checks: { cats: { options: { dogs: true } } } }\n        );\n\n        assert.lengthOf(result.nodes, 1);\n        assert.equal(result.nodes[0].all[0].id, 'cats');\n        assert.isTrue(result.nodes[0].all[0].result);\n      });\n\n      it('should combine all checks for pageLevel rules', () => {\n        const rule = new Rule({});\n\n        const result = rule.after(\n          {\n            id: 'cats',\n            pageLevel: true,\n            nodes: [\n              {\n                any: [],\n                none: [],\n                all: [\n                  {\n                    id: 'cats',\n                    result: false\n                  }\n                ]\n              },\n              {\n                any: [],\n                none: [\n                  {\n                    id: 'dogs',\n                    result: false\n                  }\n                ],\n                all: []\n              },\n              {\n                any: [\n                  {\n                    id: 'monkeys',\n                    result: false\n                  }\n                ],\n                none: [],\n                all: []\n              }\n            ]\n          },\n          { checks: { cats: { options: { dogs: true } } } }\n        );\n\n        assert.lengthOf(result.nodes, 1);\n      });\n\n      it('should throw a RuleError if check.after throws', () => {\n        const rule = new Rule(\n          { id: 'dogs', any: ['cats'] },\n          {\n            checks: {\n              cats: {\n                id: 'cats',\n                enabled: true,\n                after: () => {\n                  throw new Error('this is an error');\n                }\n              }\n            }\n          }\n        );\n        axe.setup();\n        try {\n          rule.after(\n            {\n              id: 'cats',\n              nodes: [\n                {\n                  all: [],\n                  none: [],\n                  any: [{ id: 'cats', result: true }],\n                  node: new axe.utils.DqElement(fixture)\n                }\n              ]\n            },\n            {}\n          );\n          assert.fail('Should have thrown');\n        } catch (err) {\n          assert.instanceOf(err, axe.utils.RuleError);\n          assert.include(err.message, 'this is an error');\n          assert.equal(err.ruleId, 'dogs');\n          assert.equal(err.method, 'cats#after');\n          assert.deepEqual(err.errorNode.selector, ['#fixture']);\n        }\n      });\n    });\n\n    describe('reviewOnFail', () => {\n      it('should mark checks as incomplete if reviewOnFail is set to true for all', () => {\n        axe.setup();\n        const rule = new Rule(\n          {\n            id: 'cats',\n            reviewOnFail: true,\n            all: ['cats'],\n            any: [],\n            none: []\n          },\n          {\n            checks: {\n              cats: new Check({\n                id: 'cats',\n                enabled: true,\n                after: function (results) {\n                  results[0].result = false;\n                  return results;\n                }\n              })\n            }\n          }\n        );\n\n        const result = rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                any: [],\n                none: [],\n                all: [\n                  {\n                    id: 'cats',\n                    result: true\n                  }\n                ]\n              }\n            ]\n          },\n          { checks: { cats: { options: {} } } }\n        );\n\n        assert.isEmpty(result.nodes[0].any);\n        assert.isEmpty(result.nodes[0].none);\n        assert.isUndefined(result.nodes[0].all[0].result);\n      });\n\n      it('should mark checks as incomplete if reviewOnFail is set to true for any', () => {\n        const rule = new Rule(\n          {\n            id: 'cats',\n            reviewOnFail: true,\n            all: [],\n            any: ['cats'],\n            none: []\n          },\n          {\n            checks: {\n              cats: new Check({\n                id: 'cats',\n                enabled: true,\n                after: function (results) {\n                  results[0].result = false;\n                  return results;\n                }\n              })\n            }\n          }\n        );\n\n        const result = rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                any: [\n                  {\n                    id: 'cats',\n                    result: true\n                  }\n                ],\n                none: [],\n                all: []\n              }\n            ]\n          },\n          { checks: { cats: { options: {} } } }\n        );\n\n        assert.isUndefined(result.nodes[0].any[0].result);\n        assert.isEmpty(result.nodes[0].none);\n        assert.isEmpty(result.nodes[0].all);\n      });\n\n      it('should mark checks as incomplete if reviewOnFail is set to true for none', () => {\n        const rule = new Rule(\n          {\n            id: 'cats',\n            reviewOnFail: true,\n            all: [],\n            any: [],\n            none: ['cats']\n          },\n          {\n            checks: {\n              cats: new Check({\n                id: 'cats',\n                enabled: true,\n                after: function (results) {\n                  results[0].result = true;\n                  return results;\n                }\n              })\n            }\n          }\n        );\n\n        const result = rule.after(\n          {\n            id: 'cats',\n            nodes: [\n              {\n                any: [],\n                none: [\n                  {\n                    id: 'cats',\n                    result: false\n                  }\n                ],\n                all: []\n              }\n            ]\n          },\n          { checks: { cats: { options: {} } } }\n        );\n\n        assert.isEmpty(result.nodes[0].any);\n        assert.isUndefined(result.nodes[0].none[0].result);\n        assert.isEmpty(result.nodes[0].all);\n      });\n    });\n  });\n\n  describe('spec object', () => {\n    describe('.selector', () => {\n      it('should be set', () => {\n        const spec = {\n          selector: '#monkeys'\n        };\n        assert.equal(new Rule(spec).selector, spec.selector);\n      });\n\n      it('should default to *', () => {\n        const spec = {};\n        assert.equal(new Rule(spec).selector, '*');\n      });\n    });\n\n    describe('.enabled', () => {\n      it('should be set', () => {\n        const spec = {\n          enabled: false\n        };\n        assert.equal(new Rule(spec).enabled, spec.enabled);\n      });\n\n      it('should default to true', () => {\n        const spec = {};\n        assert.isTrue(new Rule(spec).enabled);\n      });\n\n      it('should default to true if given a bad value', () => {\n        const spec = {\n          enabled: 'monkeys'\n        };\n        assert.isTrue(new Rule(spec).enabled);\n      });\n    });\n\n    describe('.excludeHidden', () => {\n      it('should be set', () => {\n        const spec = {\n          excludeHidden: false\n        };\n        assert.equal(new Rule(spec).excludeHidden, spec.excludeHidden);\n      });\n\n      it('should default to true', () => {\n        const spec = {};\n        assert.isTrue(new Rule(spec).excludeHidden);\n      });\n\n      it('should default to true if given a bad value', () => {\n        const spec = {\n          excludeHidden: 'monkeys'\n        };\n        assert.isTrue(new Rule(spec).excludeHidden);\n      });\n    });\n\n    describe('.pageLevel', () => {\n      it('should be set', () => {\n        const spec = {\n          pageLevel: false\n        };\n        assert.equal(new Rule(spec).pageLevel, spec.pageLevel);\n      });\n\n      it('should default to false', () => {\n        const spec = {};\n        assert.isFalse(new Rule(spec).pageLevel);\n      });\n\n      it('should default to false if given a bad value', () => {\n        const spec = {\n          pageLevel: 'monkeys'\n        };\n        assert.isFalse(new Rule(spec).pageLevel);\n      });\n    });\n\n    describe('.reviewOnFail', () => {\n      it('should be set', () => {\n        const spec = {\n          reviewOnFail: true\n        };\n        assert.equal(new Rule(spec).reviewOnFail, spec.reviewOnFail);\n      });\n\n      it('should default to false', () => {\n        const spec = {};\n        assert.isFalse(new Rule(spec).reviewOnFail);\n      });\n\n      it('should default to false if given a bad value', () => {\n        const spec = {\n          reviewOnFail: 'monkeys'\n        };\n        assert.isFalse(new Rule(spec).reviewOnFail);\n      });\n    });\n\n    describe('.id', () => {\n      it('should be set', () => {\n        const spec = {\n          id: 'monkeys'\n        };\n        assert.equal(new Rule(spec).id, spec.id);\n      });\n\n      it('should have no default', () => {\n        const spec = {};\n        assert.equal(new Rule(spec).id, spec.id);\n      });\n    });\n\n    describe('.impact', () => {\n      it('should be set', () => {\n        const spec = {\n          impact: 'critical'\n        };\n        assert.equal(new Rule(spec).impact, spec.impact);\n      });\n\n      it('should have no default', () => {\n        const spec = {};\n        assert.isUndefined(new Rule(spec).impact);\n      });\n\n      it('throws if impact is invalid', () => {\n        assert.throws(() => {\n          // eslint-disable-next-line no-new\n          new Rule({ impact: 'hello' });\n        });\n      });\n    });\n\n    describe('.any', () => {\n      it('should be set', () => {\n        const spec = {\n          any: [\n            {\n              name: 'monkeys'\n            },\n            {\n              name: 'bananas'\n            },\n            {\n              name: 'pajamas'\n            }\n          ]\n        };\n        assert.property(new Rule(spec), 'any');\n      });\n    });\n\n    describe('.all', () => {\n      it('should be set', () => {\n        const spec = {\n          all: [\n            {\n              name: 'monkeys'\n            },\n            {\n              name: 'bananas'\n            },\n            {\n              name: 'pajamas'\n            }\n          ]\n        };\n        assert.property(new Rule(spec), 'all');\n      });\n    });\n\n    describe('.none', () => {\n      it('should be set', () => {\n        const spec = {\n          none: [\n            {\n              name: 'monkeys'\n            },\n            {\n              name: 'bananas'\n            },\n            {\n              name: 'pajamas'\n            }\n          ]\n        };\n        assert.property(new Rule(spec), 'none');\n      });\n    });\n\n    describe('.matches', () => {\n      it('should be set', () => {\n        const spec = {\n          matches: () => {}\n        };\n        assert.equal(new Rule(spec).matches, spec.matches);\n      });\n\n      it('should default to prototype', () => {\n        const spec = {};\n        assert.equal(new Rule(spec).matches, Rule.prototype.matches);\n      });\n\n      it('should turn a string into a function', () => {\n        const spec = {\n          matches: 'function() {return \"blah\";}'\n        };\n        assert.equal(new Rule(spec).matches(), 'blah');\n      });\n    });\n\n    describe('.tags', () => {\n      it('should be set', () => {\n        const spec = {\n          tags: ['foo', 'bar']\n        };\n        assert.deepEqual(new Rule(spec).tags, spec.tags);\n      });\n\n      it('should default to empty array', () => {\n        const spec = {};\n        assert.deepEqual(new Rule(spec).tags, []);\n      });\n    });\n\n    describe('.actIds', () => {\n      it('should be set', () => {\n        const spec = {\n          actIds: ['abc123', 'xyz789']\n        };\n        assert.deepEqual(new Rule(spec).actIds, spec.actIds);\n      });\n\n      it('should default to undefined', () => {\n        const spec = {};\n        assert.isUndefined(new Rule(spec).actIds);\n      });\n    });\n  });\n\n  describe('configure', () => {\n    beforeEach(() => {\n      Rule.prototype._get = function (attr) {\n        return this[attr];\n      };\n    });\n    afterEach(() => {\n      delete Rule.prototype._get;\n    });\n    it('should be a function that takes one argument', () => {\n      assert.isFunction(Rule.prototype.configure);\n      assert.lengthOf(new Rule({}).configure, 1);\n    });\n    it('should NOT override the id', () => {\n      const rule = new Rule({ id: 'foo' });\n\n      assert.equal(rule._get('id'), 'foo');\n      rule.configure({ id: 'fong' });\n      assert.equal(rule._get('id'), 'foo');\n    });\n    it('should NOT override a random property', () => {\n      const rule = new Rule({ id: 'foo' });\n\n      rule.configure({ fong: 'fong' });\n      assert.equal(rule._get('fong'), undefined);\n    });\n    it('should override the selector', () => {\n      const rule = new Rule({ selector: 'foo' });\n\n      assert.equal(rule._get('selector'), 'foo');\n      rule.configure({ selector: 'fong' });\n      assert.equal(rule._get('selector'), 'fong');\n    });\n    it('should override excludeHidden', () => {\n      const rule = new Rule({ excludeHidden: false });\n\n      assert.equal(rule._get('excludeHidden'), false);\n      rule.configure({ excludeHidden: true });\n      assert.equal(rule._get('excludeHidden'), true);\n    });\n    it('should override enabled', () => {\n      const rule = new Rule({ enabled: false });\n\n      assert.equal(rule._get('enabled'), false);\n      rule.configure({ enabled: true });\n      assert.equal(rule._get('enabled'), true);\n    });\n    it('should override pageLevel', () => {\n      const rule = new Rule({ pageLevel: false });\n\n      assert.equal(rule._get('pageLevel'), false);\n      rule.configure({ pageLevel: true });\n      assert.equal(rule._get('pageLevel'), true);\n    });\n    it('should override reviewOnFail', () => {\n      const rule = new Rule({ reviewOnFail: false });\n\n      assert.equal(rule._get('reviewOnFail'), false);\n      rule.configure({ reviewOnFail: true });\n      assert.equal(rule._get('reviewOnFail'), true);\n    });\n    it('should override any', () => {\n      const rule = new Rule({ any: ['one', 'two'] });\n\n      assert.deepEqual(rule._get('any'), ['one', 'two']);\n      rule.configure({ any: [] });\n      assert.deepEqual(rule._get('any'), []);\n    });\n    it('should override all', () => {\n      const rule = new Rule({ all: ['one', 'two'] });\n\n      assert.deepEqual(rule._get('all'), ['one', 'two']);\n      rule.configure({ all: [] });\n      assert.deepEqual(rule._get('all'), []);\n    });\n    it('should override none', () => {\n      const rule = new Rule({ none: ['none', 'two'] });\n\n      assert.deepEqual(rule._get('none'), ['none', 'two']);\n      rule.configure({ none: [] });\n      assert.deepEqual(rule._get('none'), []);\n    });\n    it('should override tags', () => {\n      const rule = new Rule({ tags: ['tags', 'two'] });\n\n      assert.deepEqual(rule._get('tags'), ['tags', 'two']);\n      rule.configure({ tags: [] });\n      assert.deepEqual(rule._get('tags'), []);\n    });\n    it('should override matches (doT.js function)', () => {\n      const rule = new Rule({ matches: 'function () {return \"matches\";}' });\n\n      assert.equal(rule._get('matches')(), 'matches');\n      rule.configure({ matches: 'function () {return \"does not match\";}' });\n      assert.equal(rule._get('matches')(), 'does not match');\n    });\n    it('should override matches (metadata function name)', () => {\n      axe._load({});\n      metadataFunctionMap['custom-matches'] = () => {\n        return 'custom-matches';\n      };\n      metadataFunctionMap['other-matches'] = () => {\n        return 'other-matches';\n      };\n\n      const rule = new Rule({ matches: 'custom-matches' });\n\n      assert.equal(rule._get('matches')(), 'custom-matches');\n      rule.configure({ matches: 'other-matches' });\n      assert.equal(rule._get('matches')(), 'other-matches');\n\n      delete metadataFunctionMap['custom-matches'];\n      delete metadataFunctionMap['other-matches'];\n    });\n    it('should error if matches does not match an ID', () => {\n      function fn() {\n        const rule = new Rule({});\n        rule.configure({ matches: 'does-not-exist' });\n      }\n\n      assert.throws(\n        fn,\n        'Function ID does not exist in the metadata-function-map: does-not-exist'\n      );\n    });\n    it('should override impact', () => {\n      const rule = new Rule({ impact: 'minor' });\n\n      assert.equal(rule._get('impact'), 'minor');\n      rule.configure({ impact: 'serious' });\n      assert.equal(rule._get('impact'), 'serious');\n    });\n    it('should throw if impact impact', () => {\n      const rule = new Rule({ impact: 'minor' });\n\n      assert.throws(() => {\n        rule.configure({ impact: 'hello' });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/virtual-node/abstract-virtual-node.js",
    "content": "describe('AbstractVirtualNode', function () {\n  it('should be a function', function () {\n    assert.isFunction(axe.AbstractVirtualNode);\n  });\n\n  it('should throw an error when accessing props', function () {\n    function fn() {\n      var abstractNode = new axe.AbstractVirtualNode();\n      if (abstractNode.props.nodeType === 1) {\n        return;\n      }\n    }\n\n    assert.throws(fn);\n  });\n\n  it('should throw an error when accessing attrNames', function () {\n    function fn() {\n      var abstractNode = new axe.AbstractVirtualNode();\n      return abstractNode.attrNames;\n    }\n\n    assert.throws(fn, 'VirtualNode class must have an \"attrNames\" property');\n  });\n\n  it('should throw an error when accessing hasClass', function () {\n    function fn() {\n      var abstractNode = new axe.AbstractVirtualNode();\n      if (abstractNode.hasClass('foo')) {\n        return;\n      }\n    }\n\n    assert.throws(fn);\n  });\n\n  it('should throw an error when accessing attr', function () {\n    function fn() {\n      var abstractNode = new axe.AbstractVirtualNode();\n      if (abstractNode.attr('foo') === 'bar') {\n        return;\n      }\n    }\n\n    assert.throws(fn);\n  });\n\n  it('should throw an error when accessing hasAttr', function () {\n    function fn() {\n      var abstractNode = new axe.AbstractVirtualNode();\n      if (abstractNode.hasAttr('foo')) {\n        return;\n      }\n    }\n\n    assert.throws(fn);\n  });\n\n  describe('hasClass, when attr is set', function () {\n    it('should return true when the element has the class', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return 'my-class';\n      };\n\n      assert.isTrue(vNode.hasClass('my-class'));\n    });\n\n    it('should return true when the element contains more than one class', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return 'my-class a11y-focus visually-hidden';\n      };\n\n      assert.isTrue(vNode.hasClass('my-class'));\n      assert.isTrue(vNode.hasClass('a11y-focus'));\n      assert.isTrue(vNode.hasClass('visually-hidden'));\n    });\n\n    it('should return false when the element does not contain the class', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return undefined;\n      };\n\n      assert.isFalse(vNode.hasClass('my-class'));\n    });\n\n    it('should return false when the element contains only part of the class', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return 'my-class';\n      };\n      assert.isFalse(vNode.hasClass('class'));\n    });\n\n    it('should return false if className is not of type string', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return null;\n      };\n\n      assert.isFalse(vNode.hasClass('my-class'));\n    });\n\n    it('should return true for whitespace characters', function () {\n      var vNode = new axe.AbstractVirtualNode();\n      vNode.attr = function () {\n        return 'my-class\\ta11y-focus\\rvisually-hidden\\ngrid\\fcontainer';\n      };\n\n      assert.isTrue(vNode.hasClass('my-class'));\n      assert.isTrue(vNode.hasClass('a11y-focus'));\n      assert.isTrue(vNode.hasClass('visually-hidden'));\n      assert.isTrue(vNode.hasClass('grid'));\n      assert.isTrue(vNode.hasClass('container'));\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/virtual-node/serial-virtual-node.js",
    "content": "describe('SerialVirtualNode', function () {\n  var SerialVirtualNode = axe.SerialVirtualNode;\n\n  it('extends AbstractVirtualNode', function () {\n    var vNode = new SerialVirtualNode({\n      nodeName: 'div'\n    });\n    assert.instanceOf(vNode, axe.AbstractVirtualNode);\n  });\n\n  describe('props', function () {\n    it('assigns any properties to .props', function () {\n      var props = {\n        nodeType: 1,\n        nodeName: 'div',\n        someType: 'bar',\n        somethingElse: 'baz'\n      };\n      var vNode = new SerialVirtualNode(props);\n      assert.deepEqual(vNode.props, props);\n    });\n\n    it('returns a frozen object', function () {\n      var vNode = new SerialVirtualNode({ nodeName: 'div' });\n      assert.isTrue(Object.isFrozen(vNode.props), 'Expect object to be frozen');\n    });\n\n    it('takes 1 as its nodeType', function () {\n      var vNode = new SerialVirtualNode({\n        nodeType: 1,\n        nodeName: 'div'\n      });\n      assert.equal(vNode.props.nodeType, 1);\n    });\n\n    it('takes 3 as its nodeType', function () {\n      var vNode = new SerialVirtualNode({\n        nodeType: 3,\n        nodeName: '#text'\n      });\n      assert.equal(vNode.props.nodeType, 3);\n    });\n\n    it('has a default nodeType of 1', function () {\n      var vNode = new SerialVirtualNode({ nodeName: 'div' });\n      assert.equal(vNode.props.nodeType, 1);\n    });\n\n    it('does not throw if nodeType is falsy', function () {\n      [null, undefined].forEach(function (nonThrowingNodeType) {\n        assert.doesNotThrow(function () {\n          // eslint-disable-next-line no-new\n          new SerialVirtualNode({\n            nodeType: nonThrowingNodeType,\n            nodeName: 'div'\n          });\n        });\n      });\n    });\n\n    it('throws if nodeType is a not a number', function () {\n      [true, 'one', '1', { foo: 'bar' }].forEach(function (throwingNodeType) {\n        assert.throws(function () {\n          // eslint-disable-next-line no-new\n          new SerialVirtualNode({\n            nodeType: throwingNodeType,\n            nodeName: 'div'\n          });\n        });\n      });\n    });\n\n    it('converts nodeNames to lower case', function () {\n      var htmlNodes = [\n        'DIV',\n        'SPAN',\n        'INPUT',\n        'HeAdEr',\n        'TABLE',\n        'TITLE',\n        'BUTTON',\n        'Foo'\n      ];\n      htmlNodes.forEach(function (nodeName) {\n        var vNode = new SerialVirtualNode({ nodeName: nodeName });\n        assert.equal(vNode.props.nodeName, nodeName.toLowerCase());\n      });\n    });\n\n    it('defaults to the correct nodeType for certain nodeNames', function () {\n      var vNode1 = new SerialVirtualNode({ nodeName: 'DIV' });\n      assert.equal(vNode1.props.nodeType, 1);\n      var vNode2 = new SerialVirtualNode({ nodeName: '#cdata-section' });\n      assert.equal(vNode2.props.nodeType, 2);\n      var vNode3 = new SerialVirtualNode({ nodeName: '#text' });\n      assert.equal(vNode3.props.nodeType, 3);\n      var vNode8 = new SerialVirtualNode({ nodeName: '#comment' });\n      assert.equal(vNode8.props.nodeType, 8);\n      var vNode9 = new SerialVirtualNode({ nodeName: '#document' });\n      assert.equal(vNode9.props.nodeType, 9);\n      var vNode11 = new SerialVirtualNode({ nodeName: '#document-fragment' });\n      assert.equal(vNode11.props.nodeType, 11);\n    });\n\n    it('defaults to the correct nodeName for certain nodeTypes', function () {\n      var vNode2 = new SerialVirtualNode({ nodeType: 2 });\n      assert.equal(vNode2.props.nodeName, '#cdata-section');\n      var vNode3 = new SerialVirtualNode({ nodeType: 3 });\n      assert.equal(vNode3.props.nodeName, '#text');\n      var vNode8 = new SerialVirtualNode({ nodeType: 8 });\n      assert.equal(vNode8.props.nodeName, '#comment');\n      var vNode9 = new SerialVirtualNode({ nodeType: 9 });\n      assert.equal(vNode9.props.nodeName, '#document');\n      var vNode11 = new SerialVirtualNode({ nodeType: 11 });\n      assert.equal(vNode11.props.nodeName, '#document-fragment');\n    });\n\n    it('throws if nodeName is not a string', function () {\n      [123, true, null, {}, undefined, []].forEach(function (notAString) {\n        assert.throws(function () {\n          // eslint-disable-next-line no-new\n          new SerialVirtualNode({ nodeName: notAString });\n        });\n      });\n    });\n\n    it('ignores the `attributes` property', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          foo: 'foo',\n          bar: 'bar',\n          baz: 'baz'\n        }\n      });\n      assert.isUndefined(vNode.props.attributes);\n    });\n\n    it('converts type prop to lower case', function () {\n      var types = ['text', 'COLOR', 'Month', 'uRL'];\n      types.forEach(function (type) {\n        var vNode = new SerialVirtualNode({\n          nodeName: 'input',\n          type: type\n        });\n        assert.equal(vNode.props.type, type.toLowerCase());\n      });\n    });\n\n    it('converts type attribute to lower case', function () {\n      var types = ['text', 'COLOR', 'Month', 'uRL'];\n      types.forEach(function (type) {\n        var vNode = new SerialVirtualNode({\n          nodeName: 'input',\n          attributes: {\n            type: type\n          }\n        });\n        assert.equal(vNode.props.type, type.toLowerCase());\n      });\n    });\n\n    it('defaults type prop to \"text\"', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'input'\n      });\n      assert.equal(vNode.props.type, 'text');\n    });\n\n    it('default type prop to \"text\" if type is invalid', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'input',\n        attributes: {\n          type: 'woohoo'\n        }\n      });\n      assert.equal(vNode.props.type, 'text');\n    });\n\n    it('uses the type property over the type attribute', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'input',\n        type: 'month',\n        attributes: {\n          type: 'color'\n        }\n      });\n      assert.equal(vNode.props.type, 'month');\n    });\n\n    it('reflects checkbox properties', () => {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'input',\n        type: 'checkbox',\n        checked: true,\n        indeterminate: true\n      });\n      assert.equal(vNode.props.checked, true);\n      assert.equal(vNode.props.indeterminate, true);\n    });\n  });\n\n  describe('attr', function () {\n    it('returns a string value for the attribute', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          foo: 'foo',\n          bar: 123,\n          baz: true,\n          qux: ''\n        }\n      });\n      assert.equal(vNode.attr('foo'), 'foo');\n      assert.equal(vNode.attr('bar'), '123');\n      assert.equal(vNode.attr('baz'), 'true');\n      assert.equal(vNode.attr('qux'), '');\n    });\n\n    it('returns null if the attribute is null', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { foo: null }\n      });\n      assert.isNull(vNode.attr('foo'));\n    });\n\n    it('returns null if the attribute is not set', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div'\n      });\n      assert.isNull(vNode.attr('foo'));\n    });\n\n    it('throws if the value is an object (for except null)', function () {\n      [{}, [], /foo/].forEach(function (someObject) {\n        assert.throws(function () {\n          // eslint-disable-next-line no-new\n          new SerialVirtualNode({\n            nodeName: 'div',\n            attributes: { foo: someObject }\n          });\n        });\n      });\n    });\n\n    it('converts `className` to `class`', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          className: 'foo bar baz'\n        }\n      });\n      assert.equal(vNode.attr('class'), 'foo bar baz');\n    });\n\n    it('converts `htmlFor` to `for`', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          htmlFor: 'foo'\n        }\n      });\n      assert.equal(vNode.attr('for'), 'foo');\n    });\n  });\n\n  describe('hasAttr', function () {\n    it('returns true if the attribute has a value', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {\n          foo: '',\n          bar: 0,\n          baz: false\n        }\n      });\n      assert.isTrue(vNode.hasAttr('foo'));\n      assert.isTrue(vNode.hasAttr('bar'));\n      assert.isTrue(vNode.hasAttr('baz'));\n    });\n\n    it('returns true if the attribute is null', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { foo: null }\n      });\n      assert.isTrue(vNode.hasAttr('foo'));\n    });\n\n    it('returns false if the attribute is undefined', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { foo: undefined }\n      });\n      assert.isFalse(vNode.hasAttr('foo'));\n      assert.isFalse(vNode.hasAttr('bar'));\n    });\n\n    it('converts `htmlFor` to `for`', function () {\n      var nodeWithoutFor = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {}\n      });\n      var nodeWithFor = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { htmlFor: 'foo' }\n      });\n\n      assert.isFalse(nodeWithoutFor.hasAttr('for'));\n      assert.isTrue(nodeWithFor.hasAttr('for'));\n    });\n\n    it('converts `className` to `class`', function () {\n      var nodeWithoutClass = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: {}\n      });\n      var nodeWithClass = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { className: 'foo bar baz' }\n      });\n\n      assert.isFalse(nodeWithoutClass.hasAttr('class'));\n      assert.isTrue(nodeWithClass.hasAttr('class'));\n    });\n  });\n\n  describe('attrNames', function () {\n    it('should return a list of attribute names', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div',\n        attributes: { foo: 'bar' }\n      });\n\n      assert.deepEqual(vNode.attrNames, ['foo']);\n    });\n\n    it('should return an empty array if there are no attributes', function () {\n      var vNode = new SerialVirtualNode({\n        nodeName: 'div'\n      });\n      assert.deepEqual(vNode.attrNames, []);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/base/virtual-node/virtual-node.js",
    "content": "describe('VirtualNode', () => {\n  const VirtualNode = axe.VirtualNode;\n  let node;\n\n  beforeEach(() => {\n    node = document.createElement('div');\n  });\n\n  it('should be a function', () => {\n    assert.isFunction(VirtualNode);\n  });\n\n  it('should accept three parameters', () => {\n    assert.lengthOf(VirtualNode, 3);\n  });\n\n  describe('prototype', () => {\n    it('should have public properties', () => {\n      const parent = {};\n      const vNode = new VirtualNode(node, parent, 'foo');\n\n      assert.equal(vNode.shadowId, 'foo');\n      assert.typeOf(vNode.children, 'array');\n      assert.equal(vNode.actualNode, node);\n      assert.equal(vNode.parent, parent);\n    });\n\n    it('should abstract Node properties', () => {\n      node = document.createElement('input');\n      node.id = 'monkeys';\n      const vNode = new VirtualNode(node);\n\n      assert.isDefined(vNode.props);\n      assert.equal(vNode.props.nodeType, 1);\n      assert.equal(vNode.props.nodeName, 'input');\n      assert.equal(vNode.props.id, 'monkeys');\n      assert.equal(vNode.props.type, 'text');\n    });\n\n    for (const [prop, tagName, examplePropValue] of [\n      ['value', 'input', 'test value'],\n      ['selected', 'option', true],\n      ['checked', 'input', true],\n      ['indeterminate', 'input', true],\n      ['multiple', 'select', true]\n    ]) {\n      describe(`props.${prop}`, () => {\n        it(`should reflect a ${tagName} element's ${prop} property`, () => {\n          node = document.createElement(tagName);\n          let vNode = new VirtualNode(node);\n          assert.equal(vNode.props[prop], '');\n\n          node[prop] = examplePropValue;\n          vNode = new VirtualNode(node);\n          assert.equal(vNode.props[prop], examplePropValue);\n        });\n\n        it('should be undefined for a text node', () => {\n          node = document.createTextNode('text content');\n          let vNode = new VirtualNode(node);\n          assert.equal(vNode.props[prop], undefined);\n        });\n\n        // Regression test for #4316\n        it(`should be resilient to text node with un-gettable ${prop} property`, () => {\n          node = document.createTextNode('text content');\n          Object.defineProperty(node, prop, {\n            get() {\n              throw new Error('Unqueryable value');\n            }\n          });\n          let vNode = new VirtualNode(node);\n          assert.throws(() => node[prop]);\n          assert.doesNotThrow(() => vNode.props[prop]);\n          assert.equal(vNode.props[prop], undefined);\n        });\n      });\n    }\n\n    it('should lowercase type', () => {\n      node = document.createElement('input');\n      node.setAttribute('type', 'COLOR');\n      const vNode = new VirtualNode(node);\n\n      assert.equal(vNode.props.type, 'color');\n    });\n\n    it('should default type to text', () => {\n      node = document.createElement('input');\n      const vNode = new VirtualNode(node);\n\n      assert.equal(vNode.props.type, 'text');\n    });\n\n    it('should default type to text if type is invalid', () => {\n      node = document.createElement('input');\n      node.setAttribute('type', 'woohoo');\n      const vNode = new VirtualNode(node);\n\n      assert.equal(vNode.props.type, 'text');\n    });\n\n    it('should lowercase nodeName', () => {\n      node = {\n        nodeName: 'FOOBAR'\n      };\n      const vNode = new VirtualNode(node);\n\n      assert.equal(vNode.props.nodeName, 'foobar');\n    });\n\n    describe('attr', () => {\n      it('should return the value of the given attribute', () => {\n        node.setAttribute('data-foo', 'bar');\n        const vNode = new VirtualNode(node);\n\n        assert.equal(vNode.attr('data-foo'), 'bar');\n      });\n\n      it('should return null for text nodes', () => {\n        node.textContent = 'hello';\n        const vNode = new VirtualNode(node.firstChild);\n\n        assert.isNull(vNode.attr('data-foo'));\n      });\n\n      it('should return null if getAttribute is not a function', () => {\n        node = {\n          nodeName: 'DIV',\n          getAttribute: null\n        };\n        const vNode = new VirtualNode(node);\n\n        assert.isNull(vNode.attr('data-foo'));\n      });\n    });\n\n    describe('hasAttr', () => {\n      it('should return true if the element has the attribute', () => {\n        node.setAttribute('foo', 'bar');\n        const vNode = new VirtualNode(node);\n\n        assert.isTrue(vNode.hasAttr('foo'));\n      });\n\n      it('should return false if the element does not have the attribute', () => {\n        const vNode = new VirtualNode(node);\n\n        assert.isFalse(vNode.hasAttr('foo'));\n      });\n\n      it('should return false for text nodes', () => {\n        node.textContent = 'hello';\n        const vNode = new VirtualNode(node.firstChild);\n\n        assert.isFalse(vNode.hasAttr('foo'));\n      });\n\n      it('should return false if hasAttribute is not a function', () => {\n        node = {\n          nodeName: 'DIV',\n          hasAttribute: null\n        };\n        const vNode = new VirtualNode(node);\n\n        assert.isFalse(vNode.hasAttr('foo'));\n      });\n    });\n\n    describe('attrNames', () => {\n      it('should return a list of attribute names', () => {\n        node.setAttribute('foo', 'bar');\n        const vNode = new VirtualNode(node);\n\n        assert.deepEqual(vNode.attrNames, ['foo']);\n      });\n\n      it('should work with clobbered attributes', () => {\n        node = document.createElement('form');\n        node.setAttribute('id', '123');\n        node.innerHTML = '<select name=\"attributes\"></select>';\n        const vNode = new VirtualNode(node);\n\n        assert.deepEqual(vNode.attrNames, ['id']);\n      });\n\n      it('should return an empty array if there are no attributes', () => {\n        const vNode = new VirtualNode(node);\n        assert.deepEqual(vNode.attrNames, []);\n      });\n    });\n\n    describe('nodeIndex', () => {\n      it('increments nodeIndex when a parent is passed', () => {\n        const vHtml = new VirtualNode({ nodeName: 'html' });\n        const vHead = new VirtualNode({ nodeName: 'head' }, vHtml);\n        const vTitle = new VirtualNode({ nodeName: 'title' }, vHead);\n        const vBody = new VirtualNode({ nodeName: 'body' }, vHtml);\n\n        assert.equal(vHtml.nodeIndex, 0);\n        assert.equal(vHead.nodeIndex, 1);\n        assert.equal(vTitle.nodeIndex, 2);\n        assert.equal(vBody.nodeIndex, 3);\n      });\n\n      it('resets nodeIndex when no parent is passed', () => {\n        let vHtml = new VirtualNode({ nodeName: 'html' });\n        let vHead = new VirtualNode({ nodeName: 'head' }, vHtml);\n        assert.equal(vHtml.nodeIndex, 0);\n        assert.equal(vHead.nodeIndex, 1);\n\n        vHtml = new VirtualNode({ nodeName: 'html' });\n        vHead = new VirtualNode({ nodeName: 'head' }, vHtml);\n        assert.equal(vHtml.nodeIndex, 0);\n        assert.equal(vHead.nodeIndex, 1);\n      });\n    });\n\n    describe('checkbox properties', () => {\n      it('should reflect the checked property', () => {\n        const div = document.createElement('div');\n        const vDiv = new VirtualNode(div);\n        assert.isUndefined(vDiv.props.checked);\n\n        node = document.createElement('input');\n        node.setAttribute('type', 'checkbox');\n        const vUnchecked = new VirtualNode(node);\n        assert.isFalse(vUnchecked.props.checked);\n\n        node.click();\n        const vChecked = new VirtualNode(node);\n        assert.equal(vChecked.props.checked, true);\n      });\n\n      it('reflects the indeterminate property', () => {\n        const div = document.createElement('div');\n        const vDiv = new VirtualNode(div);\n        assert.isUndefined(vDiv.props.indeterminate);\n\n        node = document.createElement('input');\n        node.setAttribute('type', 'checkbox');\n        const vUnchecked = new VirtualNode(node);\n        assert.isFalse(vUnchecked.props.indeterminate);\n\n        node.indeterminate = true;\n        const vIndeterminate = new VirtualNode(node);\n        assert.isTrue(vIndeterminate.props.indeterminate);\n      });\n    });\n\n    describe.skip('isFocusable', () => {\n      let commons;\n\n      beforeEach(() => {\n        commons = axe.commons = axe.commons;\n      });\n\n      afterEach(() => {\n        axe.commons = commons;\n      });\n\n      it('should call dom.isFocusable', () => {\n        let called = false;\n        axe.commons = {\n          dom: {\n            isFocusable: () => {\n              called = true;\n            }\n          }\n        };\n        const vNode = new VirtualNode(node);\n        vNode.isFocusable;\n\n        assert.isTrue(called);\n      });\n\n      it('should only call dom.isFocusable once', () => {\n        let count = 0;\n        axe.commons = {\n          dom: {\n            isFocusable: () => {\n              count++;\n            }\n          }\n        };\n        const vNode = new VirtualNode(node);\n        vNode.isFocusable;\n        vNode.isFocusable;\n        vNode.isFocusable;\n        assert.equal(count, 1);\n      });\n    });\n\n    describe.skip('tabbableElements', () => {\n      let commons;\n\n      beforeEach(() => {\n        commons = axe.commons = axe.commons;\n      });\n\n      afterEach(() => {\n        axe.commons = commons;\n      });\n\n      it('should call dom.getTabbableElements', () => {\n        let called = false;\n        axe.commons = {\n          dom: {\n            getTabbableElements: () => {\n              called = true;\n            }\n          }\n        };\n        const vNode = new VirtualNode(node);\n        vNode.tabbableElements;\n\n        assert.isTrue(called);\n      });\n\n      it('should only call dom.getTabbableElements once', () => {\n        let count = 0;\n        axe.commons = {\n          dom: {\n            getTabbableElements: () => {\n              count++;\n            }\n          }\n        };\n        const vNode = new VirtualNode(node);\n        vNode.tabbableElements;\n        vNode.tabbableElements;\n        vNode.tabbableElements;\n        assert.equal(count, 1);\n      });\n    });\n\n    describe('getComputedStylePropertyValue', () => {\n      let computedStyle;\n\n      beforeEach(() => {\n        computedStyle = window.getComputedStyle;\n      });\n\n      afterEach(() => {\n        window.getComputedStyle = computedStyle;\n      });\n\n      it('should call window.getComputedStyle and return the property', () => {\n        let called = false;\n        window.getComputedStyle = () => {\n          called = true;\n          return {\n            getPropertyValue: () => {\n              return 'result';\n            }\n          };\n        };\n        const vNode = new VirtualNode(node);\n        const result = vNode.getComputedStylePropertyValue('prop');\n\n        assert.isTrue(called);\n        assert.equal(result, 'result');\n      });\n\n      it('should only call window.getComputedStyle and getPropertyValue once', () => {\n        let computedCount = 0;\n        let propertyCount = 0;\n        window.getComputedStyle = () => {\n          computedCount++;\n          return {\n            getPropertyValue: () => {\n              propertyCount++;\n            }\n          };\n        };\n        const vNode = new VirtualNode(node);\n        vNode.getComputedStylePropertyValue('prop');\n        vNode.getComputedStylePropertyValue('prop');\n        vNode.getComputedStylePropertyValue('prop');\n        assert.equal(computedCount, 1);\n        assert.equal(propertyCount, 1);\n      });\n    });\n\n    describe('clientRects', () => {\n      it('should call node.getClientRects', () => {\n        let called = false;\n        node.getClientRects = () => {\n          called = true;\n          return [];\n        };\n        const vNode = new VirtualNode(node);\n        vNode.clientRects;\n\n        assert.isTrue(called);\n      });\n\n      it('should only call node.getClientRects once', () => {\n        let count = 0;\n        node.getClientRects = () => {\n          count++;\n          return [];\n        };\n        const vNode = new VirtualNode(node);\n        vNode.clientRects;\n        vNode.clientRects;\n        vNode.clientRects;\n        assert.equal(count, 1);\n      });\n\n      it('should filter out 0 width rects', () => {\n        node.getClientRects = () => {\n          return [{ width: 10 }, { width: 0 }, { width: 20 }];\n        };\n        const vNode = new VirtualNode(node);\n\n        assert.deepEqual(vNode.clientRects, [{ width: 10 }, { width: 20 }]);\n      });\n    });\n\n    describe('boundingClientRect', () => {\n      it('should call node.getBoundingClientRect', () => {\n        let called = false;\n        node.getBoundingClientRect = () => {\n          called = true;\n        };\n        const vNode = new VirtualNode(node);\n        vNode.boundingClientRect;\n\n        assert.isTrue(called);\n      });\n\n      it('should only call node.getBoundingClientRect once', () => {\n        let count = 0;\n        node.getBoundingClientRect = () => {\n          count++;\n        };\n        const vNode = new VirtualNode(node);\n        vNode.boundingClientRect;\n        vNode.boundingClientRect;\n        vNode.boundingClientRect;\n        assert.equal(count, 1);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/constants.js",
    "content": "describe('axe.constants', function () {\n  'use strict';\n\n  it('should create an object', function () {\n    assert.isObject(axe.constants);\n  });\n\n  it('should have a results array', function () {\n    assert.isArray(axe.constants.results);\n  });\n\n  it('should have PASS', function () {\n    assert.equal(axe.constants.PASS, 'passed');\n  });\n\n  it('should have FAIL', function () {\n    assert.equal(axe.constants.FAIL, 'failed');\n  });\n\n  it('should have NA', function () {\n    assert.equal(axe.constants.NA, 'inapplicable');\n  });\n\n  it('should have CANTTELL', function () {\n    assert.equal(axe.constants.CANTTELL, 'cantTell');\n  });\n\n  it('should have priorities for results', function () {\n    assert.equal(axe.constants.NA_PRIO, 0);\n  });\n\n  it('should have groups for results', function () {\n    assert.equal(axe.constants.FAIL_GROUP, 'violations');\n  });\n\n  it('should have a gridSize', function () {\n    assert.equal(axe.constants.gridSize, 200);\n  });\n\n  it('should have a selectorSimilarFilterLimit', function () {\n    assert.equal(axe.constants.selectorSimilarFilterLimit, 700);\n  });\n\n  it('has a serializableErrorProps array', function () {\n    assert.isArray(axe.constants.serializableErrorProps);\n    axe.constants.serializableErrorProps.forEach(prop => {\n      assert.typeOf(prop, 'string', `prop ${prop} is not a string`);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/export.js",
    "content": "describe('export', function () {\n  'use strict';\n\n  it('should publish a global `axe` variable', function () {\n    assert.isDefined(window.axe);\n  });\n  it('should define version', function () {\n    assert.isNotNull(axe.version);\n  });\n});\n"
  },
  {
    "path": "test/core/index.js",
    "content": "describe('index', function () {\n  'use strict';\n\n  it('should redefine `define`', function () {\n    assert.equal(typeof define, 'undefined');\n  });\n  it('should redefine `require`', function () {\n    assert.equal(typeof require, 'undefined');\n  });\n});\n"
  },
  {
    "path": "test/core/log.js",
    "content": "describe('axe.log', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.log);\n  });\n  it('should invoke console.log', function () {\n    var orig = window.console;\n    if (!window.console || window.console.log) {\n      window.console = { log: function () {} };\n    }\n    var expected = ['hi', 'hello'];\n    var success = false;\n\n    window.console.log = function () {\n      success = true;\n      assert.equal(arguments[0], expected[0]);\n      assert.equal(arguments[1], expected[1]);\n    };\n\n    axe.log.apply(axe.log, expected);\n    assert.isTrue(success);\n\n    window.console = orig;\n  });\n});\n"
  },
  {
    "path": "test/core/public/cleanup.js",
    "content": "/*global cleanup */\ndescribe('cleanup', function () {\n  'use strict';\n\n  function createFrames(callback) {\n    var frame;\n    frame = document.createElement('iframe');\n    frame.src = '../mock/frames/test.html';\n    frame.addEventListener('load', function () {\n      setTimeout(callback, 500);\n    });\n    fixture.appendChild(frame);\n  }\n\n  var fixture = document.getElementById('fixture');\n\n  var assertNotCalled = function () {\n    assert.ok(false, 'Should not be called');\n  };\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe.plugins = {};\n  });\n\n  beforeEach(function () {\n    axe._audit = null;\n  });\n\n  it('should throw if no audit is configured', function () {\n    assert.throws(\n      function () {\n        axe.cleanup(document, {});\n      },\n      Error,\n      /^No audit configured/\n    );\n  });\n\n  it('should call cleanup on all plugins', function (done) {\n    /*eslint no-unused-vars: 0*/\n    var cleaned = false;\n    axe._load({\n      rules: []\n    });\n    axe.registerPlugin({\n      id: 'p',\n      run: function () {},\n      add: function (impl) {\n        this._registry[impl.id] = impl;\n      },\n      commands: []\n    });\n    axe.plugins.p.cleanup = function (res) {\n      cleaned = true;\n      res();\n    };\n    axe.cleanup(function () {\n      assert.equal(cleaned, true);\n      done();\n    }, assertNotCalled);\n  });\n\n  it('should not throw exception if no arguments are provided', function (done) {\n    var cleaned = false;\n    axe._load({\n      rules: []\n    });\n    axe.registerPlugin({\n      id: 'p',\n      run: function () {},\n      add: function (impl) {\n        this._registry[impl.id] = impl;\n      },\n      commands: []\n    });\n    axe.plugins.p.cleanup = function (res) {\n      cleaned = true;\n      res();\n    };\n    assert.doesNotThrow(function () {\n      axe.cleanup();\n      done();\n    });\n  });\n\n  it('should send command to frames to cleanup', function (done) {\n    createFrames(function () {\n      axe._load({});\n\n      var frame = fixture.querySelector('iframe');\n      var win = frame.contentWindow;\n      win.addEventListener('message', function (message) {\n        var data = JSON.parse(message.data);\n        if (data.topic === 'axe.start') {\n          assert.deepEqual(data.payload, { command: 'cleanup-plugin' });\n          done();\n        }\n      });\n\n      axe.cleanup();\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/configure.js",
    "content": "describe('axe.configure', function () {\n  'use strict';\n  // var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;\n  // var Check = axe._thisWillBeDeletedDoNotUse.base.Check;\n  var fixture = document.getElementById('fixture');\n  var axeVersion = axe.version;\n  var ver = axe.version.substring(0, axe.version.lastIndexOf('.'));\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe.version = axeVersion;\n  });\n\n  beforeEach(function () {\n    axe._audit = null;\n  });\n\n  it('should throw if audit is not configured', function () {\n    assert.throws(\n      function () {\n        axe.configure({});\n      },\n      Error,\n      /^No audit configured/\n    );\n  });\n\n  it(\"should override an audit's reporter - string\", function () {\n    axe._load({});\n    assert.isNull(axe._audit.reporter);\n\n    axe.configure({ reporter: 'v1' });\n    assert.equal(axe._audit.reporter, 'v1');\n  });\n\n  it('should not allow setting to an un-registered reporter', function () {\n    axe._load({ reporter: 'v1' });\n    axe.configure({ reporter: 'no-exist-evar-plz' });\n    assert.equal(axe._audit.reporter, 'v1');\n  });\n\n  it('should allow for addition of rules', function () {\n    axe._load({});\n    axe.configure({\n      rules: [\n        {\n          id: 'bob',\n          metadata: {\n            joe: 'joe'\n          }\n        }\n      ]\n    });\n\n    assert.lengthOf(axe._audit.rules, 1);\n    // TODO: this does not work yet thanks to webpack\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    assert.equal(axe._audit.rules[0].id, 'bob');\n    assert.deepEqual(axe._audit.data.rules.bob.joe, 'joe');\n  });\n\n  it('should throw error if rules property is invalid', function () {\n    assert.throws(function () {\n      (axe.configure({ rules: 'hello' }),\n        TypeError,\n        /^Rules property must be an array/);\n    });\n  });\n\n  it('should throw error if rule is invalid', function () {\n    assert.throws(function () {\n      (axe.configure({ rules: ['hello'] }),\n        TypeError,\n        /Configured rule \"hello\" is invalid/);\n    });\n  });\n\n  it('should throw error if rule does not have an id', function () {\n    assert.throws(function () {\n      (axe.configure({ rules: [{ foo: 'bar' }] }),\n        TypeError,\n        /Configured rule \"{foo:\\\"bar\\\"}\" is invalid/);\n    });\n  });\n\n  it('should call setBranding when passed options', function () {\n    axe._load({});\n    axe.configure({\n      rules: [\n        {\n          id: 'bob',\n          selector: 'pass'\n        }\n      ],\n      branding: {}\n    });\n    assert.lengthOf(axe._audit.rules, 1);\n    assert.equal(\n      axe._audit.data.rules.bob.helpUrl,\n      'https://dequeuniversity.com/rules/axe/' + ver + '/bob?application=axeAPI'\n    );\n    axe.configure({\n      branding: {\n        application: 'thing',\n        brand: 'thung'\n      }\n    });\n    assert.equal(\n      axe._audit.data.rules.bob.helpUrl,\n      'https://dequeuniversity.com/rules/thung/' +\n        ver +\n        '/bob?application=thing'\n    );\n  });\n\n  it('sets branding on newly configured rules', function () {\n    axe._load({});\n    axe.configure({\n      branding: {\n        application: 'thing',\n        brand: 'thung'\n      }\n    });\n    axe.configure({\n      rules: [\n        {\n          id: 'bob',\n          selector: 'pass'\n        }\n      ]\n    });\n\n    assert.equal(\n      axe._audit.data.rules.bob.helpUrl,\n      'https://dequeuniversity.com/rules/thung/' +\n        ver +\n        '/bob?application=thing'\n    );\n  });\n\n  it('should allow for overwriting of rules', function () {\n    axe._load({\n      data: {\n        rules: {\n          bob: 'not-joe'\n        }\n      },\n      rules: {\n        id: 'bob',\n        selector: 'fail'\n      }\n    });\n    axe.configure({\n      rules: [\n        {\n          id: 'bob',\n          selector: 'pass',\n          metadata: {\n            joe: 'joe'\n          }\n        }\n      ]\n    });\n\n    assert.lengthOf(axe._audit.rules, 1);\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    assert.equal(axe._audit.rules[0].id, 'bob');\n    assert.equal(axe._audit.rules[0].selector, 'pass');\n    assert.equal(axe._audit.data.rules.bob.joe, 'joe');\n  });\n\n  it('should allow for the addition of checks', function () {\n    axe._load({});\n    axe.configure({\n      checks: [\n        {\n          id: 'bob',\n          options: { value: true },\n          metadata: {\n            joe: 'joe'\n          }\n        }\n      ]\n    });\n\n    // assert.instanceOf(axe._audit.checks.bob, Check);\n    assert.equal(axe._audit.checks.bob.id, 'bob');\n    assert.isTrue(axe._audit.checks.bob.options.value);\n    assert.equal(axe._audit.data.checks.bob.joe, 'joe');\n  });\n\n  it('should throw error if checks property is invalid', function () {\n    assert.throws(function () {\n      (axe.configure({ checks: 'hello' }),\n        TypeError,\n        /^Checks property must be an array/);\n    });\n  });\n\n  it('should throw error if check is invalid', function () {\n    assert.throws(function () {\n      (axe.configure({ checks: ['hello'] }),\n        TypeError,\n        /Configured check \"hello\" is invalid/);\n    });\n  });\n\n  it('should throw error if check does not have an id', function () {\n    assert.throws(function () {\n      (axe.configure({ checks: [{ foo: 'bar' }] }),\n        TypeError,\n        /Configured check \"{foo:\\\"bar\\\"}\" is invalid/);\n    });\n  });\n\n  it('should allow for the overwriting of checks', function () {\n    axe._load({\n      data: {\n        checks: {\n          bob: 'not-joe'\n        }\n      },\n      checks: [\n        {\n          id: 'bob',\n          options: { value: false }\n        }\n      ]\n    });\n    axe.configure({\n      checks: [\n        {\n          id: 'bob',\n          options: { value: true },\n          metadata: {\n            joe: 'joe'\n          }\n        }\n      ]\n    });\n\n    // assert.instanceOf(axe._audit.checks.bob, Check);\n    assert.equal(axe._audit.checks.bob.id, 'bob');\n    assert.isTrue(axe._audit.checks.bob.options.value);\n    assert.equal(axe._audit.data.checks.bob.joe, 'joe');\n  });\n\n  it('should create an execution context for check messages', function () {\n    axe._load({});\n    axe.configure({\n      checks: [\n        {\n          id: 'bob',\n          metadata: {\n            messages: {\n              pass: \"function () { return 'Bob' + ' John';}\",\n              fail: 'Bob Pete'\n            }\n          }\n        }\n      ]\n    });\n\n    assert.isFunction(axe._audit.data.checks.bob.messages.pass);\n    assert.isString(axe._audit.data.checks.bob.messages.fail);\n    assert.equal(axe._audit.data.checks.bob.messages.pass(), 'Bob John');\n    assert.equal(axe._audit.data.checks.bob.messages.fail, 'Bob Pete');\n  });\n\n  it('overrides the default value of audit.tagExclude', function () {\n    axe._load({});\n    assert.deepEqual(axe._audit.tagExclude, ['experimental', 'deprecated']);\n\n    axe.configure({\n      tagExclude: ['ninjas']\n    });\n    assert.deepEqual(axe._audit.tagExclude, ['ninjas']);\n  });\n\n  it('disables all untouched rules with disableOtherRules', function () {\n    axe._load({\n      rules: [{ id: 'captain-america' }, { id: 'thor' }, { id: 'spider-man' }]\n    });\n    axe.configure({\n      disableOtherRules: true,\n      rules: [{ id: 'captain-america' }, { id: 'black-panther' }]\n    });\n\n    assert.lengthOf(axe._audit.rules, 4);\n    assert.equal(axe._audit.rules[0].id, 'captain-america');\n    assert.equal(axe._audit.rules[0].enabled, true);\n    assert.equal(axe._audit.rules[1].id, 'thor');\n    assert.equal(axe._audit.rules[1].enabled, false);\n    assert.equal(axe._audit.rules[2].id, 'spider-man');\n    assert.equal(axe._audit.rules[2].enabled, false);\n    assert.equal(axe._audit.rules[3].id, 'black-panther');\n    assert.equal(axe._audit.rules[3].enabled, true);\n  });\n\n  it(\"should allow overriding an audit's noHtml\", function () {\n    axe._load({});\n    assert.isFalse(axe._audit.noHtml);\n\n    axe.configure({ noHtml: true });\n    assert.isTrue(axe._audit.noHtml);\n  });\n\n  it(\"should allow overriding an audit's allowedOrigins\", function () {\n    axe._load({});\n    assert.notDeepEqual(axe._audit.allowedOrigins, ['foo']);\n\n    axe.configure({ allowedOrigins: ['foo'] });\n    assert.deepEqual(axe._audit.allowedOrigins, ['foo']);\n  });\n\n  it('should throw error if allowedOrigins is not an array', function () {\n    axe._load({});\n    assert.throws(function () {\n      axe.configure({ allowedOrigins: 'foo' });\n    });\n  });\n\n  it(\"should throw error if the origin is '*'\", function () {\n    axe._load({});\n    assert.throws(function () {\n      axe.configure({ allowedOrigins: ['foo', '*'] });\n    });\n  });\n\n  describe('given a locale object', function () {\n    beforeEach(function () {\n      axe._load({});\n\n      axe.configure({\n        rules: [\n          {\n            id: 'greeting',\n            selector: 'div',\n            excludeHidden: false,\n            tags: ['foo', 'bar'],\n            metadata: {\n              description: 'This is a rule that rules',\n              help: 'ABCDEFGHIKLMNOPQRSTVXYZ'\n            }\n          }\n        ],\n        checks: [\n          {\n            id: 'banana',\n            evaluate: function () {},\n            metadata: {\n              impact: 'srsly serious',\n              messages: {\n                pass: 'yay',\n                fail: 'boo',\n                incomplete: {\n                  foo: 'a',\n                  bar: 'b',\n                  baz: 'c'\n                }\n              }\n            }\n          }\n        ]\n      });\n    });\n\n    it('should update check and rule metadata', function () {\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          rules: {\n            greeting: {\n              description: 'hello',\n              help: 'hi'\n            }\n          },\n          checks: {\n            banana: {\n              pass: 'pizza',\n              fail: 'icecream',\n              incomplete: {\n                foo: 'meat',\n                bar: 'fruit',\n                baz: 'vegetables'\n              }\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.equal(localeData.rules.greeting.help, 'hi');\n      assert.equal(localeData.rules.greeting.description, 'hello');\n      assert.equal(localeData.checks.banana.messages.pass, 'pizza');\n      assert.equal(localeData.checks.banana.messages.fail, 'icecream');\n      assert.deepEqual(localeData.checks.banana.messages.incomplete, {\n        foo: 'meat',\n        bar: 'fruit',\n        baz: 'vegetables'\n      });\n    });\n\n    it('should merge locales (favoring \"new\")', function () {\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          rules: { greeting: { description: 'hello' } },\n          checks: {\n            banana: {\n              fail: 'icecream'\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.equal(localeData.rules.greeting.help, 'ABCDEFGHIKLMNOPQRSTVXYZ');\n      assert.equal(localeData.rules.greeting.description, 'hello');\n      assert.equal(localeData.checks.banana.messages.pass, 'yay');\n      assert.equal(localeData.checks.banana.messages.fail, 'icecream');\n      assert.deepEqual(localeData.checks.banana.messages.incomplete, {\n        foo: 'a',\n        bar: 'b',\n        baz: 'c'\n      });\n    });\n\n    it('sets the lang property', function () {\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          rules: { greeting: { description: 'hello' } },\n          checks: {\n            banana: {\n              fail: 'icecream'\n            }\n          }\n        }\n      });\n\n      assert.equal(axe._audit.lang, 'lol');\n    });\n\n    it('should call doT.compile if a messages uses doT syntax', function () {\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          rules: { greeting: { description: 'hello' } },\n          checks: {\n            banana: {\n              fail: 'icecream {{=it.data.value}}'\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.isTrue(\n        typeof localeData.checks.banana.messages.fail === 'function'\n      );\n    });\n\n    it('should leave the messages as a string if it does not use doT syntax', function () {\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          rules: { greeting: { description: 'hello' } },\n          checks: {\n            banana: {\n              fail: 'icecream ${data.value}'\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.isTrue(typeof localeData.checks.banana.messages.fail === 'string');\n    });\n\n    it('should update failure messages', function () {\n      axe._load({\n        data: {\n          failureSummaries: {\n            any: {\n              failureMessage: function () {\n                return 'failed any';\n              }\n            },\n            none: {\n              failureMessage: function () {\n                return 'failed none';\n              }\n            }\n          },\n          incompleteFallbackMessage: function () {\n            return 'failed incomplete';\n          }\n        }\n      });\n\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          failureSummaries: {\n            any: {\n              failureMessage: 'foo'\n            },\n            none: {\n              failureMessage: 'bar'\n            }\n          },\n          incompleteFallbackMessage: 'baz'\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');\n      assert.equal(localeData.failureSummaries.none.failureMessage, 'bar');\n      assert.equal(localeData.incompleteFallbackMessage, 'baz');\n    });\n\n    it('should merge failure messages', function () {\n      axe._load({\n        data: {\n          failureSummaries: {\n            any: {\n              failureMessage: function () {\n                return 'failed any';\n              }\n            },\n            none: {\n              failureMessage: function () {\n                return 'failed none';\n              }\n            }\n          },\n          incompleteFallbackMessage: function () {\n            return 'failed incomplete';\n          }\n        }\n      });\n\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          failureSummaries: {\n            any: {\n              failureMessage: 'foo'\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.equal(localeData.failureSummaries.any.failureMessage, 'foo');\n      assert.equal(\n        localeData.failureSummaries.none.failureMessage(),\n        'failed none'\n      );\n      assert.equal(localeData.incompleteFallbackMessage(), 'failed incomplete');\n    });\n\n    it('should not strip newline characters from doT template', function () {\n      axe._load({\n        data: {\n          failureSummaries: {\n            any: {\n              failureMessage: function () {\n                return 'failed any';\n              }\n            }\n          }\n        }\n      });\n\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          failureSummaries: {\n            any: {\n              failureMessage:\n                \"Fix any of the following:{{~it:value}}\\n  {{=value.split('\\\\n').join('\\\\n  ')}}{{~}}\"\n            }\n          }\n        }\n      });\n\n      var audit = axe._audit;\n      var localeData = audit.data;\n\n      assert.equal(\n        localeData.failureSummaries.any.failureMessage(['1', '2', '3']),\n        'Fix any of the following:\\n  1\\n  2\\n  3'\n      );\n    });\n\n    describe('only given checks', function () {\n      it('should not error', function () {\n        assert.doesNotThrow(function () {\n          axe.configure({\n            locale: {\n              lang: 'lol',\n              checks: {\n                banana: {\n                  fail: 'icecream',\n                  incomplete: {\n                    baz: 'vegetables'\n                  }\n                }\n              }\n            }\n          });\n        });\n      });\n    });\n\n    describe('only given rules', function () {\n      it('should not error', function () {\n        assert.doesNotThrow(function () {\n          axe.configure({\n            locale: {\n              rules: { greeting: { help: 'foo', description: 'bar' } }\n            }\n          });\n        });\n      });\n    });\n\n    describe('check incomplete messages', function () {\n      beforeEach(function () {\n        axe.configure({\n          checks: [\n            {\n              id: 'panda',\n              evaluate: function () {},\n              metadata: {\n                impact: 'yep',\n                messages: {\n                  pass: 'p',\n                  fail: 'f',\n                  incomplete: 'i'\n                }\n              }\n            }\n          ]\n        });\n      });\n\n      it('should support strings', function () {\n        axe.configure({\n          locale: {\n            checks: {\n              panda: {\n                incomplete: 'radio'\n              }\n            }\n          }\n        });\n\n        assert.equal(axe._audit.data.checks.panda.messages.incomplete, 'radio');\n      });\n\n      it('should shallow-merge objects', function () {\n        axe.configure({\n          locale: {\n            lang: 'lol',\n            checks: {\n              banana: {\n                incomplete: {\n                  baz: 'vegetables'\n                }\n              }\n            }\n          }\n        });\n\n        assert.deepEqual(axe._audit.data.checks.banana.messages.incomplete, {\n          foo: 'a',\n          bar: 'b',\n          baz: 'vegetables'\n        });\n      });\n    });\n\n    // This test ensures we do not drop additional properties added to\n    // checks. See https://github.com/dequelabs/axe-core/pull/1036/files#r207738673\n    // for reasoning.\n    it('should keep existing properties on check data', function () {\n      axe.configure({\n        checks: [\n          {\n            id: 'banana',\n            metadata: {\n              impact: 'potato',\n              foo: 'bar',\n              messages: {\n                pass: 'pass',\n                fail: 'fail',\n                incomplete: 'incomplete'\n              }\n            }\n          }\n        ]\n      });\n\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          checks: {\n            banana: {\n              pass: 'yay banana'\n            }\n          }\n        }\n      });\n\n      var banana = axe._audit.data.checks.banana;\n      assert.equal(banana.impact, 'potato');\n      assert.equal(banana.foo, 'bar');\n      assert.equal(banana.messages.pass, 'yay banana');\n    });\n\n    it('should error when provided an unknown rule id', function () {\n      assert.throws(function () {\n        axe.configure({\n          locale: {\n            rules: { nope: { help: 'helpme' } }\n          }\n        });\n      }, /unknown rule: \"nope\"/);\n    });\n\n    it('should error when provided an unknown check id', function () {\n      assert.throws(function () {\n        axe.configure({\n          locale: {\n            checks: { nope: { pass: 'helpme' } }\n          }\n        });\n      }, /unknown check: \"nope\"/);\n    });\n\n    it('should error when provided an unknown failure summary', function () {\n      assert.throws(function () {\n        axe.configure({\n          locale: {\n            failureSummaries: {\n              nope: { failureMessage: 'helpme' }\n            }\n          }\n        });\n      });\n    });\n\n    it('should set default locale', function () {\n      assert.isNull(axe._audit._defaultLocale);\n      axe.configure({\n        locale: {\n          lang: 'lol',\n          checks: {\n            banana: {\n              pass: 'yay banana'\n            }\n          }\n        }\n      });\n      assert.ok(axe._audit._defaultLocale);\n    });\n\n    describe('also given metadata', function () {\n      it('should favor the locale', function () {\n        axe.configure({\n          locale: {\n            lang: 'lol',\n            rules: {\n              greeting: {\n                help: 'hi'\n              }\n            }\n          },\n          rules: [\n            {\n              id: 'greeting',\n              metadata: {\n                help: 'potato'\n              }\n            }\n          ]\n        });\n\n        var audit = axe._audit;\n        var localeData = audit.data;\n\n        assert.equal(localeData.rules.greeting.help, 'hi');\n      });\n    });\n\n    describe('after locale has been set', function () {\n      describe('the provided messages', function () {\n        it('should allow for doT templating', function () {\n          axe.configure({\n            locale: {\n              lang: 'foo',\n              rules: {\n                greeting: {\n                  help: 'foo: {{=it.data}}.'\n                }\n              }\n            }\n          });\n\n          var greeting = axe._audit.data.rules.greeting;\n          var value = greeting.help({\n            data: 'bar'\n          });\n          assert.equal(value, 'foo: bar.');\n        });\n      });\n    });\n  });\n\n  describe('given an axeVersion property', function () {\n    beforeEach(function () {\n      axe._load({});\n      axe.version = '1.2.3';\n    });\n\n    it('should not throw if version matches axe.version', function () {\n      assert.doesNotThrow(function fn() {\n        axe.configure({\n          axeVersion: '1.2.3'\n        });\n\n        axe.version = '1.2.3-canary.2664bae';\n        axe.configure({\n          axeVersion: '1.2.3-canary.2664bae'\n        });\n      });\n    });\n\n    it('should not throw if patch version is less than axe.version', function () {\n      assert.doesNotThrow(function fn() {\n        axe.configure({\n          axeVersion: '1.2.0'\n        });\n      });\n    });\n\n    it('should not throw if minor version is less than axe.version', function () {\n      assert.doesNotThrow(function fn() {\n        axe.configure({\n          axeVersion: '1.1.9'\n        });\n      });\n    });\n\n    it('should not throw if versions match and axe has a canary version', function () {\n      axe.version = '1.2.3-canary.2664bae';\n      assert.doesNotThrow(function fn() {\n        axe.configure({\n          axeVersion: '1.2.3'\n        });\n      });\n    });\n\n    it('should throw if invalid version', function () {\n      assert.throws(function fn() {\n        axe.configure({\n          axeVersion: '2'\n        });\n      }, 'Invalid configured version 2');\n\n      assert.throws(function fn() {\n        axe.configure({\n          axeVersion: '2..'\n        });\n      }, 'Invalid configured version 2..');\n    });\n\n    it('should throw if major version is different than axe.version', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '2.0.0'\n          },\n          /^Configured version/\n        );\n      });\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '0.1.2'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should throw if minor version is greater than axe.version', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '1.3.0'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should throw if patch version is greater than axe.version', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '1.2.9'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should throw if versions match and axeVersion has a canary version', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '1.2.3-canary.2664bae'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should throw if versions match and both have a canary version', function () {\n      axe.version = '1.2.3-canary.2664bae';\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            axeVersion: '1.2.3-canary.a5d727c'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should accept ver property as fallback', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            ver: '1.3.0'\n          },\n          /^Configured version/\n        );\n      });\n    });\n\n    it('should accept axeVersion over ver property', function () {\n      assert.throws(function fn() {\n        axe.configure(\n          {\n            ver: '0.1.2',\n            axeVersion: '1.3.0'\n          },\n          /^Configured version 1\\.3\\.0/\n        );\n      });\n    });\n  });\n\n  describe('given a standards object', function () {\n    beforeEach(function () {\n      axe._load({});\n    });\n\n    describe('ariaAttrs', function () {\n      it('should allow creating new attr', function () {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'string'\n              }\n            }\n          }\n        });\n\n        var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;\n        assert.equal(ariaAttr.type, 'string');\n      });\n\n      it('should override existing attr', function () {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'string'\n              }\n            }\n          }\n        });\n\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'mntoken',\n                values: ['foo', 'bar']\n              }\n            }\n          }\n        });\n\n        var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;\n        assert.equal(ariaAttr.type, 'mntoken');\n        assert.deepEqual(ariaAttr.values, ['foo', 'bar']);\n      });\n\n      it('should merge existing attr', function () {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'mntoken',\n                values: ['foo', 'bar']\n              }\n            }\n          }\n        });\n\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'mntokens'\n              }\n            }\n          }\n        });\n\n        var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;\n        assert.equal(ariaAttr.type, 'mntokens');\n        assert.deepEqual(ariaAttr.values, ['foo', 'bar']);\n      });\n\n      it('should override and not merge array', function () {\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                type: 'mntoken',\n                values: ['foo', 'bar']\n              }\n            }\n          }\n        });\n\n        axe.configure({\n          standards: {\n            ariaAttrs: {\n              newAttr: {\n                values: ['baz']\n              }\n            }\n          }\n        });\n\n        var ariaAttr = axe._audit.standards.ariaAttrs.newAttr;\n        assert.deepEqual(ariaAttr.values, ['baz']);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/finish-run.js",
    "content": "describe('axe.finishRun', function () {\n  var fixture = document.querySelector('#fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('takes a single partial results and outputs a finished report', function (done) {\n    axe\n      .runPartial()\n      .then(function (result) {\n        return axe.finishRun([result]);\n      })\n      .then(function (results) {\n        assert.property(results, 'violations');\n        assert.property(results, 'passes');\n        assert.property(results, 'incomplete');\n        assert.property(results, 'inapplicable');\n        done();\n      })\n      .catch(done);\n  });\n\n  it('does not mutate the options object', function (done) {\n    var options = {};\n    axe\n      .runPartial(options)\n      .then(function (result) {\n        return axe.finishRun([result], options);\n      })\n      .then(function () {\n        assert.deepEqual(options, {});\n        done();\n      })\n      .catch(done);\n  });\n\n  it('uses option.reporter to create the report', function (done) {\n    axe\n      .runPartial()\n      .then(function (partialResult) {\n        return axe.finishRun([partialResult], { reporter: 'raw' });\n      })\n      .then(function (rawResults) {\n        assert.notEqual(rawResults.length, 0);\n        rawResults.forEach(function (rawResult) {\n          assert.property(rawResult, 'violations');\n          assert.property(rawResult, 'passes');\n          assert.property(rawResult, 'incomplete');\n          assert.property(rawResult, 'inapplicable');\n        });\n        done();\n      })\n      .catch(done);\n  });\n\n  it('defaults options.reporter to v1', function (done) {\n    axe\n      .runPartial()\n      .then(function (partialResult) {\n        return axe.finishRun([partialResult]);\n      })\n      .then(function (results) {\n        assert.equal(results.toolOptions.reporter, 'v1');\n        done();\n      })\n      .catch(done);\n  });\n\n  it('normalizes the runOnly option in the reporter', function (done) {\n    axe\n      .runPartial()\n      .then(function (partialResult) {\n        return axe.finishRun([partialResult], { runOnly: 'region' });\n      })\n      .then(function (results) {\n        assert.deepEqual(results.toolOptions.runOnly, {\n          type: 'rule',\n          values: ['region']\n        });\n        done();\n      })\n      .catch(done);\n  });\n\n  it('takes partialResult.environmentData to the reporter', function (done) {\n    var testEngine = {\n      name: 'dummy-engine',\n      version: '1.2.3.4.5'\n    };\n    axe\n      .runPartial()\n      .then(function (partialResult) {\n        partialResult.environmentData = { testEngine: testEngine };\n        return axe.finishRun([partialResult], { runOnly: 'region' });\n      })\n      .then(function (results) {\n        assert.deepEqual(results.testEngine, testEngine);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('can report violations results', function (done) {\n    fixture.innerHTML = '<div aria-label=\"foo\"></div>';\n    axe\n      .runPartial(\n        { include: [['#fixture']] },\n        { runOnly: 'aria-prohibited-attr' }\n      )\n      .then(function (result) {\n        return axe.finishRun([result]);\n      })\n      .then(function (results) {\n        assert.lengthOf(results.violations, 1);\n        assert.lengthOf(results.passes, 0);\n        assert.lengthOf(results.incomplete, 0);\n        assert.lengthOf(results.inapplicable, 0);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('can report passes results', function (done) {\n    fixture.innerHTML = '<div role=\"button\" aria-label=\"foo\"></div>';\n\n    axe\n      .runPartial({ include: [['#fixture']] }, { runOnly: 'aria-allowed-attr' })\n      .then(function (result) {\n        return axe.finishRun([result]);\n      })\n      .then(function (results) {\n        assert.lengthOf(results.violations, 0);\n        assert.lengthOf(results.passes, 1);\n        assert.lengthOf(results.incomplete, 0);\n        assert.lengthOf(results.inapplicable, 0);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('can report incomplete results', function (done) {\n    fixture.innerHTML = '<div aria-describedby=\"missing\"></div>';\n\n    axe\n      .runPartial(\n        { include: [['#fixture']] },\n        { runOnly: 'aria-valid-attr-value' }\n      )\n      .then(function (result) {\n        return axe.finishRun([result]);\n      })\n      .then(function (results) {\n        assert.lengthOf(results.violations, 0);\n        assert.lengthOf(results.passes, 0);\n        assert.lengthOf(results.incomplete, 1);\n        assert.lengthOf(results.inapplicable, 0);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('can report inapplicable results', function (done) {\n    axe\n      .runPartial({ include: [['#fixture']] }, { runOnly: 'aria-allowed-attr' })\n      .then(function (result) {\n        return axe.finishRun([result]);\n      })\n      .then(function (results) {\n        assert.lengthOf(results.violations, 0);\n        assert.lengthOf(results.passes, 0);\n        assert.lengthOf(results.incomplete, 0);\n        assert.lengthOf(results.inapplicable, 1);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('takes multiple partial results and outputs a finished report', function (done) {\n    fixture.innerHTML =\n      '<div id=\"fail\" aria-label=\"foo\"></div>' +\n      '<div id=\"pass\" role=\"button\" aria-label=\"foo\"></div>' +\n      '<div id=\"incomplete\" aria-describedby=\"missing\"></div>';\n    var allResults = [];\n\n    axe\n      .runPartial({ include: [['#pass']] }, { runOnly: 'aria-allowed-attr' })\n      .then(function (results) {\n        allResults.push(results);\n        return axe.runPartial(\n          { include: [['#fail']] },\n          { runOnly: 'aria-prohibited-attr' }\n        );\n      })\n      .then(function (results) {\n        allResults.push(results);\n        return axe.runPartial(\n          { include: [['#incomplete']] },\n          { runOnly: 'aria-valid-attr-value' }\n        );\n      })\n      .then(function (results) {\n        return axe.finishRun(allResults.concat(results));\n      })\n      .then(function (results) {\n        assert.lengthOf(results.violations, 1);\n        assert.lengthOf(results.passes, 1);\n        assert.lengthOf(results.incomplete, 1);\n        assert.lengthOf(results.inapplicable, 0);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('rejects with sync reporter errors', async () => {\n    axe.addReporter('throwing', () => {\n      throw new Error('Something went wrong');\n    });\n    const options = { reporter: 'throwing' };\n\n    fixture.innerHTML = '<h1>Hello world</h1>';\n    const partial = await axe.runPartial('#fixture', options);\n    try {\n      await axe.finishRun([partial], options);\n      assert.fail('Should have thrown');\n    } catch (err) {\n      assert.equal(err.message, 'Something went wrong');\n    }\n  });\n\n  it('rejects with async reporter errors', async () => {\n    axe.addReporter('throwing', (results, options, resolve, reject) => {\n      setTimeout(() => {\n        reject(new Error('Something went wrong'));\n      }, 10);\n    });\n    const options = { reporter: 'throwing' };\n\n    fixture.innerHTML = '<h1>Hello world</h1>';\n    const partial = await axe.runPartial('#fixture', options);\n    try {\n      await axe.finishRun([partial], options);\n      assert.fail('Should have thrown');\n    } catch (err) {\n      assert.equal(err.message, 'Something went wrong');\n    }\n  });\n\n  describe('frames', function () {\n    function createIframe(html, parent) {\n      return new Promise(function (resolve) {\n        parent = parent || fixture;\n        var doc = parent.ownerDocument;\n        var iframe = doc.createElement('iframe');\n        parent.appendChild(iframe);\n        var frameDoc = iframe.contentDocument;\n        frameDoc.write(html + '<script src=\"/axe.js\"></script>');\n        frameDoc.close();\n        frameDoc.querySelector('script').onload = function () {\n          resolve(iframe.contentWindow);\n        };\n      });\n    }\n\n    it('reconstructs which node is in which frame', function (done) {\n      createIframe('<h1></h1>')\n        .then(function (frameWin) {\n          return Promise.all([\n            window.axe.runPartial({ runOnly: 'empty-heading' }),\n            frameWin.axe.runPartial({ runOnly: 'empty-heading' })\n          ]);\n        })\n        .then(axe.finishRun)\n        .then(function (results) {\n          var nodes = results.violations[0].nodes;\n          assert.deepEqual(nodes[0].target, ['iframe', 'h1']);\n          done();\n        })\n        .catch(done);\n    });\n\n    it('handles nodes in nested iframes', function (done) {\n      var windows = [window];\n      fixture.innerHTML = '<h1></h1>';\n      createIframe('<h2></h2>')\n        .then(function (frameWin) {\n          windows.push(frameWin);\n          return createIframe('<h3></h3>', frameWin.document.body);\n        })\n        .then(function (nestedWin) {\n          windows.push(nestedWin);\n          var promisedResults = windows.map(function (win) {\n            return win.axe.runPartial({ runOnly: 'empty-heading' });\n          });\n          return Promise.all(promisedResults);\n        })\n        .then(axe.finishRun)\n        .then(function (results) {\n          var nodes = results.violations[0].nodes;\n          assert.deepEqual(nodes[0].target, ['h1']);\n          assert.deepEqual(nodes[1].target, ['iframe', 'h2']);\n          assert.deepEqual(nodes[2].target, ['iframe', 'iframe', 'h3']);\n          done();\n        })\n        .catch(done);\n    });\n\n    it('should handle null results and set target correctly', function (done) {\n      var windows = [window];\n      fixture.innerHTML = '<h1></h1>';\n      createIframe('<h2></h2>')\n        .then(function (frameWin) {\n          windows.push(frameWin);\n          return createIframe('<h3></h3>');\n        })\n        .then(function (nestedWin) {\n          windows.push(nestedWin);\n          var promisedResults = windows.map(function (win) {\n            return win.axe.runPartial({ runOnly: 'empty-heading' });\n          });\n          return Promise.all(promisedResults);\n        })\n        .then(function (partialResults) {\n          partialResults[1] = null;\n          return partialResults;\n        })\n        .then(axe.finishRun)\n        .then(function (results) {\n          var nodes = results.violations[0].nodes;\n          assert.deepEqual(nodes[0].target, ['h1']);\n          assert.deepEqual(nodes[1].target, ['iframe:nth-child(3)', 'h3']);\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  describe('calling audit.after', function () {\n    it('passes results with iframe ancestries', function (done) {\n      fixture.innerHTML = '<i id=\"i\"></i> <i id=\"i\"></i>';\n      axe\n        .runPartial(fixture, { runOnly: 'duplicate-id' })\n        .then(function (partialResult) {\n          return axe.finishRun([partialResult], { runOnly: 'duplicate-id' });\n        })\n        .then(function (result) {\n          var nodes = result.violations[0].nodes;\n          var relatedNodes = nodes[0].any[0].relatedNodes;\n\n          assert.lengthOf(nodes, 1);\n          assert.deepEqual(nodes[0].target, ['i:nth-child(1)']);\n          assert.lengthOf(relatedNodes, 1);\n          assert.deepEqual(relatedNodes[0].target, ['i:nth-child(2)']);\n          done();\n        })\n        .catch(done);\n    });\n\n    it('provides the options object', function (done) {\n      var spy;\n      fixture.innerHTML = '<i id=\"i\"></i> <i id=\"i\"></i>';\n      axe\n        .runPartial(fixture, { runOnly: 'duplicate-id' })\n        .then(function (partialResult) {\n          spy = sinon.spy(axe._audit, 'after');\n          return axe.finishRun([partialResult], { runOnly: 'duplicate-id' });\n        })\n        .then(function () {\n          assert.lengthOf(axe._audit.after.args, 1);\n          assert.deepEqual(axe._audit.after.args[0][1], {\n            runOnly: { type: 'rule', values: ['duplicate-id'] },\n            reporter: 'v1'\n          });\n          spy.restore();\n          done();\n        })\n        .catch(function (err) {\n          spy.restore();\n          done(err);\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/frame-messenger.js",
    "content": "describe('frameMessenger', function () {\n  var stub;\n\n  after(function () {\n    stub.restore();\n  });\n\n  it('should call into axe.utils.respondable.updateMessenger', function () {\n    stub = sinon.stub(axe.utils.respondable, 'updateMessenger');\n    axe.frameMessenger();\n    assert.isTrue(stub.called);\n  });\n});\n"
  },
  {
    "path": "test/core/public/get-rules.js",
    "content": "describe('axe.getRules', function () {\n  'use strict';\n  var ver = axe.version.substring(0, axe.version.lastIndexOf('.'));\n\n  beforeEach(function () {\n    axe._load({\n      messages: [],\n      rules: [\n        {\n          id: 'awesomeRule1',\n          selector: '',\n          excludeHidden: false,\n          any: [],\n          tags: ['tag1']\n        },\n        {\n          id: 'awesomeRule2',\n          any: [],\n          tags: ['tag1', 'tag2'],\n          actIds: ['abc123', 'xyz789']\n        }\n      ],\n      data: {\n        rules: {\n          awesomeRule1: {\n            description: 'some interesting information',\n            help: 'halp'\n          },\n          awesomeRule2: {\n            description: 'also some interesting information',\n            help: 'halp me!'\n          }\n        }\n      }\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should return rules', function () {\n    var retValue = axe.getRules(['tag1']);\n    assert.isArray(retValue);\n    assert.lengthOf(retValue, 2);\n    assert.equal(retValue[0].ruleId, 'awesomeRule1');\n    assert.equal(retValue[0].description, 'some interesting information');\n    assert.equal(retValue[0].help, 'halp');\n    assert.equal(\n      retValue[0].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule1?application=axeAPI'\n    );\n    assert.deepEqual(retValue[0].tags, ['tag1']);\n\n    assert.equal(retValue[1].ruleId, 'awesomeRule2');\n    assert.equal(retValue[1].description, 'also some interesting information');\n    assert.equal(retValue[1].help, 'halp me!');\n    assert.equal(\n      retValue[1].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule2?application=axeAPI'\n    );\n    assert.deepEqual(retValue[1].tags, ['tag1', 'tag2']);\n    assert.deepEqual(retValue[1].actIds, ['abc123', 'xyz789']);\n\n    retValue = axe.getRules(['tag2']);\n    assert.isArray(retValue);\n    assert.lengthOf(retValue, 1);\n    assert.equal(retValue[0].ruleId, 'awesomeRule2');\n    assert.equal(retValue[0].description, 'also some interesting information');\n    assert.equal(retValue[0].help, 'halp me!');\n    assert.equal(\n      retValue[0].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule2?application=axeAPI'\n    );\n    assert.deepEqual(retValue[0].tags, ['tag1', 'tag2']);\n    assert.deepEqual(retValue[0].actIds, ['abc123', 'xyz789']);\n  });\n\n  it('should not return nothing', function () {\n    var retValue = axe.getRules(['bob']);\n    assert.isArray(retValue);\n    assert.lengthOf(retValue, 0);\n  });\n\n  it('should return all rules if given no tags - undefined', function () {\n    var retValue = axe.getRules();\n    assert.equal(retValue[0].ruleId, 'awesomeRule1');\n    assert.equal(retValue[0].description, 'some interesting information');\n    assert.equal(retValue[0].help, 'halp');\n    assert.equal(\n      retValue[0].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule1?application=axeAPI'\n    );\n    assert.deepEqual(retValue[0].tags, ['tag1']);\n\n    assert.equal(retValue[1].ruleId, 'awesomeRule2');\n    assert.equal(retValue[1].description, 'also some interesting information');\n    assert.equal(retValue[1].help, 'halp me!');\n    assert.equal(\n      retValue[1].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule2?application=axeAPI'\n    );\n    assert.deepEqual(retValue[1].tags, ['tag1', 'tag2']);\n    assert.deepEqual(retValue[1].actIds, ['abc123', 'xyz789']);\n  });\n\n  it('should return all rules if given empty array', function () {\n    var retValue = axe.getRules([]);\n    assert.equal(retValue[0].ruleId, 'awesomeRule1');\n    assert.equal(retValue[0].description, 'some interesting information');\n    assert.equal(retValue[0].help, 'halp');\n    assert.equal(\n      retValue[0].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule1?application=axeAPI'\n    );\n    assert.deepEqual(retValue[0].tags, ['tag1']);\n\n    assert.equal(retValue[1].ruleId, 'awesomeRule2');\n    assert.equal(retValue[1].description, 'also some interesting information');\n    assert.equal(retValue[1].help, 'halp me!');\n    assert.equal(\n      retValue[1].helpUrl,\n      'https://dequeuniversity.com/rules/axe/' +\n        ver +\n        '/awesomeRule2?application=axeAPI'\n    );\n    assert.deepEqual(retValue[1].tags, ['tag1', 'tag2']);\n    assert.deepEqual(retValue[1].actIds, ['abc123', 'xyz789']);\n  });\n});\n"
  },
  {
    "path": "test/core/public/load.js",
    "content": "describe('axe._load', function () {\n  var fixture = document.querySelector('#fixture');\n  var captureError = axe.testUtils.captureError;\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe._load);\n  });\n\n  it('should push rules on the Audit', function () {\n    var mockAudit = {\n      rules: [{ id: 'monkeys' }, { id: 'bananas' }]\n    };\n\n    axe._load(mockAudit);\n    // TODO: this does not work yet thanks to webpack\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    // assert.instanceOf(axe._audit.rules[1], Rule);\n    assert.equal(axe._audit.rules[0].id, 'monkeys');\n    assert.equal(axe._audit.rules[1].id, 'bananas');\n  });\n\n  it('should load with a lang', function () {\n    axe._load({\n      lang: 'ja'\n    });\n    assert.equal(axe._audit.lang, 'ja');\n  });\n\n  describe('respondable subscriber', function () {\n    it('should add a respondable subscriber for axe.ping', function (done) {\n      var winParent = window.parent;\n      var mockAudit = {\n        rules: [{ id: 'monkeys' }, { id: 'bananas' }]\n      };\n\n      axe._load(mockAudit);\n\n      var frame = document.createElement('iframe');\n      frame.src = '../mock/frames/test.html';\n      frame.addEventListener('load', function () {\n        var win = frame.contentWindow;\n        window.parent = win;\n        win.postMessage = captureError(function (message) {\n          var data = JSON.parse(message);\n          assert.deepEqual(data.payload, { axe: true });\n          window.parent = winParent;\n          done();\n        }, done);\n        axe.utils.respondable(win, 'axe.ping', { axe: true });\n      });\n\n      fixture.appendChild(frame);\n    });\n\n    describe('given command rules', function () {\n      // todo: see issue - https://github.com/dequelabs/axe-core/issues/2168\n      it.skip('should call `runRules` and default context to empty object', function (done) {\n        var mockAudit = {\n          rules: []\n        };\n        var origSub = window.utils.respondable.subscribe;\n        var orig = window.runRules;\n        window.runRules = function (context, options, callback) {\n          assert.deepEqual(context, {});\n          assert.isFunction(callback);\n          done();\n        };\n\n        axe.utils.respondable.subscribe = function (topic, callback) {\n          callback(\n            { data: 'iscool', command: 'rules' },\n            undefined,\n            function (response) {\n              // ping callback will call this response function\n              assert.ok(response);\n            }\n          );\n        };\n        axe._load(mockAudit);\n\n        window.utils.respondable.subscribe = origSub;\n        window.runRules = orig;\n      });\n\n      // todo: see issue - https://github.com/dequelabs/axe-core/issues/2168\n      it.skip('should pass data.context to `runRules`', function (done) {\n        var origSub = window.utils.respondable.subscribe;\n        var orig = window.runRules;\n        window.runRules = function (context, options, callback) {\n          assert.deepEqual(context, { include: ['monkeys'] });\n          assert.isFunction(callback);\n          done();\n        };\n\n        axe.utils.respondable.subscribe = function (topic, callback) {\n          callback(\n            { command: 'rules', context: { include: ['monkeys'] } },\n            undefined,\n            function (response) {\n              assert.ok(response);\n            }\n          );\n        };\n        axe._load({\n          rules: []\n        });\n\n        window.utils.respondable.subscribe = origSub;\n        window.runRules = orig;\n      });\n\n      // todo: see issue - https://github.com/dequelabs/axe-core/issues/2168\n      it.skip('should default include to current document if none are found', function (done) {\n        var origSub = axe.utils.respondable.subscribe;\n        var orig = window.runRules;\n        var expected = { include: [document] };\n        window.runRules = function (context) {\n          assert.deepEqual(context, expected);\n          done();\n        };\n\n        axe.utils.respondable.subscribe = function (topic, callback) {\n          callback(\n            { command: 'rules', context: { include: [] } },\n            undefined,\n            function () {}\n          );\n        };\n        axe._load({\n          rules: []\n        });\n        window.runRules = orig;\n        axe.utils.respondable.subscribe = origSub;\n      });\n    });\n\n    describe('given command cleanup-plugins', function () {\n      // todo: see issue - https://github.com/dequelabs/axe-core/issues/2168\n      it.skip('should call `cleanup`', function (done) {\n        var mockAudit = {\n          rules: []\n        };\n        var origSub = window.utils.respondable.subscribe;\n        var orig = window.cleanup;\n        window.cleanup = function (callback) {\n          assert.isFunction(callback);\n          done();\n        };\n\n        axe.utils.respondable.subscribe = function (topic, callback) {\n          callback(\n            {\n              command: 'cleanup-plugin'\n            },\n            undefined,\n            function (response) {\n              // ping callback will call this response function\n              assert.ok(response);\n            }\n          );\n        };\n        axe._load(mockAudit);\n\n        window.utils.respondable.subscribe = origSub;\n        window.cleanup = orig;\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/plugins.js",
    "content": "describe('plugins', () => {\n  function createFrames(callback) {\n    let frame;\n    const num = 2;\n    let loaded = 0;\n\n    function onLoad() {\n      loaded++;\n      if (loaded >= num) {\n        callback();\n      }\n    }\n\n    frame = document.createElement('iframe');\n    frame.id = 'target';\n    frame.src = '../mock/frames/frame-frame.html';\n\n    frame.addEventListener('load', onLoad);\n    fixture.appendChild(frame);\n\n    frame = document.createElement('iframe');\n    frame.src = '../mock/frames/frame-frame.html';\n    frame.addEventListener('load', onLoad);\n    fixture.appendChild(frame);\n  }\n\n  const fixture = document.getElementById('fixture');\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    axe._audit = null;\n  });\n\n  beforeEach(() => {\n    axe._load({\n      rules: []\n    });\n  });\n\n  it('Should have registerPlugin function', () => {\n    assert.ok(axe.registerPlugin);\n    assert.equal(typeof axe.registerPlugin, 'function');\n  });\n\n  it('should have an empty set of plugins', () => {\n    assert.deepEqual({}, axe.plugins);\n  });\n\n  it('should add a plugin to the plugins list and a command to the audit commands', () => {\n    axe.registerPlugin({\n      id: 'my-plugin',\n      run: 'run',\n      commands: [\n        {\n          id: 'my-command',\n          callback: 'callback'\n        }\n      ]\n    });\n    assert.ok(axe.plugins['my-plugin']);\n    assert.equal(axe.plugins['my-plugin']._run, 'run');\n    assert.equal(axe._audit.commands['my-command'], 'callback');\n  });\n  describe('Plugin class', () => {\n    it('should call the run function of the registered plugin, when run is called', () => {\n      let called = false;\n      axe.registerPlugin({\n        id: 'my-plugin',\n        run: function (id, action, options) {\n          called = {\n            id: id,\n            action: action,\n            options: options\n          };\n        },\n        commands: [\n          {\n            id: 'my-command',\n            callback: 'callback'\n          }\n        ]\n      });\n      axe.plugins['my-plugin'].run('id', 'action', 'options');\n      assert.deepEqual(\n        { id: 'id', action: 'action', options: 'options' },\n        called\n      );\n    });\n  });\n  describe('Plugin.protoype.run', () => {\n    afterEach(() => {\n      fixture.innerHTML = '';\n      axe._audit = null;\n      axe.plugins = {};\n    });\n    beforeEach(() => {\n      axe._load({\n        rules: []\n      });\n      axe.registerPlugin({\n        id: 'multi',\n        run: function (id, action, options, callback) {\n          this._registry[id][action].call(\n            this._registry[id],\n            options,\n            callback\n          );\n        },\n        commands: [\n          {\n            id: 'run-multi',\n            callback: function (data, callback) {\n              return axe.plugins.multi.run(\n                data.parameter,\n                data.action,\n                data.options,\n                callback\n              );\n            }\n          }\n        ]\n      });\n      axe.plugins.multi.add({\n        id: 'hideall',\n        cleanup: done => {\n          done();\n        },\n        run: function (options, callback) {\n          let frames;\n          const q = axe.utils.queue();\n\n          frames = axe.utils.toArray(\n            document.querySelectorAll('iframe, frame')\n          );\n          frames.forEach(function (frame) {\n            q.defer(function (resolve, reject) {\n              axe.utils.sendCommandToFrame(\n                frame,\n                {\n                  options: options,\n                  command: 'run-multi',\n                  parameter: 'hideall',\n                  action: 'run'\n                },\n                resolve,\n                reject\n              );\n            });\n          });\n\n          q.defer(done => {\n            // implementation\n            done('ola!');\n          });\n          q.then(function (data) {\n            // done with all the frames\n            let results = [];\n            data.forEach(function (datum) {\n              if (datum) {\n                results = results.concat(datum);\n              }\n            });\n            callback(results);\n          }).catch(callback);\n        }\n      });\n    });\n    it('should work without frames', done => {\n      axe.plugins.multi.run('hideall', 'run', {}, function (results) {\n        assert.deepEqual(results, ['ola!']);\n        done();\n      });\n    });\n    it('should work with frames', done => {\n      createFrames(() => {\n        setTimeout(() => {\n          axe.plugins.multi.run('hideall', 'run', {}, function (results) {\n            assert.deepEqual(results, ['ola!', 'ola!', 'ola!', 'ola!', 'ola!']);\n            done();\n          });\n        }, 500);\n      });\n    });\n    it(\"should call the implementation's cleanup function\", done => {\n      let called = false;\n      axe.plugins.multi.cleanup = doneFn => {\n        called = true;\n        doneFn();\n      };\n      axe.plugins.multi.cleanup(() => {\n        assert.ok(called);\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/reporter.js",
    "content": "describe('axe.reporter', function () {\n  'use strict';\n\n  var orig = {};\n  before(function () {\n    orig.reporters = window.reporters;\n  });\n\n  after(function () {\n    Object.keys(orig).forEach(function (k) {\n      window[k] = orig[k];\n    });\n  });\n\n  it('should add reporter with given name', function () {\n    axe.addReporter('bob', 'joe');\n    assert.equal(axe.getReporter('bob'), 'joe');\n  });\n\n  it('returns false when reporter does not exist', function () {\n    assert.isFalse(axe.hasReporter('fancy-bob'));\n  });\n\n  it('returns true when reporter exists', function () {\n    axe.addReporter('sponge');\n    assert.isTrue(axe.hasReporter('sponge'));\n  });\n});\n"
  },
  {
    "path": "test/core/public/reset.js",
    "content": "describe('axe.reset', function () {\n  'use strict';\n\n  // var Rule = axe._thisWillBeDeletedDoNotUse.base.Rule;\n  var fixture = document.getElementById('fixture');\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  beforeEach(function () {\n    axe._audit = null;\n  });\n\n  it('should throw if no audit is configured', function () {\n    assert.throws(\n      function () {\n        axe.reset(\n          function () {},\n          function () {}\n        );\n      },\n      Error,\n      /^No audit configured/\n    );\n  });\n\n  it('should restore the default configuration', function () {\n    axe._load({\n      data: {\n        rules: {\n          bob: {\n            knows: 'not-joe'\n          }\n        }\n      },\n      rules: [\n        {\n          id: 'bob',\n          selector: 'fail'\n        }\n      ],\n      reporter: 'v2'\n    });\n    assert.lengthOf(axe._audit.rules, 1);\n    // TODO: this does not work yet thanks to webpack\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    assert.equal(axe._audit.rules[0].id, 'bob');\n    assert.equal(axe._audit.rules[0].selector, 'fail');\n    assert.equal(axe._audit.reporter, 'v2');\n\n    axe.configure({\n      rules: [\n        {\n          id: 'bob',\n          selector: 'pass',\n          metadata: {\n            knows: 'joe'\n          }\n        }\n      ],\n      reporter: 'raw'\n    });\n    assert.lengthOf(axe._audit.rules, 1);\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    assert.equal(axe._audit.rules[0].id, 'bob');\n    assert.equal(axe._audit.rules[0].selector, 'pass');\n    assert.equal(axe._audit.reporter, 'raw');\n    assert.equal(axe._audit.data.rules.bob.knows, 'joe');\n\n    axe.reset();\n\n    assert.lengthOf(axe._audit.rules, 1);\n    // assert.instanceOf(axe._audit.rules[0], Rule);\n    assert.equal(axe._audit.rules[0].id, 'bob');\n    assert.equal(axe._audit.rules[0].selector, 'fail');\n    assert.equal(axe._audit.reporter, 'v2');\n    assert.equal(axe._audit.data.rules.bob.knows, 'not-joe');\n  });\n\n  describe('when custom locale was provided', function () {\n    beforeEach(function () {\n      axe._load({\n        data: {\n          checks: {\n            banana: {\n              impact: 'serious',\n              messages: {\n                pass: 'yay',\n                fail: 'boo',\n                incomplete: 'donno'\n              }\n            }\n          }\n        },\n        checks: [\n          {\n            id: 'banana',\n            evaluate: function () {}\n          }\n        ]\n      });\n    });\n\n    it('should restore the original locale', function () {\n      axe.configure({\n        locale: {\n          checks: {\n            banana: {\n              pass: 'wonderful',\n              fail: 'horrible job',\n              incomplete: 'donno'\n            }\n          }\n        }\n      });\n\n      axe.reset();\n\n      var banana = axe._audit.data.checks.banana;\n      assert.equal(banana.impact, 'serious');\n      assert.equal(banana.messages.pass, 'yay');\n      assert.equal(banana.messages.fail, 'boo');\n      assert.equal(banana.messages.incomplete, 'donno');\n    });\n  });\n\n  it('should restore standards object', function () {\n    axe._load({});\n\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-live': {\n            type: 'string'\n          }\n        }\n      }\n    });\n\n    axe.reset();\n\n    var ariaLiveAttr = axe._audit.standards.ariaAttrs['aria-live'];\n    assert.equal(ariaLiveAttr.type, 'nmtoken');\n  });\n});\n"
  },
  {
    "path": "test/core/public/run-partial.js",
    "content": "describe('axe.runPartial', function () {\n  var fixture = document.getElementById('fixture');\n  var DqElement = axe.utils.DqElement;\n  var dqElementKeys = Object.keys(new DqElement(null).toJSON());\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('Uses axe._tree if it already exists', function (done) {\n    axe._tree = [axe.setup(fixture)];\n    fixture.innerHTML = '<img>';\n    axe\n      .runPartial(document, { runOnly: 'image-alt' })\n      .then(function (partialResult) {\n        var result = partialResult.results[0];\n        // 0, because <img> was added after the tree was constructed\n        assert.lengthOf(result.nodes, 0);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('cleans up after resolving', function (done) {\n    axe\n      .runPartial(document, { runOnly: 'image-alt' })\n      .then(function () {\n        assert.isUndefined(axe._tree);\n        assert.isUndefined(axe._selectorData);\n        assert.isFalse(axe._running);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('normalizes the options argument', function (done) {\n    axe\n      .runPartial(/* no context */ { runOnly: 'image-alt' })\n      .then(function (partialResult) {\n        assert.lengthOf(partialResult.results, 1);\n        assert.equal(partialResult.results[0].id, 'image-alt');\n        done();\n      })\n      .catch(done);\n  });\n\n  it('does not mutate the options object', function (done) {\n    var options = {};\n    axe\n      .runPartial(options)\n      .then(function () {\n        assert.deepEqual(options, {});\n        done();\n      })\n      .catch(done);\n  });\n\n  it('ignores { elementRef: true } option', async () => {\n    const options = { elementRef: true };\n    const result = await axe.runPartial(options);\n    for (const nodeResult of result.results[0].nodes) {\n      assert.isUndefined(nodeResult.node.element);\n    }\n  });\n\n  describe('result', function () {\n    var partialResult;\n    before(function (done) {\n      fixture.innerHTML = '<img>';\n      axe.runPartial(document, { runOnly: 'image-alt' }).then(function (out) {\n        partialResult = out;\n        done();\n      });\n    });\n\n    it('returns a result with all the valid properties', function () {\n      var result = partialResult.results[0];\n      assert.lengthOf(partialResult.results, 1);\n      assert.hasAllKeys(result, [\n        'id',\n        'result',\n        'pageLevel',\n        'impact',\n        'nodes'\n      ]);\n      assert.equal(result.id, 'image-alt');\n\n      var checkResult = result.nodes[0];\n      assert.lengthOf(result.nodes, 1);\n      assert.hasAllKeys(checkResult, ['any', 'all', 'none', 'node']);\n      assert.deepEqual(checkResult.node.selector, ['img']);\n    });\n\n    it('returns check results with a serialized node', function () {\n      var checkResult = partialResult.results[0].nodes[0];\n      assert.lengthOf(partialResult.results[0].nodes, 1);\n      assert.hasAllKeys(checkResult, ['any', 'all', 'none', 'node']);\n      assert.notInstanceOf(checkResult.node, DqElement);\n      assert.hasAllKeys(checkResult.node, dqElementKeys);\n    });\n\n    it('does not return DqElement objects', () => {\n      for (const result of partialResult.results) {\n        for (const nodeResult of result.nodes) {\n          assert.notInstanceOf(nodeResult.node, DqElement);\n          const checks = [\n            ...nodeResult.any,\n            ...nodeResult.all,\n            ...nodeResult.none\n          ];\n          for (const check of checks) {\n            for (const relatedNode of check.relatedNodes) {\n              assert.notInstanceOf(relatedNode, DqElement);\n            }\n          }\n        }\n      }\n    });\n\n    it('can be serialized using JSON.stringify', function () {\n      assert.doesNotThrow(function () {\n        JSON.stringify(partialResult);\n      });\n    });\n  });\n\n  describe('frames', function () {\n    var partialResult;\n    before(function (done) {\n      fixture.innerHTML =\n        '<main>' +\n        ' <iframe id=\"foo\"></iframe>' +\n        ' <iframe id=\"bar\"></iframe>' +\n        '</main>' +\n        '<iframe id=\"baz\"></iframe>';\n\n      axe\n        .runPartial('#fixture > main', { runOnly: 'image-alt' })\n        .then(function (out) {\n          partialResult = out;\n          done();\n        });\n    });\n\n    it('only has frames in context', function () {\n      assert.lengthOf(partialResult.frames, 2);\n      assert.deepEqual(partialResult.frames[0].selector, ['#foo']);\n      assert.deepEqual(partialResult.frames[1].selector, ['#bar']);\n    });\n\n    it('provides serialized frame info', function () {\n      partialResult.frames.forEach(function (frame) {\n        assert.hasAllKeys(frame, dqElementKeys);\n      });\n    });\n  });\n\n  describe('environmentData', function () {\n    it('includes environment data for the initiator', function (done) {\n      var context = {\n        include: [['#fixture']]\n      };\n      axe\n        .runPartial(context, { runOnly: 'image-alt' })\n        .then(function (out) {\n          var keys = Object.keys(axe.utils.getEnvironmentData());\n          assert.hasAllKeys(out.environmentData, keys);\n          done();\n        })\n        .catch(done);\n    });\n\n    it('is undefined for frames', function (done) {\n      var context = {\n        include: [['#fixture']],\n        initiator: false\n      };\n      axe\n        .runPartial(context, { runOnly: 'image-alt' })\n        .then(function (out) {\n          assert.isUndefined(out.environmentData);\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  describe('guards', function () {\n    var audit = axe._audit;\n    afterEach(function () {\n      axe._audit = audit;\n      axe._running = false;\n    });\n\n    it('throws when axe._audit is undefined', function () {\n      axe._audit = null;\n      assert.throws(function () {\n        axe.runPartial();\n      });\n    });\n\n    it('throws if axe is already running', function (done) {\n      axe.runPartial().then(function () {\n        done();\n      });\n      assert.throws(function () {\n        axe.runPartial();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/public/run-rules.js",
    "content": "describe('runRules', () => {\n  let ver = axe.version.substring(0, axe.version.lastIndexOf('.'));\n  const { captureError } = axe.testUtils;\n\n  function iframeReady(src, context, id, cb) {\n    let i = document.createElement('iframe');\n    i.addEventListener('load', () => {\n      cb();\n    });\n    i.src = src;\n    i.id = id;\n    context.appendChild(i);\n  }\n\n  function createFrames(url, callback) {\n    let frame,\n      num = 2;\n    let loaded = 0;\n\n    if (typeof url === 'function') {\n      callback = url;\n      url = '../mock/frames/frame-frame.html';\n    }\n\n    function onLoad() {\n      loaded++;\n      if (loaded >= num) {\n        callback();\n      }\n    }\n\n    frame = document.createElement('iframe');\n    frame.src = url;\n\n    frame.addEventListener('load', onLoad);\n    fixture.appendChild(frame);\n\n    frame = document.createElement('iframe');\n    frame.src = '../mock/frames/nocode.html';\n    frame.addEventListener('load', onLoad);\n    fixture.appendChild(frame);\n    return frame;\n  }\n\n  let fixture = document.getElementById('fixture');\n\n  let isNotCalled;\n  beforeEach(() => {\n    isNotCalled = err => {\n      throw err || new Error('Reject should not be called');\n    };\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    axe._audit = null;\n    axe.teardown();\n  });\n\n  it('should work', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    let frame = document.createElement('iframe');\n    frame.src = '../mock/frames/frame-frame.html';\n\n    frame.addEventListener('load', () => {\n      setTimeout(() => {\n        axe._runRules(\n          document,\n          {},\n          captureError(r => {\n            assert.lengthOf(r[0].passes, 3);\n            done();\n          }, done),\n          err => done(err)\n        );\n      }, 500);\n    });\n    fixture.appendChild(frame);\n  });\n\n  it('should properly order iframes', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'iframe',\n          selector: 'iframe',\n          any: ['iframe']\n        }\n      ],\n      checks: [{ id: 'iframe', evaluate: () => true }],\n      messages: {}\n    });\n\n    let frame = document.createElement('iframe');\n    frame.addEventListener('load', () => {\n      setTimeout(() => {\n        axe._runRules(\n          document,\n          {},\n          captureError(r => {\n            let nodes = r[0].passes.map(detail => {\n              return detail.node.selector;\n            });\n            assert.deepEqual(nodes, [\n              ['#level0'],\n              ['#level0', '#level1'],\n              ['#level0', '#level1', '#level2a'],\n              ['#level0', '#level1', '#level2b']\n            ]);\n            done();\n          }, done),\n          isNotCalled\n        );\n      }, 500);\n    });\n    frame.id = 'level0';\n    frame.src = '../mock/frames/nested0.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should properly calculate context and return results from matching frames', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'div#target',\n          selector: '#target',\n          any: ['has-target']\n        },\n        {\n          id: 'first-div',\n          selector: 'div:not(#fixture)',\n          any: ['first-div']\n        }\n      ],\n      checks: [\n        { id: 'has-target', evaluate: () => true },\n        {\n          id: 'first-div',\n          evaluate(node) {\n            this.relatedNodes([node]);\n            return false;\n          },\n          after(results) {\n            if (results.length) {\n              results[0].result = true;\n            }\n            return [results[0]];\n          }\n        }\n      ],\n      messages: {}\n    });\n\n    iframeReady('../mock/frames/context.html', fixture, 'context-test', () => {\n      let div = document.createElement('div');\n      fixture.appendChild(div);\n\n      axe._runRules(\n        '#fixture',\n        {},\n        captureError(results => {\n          assert.deepEqual(JSON.parse(JSON.stringify(results)), [\n            {\n              id: 'div#target',\n              helpUrl:\n                'https://dequeuniversity.com/rules/axe/' +\n                ver +\n                '/div#target?application=axeAPI',\n              pageLevel: false,\n              impact: null,\n              inapplicable: [],\n              incomplete: [],\n              violations: [],\n              passes: [\n                {\n                  result: 'passed',\n                  impact: null,\n                  node: {\n                    selector: ['#context-test', '#target'],\n                    ancestry: [\n                      'html > body > div:nth-child(1) > iframe:nth-child(1)',\n                      'html > body > div:nth-child(2)'\n                    ],\n                    xpath: [\n                      \"//iframe[@id='context-test']\",\n                      \"//div[@id='target']\"\n                    ],\n                    source: '<div id=\"target\"></div>',\n                    nodeIndexes: [12, 14],\n                    fromFrame: true\n                  },\n                  any: [\n                    {\n                      id: 'has-target',\n                      data: null,\n                      relatedNodes: []\n                    }\n                  ],\n                  all: [],\n                  none: []\n                }\n              ],\n              result: 'passed',\n              tags: []\n            },\n            {\n              id: 'first-div',\n              helpUrl:\n                'https://dequeuniversity.com/rules/axe/' +\n                ver +\n                '/first-div?application=axeAPI',\n              pageLevel: false,\n              impact: null,\n              inapplicable: [],\n              incomplete: [],\n              violations: [],\n              passes: [\n                {\n                  result: 'passed',\n                  impact: null,\n                  node: {\n                    selector: ['#context-test', '#foo'],\n                    ancestry: [\n                      'html > body > div:nth-child(1) > iframe:nth-child(1)',\n                      'html > body > div:nth-child(1)'\n                    ],\n                    xpath: [\"//iframe[@id='context-test']\", \"//div[@id='foo']\"],\n                    source:\n                      '<div id=\"foo\">\\n      <div id=\"bar\"></div>\\n    </div>',\n                    nodeIndexes: [12, 9],\n                    fromFrame: true\n                  },\n                  any: [\n                    {\n                      id: 'first-div',\n                      data: null,\n                      relatedNodes: [\n                        {\n                          selector: ['#context-test', '#foo'],\n                          ancestry: [\n                            'html > body > div:nth-child(1) > iframe:nth-child(1)',\n                            'html > body > div:nth-child(1)'\n                          ],\n                          xpath: [\n                            \"//iframe[@id='context-test']\",\n                            \"//div[@id='foo']\"\n                          ],\n                          source:\n                            '<div id=\"foo\">\\n      <div id=\"bar\"></div>\\n    </div>',\n                          nodeIndexes: [12, 9],\n                          fromFrame: true\n                        }\n                      ]\n                    }\n                  ],\n                  all: [],\n                  none: []\n                }\n              ],\n              result: 'passed',\n              tags: []\n            }\n          ]);\n          done();\n        }, done),\n        isNotCalled\n      );\n    });\n  });\n\n  it('should reject if the context is invalid', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'div#target',\n          selector: '#target',\n          any: ['has-target']\n        }\n      ],\n      messages: {}\n    });\n\n    iframeReady('../mock/frames/context.html', fixture, 'context-test', () => {\n      axe._runRules(\n        '#not-happening',\n        {},\n        () => {\n          assert.fail('This selector should not exist.');\n        },\n        captureError(error => {\n          assert.isOk(error);\n          assert.equal(\n            error.message,\n            'No elements found for include in page Context'\n          );\n          done();\n        }, done)\n      );\n    });\n  });\n\n  it('should accept a jQuery-like object', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'test',\n          selector: '*',\n          none: ['bob']\n        }\n      ],\n      checks: [{ id: 'bob', evaluate: () => true }]\n    });\n\n    fixture.innerHTML =\n      '<div id=\"t1\"><span></span></div><div id=\"t2\"><em></em></div>';\n\n    let $test = {\n      0: fixture.querySelector('#t1'),\n      1: fixture.querySelector('#t2'),\n      length: 2\n    };\n\n    axe.run(\n      $test,\n      captureError((err, results) => {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1);\n        assert.lengthOf(results.violations[0].nodes, 4);\n        assert.deepEqual(results.violations[0].nodes[0].target, ['#t1']);\n        // assert.deepEqual(results.violations[0].nodes[1].target, ['span']);\n        assert.deepEqual(results.violations[0].nodes[2].target, ['#t2']);\n        // assert.deepEqual(results.violations[0].nodes[3].target, ['em']);\n        done();\n      }, done)\n    );\n  });\n\n  it('should accept a NodeList', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'test',\n          selector: '*',\n          none: ['fred']\n        }\n      ],\n      checks: [{ id: 'fred', evaluate: () => true }]\n    });\n\n    fixture.innerHTML =\n      '<div class=\"foo\" id=\"t1\"><span></span></div><div class=\"foo\" id=\"t2\"><em></em></div>';\n\n    let test = fixture.querySelectorAll('.foo');\n    axe.run(\n      test,\n      captureError((err, results) => {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1);\n        assert.lengthOf(results.violations[0].nodes, 4);\n        assert.deepEqual(results.violations[0].nodes[0].target, ['#t1']);\n        // assert.deepEqual(results.violations[0].nodes[1].target, ['span']);\n        assert.deepEqual(results.violations[0].nodes[2].target, ['#t2']);\n        // assert.deepEqual(results.violations[0].nodes[3].target, ['em']);\n        done();\n      }, done)\n    );\n  });\n\n  it('should pull metadata from configuration', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'div#target',\n          selector: '#target',\n          any: ['has-target']\n        },\n        {\n          id: 'first-div',\n          selector: 'div#fixture div',\n          any: ['first-div']\n        }\n      ],\n      checks: [\n        { id: 'has-target', evaluate: () => false },\n        {\n          id: 'first-div',\n          evaluate(node) {\n            this.relatedNodes([node]);\n            return false;\n          },\n          after(results) {\n            if (results.length) {\n              results[0].result = true;\n            }\n            return [results[0]];\n          }\n        }\n      ],\n      data: {\n        rules: {\n          'div#target': {\n            foo: 'bar',\n            stuff: 'blah'\n          },\n          'first-div': {\n            bar: 'foo',\n            stuff: 'no'\n          }\n        },\n        checks: {\n          'first-div': {\n            thingy: true,\n            impact: 'serious',\n            messages: {\n              fail(checkResult) {\n                return checkResult.id === 'first-div'\n                  ? 'failing is not good'\n                  : 'y u wrong rule?';\n              },\n              pass(checkResult) {\n                return checkResult.id === 'first-div'\n                  ? 'passing is good'\n                  : 'y u wrong rule?';\n              }\n            }\n          },\n          'has-target': {\n            otherThingy: true,\n            impact: 'moderate',\n            messages: {\n              fail(checkResult) {\n                return checkResult.id === 'has-target'\n                  ? 'failing is not good'\n                  : 'y u wrong rule?';\n              },\n              pass(checkResult) {\n                return checkResult.id === 'has-target'\n                  ? 'passing is good'\n                  : 'y u wrong rule?';\n              }\n            }\n          }\n        }\n      }\n    });\n    fixture.innerHTML = '<div id=\"target\">Target!</div><div>ok</div>';\n    axe._runRules(\n      '#fixture',\n      {},\n      captureError(results => {\n        assert.deepEqual(JSON.parse(JSON.stringify(results)), [\n          {\n            id: 'div#target',\n            helpUrl:\n              'https://dequeuniversity.com/rules/axe/' +\n              ver +\n              '/div#target?application=axeAPI',\n            pageLevel: false,\n            foo: 'bar',\n            stuff: 'blah',\n            impact: 'moderate',\n            passes: [],\n            inapplicable: [],\n            incomplete: [],\n            violations: [\n              {\n                result: 'failed',\n                node: {\n                  selector: ['#target'],\n                  ancestry: [\n                    'html > body > div:nth-child(1) > div:nth-child(1)'\n                  ],\n                  xpath: [\"//div[@id='target']\"],\n                  source: '<div id=\"target\">Target!</div>',\n                  nodeIndexes: [12],\n                  fromFrame: false\n                },\n                impact: 'moderate',\n                any: [\n                  {\n                    impact: 'moderate',\n                    otherThingy: true,\n                    message: 'failing is not good',\n                    id: 'has-target',\n                    data: null,\n                    relatedNodes: []\n                  }\n                ],\n                all: [],\n                none: []\n              }\n            ],\n            result: 'failed',\n            tags: []\n          },\n          {\n            id: 'first-div',\n            helpUrl:\n              'https://dequeuniversity.com/rules/axe/' +\n              ver +\n              '/first-div?application=axeAPI',\n            pageLevel: false,\n            bar: 'foo',\n            stuff: 'no',\n            impact: null,\n            inapplicable: [],\n            incomplete: [],\n            violations: [],\n            passes: [\n              {\n                result: 'passed',\n                impact: null,\n                node: {\n                  selector: ['#target'],\n                  xpath: [\"//div[@id='target']\"],\n                  ancestry: [\n                    'html > body > div:nth-child(1) > div:nth-child(1)'\n                  ],\n                  source: '<div id=\"target\">Target!</div>',\n                  nodeIndexes: [12],\n                  fromFrame: false\n                },\n                any: [\n                  {\n                    impact: 'serious',\n                    id: 'first-div',\n                    thingy: true,\n                    message: 'passing is good',\n                    data: null,\n                    relatedNodes: [\n                      {\n                        selector: ['#target'],\n                        ancestry: [\n                          'html > body > div:nth-child(1) > div:nth-child(1)'\n                        ],\n                        xpath: [\"//div[@id='target']\"],\n                        source: '<div id=\"target\">Target!</div>',\n                        nodeIndexes: [12],\n                        fromFrame: false\n                      }\n                    ]\n                  }\n                ],\n                all: [],\n                none: []\n              }\n            ],\n            result: 'passed',\n            tags: []\n          }\n        ]);\n        done();\n      }, done),\n      isNotCalled\n    );\n  });\n\n  it('should call the reject argument if an error occurs', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'invalidRule'\n        }\n      ],\n      checks: [],\n      messages: {}\n    });\n\n    createFrames(() => {\n      setTimeout(() => {\n        axe._runRules(\n          document,\n          {},\n          () => {\n            done(new Error('You shall not pass!'));\n          },\n          captureError(err => {\n            assert.instanceOf(err, Error);\n            done();\n          }, done)\n        );\n      }, 100);\n    });\n  });\n\n  it('should resolve to cantTell when a rule fails', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'incomplete-1',\n          selector: '*',\n          none: ['undeffed']\n        },\n        {\n          id: 'incomplete-2',\n          selector: '*',\n          none: ['thrower']\n        }\n      ],\n      checks: [\n        { id: 'undeffed', evaluate: () => undefined },\n        {\n          id: 'thrower',\n          evaluate: () => {\n            throw new Error('Check failed to complete');\n          }\n        }\n      ]\n    });\n\n    fixture.innerHTML = '<div></div>';\n\n    axe.run(\n      '#fixture',\n      captureError((err, results) => {\n        assert.isNull(err);\n        assert.lengthOf(results.incomplete, 2);\n        assert.equal(results.incomplete[0].id, 'incomplete-1');\n        assert.equal(results.incomplete[1].id, 'incomplete-2');\n        assert.isNotNull(results.incomplete[1].error);\n        done();\n      }, done)\n    );\n  });\n\n  it('should resolve to cantTell if an error occurs inside frame rules', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'incomplete-1',\n          selector: '.nogo',\n          none: ['undeffed']\n        },\n        {\n          id: 'incomplete-2',\n          selector: '.nogo',\n          none: ['thrower']\n        }\n      ],\n      checks: [\n        { id: 'undeffed', evaluate: () => false },\n        { id: 'thrower', evaluate: () => false }\n      ]\n    });\n\n    iframeReady(\n      '../mock/frames/rule-error.html',\n      fixture,\n      'context-test',\n      () => {\n        axe.run(\n          '#fixture',\n          captureError((err, results) => {\n            assert.isNull(err);\n            assert.lengthOf(results.incomplete, 2);\n            assert.equal(results.incomplete[0].id, 'incomplete-1');\n            assert.equal(results.incomplete[1].id, 'incomplete-2');\n            assert.isNotNull(results.incomplete[1].error);\n            done();\n          }, done)\n        );\n      }\n    );\n  });\n\n  it('should cascade `no elements found` errors in frames to reject run_rules', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'invalidRule'\n        }\n      ],\n      checks: [],\n      messages: {}\n    });\n    fixture.innerHTML = '<div id=\"outer\"></div>';\n    let outer = document.getElementById('outer');\n\n    iframeReady('../mock/frames/context.html', outer, 'target', () => {\n      axe._runRules(\n        [['#target', '#elementNotFound']],\n        {},\n        function resolve() {\n          done(new Error('frame should have thrown an error'));\n        },\n        captureError(function reject(err) {\n          assert.instanceOf(err, Error);\n          assert.include(\n            err.message,\n            'No elements found for include in frame Context'\n          );\n          done();\n        }, done)\n      );\n    });\n  });\n\n  it('should not call reject when the resolve throws', done => {\n    let rejectCalled = false;\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    function resolve() {\n      setTimeout(() => {\n        assert.isFalse(rejectCalled);\n        axe.log = log;\n        done();\n      }, 20);\n      throw new Error('err');\n    }\n    function reject() {\n      rejectCalled = true;\n    }\n\n    let log = axe.log;\n    axe.log = e => {\n      assert.equal(e.message, 'err');\n      axe.log = log;\n    };\n    axe._runRules(document, {}, resolve, reject);\n  });\n\n  it('should ignore iframes if `iframes` === false', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    let frame = document.createElement('iframe');\n    frame.src = '../mock/frames/frame-frame.html';\n\n    frame.addEventListener('load', () => {\n      setTimeout(() => {\n        axe._runRules(\n          document,\n          { iframes: false, elementRef: true },\n          captureError(r => {\n            assert.lengthOf(r[0].passes, 1);\n            assert.equal(\n              r[0].passes[0].node.element.ownerDocument,\n              document,\n              'Result should not be in an iframe'\n            );\n            done();\n          }, done),\n          isNotCalled\n        );\n      }, 500);\n    });\n    fixture.appendChild(frame);\n  });\n\n  it('should not fail if `include` / `exclude` is overwritten', done => {\n    function invalid() {\n      throw new Error('nope!');\n    }\n    Array.prototype.include = invalid;\n    Array.prototype.exclude = invalid;\n\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    axe._runRules(\n      [document],\n      {},\n      captureError(r => {\n        assert.lengthOf(r[0].passes, 1);\n\n        delete Array.prototype.include;\n        delete Array.prototype.exclude;\n        done();\n      }, done),\n      isNotCalled\n    );\n  });\n\n  it('should return a cleanup method', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    axe._runRules(\n      document,\n      {},\n      captureError(function resolve(out, cleanup) {\n        assert.isDefined(axe._tree);\n        assert.isDefined(axe._selectorData);\n\n        cleanup();\n        assert.isUndefined(axe._tree);\n        assert.isUndefined(axe._selectorData);\n        done();\n      }, done),\n      isNotCalled\n    );\n  });\n\n  it('should clear up axe._tree / axe._selectorData after an error', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'invalidRule'\n        }\n      ],\n      checks: [],\n      messages: {}\n    });\n\n    createFrames(() => {\n      setTimeout(() => {\n        axe._runRules(\n          document,\n          {},\n          isNotCalled,\n          captureError(() => {\n            assert.isUndefined(axe._tree);\n            assert.isUndefined(axe._selectorData);\n            done();\n          }, done)\n        );\n      }, 100);\n    });\n  });\n\n  // todo: see issue - https://github.com/dequelabs/axe-core/issues/2168\n  it.skip('should clear the memoized cache for each function', done => {\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: 'html',\n          any: ['html']\n        }\n      ],\n      checks: [{ id: 'html', evaluate: () => true }],\n      messages: {}\n    });\n\n    axe._runRules(\n      document,\n      {},\n      captureError(function resolve(out, cleanup) {\n        let called = false;\n        axe._memoizedFns = [\n          {\n            clear: () => {\n              called = true;\n            }\n          }\n        ];\n\n        cleanup();\n        assert.isTrue(called);\n        done();\n      }, done),\n      isNotCalled\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/public/run-virtual-rule.js",
    "content": "describe('axe.runVirtualRule', function () {\n  beforeEach(function () {\n    axe._load({\n      rules: [\n        {\n          id: 'test',\n          selector: '*',\n          none: ['fred']\n        }\n      ],\n      checks: [\n        {\n          id: 'fred',\n          evaluate: function () {\n            return true;\n          }\n        }\n      ]\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should throw if the rule does not exist', function () {\n    axe._audit.rules = [];\n    function fn() {\n      axe.runVirtualRule('aria-roles', { nodeName: 'div' });\n    }\n\n    assert.throws(fn);\n  });\n\n  it('should modify the rule to not excludeHidden', function () {\n    axe._audit.rules = [\n      {\n        id: 'aria-roles',\n        excludeHidden: true,\n        runSync: function () {\n          assert.isFalse(this.excludeHidden);\n\n          return {\n            id: 'aria-roles',\n            nodes: []\n          };\n        }\n      }\n    ];\n\n    axe.runVirtualRule('aria-roles', { nodeName: 'div' });\n  });\n\n  it('should not modify the original rule', function () {\n    axe._audit.rules = [\n      {\n        id: 'aria-roles',\n        excludeHidden: true,\n        runSync: function () {\n          assert.notEqual(this, axe._audit.rules[0]);\n\n          return {\n            id: 'aria-roles',\n            nodes: []\n          };\n        }\n      }\n    ];\n\n    axe.runVirtualRule('aria-roles', { nodeName: 'div' });\n  });\n\n  it('should call rule.runSync', function () {\n    var called = false;\n    axe._audit.rules = [\n      {\n        id: 'aria-roles',\n        runSync: function () {\n          called = true;\n          return {\n            id: 'aria-roles',\n            nodes: []\n          };\n        }\n      }\n    ];\n\n    axe.runVirtualRule('aria-roles', { nodeName: 'div' });\n    assert.isTrue(called);\n  });\n\n  describe('context', () => {\n    const { Context } = axe._thisWillBeDeletedDoNotUse.base;\n    it('passes context with vNode included to rule.runSync', function () {\n      var node = new axe.SerialVirtualNode({ nodeName: 'div' });\n      axe._audit.rules = [\n        {\n          id: 'aria-roles',\n          runSync: function (context) {\n            assert.equal(typeof context, 'object');\n            assert.isTrue(Array.isArray(context.include));\n            assert.equal(context.include[0], node);\n\n            return {\n              id: 'aria-roles',\n              nodes: []\n            };\n          }\n        }\n      ];\n\n      axe.runVirtualRule('aria-roles', node);\n    });\n\n    it('has all properties a normal context has', () => {\n      const contextProps = Object.entries(new Context())\n        .filter(arg => typeof arg[1] !== 'function')\n        .map(([key]) => key)\n        .sort();\n\n      var node = new axe.SerialVirtualNode({ nodeName: 'div' });\n      axe._audit.rules = [\n        {\n          id: 'aria-roles',\n          runSync: function (context) {\n            const virtualContextProps = Object.keys(context).sort();\n            assert.deepEqual(virtualContextProps, contextProps);\n            return {\n              id: 'aria-roles',\n              nodes: []\n            };\n          }\n        }\n      ];\n      axe.runVirtualRule('aria-roles', node);\n    });\n  });\n\n  it('should pass through options to rule.runSync', function () {\n    axe._audit.rules = [\n      {\n        id: 'aria-roles',\n        runSync: function (context, options) {\n          assert.equal(options.foo, 'bar');\n\n          return {\n            id: 'aria-roles',\n            nodes: []\n          };\n        }\n      }\n    ];\n\n    axe.runVirtualRule('aria-roles', { nodeName: 'div' }, { foo: 'bar' });\n  });\n\n  it('should convert a serialised node into a VirtualNode', function () {\n    var serialNode = {\n      nodeName: 'div',\n      foo: 'bar',\n      attributes: {\n        bar: 'baz'\n      }\n    };\n    axe._audit.rules = [\n      {\n        id: 'aria-roles',\n        runSync: function (context) {\n          var node = context.include[0];\n          assert.instanceOf(node, axe.AbstractVirtualNode);\n          assert.equal(node.props.foo, 'bar');\n          assert.equal(node.attr('bar'), 'baz');\n\n          return {\n            id: 'aria-roles',\n            nodes: []\n          };\n        }\n      }\n    ];\n\n    axe.runVirtualRule('aria-roles', serialNode);\n  });\n\n  it('should return correct structure', function () {\n    var results = axe.runVirtualRule('test', { nodeName: 'div' });\n    assert.isDefined(results.violations);\n    assert.isDefined(results.passes);\n    assert.isDefined(results.incomplete);\n    assert.isDefined(results.inapplicable);\n    assert.isDefined(results.testEngine);\n    assert.isDefined(results.toolOptions);\n  });\n});\n"
  },
  {
    "path": "test/core/public/run.js",
    "content": "describe('axe.run', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var noop = function () {};\n  var origRunRules = axe._runRules;\n  var captureError = axe.testUtils.captureError;\n\n  beforeEach(function () {\n    axe._load({\n      rules: [\n        {\n          id: 'test',\n          selector: '*',\n          none: ['fred']\n        }\n      ],\n      checks: [\n        {\n          id: 'fred',\n          evaluate: function (node) {\n            this.relatedNodes([node]);\n            return true;\n          }\n        }\n      ]\n    });\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._audit = null;\n    axe._runRules = origRunRules;\n    axe._running = false;\n  });\n\n  it('takes context, options and callback as parameters', function (done) {\n    fixture.innerHTML = '<div id=\"t1\"></div>';\n    var options = {\n      runOnly: {\n        type: 'rule',\n        values: ['test']\n      }\n    };\n\n    axe.run(['#t1'], options, function () {\n      assert.ok(true, 'test completed');\n      done();\n    });\n  });\n\n  it('uses document as content if it is not specified', function (done) {\n    axe._runRules = function (ctxt) {\n      assert.equal(ctxt, document);\n      done();\n    };\n\n    axe.run({ someOption: true }, noop);\n  });\n\n  it('uses an object as options if it is not specified', function (done) {\n    axe._runRules = function (ctxt, opt) {\n      assert.isObject(opt);\n      done();\n    };\n    axe.run(document, noop);\n  });\n\n  it('does not mutate the options object', function (done) {\n    var options = {};\n    axe.run(options, function () {\n      assert.deepEqual(options, {});\n      done();\n    });\n  });\n\n  it('works with performance logging enabled', function (done) {\n    axe.run(document, { performanceTimer: true }, function (err, result) {\n      assert.isObject(result);\n      done();\n    });\n  });\n\n  describe('identifies context objects', () => {\n    it('based on the include property', done => {\n      axe._runRules = ctxt => {\n        assert.deepEqual(ctxt, { include: '#BoggyB' });\n        done();\n      };\n      axe.run({ include: '#BoggyB' }, noop);\n    });\n\n    it('based on the exclude property', done => {\n      axe._runRules = ctxt => {\n        assert.deepEqual(ctxt, { exclude: '#BoggyB' });\n        done();\n      };\n      axe.run({ exclude: '#BoggyB' }, noop);\n    });\n\n    it('based on the fromFrames property', done => {\n      axe._runRules = ctxt => {\n        assert.deepEqual(ctxt, { fromFrames: ['#myFrame'] });\n        done();\n      };\n      axe.run({ fromFrames: ['#myFrame'] }, noop);\n    });\n\n    it('based on the fromShadowDom property', done => {\n      axe._runRules = ctxt => {\n        assert.deepEqual(ctxt, { fromShadowDom: ['#myFrame'] });\n        done();\n      };\n      axe.run({ fromShadowDom: ['#myFrame'] }, noop);\n    });\n\n    it('ignores objects with none of those properties', done => {\n      axe._runRules = (ctxt, opt) => {\n        assert.deepEqual(opt.HHG, 'hallelujah');\n        done();\n      };\n      axe.run({ HHG: 'hallelujah' }, noop);\n    });\n  });\n\n  it('does not fail if no callback is specified', function (done) {\n    assert.doesNotThrow(function () {\n      axe.run(done);\n    });\n  });\n\n  it('should error if axe is already running', function (done) {\n    axe.run(noop);\n    axe.run(function (err) {\n      assert.isTrue(err.indexOf('Axe is already running') !== -1);\n      done();\n    });\n  });\n\n  describe('callback', function () {\n    it('gives errors to the first argument on the callback', function (done) {\n      axe._runRules = function (ctxt, opt, resolve, reject) {\n        axe._runRules = origRunRules;\n        reject('Ninja rope!');\n      };\n\n      axe.run({ reporter: 'raw' }, function (err) {\n        assert.equal(err, 'Ninja rope!');\n        done();\n      });\n    });\n\n    it('gives results to the second argument on the callback', function (done) {\n      axe._runRules = function (ctxt, opt, resolve) {\n        axe._runRules = origRunRules;\n        resolve('MB Bomb', noop);\n      };\n\n      axe.run({ reporter: 'raw' }, function (err, result) {\n        assert.equal(err, null);\n        assert.equal(result, 'MB Bomb');\n        done();\n      });\n    });\n\n    it('does not run the callback twice if it throws', function (done) {\n      var calls = 0;\n      axe._runRules = function (ctxt, opt, resolve) {\n        resolve([], noop);\n      };\n\n      var log = axe.log;\n      axe.log = function (e) {\n        assert.equal(e.message, 'err');\n        axe.log = log;\n      };\n      axe.run(function () {\n        calls += 1;\n        if (calls === 1) {\n          setTimeout(function () {\n            assert.equal(calls, 1);\n            axe.log = log;\n            done();\n          }, 20);\n        }\n        throw new Error('err');\n      });\n    });\n\n    it('is called after cleanup', function (done) {\n      var isClean = false;\n      axe._runRules = function (ctxt, opt, resolve) {\n        axe._runRules = origRunRules;\n        // Check that cleanup is called before the callback is executed\n        resolve('MB Bomb', function cleanup() {\n          isClean = true;\n        });\n      };\n\n      axe.run({ reporter: 'raw' }, function () {\n        assert.isTrue(isClean, 'cleanup must be called first');\n        done();\n      });\n    });\n\n    it('rejects with sync reporter errors', done => {\n      axe.addReporter('throwing', () => {\n        throw new Error('Something went wrong');\n      });\n      axe.run({ reporter: 'throwing' }, err => {\n        assert.equal(err.message, 'Something went wrong');\n        done();\n      });\n    });\n\n    it('rejects with async reporter errors', done => {\n      axe.addReporter('throwing', (results, options, resolve, reject) => {\n        setTimeout(() => {\n          reject(new Error('Something went wrong'));\n        }, 10);\n      });\n      axe.run({ reporter: 'throwing' }, err => {\n        assert.equal(err.message, 'Something went wrong');\n        done();\n      });\n    });\n  });\n\n  describe('promise result', function () {\n    /*eslint indent: 0*/\n    var promiseIt = window.Promise ? it : it.skip;\n\n    promiseIt('returns an error to catch if axe fails', function (done) {\n      axe._runRules = function (ctxt, opt, resolve, reject) {\n        axe._runRules = origRunRules;\n        reject('I surrender!');\n      };\n\n      var p = axe.run({ reporter: 'raw' });\n      p.then(noop).catch(function (err) {\n        assert.equal(err, 'I surrender!');\n        done();\n      });\n\n      assert.instanceOf(p, window.Promise);\n    });\n\n    promiseIt('returns a promise if no callback was given', function (done) {\n      axe._runRules = function (ctxt, opt, resolve) {\n        axe._runRules = origRunRules;\n        resolve('World party', noop);\n      };\n\n      var p = axe.run({ reporter: 'raw' });\n      p.then(function (result) {\n        assert.equal(result, 'World party');\n        done();\n      });\n\n      assert.instanceOf(p, window.Promise);\n    });\n\n    promiseIt('does not error if then() throws', function (done) {\n      axe._runRules = function (ctxt, opt, resolve) {\n        resolve([], noop);\n      };\n\n      axe\n        .run()\n        .then(\n          function () {\n            throw new Error('err');\n          },\n          function (e) {\n            assert.isNotOk(e, 'Caught callback error in the wrong place');\n            done();\n          }\n        )\n        .catch(function (e) {\n          assert.equal(e.message, 'err');\n          done();\n        });\n    });\n\n    promiseIt('is called after cleanup', function (done) {\n      var isClean = false;\n      axe._runRules = function (ctxt, opt, resolve) {\n        axe._runRules = origRunRules;\n        // Check that cleanup is called before the callback is executed\n        resolve('MB Bomb', function cleanup() {\n          isClean = true;\n        });\n      };\n\n      axe\n        .run({ reporter: 'raw' })\n        .then(function () {\n          assert(isClean, 'cleanup must be called first');\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  describe('option reporter', function () {\n    it('sets v1 as the default reporter if audit.reporter is null', function (done) {\n      axe._runRules = function (ctxt, opt) {\n        assert.equal(opt.reporter, 'v1');\n        axe._runRules = origRunRules;\n        done();\n      };\n      axe._audit.reporter = null;\n      axe.run(document, noop);\n    });\n\n    it('uses the audit.reporter if no reporter is set in options', function (done) {\n      axe._runRules = function (ctxt, opt) {\n        assert.equal(opt.reporter, 'raw');\n        axe._runRules = origRunRules;\n        done();\n      };\n      axe._audit.reporter = 'raw';\n      axe.run(document, noop);\n    });\n\n    it('does not override if another reporter is set', function (done) {\n      axe._runRules = function (ctxt, opt) {\n        assert.equal(opt.reporter, 'raw');\n        axe._runRules = origRunRules;\n        done();\n      };\n      axe._audit.reporter = null;\n      axe.run(document, { reporter: 'raw' }, noop);\n    });\n  });\n\n  describe('option xpath', function () {\n    it('returns no xpath if the xpath option is not set', function (done) {\n      axe.run('#fixture', function (err, result) {\n        assert.isUndefined(result.violations[0].nodes[0].xpath);\n        done();\n      });\n    });\n\n    it('returns the xpath if the xpath option is true', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          xpath: true\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(result.violations[0].nodes[0].xpath, [\n            \"//div[@id='fixture']\"\n          ]);\n          done();\n        }, done)\n      );\n    });\n\n    it('returns xpath on related nodes', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          xpath: true\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(\n            result.violations[0].nodes[0].none[0].relatedNodes[0].xpath,\n            [\"//div[@id='fixture']\"]\n          );\n          done();\n        }, done)\n      );\n    });\n\n    it('returns the xpath on any reporter', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          xpath: true,\n          reporter: 'no-passes'\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(result.violations[0].nodes[0].xpath, [\n            \"//div[@id='fixture']\"\n          ]);\n          done();\n        }, done)\n      );\n    });\n  });\n\n  describe('option absolutePaths', function () {\n    it('returns relative paths when falsy', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          absolutePaths: 0\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(result.violations[0].nodes[0].target, ['#fixture']);\n          done();\n        }, done)\n      );\n    });\n\n    it('returns absolute paths when truthy', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          absolutePaths: 'yes please'\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(result.violations[0].nodes[0].target, [\n            'html > body > #fixture'\n          ]);\n          done();\n        }, done)\n      );\n    });\n\n    it('returns absolute paths on related nodes', function (done) {\n      axe.run(\n        '#fixture',\n        {\n          absolutePaths: true\n        },\n        captureError(function (err, result) {\n          assert.deepEqual(\n            result.violations[0].nodes[0].none[0].relatedNodes[0].target,\n            ['html > body > #fixture']\n          );\n          done();\n        }, done)\n      );\n    });\n  });\n});\n\ndescribe('axe.run iframes', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var origRunRules = axe._runRules;\n  var captureError = axe.testUtils.captureError;\n\n  beforeEach(function () {\n    fixture.innerHTML = '<div id=\"target\">Target in top frame</div>';\n    axe._load({\n      rules: [\n        {\n          id: 'html',\n          selector: '#target',\n          none: ['fred']\n        }\n      ],\n      checks: [\n        {\n          id: 'fred',\n          evaluate: function () {\n            return true;\n          }\n        }\n      ]\n    });\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._audit = null;\n    axe._runRules = origRunRules;\n  });\n\n  it('includes iframes by default', function (done) {\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      axe.run(\n        '#fixture',\n        {},\n        captureError(function (err, result) {\n          assert.equal(result.violations.length, 1);\n          var violation = result.violations[0];\n          assert.equal(\n            violation.nodes.length,\n            2,\n            'one node for top frame, one for iframe'\n          );\n          assert.isTrue(\n            violation.nodes.some(function (node) {\n              return node.target.length === 1 && node.target[0] === '#target';\n            }),\n            'one result from top frame'\n          );\n          assert.isTrue(\n            violation.nodes.some(function (node) {\n              return node.target.length === 2 && node.target[0] === 'iframe';\n            }),\n            'one result from iframe'\n          );\n          done();\n        }, done)\n      );\n    });\n\n    frame.src = '../mock/frames/test.html';\n    fixture.appendChild(frame);\n  });\n\n  it('excludes iframes if iframes is false', function (done) {\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      axe.run(\n        '#fixture',\n        { iframes: false },\n        captureError(function (err, result) {\n          assert.equal(result.violations.length, 1);\n          var violation = result.violations[0];\n          assert.equal(violation.nodes.length, 1, 'only top frame');\n          assert.equal(violation.nodes[0].target.length, 1);\n          assert.equal(violation.nodes[0].target[0], '#target');\n          done();\n        }, done)\n      );\n    });\n\n    frame.src = '../mock/frames/test.html';\n    fixture.appendChild(frame);\n  });\n\n  it('ignores unexpected messages from non-axe iframes', function (done) {\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      axe.run(\n        '#fixture',\n        {},\n        captureError(function (err, result) {\n          assert.isNull(err);\n          assert.equal(result.violations.length, 1);\n          done();\n        }, done)\n      );\n    });\n\n    frame.src = '../mock/frames/with-echo.html';\n    fixture.appendChild(frame);\n  });\n\n  it('ignores unexpected messages from axe iframes', function (done) {\n    var frame = document.createElement('iframe');\n\n    frame.addEventListener('load', function () {\n      axe.run(\n        '#fixture',\n        {},\n        captureError(function (err, result) {\n          assert.isNull(err);\n          assert.equal(result.violations.length, 1);\n          done();\n        }, done)\n      );\n    });\n\n    frame.src = '../mock/frames/with-echo-axe.html';\n    fixture.appendChild(frame);\n  });\n});\n"
  },
  {
    "path": "test/core/public/setup.js",
    "content": "describe('axe.setup', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe.teardown();\n  });\n\n  it('should setup the tree', function () {\n    axe._tree = undefined;\n    axe.setup();\n    assert.exists(axe._tree);\n  });\n\n  it('should default the tree to use html element', function () {\n    axe.setup();\n    assert.equal(axe._tree[0].actualNode, document.documentElement);\n  });\n\n  it('should use the passed in node as the root of the tree', function () {\n    axe.setup(document.body);\n    assert.equal(axe._tree[0].actualNode, document.body);\n  });\n\n  it('should return the root node', function () {\n    var vNode = axe.setup(document.body);\n    assert.equal(vNode.actualNode, document.body);\n  });\n\n  it('should setup selector data', function () {\n    axe._selectorData = undefined;\n    axe.setup();\n    assert.exists(axe._selectorData);\n  });\n\n  it('takes documentElement when passed the document', () => {\n    axe.setup(document);\n    assert.equal(axe._tree[0].actualNode, document.documentElement);\n  });\n\n  it('should throw if called twice in a row', function () {\n    function fn() {\n      axe.setup();\n      axe.setup();\n    }\n\n    assert.throws(fn);\n  });\n});\n"
  },
  {
    "path": "test/core/public/teardown.js",
    "content": "describe('axe.teardown', function () {\n  'use strict';\n\n  it('should reset the tree', function () {\n    axe._tree = 'foo';\n    axe.teardown();\n    assert.isUndefined(axe._tree);\n  });\n\n  it('should reset selector data', function () {\n    axe._selectorData = 'foo';\n    axe.teardown();\n    assert.isUndefined(axe._selectorData);\n  });\n\n  it('should reset selector data', function () {\n    axe._selectCache = 'foo';\n    axe.teardown();\n    assert.isUndefined(axe._selectCache);\n  });\n\n  it('should reset memozied functions', function () {\n    var orgFn = axe._memoizedFns[0];\n    var called = false;\n    axe._memoizedFns[0] = {\n      clear: function () {\n        called = true;\n      }\n    };\n    axe.teardown();\n    assert.isTrue(called);\n    axe._memoizedFns[0] = orgFn;\n  });\n\n  it('should reset the cache', function () {\n    var orgFn = axe._cache.clear;\n    var called = false;\n    axe._cache.clear = function () {\n      called = true;\n    };\n    axe.teardown();\n    assert.isTrue(called);\n    axe._cache.clear = orgFn;\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/helpers/000_runfirst.js",
    "content": "// HACK: make \"helpers\" a global, as the tests rely on it.\nhelpers = axe._thisWillBeDeletedDoNotUse.helpers;\n"
  },
  {
    "path": "test/core/reporters/helpers/failure-summary.js",
    "content": "describe('helpers.failureSummary', function () {\n  'use strict';\n  beforeEach(function () {\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {\n        failureSummaries: {\n          none: {\n            failureMessage: function anonymous(it) {\n              var out = 'Fix all of the following: \\n';\n              var arr1 = it;\n              if (arr1) {\n                var value,\n                  i1 = -1,\n                  l1 = arr1.length - 1;\n                while (i1 < l1) {\n                  value = arr1[(i1 += 1)];\n                  out += ' ' + value + '\\n';\n                }\n              }\n              return out;\n            }\n          },\n          all: {\n            failureMessage: function anonymous() {\n              throw new Error('shouldnt be executed');\n            }\n          },\n          any: {\n            failureMessage: function anonymous(it) {\n              var out = 'Fix any of the following: \\n';\n              var arr1 = it;\n              if (arr1) {\n                var value,\n                  i1 = -1,\n                  l1 = arr1.length - 1;\n                while (i1 < l1) {\n                  value = arr1[(i1 += 1)];\n                  out += ' ' + value + '\\n';\n                }\n              }\n              return out;\n            }\n          }\n        }\n      }\n    });\n  });\n\n  it('should concatenate none and all', function () {\n    var summary = helpers.failureSummary({\n      result: 'failed',\n      any: [],\n      all: [\n        {\n          id: '3',\n          message: '3'\n        }\n      ],\n      none: [\n        {\n          id: '1',\n          message: '1'\n        },\n        {\n          id: '2',\n          message: '2'\n        }\n      ]\n    });\n\n    assert.equal(summary, 'Fix all of the following: \\n 1\\n 2\\n 3\\n');\n  });\n\n  it('should return a list of ANYs if none return true', function () {\n    var summary = helpers.failureSummary({\n      result: 'failed',\n      any: [\n        {\n          id: '1',\n          message: '1'\n        },\n        {\n          id: '2',\n          message: '2'\n        },\n        {\n          id: '3',\n          message: '3'\n        }\n      ],\n      none: [],\n      all: []\n    });\n\n    assert.equal(summary, 'Fix any of the following: \\n 1\\n 2\\n 3\\n');\n  });\n\n  it('should concatenate anys', function () {\n    var summary = helpers.failureSummary({\n      result: 'failed',\n      any: [\n        {\n          id: '1',\n          message: '1'\n        },\n        {\n          id: '2',\n          message: '2'\n        },\n        {\n          id: '3',\n          message: '3'\n        }\n      ],\n      all: [],\n      none: [\n        {\n          id: '4',\n          message: '4'\n        }\n      ]\n    });\n\n    assert.equal(\n      summary,\n      'Fix all of the following: \\n 4\\n\\n\\nFix any of the following: \\n 1\\n 2\\n 3\\n'\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/helpers/incomplete-fallback-msg.js",
    "content": "describe('helpers.incompleteFallbackMessage', function () {\n  'use strict';\n\n  it('returns a non-empty string by default', function () {\n    var summary = helpers.incompleteFallbackMessage();\n    assert.typeOf(summary, 'string');\n    assert.notEqual(summary, '');\n  });\n\n  it('should return a string', function () {\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {\n        incompleteFallbackMessage: 'Dogs are the best'\n      }\n    });\n    var summary = helpers.incompleteFallbackMessage();\n    assert.equal(summary, 'Dogs are the best');\n  });\n\n  it('should handle doT.js template function', function () {\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {\n        incompleteFallbackMessage: function anonymous() {\n          return 'Dogs are the best';\n        }\n      }\n    });\n\n    var summary = helpers.incompleteFallbackMessage();\n    assert.equal(summary, 'Dogs are the best');\n  });\n\n  describe('when passed an invalid value', function () {\n    it('returns `` when set to an object', function () {\n      axe._load({\n        messages: {},\n        rules: [],\n        data: {\n          incompleteFallbackMessage: {}\n        }\n      });\n      assert.equal(helpers.incompleteFallbackMessage(), '');\n    });\n\n    it('returns `` when set to null', function () {\n      axe._load({\n        messages: {},\n        rules: [],\n        data: {\n          incompleteFallbackMessage: null\n        }\n      });\n      assert.equal(helpers.incompleteFallbackMessage(), '');\n    });\n\n    it('returns `` when set to undefined', function () {\n      axe._load({\n        messages: {},\n        rules: [],\n        data: {\n          incompleteFallbackMessage: undefined\n        }\n      });\n      assert.equal(helpers.incompleteFallbackMessage(), '');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/helpers/process-aggregate.js",
    "content": "describe('helpers.processAggregate', function () {\n  'use strict';\n  var results, options;\n  const helpers = axe._thisWillBeDeletedDoNotUse.helpers;\n  const fixture = document.getElementById('fixture');\n\n  beforeEach(function () {\n    results = [\n      {\n        id: 'passed-rule',\n        passes: [\n          {\n            result: 'passed',\n            node: {\n              element: document.createElement('div'),\n              selector: ['header > .thing'],\n              source: '<div class=\"thing\">Thing</div>',\n              xpath: ['/header/div[@class=\"thing\"]'],\n              ancestry: ['html > body > header > div']\n            },\n            any: [\n              {\n                id: 'passed-rule',\n                relatedNodes: [\n                  {\n                    element: document.createElement('div'),\n                    selector: ['footer > .thing'],\n                    source: '<div class=\"thing\">Thing</div>',\n                    xpath: ['/footer/div[@class=\"thing\"]'],\n                    ancestry: ['html > body > footer > div']\n                  }\n                ]\n              }\n            ],\n            all: [],\n            none: []\n          },\n          {\n            result: 'passed',\n            node: {\n              element: document.createElement('div'),\n              selector: ['main > .thing'],\n              source: '<div class=\"thing\">Thing</div>',\n              xpath: ['/main/div[@class=\"thing\"]'],\n              ancestry: ['html > body > main > div']\n            },\n            any: [\n              {\n                id: 'passed-rule',\n                relatedNodes: [\n                  {\n                    element: document.createElement('div'),\n                    selector: ['footer > .thing'],\n                    source: '<div class=\"thing\">Thing</div>',\n                    xpath: ['/footer/div[@class=\"thing\"]'],\n                    ancestry: ['html > body > footer > div']\n                  }\n                ]\n              }\n            ],\n            all: [],\n            none: []\n          }\n        ],\n        inapplicable: [],\n        incomplete: [],\n        violations: []\n      },\n      {\n        id: 'failed-rule',\n        violations: [\n          {\n            result: 'failed',\n            node: {\n              selector: ['#dopel'],\n              source: ['<input id=\"dopel\"/>'],\n              xpath: '/main/input[@id=\"dopel\"]',\n              ancestry: ['html > body > main > input:nth-child(1)'],\n              fromFrame: true\n            },\n            any: [\n              {\n                id: 'failed-rule',\n                relatedNodes: [\n                  {\n                    element: document.createElement('input'),\n                    selector: ['#dopel'],\n                    source: ['<input id=\"dopel\"/>'],\n                    xpath: ['/main/input[@id=\"dopel\"]'],\n                    ancestry: ['html > body > main > input:nth-child(2)'],\n                    fromFrame: true\n                  }\n                ]\n              }\n            ],\n            all: [],\n            none: []\n          },\n          {\n            result: 'failed',\n            node: {\n              selector: ['#dopell'],\n              source: '<input id=\"dopell\"/>',\n              xpath: ['/header/input[@id=\"dopell\"]'],\n              ancestry: ['html > body > main > input:nth-child(1)'],\n              fromFrame: true\n            },\n            any: [\n              {\n                id: 'failed-rule',\n                relatedNodes: [\n                  {\n                    element: document.createElement('input'),\n                    selector: ['#dopell'],\n                    source: '<input id=\"dopell\"/>',\n                    xpath: ['/header/input[@id=\"dopell\"]'],\n                    ancestry: ['html > body > main > input:nth-child(2)'],\n                    fromFrame: true\n                  }\n                ]\n              }\n            ],\n            all: [],\n            none: []\n          }\n        ],\n        inapplicable: [],\n        passes: [],\n        incomplete: []\n      }\n    ];\n  });\n\n  it('should remove the `result` property from each node in each ruleResult', function () {\n    assert.isDefined(\n      results.find(function (r) {\n        return r.id === 'passed-rule';\n      }).passes[0].result\n    );\n\n    var resultObject = helpers.processAggregate(results, {});\n    var ruleResult = resultObject.passes.find(function (r) {\n      return r.id === 'passed-rule';\n    });\n    assert.isUndefined(ruleResult.nodes[0].result);\n  });\n\n  it('should remove the `node` property from each node in each ruleResult', function () {\n    assert.isDefined(\n      results.find(function (r) {\n        return r.id === 'passed-rule';\n      }).passes[0].node\n    );\n\n    var resultObject = helpers.processAggregate(results, {});\n    var ruleResult = resultObject.passes.find(function (r) {\n      return r.id === 'passed-rule';\n    });\n    assert.isUndefined(ruleResult.nodes[0].node);\n  });\n\n  it('handles when a relatedNode is undefined', () => {\n    // Add undefined to failed-rule\n    results[1].violations[0].any[0].relatedNodes.unshift(undefined);\n    const resultObject = helpers.processAggregate(results, {\n      xpath: true,\n      elementRef: true,\n      ancestry: true\n    });\n    const { relatedNodes } = resultObject.violations[0].nodes[0].any[0];\n    assert.deepEqual(relatedNodes[0], {\n      html: 'Undefined',\n      target: [':root'],\n      ancestry: [':root'],\n      xpath: ['/'],\n      element: null\n    });\n  });\n\n  describe('axe.configure({ noHtml: true })', () => {\n    afterEach(() => {\n      axe.reset();\n    });\n\n    it('sets html to null on nodes', () => {\n      axe.configure({ noHtml: true });\n      const { passes, violations } = helpers.processAggregate(results, {});\n      assert.isNull(passes[0].nodes[0].html);\n      assert.isNull(violations[0].nodes[0].html);\n    });\n\n    it('sets html to null on relatedNodes', () => {\n      axe.configure({ noHtml: true });\n      const { passes, violations } = helpers.processAggregate(results, {});\n      assert.isNull(passes[0].nodes[0].any[0].relatedNodes[0].html);\n      assert.isNull(violations[0].nodes[0].any[0].relatedNodes[0].html);\n    });\n  });\n\n  describe('`options` argument', function () {\n    describe('`resultTypes` option', function () {\n      it('should reduce the unwanted result types to 1 in the `resultObject`', function () {\n        var resultObject = helpers.processAggregate(results, {\n          resultTypes: ['violations']\n        });\n        assert.isDefined(resultObject.passes);\n        assert.equal(resultObject.passes[0].nodes.length, 1);\n        assert.isDefined(resultObject.violations);\n        assert.equal(resultObject.violations[0].nodes.length, 2);\n        resultObject = helpers.processAggregate(results, {\n          resultTypes: ['passes']\n        });\n        assert.equal(resultObject.passes[0].nodes.length, 2);\n        assert.equal(resultObject.violations[0].nodes.length, 1);\n        assert.isDefined(resultObject.incomplete);\n        assert.isDefined(resultObject.inapplicable);\n      });\n\n      it('should not compute selectors of filtered nodes', () => {\n        const dqElm = new axe.utils.DqElement(fixture);\n        Object.defineProperty(dqElm, 'ancestry', {\n          get() {\n            throw new Error('Should not be called');\n          }\n        });\n        results[0].passes[1].node = dqElm;\n        assert.doesNotThrow(() => {\n          helpers.processAggregate(results, {\n            resultTypes: ['violations']\n          });\n        });\n      });\n    });\n\n    describe('`elementRef` option', function () {\n      describe('when set to true', function () {\n        before(function () {\n          options = { elementRef: true };\n        });\n\n        describe(\"when node's, or relatedNode's, `fromFrame` equals false\", function () {\n          it('should add an `element` property to the subResult nodes or relatedNodes', function () {\n            var resultObject = helpers.processAggregate(results, options);\n            assert.isDefined(resultObject.passes[0].nodes[0].element);\n            assert.isDefined(\n              resultObject.passes[0].nodes[0].any[0].relatedNodes[0].element\n            );\n          });\n        });\n\n        describe(\"when node's, or relatedNode's, `fromFrame` equals true\", function () {\n          it('should NOT add an `element` property to the subResult nodes or relatedNodes', function () {\n            var resultObject = helpers.processAggregate(results, options);\n            assert.isUndefined(resultObject.violations[0].nodes[0].element);\n            assert.isUndefined(\n              resultObject.violations[0].nodes[0].any[0].relatedNodes[0].element\n            );\n          });\n        });\n      });\n\n      describe('when set to false', function () {\n        before(function () {\n          options = { elementRef: false };\n        });\n\n        it('should NOT add an `element` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, options);\n          assert.isUndefined(resultObject.passes[0].nodes[0].element);\n          assert.isUndefined(resultObject.violations[0].nodes[0].element);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].element\n          );\n          assert.isUndefined(\n            resultObject.violations[0].nodes[0].any[0].relatedNodes[0].element\n          );\n        });\n      });\n\n      describe('when not set at all', function () {\n        it('should NOT add an `element` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {});\n          assert.isUndefined(resultObject.passes[0].nodes[0].element);\n          assert.isUndefined(resultObject.violations[0].nodes[0].element);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].element\n          );\n          assert.isUndefined(\n            resultObject.violations[0].nodes[0].any[0].relatedNodes[0].element\n          );\n        });\n      });\n    });\n\n    describe('`selectors` option', function () {\n      describe('when set to false', function () {\n        before(function () {\n          options = { selectors: false };\n        });\n\n        describe(\"when node's, or relatedNode's, `fromFrame` equals true\", function () {\n          it('should add a `target` property to the subResult nodes or relatedNodes', function () {\n            var resultObject = helpers.processAggregate(results, options);\n            assert.isDefined(resultObject.violations[0].nodes[0].target);\n            assert.isDefined(\n              resultObject.violations[0].nodes[0].any[0].relatedNodes[0].target\n            );\n          });\n        });\n\n        describe(\"when node's, or relatedNode's, `fromFrame` equals false\", function () {\n          it('should NOT add a `target` property to the subResult nodes or relatedNodes', function () {\n            var resultObject = helpers.processAggregate(results, options);\n            assert.isUndefined(resultObject.passes[0].nodes[0].target);\n            assert.isUndefined(\n              resultObject.passes[0].nodes[0].any[0].relatedNodes[0].target\n            );\n          });\n\n          it('should not call the nodes selector property', () => {\n            const dqElm = new axe.utils.DqElement(fixture);\n            Object.defineProperty(dqElm, 'selector', {\n              get() {\n                throw new Error('Should not be called');\n              }\n            });\n            results[0].passes[0].node = dqElm;\n            assert.doesNotThrow(() => {\n              helpers.processAggregate(results, options);\n            });\n          });\n        });\n      });\n\n      describe('when set to true', function () {\n        before(function () {\n          options = { selectors: true };\n        });\n\n        it('should add a `target` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, options);\n          assert.isDefined(resultObject.passes[0].nodes[0].target);\n          assert.isDefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].target\n          );\n        });\n      });\n\n      describe('when not set at all', function () {\n        it('should add a `target` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {});\n          assert.isDefined(resultObject.passes[0].nodes[0].target);\n          assert.isDefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].target\n          );\n        });\n      });\n    });\n\n    describe('`ancestry` option', function () {\n      describe('when set to true', function () {\n        it('should add an `ancestry` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {\n            ancestry: true\n          });\n          assert.isDefined(resultObject.passes[0].nodes[0].ancestry);\n          assert.isDefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].ancestry\n          );\n        });\n      });\n\n      describe('when set to false', function () {\n        it('should NOT add an `ancestry` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {\n            ancestry: false\n          });\n          assert.isUndefined(resultObject.passes[0].nodes[0].ancestry);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].ancestry\n          );\n        });\n\n        it('should not call the nodes ancestry property', () => {\n          const dqElm = new axe.utils.DqElement(fixture, options, {\n            selector: ['div'] // prevent axe._selectorData error\n          });\n          Object.defineProperty(dqElm, 'ancestry', {\n            get() {\n              throw new Error('Should not be called');\n            }\n          });\n          results[0].passes[0].node = dqElm;\n          assert.doesNotThrow(() => {\n            helpers.processAggregate(results, options);\n          });\n        });\n      });\n\n      describe('when not set at all', function () {\n        it('should NOT add an `ancestry` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {});\n          assert.isUndefined(resultObject.passes[0].nodes[0].ancestry);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].ancestry\n          );\n        });\n      });\n    });\n\n    describe('`xpath` option', function () {\n      describe('when set to true', function () {\n        before(function () {\n          options = { xpath: true };\n        });\n\n        it('should add an `xpath` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, options);\n          assert.isDefined(resultObject.passes[0].nodes[0].xpath);\n          assert.isDefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].xpath\n          );\n        });\n      });\n\n      describe('when set to false', function () {\n        before(function () {\n          options = { xpath: false };\n        });\n\n        it('should NOT add an `xpath` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, options);\n          assert.isUndefined(resultObject.passes[0].nodes[0].xpath);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].xpath\n          );\n        });\n      });\n\n      describe('when not set at all', function () {\n        it('should NOT add an `xpath` property to the subResult nodes or relatedNodes', function () {\n          var resultObject = helpers.processAggregate(results, {});\n          assert.isUndefined(resultObject.passes[0].nodes[0].xpath);\n          assert.isUndefined(\n            resultObject.passes[0].nodes[0].any[0].relatedNodes[0].xpath\n          );\n        });\n\n        it('should not call the nodes xpath property', () => {\n          const dqElm = new axe.utils.DqElement(fixture, options, {\n            selector: ['div'] // prevent axe._selectorData error\n          });\n          Object.defineProperty(dqElm, 'xpath', {\n            get() {\n              throw new Error('Should not be called');\n            }\n          });\n          results[0].passes[0].node = dqElm;\n          assert.doesNotThrow(() => {\n            helpers.processAggregate(results, options);\n          });\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/na.js",
    "content": "describe('reporters - na', function () {\n  'use strict';\n  var runResults,\n    _results = [\n      {\n        id: 'noMatch',\n        helpUrl: 'somewhere',\n        description: 'stuff',\n        result: 'inapplicable',\n        impact: null,\n        tags: ['tag3'],\n        violations: [],\n        passes: []\n      },\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        result: 'passed',\n        impact: null,\n        tags: ['tag1'],\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            impact: null,\n            any: [\n              {\n                result: true,\n                relatedNodes: [\n                  {\n                    selector: 'bob',\n                    source: 'fred'\n                  }\n                ],\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                relatedNodes: [\n                  {\n                    selector: 'joe',\n                    source: 'bob'\n                  }\n                ],\n                result: false,\n                data: 'pillock',\n                impact: 'cats'\n              },\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            any: [\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            none: [\n              {\n                relatedNodes: [],\n                result: false\n              }\n            ],\n            node: {\n              selector: ['q', 'r', 'pillock'],\n              source: '<pillock>george bush</pillock>'\n            },\n            impact: 'cats'\n          }\n        ]\n      }\n    ];\n\n  beforeEach(function () {\n    runResults = JSON.parse(JSON.stringify(_results));\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {}\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should merge the runRules results into violations, passes and inapplicable', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.isObject(results);\n      assert.isArray(results.violations);\n      assert.lengthOf(results.violations, 1);\n      assert.isArray(results.passes);\n      assert.lengthOf(results.passes, 1);\n      assert.isArray(results.inapplicable);\n      assert.lengthOf(results.inapplicable, 1);\n    });\n  });\n  it('should add the rule id to the rule result', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].id, 'idkStuff');\n      assert.equal(results.passes[0].id, 'gimmeLabel');\n      assert.equal(results.inapplicable[0].id, 'noMatch');\n    });\n  });\n  it('should add tags to the rule result', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.deepEqual(results.violations[0].tags, ['tag2']);\n      assert.deepEqual(results.passes[0].tags, ['tag1']);\n      assert.deepEqual(results.inapplicable[0].tags, ['tag3']);\n    });\n  });\n  it('should add the rule help to the rule result', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.ok(!results.violations[0].helpUrl);\n      assert.equal(results.passes[0].helpUrl, 'things');\n      assert.equal(results.inapplicable[0].helpUrl, 'somewhere');\n    });\n  });\n  it('should add the html to the node data', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.equal(\n        results.violations[0].nodes[0].html,\n        '<pillock>george bush</pillock>'\n      );\n      assert.equal(results.passes[0].nodes[0].html, '<minkey>chimp</minky>');\n    });\n  });\n  it('should add the target selector array to the node data', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'q',\n        'r',\n        'pillock'\n      ]);\n    });\n  });\n  it('should add the description to the rule result', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].description, 'something more nifty');\n      assert.equal(results.passes[0].description, 'something nifty');\n    });\n  });\n  it('should add the impact to the rule result', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].impact, 'cats');\n      assert.equal(results.violations[0].nodes[0].impact, 'cats');\n      assert.ok(!results.passes[0].impact);\n      assert.ok(!results.passes[0].nodes[0].impact);\n      assert.isNull(results.passes[0].impact);\n      assert.isNull(results.passes[0].nodes[0].impact);\n    });\n  });\n  it('should map relatedNodes', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.lengthOf(results.violations[0].nodes[0].all[0].relatedNodes, 1);\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].target,\n        'joe'\n      );\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].html,\n        'bob'\n      );\n\n      assert.lengthOf(results.passes[0].nodes[0].any[0].relatedNodes, 1);\n      assert.equal(\n        results.passes[0].nodes[0].any[0].relatedNodes[0].target,\n        'bob'\n      );\n      assert.equal(\n        results.passes[0].nodes[0].any[0].relatedNodes[0].html,\n        'fred'\n      );\n    });\n  });\n  it('should add environment data', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.isDefined(results.url);\n      assert.isDefined(results.timestamp);\n      assert.isDefined(results.testEnvironment);\n      assert.isDefined(results.testRunner);\n    });\n  });\n  it('should add toolOptions property', function () {\n    axe.getReporter('na')(runResults, {}, function (results) {\n      assert.isDefined(results.toolOptions);\n    });\n  });\n  it('uses the environmentData option instead of environment data if specified', function () {\n    var environmentData = {\n      myReporter: 'hello world'\n    };\n    axe.getReporter('na')(\n      runResults,\n      { environmentData: environmentData },\n      function (results) {\n        assert.equal(results.myReporter, 'hello world');\n        assert.isUndefined(results.url);\n        assert.isUndefined(results.timestamp);\n        assert.isUndefined(results.testEnvironment);\n        assert.isUndefined(results.testRunner);\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/no-passes.js",
    "content": "describe('reporters - no-passes', function () {\n  'use strict';\n  var runResults,\n    _results = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        tags: ['tag1'],\n        result: 'passed',\n        impact: null,\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                impact: null,\n                relatedNodes: [\n                  {\n                    selector: 'bob',\n                    source: 'fred'\n                  }\n                ],\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                relatedNodes: [\n                  {\n                    selector: 'joe',\n                    source: 'bob'\n                  }\n                ],\n                result: false,\n                data: 'pillock',\n                impact: 'cats'\n              },\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            any: [\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            none: [\n              {\n                relatedNodes: [],\n                result: false\n              }\n            ],\n            node: {\n              selector: ['q', 'r', 'pillock'],\n              source: '<pillock>george bush</pillock>'\n            },\n            impact: 'cats'\n          }\n        ]\n      }\n    ];\n  beforeEach(function () {\n    runResults = JSON.parse(JSON.stringify(_results));\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {}\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should merge the runRules results into violations and  exclude passes', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.isObject(results);\n      assert.isArray(results.violations);\n      assert.lengthOf(results.violations, 1);\n      assert.isUndefined(results.passes);\n    });\n  });\n  it('should add the rule id to the rule result', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].id, 'idkStuff');\n    });\n  });\n  it('should add tags to the rule result', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.deepEqual(results.violations[0].tags, ['tag2']);\n    });\n  });\n  it('should add the rule help to the rule result', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.isNotOk(results.violations[0].helpUrl);\n    });\n  });\n  it('should add the html to the node data', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.equal(\n        results.violations[0].nodes[0].html,\n        '<pillock>george bush</pillock>'\n      );\n    });\n  });\n  it('should add the target selector array to the node data', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'q',\n        'r',\n        'pillock'\n      ]);\n    });\n  });\n  it('should add the description to the rule result', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].description, 'something more nifty');\n    });\n  });\n  it('should add the impact to the rule result', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].impact, 'cats');\n      assert.equal(results.violations[0].nodes[0].impact, 'cats');\n    });\n  });\n  it('should map relatedNodes', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.lengthOf(results.violations[0].nodes[0].all[0].relatedNodes, 1);\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].target,\n        'joe'\n      );\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].html,\n        'bob'\n      );\n    });\n  });\n  it('should add environment data', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.isDefined(results.url);\n      assert.isDefined(results.timestamp);\n      assert.isDefined(results.testEnvironment);\n      assert.isDefined(results.testRunner);\n    });\n  });\n  it('should add toolOptions property', function () {\n    axe.getReporter('no-passes')(runResults, {}, function (results) {\n      assert.isDefined(results.toolOptions);\n    });\n  });\n  it('uses the environmentData option instead of environment data if specified', function () {\n    var environmentData = {\n      myReporter: 'hello world'\n    };\n    axe.getReporter('no-passes')(\n      runResults,\n      { environmentData: environmentData },\n      function (results) {\n        assert.equal(results.myReporter, 'hello world');\n        assert.isUndefined(results.url);\n        assert.isUndefined(results.timestamp);\n        assert.isUndefined(results.testEnvironment);\n        assert.isUndefined(results.testRunner);\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/raw-env.js",
    "content": "describe('reporters - raw-env', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  function createDqElement() {\n    var node = document.createElement('div');\n    fixture.appendChild(node);\n    return new axe.utils.DqElement(node);\n  }\n\n  var runResults;\n\n  beforeEach(function () {\n    runResults = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        tags: ['tag1'],\n        result: 'passed',\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                data: 'minkey',\n                relatedNodes: []\n              }\n            ],\n            all: [],\n            none: [],\n            node: createDqElement()\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                result: false,\n                data: 'pillock',\n                impact: 'cats',\n                relatedNodes: []\n              }\n            ],\n            any: [],\n            none: [],\n            node: createDqElement(),\n            impact: 'cats'\n          }\n        ]\n      },\n      {\n        id: 'bypass',\n        description: 'something even more nifty',\n        tags: ['tag3'],\n        impact: 'monkeys',\n        result: 'failed',\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            impact: 'monkeys',\n            none: [\n              {\n                data: 'foon',\n                impact: 'monkeys',\n                result: true,\n                relatedNodes: []\n              }\n            ],\n            any: [],\n            all: [],\n            node: createDqElement()\n          }\n        ]\n      },\n      {\n        id: 'blinky',\n        description: 'something awesome',\n        tags: ['tag4'],\n        violations: [],\n        result: 'passed',\n        passes: [\n          {\n            result: 'passed',\n            any: [],\n            all: [],\n            none: [\n              {\n                data: 'clueso',\n                result: true,\n                relatedNodes: []\n              }\n            ],\n            node: createDqElement()\n          }\n        ]\n      }\n    ];\n\n    axe.testUtils.fixtureSetup();\n\n    axe._load({});\n    axe._cache.set('selectorData', {});\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    sinon.restore();\n  });\n\n  it('should serialize DqElements (#1195)', function () {\n    axe.getReporter('rawEnv')(runResults, {}, function (results) {\n      for (var i = 0; i < results.length; i++) {\n        var result = results[i];\n        for (var j = 0; j < result.passes.length; j++) {\n          var p = result.passes[j];\n          assert.notInstanceOf(p.node, axe.utils.DqElement);\n        }\n      }\n    });\n  });\n\n  it('should pass env object', function () {\n    axe.getReporter('rawEnv')(runResults, {}, function (results) {\n      assert.isDefined(results.env);\n      assert.isDefined(results.env.url);\n      assert.isDefined(results.env.timestamp);\n      assert.isDefined(results.env.testEnvironment);\n      assert.isDefined(results.env.testRunner);\n    });\n  });\n\n  it('uses the environmentData option instead of environment data if specified', function () {\n    var environmentData = {\n      myReporter: 'hello world'\n    };\n    axe.getReporter('rawEnv')(\n      runResults,\n      { environmentData: environmentData },\n      function (results) {\n        assert.deepEqual(results.env, environmentData);\n      }\n    );\n  });\n\n  it('uses nodeSerializer', done => {\n    var rawReporter = axe.getReporter('rawEnv');\n    var spy = sinon.spy(axe.utils.nodeSerializer, 'mapRawNodeResults');\n    rawReporter(\n      runResults,\n      {},\n      function () {\n        assert.isTrue(spy.called);\n        done();\n      },\n      done\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/raw.js",
    "content": "describe('reporters - raw', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  function createDqElement() {\n    var node = document.createElement('div');\n    fixture.appendChild(node);\n    return new axe.utils.DqElement(node);\n  }\n\n  var runResults;\n\n  beforeEach(function () {\n    runResults = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        tags: ['tag1'],\n        result: 'passed',\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                data: 'minkey',\n                relatedNodes: []\n              }\n            ],\n            all: [],\n            none: [],\n            node: createDqElement()\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                result: false,\n                data: 'pillock',\n                impact: 'cats',\n                relatedNodes: []\n              }\n            ],\n            any: [],\n            none: [],\n            node: createDqElement(),\n            impact: 'cats'\n          }\n        ]\n      },\n      {\n        id: 'bypass',\n        description: 'something even more nifty',\n        tags: ['tag3'],\n        impact: 'monkeys',\n        result: 'failed',\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            impact: 'monkeys',\n            none: [\n              {\n                data: 'foon',\n                impact: 'monkeys',\n                result: true,\n                relatedNodes: []\n              }\n            ],\n            any: [],\n            all: [],\n            node: createDqElement()\n          }\n        ]\n      },\n      {\n        id: 'blinky',\n        description: 'something awesome',\n        tags: ['tag4'],\n        violations: [],\n        result: 'passed',\n        passes: [\n          {\n            result: 'passed',\n            any: [],\n            all: [],\n            none: [\n              {\n                data: 'clueso',\n                result: true,\n                relatedNodes: []\n              }\n            ],\n            node: createDqElement()\n          }\n        ]\n      }\n    ];\n\n    axe.testUtils.fixtureSetup();\n\n    axe._load({});\n    axe._cache.set('selectorData', {});\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    sinon.restore();\n  });\n\n  it('should serialize DqElements', function (done) {\n    axe.getReporter('rawEnv')(runResults, {}, function (results) {\n      for (var i = 0; i < results.length; i++) {\n        var result = results[i];\n        for (var j = 0; j < result.passes.length; j++) {\n          var p = result.passes[j];\n          assert.notInstanceOf(p.node, axe.utils.DqElement);\n        }\n      }\n      done();\n    });\n  });\n\n  it('does not throw on serialized nodes', function (done) {\n    var rawReporter = axe.getReporter('rawEnv');\n    rawReporter(runResults, {}, function (serializedResults) {\n      assert.doesNotThrow(function () {\n        rawReporter(serializedResults, {}, function () {\n          done();\n        });\n      });\n    });\n  });\n\n  it('uses nodeSerializer', done => {\n    var rawReporter = axe.getReporter('raw');\n    var spy = sinon.spy(axe.utils.nodeSerializer, 'mapRawNodeResults');\n    rawReporter(\n      runResults,\n      {},\n      function () {\n        assert.isTrue(spy.called);\n        done();\n      },\n      done\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/v1.js",
    "content": "describe('reporters - v1', function () {\n  'use strict';\n  var runResults,\n    _results = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        tags: ['tag1'],\n        result: 'passed',\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                result: false,\n                data: 'pillock',\n                impact: 'cats'\n              }\n            ],\n            any: [],\n            none: [],\n            node: {\n              selector: ['q', 'r', 'pillock'],\n              source: '<pillock>george bush</pillock>'\n            },\n            impact: 'cats'\n          }\n        ]\n      },\n      {\n        id: 'bypass',\n        description: 'something even more nifty',\n        tags: ['tag3'],\n        impact: 'monkeys',\n        result: 'failed',\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            impact: 'monkeys',\n            none: [\n              {\n                data: 'foon',\n                impact: 'monkeys',\n                result: true\n              }\n            ],\n            any: [],\n            all: [],\n            node: {\n              selector: ['foon'],\n              source: '<foon>telephone</foon>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'incomplete',\n        description: 'something yet more nifty',\n        tags: ['tag4'],\n        impact: 'monkeys',\n        result: 'failed',\n        passes: [],\n        violations: [],\n        incomplete: [\n          {\n            result: 'failed',\n            impact: 'monkeys',\n            none: [\n              {\n                data: 'foon',\n                impact: 'monkeys',\n                result: true\n              }\n            ],\n            any: [],\n            all: [],\n            node: {\n              selector: ['foon'],\n              source: '<foon>telephone</foon>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'blinky',\n        description: 'something awesome',\n        tags: ['tag4'],\n        violations: [],\n        result: 'passed',\n        passes: [\n          {\n            result: 'passed',\n            none: [\n              {\n                data: 'clueso',\n                result: true\n              }\n            ],\n            node: {\n              selector: ['a', 'b', 'clueso'],\n              source: '<clueso>nincompoop</clueso>'\n            }\n          }\n        ]\n      }\n    ];\n  beforeEach(function () {\n    runResults = JSON.parse(JSON.stringify(_results));\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {\n        failureSummaries: {\n          none: {\n            failureMessage: function anonymous(it) {\n              var out = 'Fix any of the following: \\n';\n              var arr1 = it;\n              if (arr1) {\n                var value,\n                  i1 = -1,\n                  l1 = arr1.length - 1;\n                while (i1 < l1) {\n                  value = arr1[(i1 += 1)];\n                  out += ' ' + value + '\\n';\n                }\n              }\n              return out;\n            }\n          },\n          all: {\n            failureMessage: function anonymous() {\n              throw new Error('shouldnt be executed');\n            }\n          },\n          any: {\n            failureMessage: function anonymous(it) {\n              var out = 'Fix all of the following: \\n';\n              var arr1 = it;\n              if (arr1) {\n                var value,\n                  i1 = -1,\n                  l1 = arr1.length - 1;\n                while (i1 < l1) {\n                  value = arr1[(i1 += 1)];\n                  out += ' ' + value + '\\n';\n                }\n              }\n              return out;\n            }\n          }\n        }\n      }\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should merge the runRules results into violations and passes', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.isObject(results);\n      assert.isArray(results.violations);\n      assert.lengthOf(results.violations, 2);\n      assert.isArray(results.passes);\n      assert.lengthOf(results.passes, 2);\n    });\n  });\n  it('should add the rule id to the rule result', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].id, 'idkStuff');\n      assert.equal(results.violations[1].id, 'bypass');\n      assert.equal(results.passes[0].id, 'gimmeLabel');\n      assert.equal(results.passes[1].id, 'blinky');\n    });\n  });\n  it('should add tags to the rule result', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.deepEqual(results.violations[0].tags, ['tag2']);\n      assert.deepEqual(results.violations[1].tags, ['tag3']);\n      assert.deepEqual(results.passes[0].tags, ['tag1']);\n      assert.deepEqual(results.passes[1].tags, ['tag4']);\n    });\n  });\n  it('should add the rule help to the rule result', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.isNotOk(results.violations[0].helpUrl);\n      assert.isNotOk(results.violations[1].helpUrl);\n      assert.equal(results.passes[0].helpUrl, 'things');\n      assert.isNotOk(results.passes[1].helpUrl);\n    });\n  });\n  it('should add the html to the node data', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.equal(\n        results.violations[0].nodes[0].html,\n        '<pillock>george bush</pillock>'\n      );\n      assert.equal(\n        results.violations[1].nodes[0].html,\n        '<foon>telephone</foon>'\n      );\n      assert.equal(results.passes[0].nodes[0].html, '<minkey>chimp</minky>');\n      assert.equal(\n        results.passes[1].nodes[0].html,\n        '<clueso>nincompoop</clueso>'\n      );\n    });\n  });\n  it('should add the failure summary to the node data', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.equal(\n        typeof results.violations[0].nodes[0].failureSummary,\n        'string'\n      );\n      assert.equal(\n        typeof results.incomplete[0].nodes[0].failureSummary,\n        'string'\n      );\n    });\n  });\n  it('should add the target selector array to the node data', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'q',\n        'r',\n        'pillock'\n      ]);\n    });\n  });\n  it('should add the description to the rule result', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].description, 'something more nifty');\n      assert.equal(\n        results.violations[1].description,\n        'something even more nifty'\n      );\n      assert.equal(results.passes[0].description, 'something nifty');\n      assert.equal(results.passes[1].description, 'something awesome');\n    });\n  });\n  it('should add the impact to the rule result', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].impact, 'cats');\n      assert.equal(results.violations[0].nodes[0].impact, 'cats');\n      assert.equal(results.violations[1].impact, 'monkeys');\n      assert.equal(results.violations[1].nodes[0].impact, 'monkeys');\n    });\n  });\n  it('should add environment data', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.isDefined(results.url);\n      assert.isDefined(results.timestamp);\n      assert.isDefined(results.testEnvironment);\n      assert.isDefined(results.testRunner);\n    });\n  });\n  it('should add toolOptions property', function () {\n    axe.getReporter('v1')(runResults, {}, function (results) {\n      assert.isDefined(results.toolOptions);\n    });\n  });\n  it('uses the environmentData option instead of environment data if specified', function () {\n    var environmentData = {\n      myReporter: 'hello world'\n    };\n    axe.getReporter('v1')(\n      runResults,\n      { environmentData: environmentData },\n      function (results) {\n        assert.equal(results.myReporter, 'hello world');\n        assert.isUndefined(results.url);\n        assert.isUndefined(results.timestamp);\n        assert.isUndefined(results.testEnvironment);\n        assert.isUndefined(results.testRunner);\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/reporters/v2.js",
    "content": "describe('reporters - v2', function () {\n  'use strict';\n  var runResults,\n    _results = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        result: 'passed',\n        impact: null,\n        tags: ['tag1'],\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                impact: null,\n                relatedNodes: [\n                  {\n                    selector: 'bob',\n                    source: 'fred'\n                  }\n                ],\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                relatedNodes: [\n                  {\n                    selector: 'joe',\n                    source: 'bob'\n                  }\n                ],\n                result: false,\n                data: 'pillock',\n                impact: 'cats'\n              },\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            any: [\n              {\n                relatedNodes: [],\n                result: true\n              }\n            ],\n            none: [\n              {\n                relatedNodes: [],\n                result: false\n              }\n            ],\n            node: {\n              selector: ['q', 'r', 'pillock'],\n              source: '<pillock>george bush</pillock>'\n            },\n            impact: 'cats'\n          }\n        ]\n      }\n    ];\n  beforeEach(function () {\n    runResults = JSON.parse(JSON.stringify(_results));\n    axe._load({\n      messages: {},\n      rules: [],\n      data: {}\n    });\n  });\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should merge the runRules results into violations and passes', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.isObject(results);\n      assert.isArray(results.violations);\n      assert.lengthOf(results.violations, 1);\n      assert.isArray(results.passes);\n      assert.lengthOf(results.passes, 1);\n    });\n  });\n  it('should add the rule id to the rule result', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].id, 'idkStuff');\n      assert.equal(results.passes[0].id, 'gimmeLabel');\n    });\n  });\n  it('should add tags to the rule result', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.deepEqual(results.violations[0].tags, ['tag2']);\n      assert.deepEqual(results.passes[0].tags, ['tag1']);\n    });\n  });\n  it('should add the rule help to the rule result', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.isNotOk(results.violations[0].helpUrl);\n      assert.equal(results.passes[0].helpUrl, 'things');\n    });\n  });\n  it('should add the html to the node data', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.equal(\n        results.violations[0].nodes[0].html,\n        '<pillock>george bush</pillock>'\n      );\n      assert.equal(results.passes[0].nodes[0].html, '<minkey>chimp</minky>');\n    });\n  });\n  it('should add the target selector array to the node data', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.ok(results.violations[0].nodes);\n      assert.equal(results.violations[0].nodes.length, 1);\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'q',\n        'r',\n        'pillock'\n      ]);\n    });\n  });\n  it('should add the description to the rule result', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].description, 'something more nifty');\n      assert.equal(results.passes[0].description, 'something nifty');\n    });\n  });\n  it('should add the impact to the rule result', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.equal(results.violations[0].impact, 'cats');\n      assert.equal(results.violations[0].nodes[0].impact, 'cats');\n      assert.isNotOk(results.passes[0].impact);\n      assert.isNotOk(results.passes[0].nodes[0].impact);\n    });\n  });\n  it('should map relatedNodes', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.lengthOf(results.violations[0].nodes[0].all[0].relatedNodes, 1);\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].target,\n        'joe'\n      );\n      assert.equal(\n        results.violations[0].nodes[0].all[0].relatedNodes[0].html,\n        'bob'\n      );\n\n      assert.lengthOf(results.passes[0].nodes[0].any[0].relatedNodes, 1);\n      assert.equal(\n        results.passes[0].nodes[0].any[0].relatedNodes[0].target,\n        'bob'\n      );\n      assert.equal(\n        results.passes[0].nodes[0].any[0].relatedNodes[0].html,\n        'fred'\n      );\n    });\n  });\n  it('should add environment data', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.isDefined(results.url);\n      assert.isDefined(results.timestamp);\n      assert.isDefined(results.testEnvironment);\n      assert.isDefined(results.testRunner);\n    });\n  });\n  it('should add toolOptions property', function () {\n    axe.getReporter('v2')(runResults, {}, function (results) {\n      assert.isDefined(results.toolOptions);\n    });\n  });\n  it('uses the environmentData option instead of environment data if specified', function () {\n    var environmentData = {\n      myReporter: 'hello world'\n    };\n    axe.getReporter('v2')(\n      runResults,\n      { environmentData: environmentData },\n      function (results) {\n        assert.equal(results.myReporter, 'hello world');\n        assert.isUndefined(results.url);\n        assert.isUndefined(results.timestamp);\n        assert.isUndefined(results.testEnvironment);\n        assert.isUndefined(results.testRunner);\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/aggregate.js",
    "content": "describe('aggregate', function () {\n  'use strict';\n\n  var map = ['youngling', 'padawan', 'knight', 'master', 'grand master'];\n\n  var values = ['knight', 'master', 'padawan'];\n\n  it('takes a map, values array and initial value', function () {\n    assert.isFunction(axe.utils.aggregate);\n    assert.lengthOf(axe.utils.aggregate, 3);\n\n    assert.doesNotThrow(function () {\n      axe.utils.aggregate(map, values, 'youngling');\n    });\n  });\n\n  it('does not change the values array', function () {\n    var copy = [].concat(values);\n    axe.utils.aggregate(map, values, 'youngling');\n    assert.deepEqual(values, copy);\n  });\n\n  it('picks the value with the highest index in the map, from the list of values', function () {\n    var result = axe.utils.aggregate(map, ['knight', 'master', 'youngling']);\n    assert.equal(result, 'master');\n  });\n\n  it('considers the initial value in addition to the other values', function () {\n    var result = axe.utils.aggregate(\n      map,\n      ['knight', 'master', 'youngling'],\n      'grand master'\n    );\n    assert.equal(result, 'grand master');\n  });\n\n  it('ignores values not on the map', function () {\n    var result = axe.utils.aggregate(\n      map,\n      ['bounty hunter', 'sith lord'],\n      'youngling'\n    );\n    assert.equal(result, 'youngling');\n  });\n\n  it('returns undefined if no value was found', function () {\n    assert.isUndefined(axe.utils.aggregate(map, ['smugler', 'droid']));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/aggregateChecks.js",
    "content": "describe('axe.utils.aggregateChecks', function () {\n  'use strict';\n  var FAIL = axe.constants.FAIL;\n  var PASS = axe.constants.PASS;\n  var CANTTELL = axe.constants.CANTTELL;\n  var NA = axe.constants.NA;\n\n  // create an object of check results, padding input with defaults and\n  // wrapping arrays where required\n  function createTestCheckResults(node) {\n    ['any', 'all', 'none'].forEach(function (type) {\n      if (typeof node[type] === 'undefined') {\n        node[type] = [];\n      } else if (Array.isArray(node[type])) {\n        node[type] = node[type].map(function (val) {\n          if (typeof val !== 'object') {\n            return { result: val };\n          } else {\n            return val;\n          }\n        });\n      } else {\n        if (typeof node[type] !== 'object') {\n          node[type] = { result: node[type] };\n        }\n        node[type] = [node[type]];\n      }\n    });\n    return node;\n  }\n\n  beforeEach(function () {\n    axe._load({});\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.aggregateChecks);\n  });\n\n  it('Should be `inapplicable` when no results are given', function () {\n    var ruleResult = axe.utils.aggregateChecks(createTestCheckResults({}));\n\n    assert.equal(ruleResult.result, NA);\n  });\n\n  it('sets result  to cantTell when result is not a boolean', function () {\n    var values = [undefined, null, 0, 'true', {}, NaN];\n    values.forEach(function (value) {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [{ result: value }]\n        })\n      );\n      assert.equal(checkResult.result, CANTTELL);\n    });\n  });\n\n  it('returns impact for fail and canttell', function () {\n    var failCheck = axe.utils.aggregateChecks(\n      createTestCheckResults({\n        any: [{ result: false, impact: 'serious' }]\n      })\n    );\n    var canttellCheck = axe.utils.aggregateChecks(\n      createTestCheckResults({\n        any: [{ result: undefined, impact: 'moderate' }]\n      })\n    );\n\n    assert.equal(failCheck.impact, 'serious');\n    assert.equal(canttellCheck.impact, 'moderate');\n  });\n\n  it('sets impact to null for pass', function () {\n    var passCheck = axe.utils.aggregateChecks(\n      createTestCheckResults({\n        any: [{ result: true, impact: 'serious' }]\n      })\n    );\n    assert.isNull(passCheck.impact);\n  });\n\n  describe('none', function () {\n    it('gives result FAIL when any is true', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          none: [false, true, undefined]\n        })\n      );\n\n      assert.equal(checkResult.result, FAIL);\n    });\n\n    it('gives result CANTTELL when none is true and any is not a boolean', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          none: [undefined, false]\n        })\n      );\n      assert.equal(checkResult.result, CANTTELL);\n    });\n\n    it('gives result PASS when all are FALSE', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          none: [false, false]\n        })\n      );\n      assert.equal(checkResult.result, PASS);\n    });\n  });\n\n  describe('any', function () {\n    it('gives result PASS when any is true', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [undefined, true]\n        })\n      );\n      assert.equal(checkResult.result, PASS);\n    });\n\n    it('gives result CANTTELL when none is true and any is not a bool', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [undefined, false]\n        })\n      );\n      assert.equal(checkResult.result, CANTTELL);\n    });\n\n    it('gives result FAIL when all are false', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [false, false]\n        })\n      );\n      assert.equal(checkResult.result, FAIL);\n    });\n  });\n\n  describe('all', function () {\n    it('gives result FAIL when any is false', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          all: [false, true, undefined]\n        })\n      );\n\n      assert.equal(checkResult.result, FAIL);\n    });\n\n    it('gives result CANTTELL when none is false and any is not a boolean', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          all: [undefined, true]\n        })\n      );\n      assert.equal(checkResult.result, CANTTELL);\n    });\n\n    it('gives result PASS when all are true', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          all: [true, true]\n        })\n      );\n      assert.equal(checkResult.result, PASS);\n    });\n  });\n\n  describe('combined', function () {\n    it('gives result PASS when all are PASS', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: true,\n          all: true,\n          none: false\n        })\n      );\n\n      assert.equal(checkResult.result, PASS);\n    });\n\n    it('gives result CANTTELL when none is FAIL and any is CANTTELL', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: 0,\n          all: true,\n          none: false\n        })\n      );\n      assert.equal(checkResult.result, CANTTELL);\n    });\n\n    it('gives result FAIL when any are FAIL', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: 0,\n          all: false,\n          none: false\n        })\n      );\n      assert.equal(checkResult.result, FAIL);\n    });\n\n    it('ignores fail checks on any, if at least one passed', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [false, undefined, true], // cantTell\n          none: [true, false] // fail\n        })\n      );\n\n      assert.lengthOf(checkResult.any, 0);\n      assert.lengthOf(checkResult.none, 1);\n    });\n\n    it('includes cantTell checks from any if there are no fails', function () {\n      var checkResult = axe.utils.aggregateChecks(\n        createTestCheckResults({\n          any: [undefined, undefined, false], // cantTell\n          none: [undefined, false] // cantTell\n        })\n      );\n\n      assert.lengthOf(checkResult.any, 2);\n      assert.lengthOf(checkResult.none, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/aggregateNodeResults.js",
    "content": "describe('axe.utils.aggregateNodeResults', function () {\n  'use strict';\n  var FAIL = 'failed';\n  var PASS = 'passed';\n  var CANTTELL = 'cantTell';\n  var INAPPLICABLE = 'inapplicable';\n\n  // create an array of check results, padding input with defaults and\n  // wrapping arrays where required\n  function createTestResults() {\n    var args = [].slice.call(arguments);\n    return args.map(function (node) {\n      ['any', 'all', 'none'].forEach(function (type) {\n        if (typeof node[type] === 'undefined') {\n          node[type] = [];\n        } else if (Array.isArray(node[type])) {\n          node[type] = node[type].map(function (val) {\n            if (typeof val !== 'object') {\n              return { result: val };\n            } else {\n              return val;\n            }\n          });\n        } else {\n          if (typeof node[type] !== 'object') {\n            node[type] = { result: node[type] };\n          }\n          node[type] = [node[type]];\n        }\n      });\n      return node;\n    });\n  }\n\n  beforeEach(function () {\n    axe._load({});\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.aggregateNodeResults);\n  });\n\n  it('Should be `inapplicable` when no results are given', function () {\n    var ruleResult = axe.utils.aggregateNodeResults([]);\n    assert.equal(ruleResult.result, INAPPLICABLE);\n  });\n\n  it('should assign FAIL to ruleResult over PASS', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({ all: false }, { all: true }, { all: true })\n    );\n    assert.equal(ruleResult.result, FAIL);\n    assert.lengthOf(ruleResult.violations, 1);\n    assert.lengthOf(ruleResult.passes, 2);\n  });\n\n  it('should assign FAIL to ruleResult over CANTTELL', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({ all: false }, { all: 0 }, { all: true })\n    );\n    assert.equal(ruleResult.result, FAIL);\n    assert.lengthOf(ruleResult.violations, 1);\n    assert.lengthOf(ruleResult.incomplete, 1);\n    assert.lengthOf(ruleResult.passes, 1);\n  });\n\n  it('should assign PASS to ruleResult if there are only passing checks', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({ all: true }, { all: true }, { all: true })\n    );\n    assert.equal(ruleResult.result, PASS);\n    assert.lengthOf(ruleResult.passes, 3);\n    assert.lengthOf(ruleResult.violations, 0);\n  });\n\n  it('should assign FAIL if there are no passing anys checks', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({ any: false }, { any: false }, { any: false })\n    );\n    assert.equal(ruleResult.result, FAIL);\n    assert.lengthOf(ruleResult.violations, 3);\n    assert.lengthOf(ruleResult.passes, 0);\n  });\n\n  it('should assign CANTTELL over PASS', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({ all: true }, { all: 0 }, { all: 0 })\n    );\n    assert.equal(ruleResult.result, CANTTELL);\n    assert.lengthOf(ruleResult.incomplete, 2);\n    assert.lengthOf(ruleResult.passes, 1);\n  });\n\n  it('should provide impact on incomplete', function () {\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults({\n        none: { result: undefined, impact: 'serious' }\n      })\n    );\n    assert.equal(ruleResult.impact, 'serious');\n  });\n\n  it('should raise the highest \"raisedMetadata\" on failing checks', function () {\n    /*eslint indent:0 */\n    var ruleResult = axe.utils.aggregateNodeResults(\n      createTestResults(\n        {\n          none: { result: true, impact: 'moderate' },\n          any: { result: true, impact: 'minor' },\n          all: [\n            { result: true, impact: 'critical' },\n            { result: false, impact: 'serious' }\n          ]\n        },\n        { none: { result: undefined, impact: 'critical' } },\n        { none: { result: false, impact: 'critical' } }\n      )\n    );\n    assert.equal(ruleResult.impact, 'serious');\n    assert.equal(ruleResult.violations[0].impact, 'serious');\n    assert.equal(ruleResult.incomplete[0].impact, 'critical');\n    assert.isNull(ruleResult.passes[0].impact);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/aggregateResult.js",
    "content": "describe('axe.utils.aggregateResult', function () {\n  'use strict';\n\n  var results,\n    _results = [\n      {\n        id: 'gimmeLabel',\n        helpUrl: 'things',\n        description: 'something nifty',\n        tags: ['tag1'],\n        result: 'passed',\n        violations: [],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'bypass',\n        description: 'something even more nifty',\n        tags: ['tag3'],\n        impact: 'monkeys',\n        result: 'failed',\n        passes: [],\n        violations: [\n          {\n            result: 'failed',\n            impact: 'monkeys',\n            none: [\n              {\n                data: 'foon',\n                impact: 'monkeys',\n                result: true\n              }\n            ],\n            any: [],\n            all: [],\n            node: {\n              selector: ['foon'],\n              source: '<foon>telephone</foon>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'idkStuff',\n        description: 'something more nifty',\n        pageLevel: true,\n        result: 'failed',\n        impact: 'cats',\n        tags: ['tag2'],\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                result: true,\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ],\n        violations: [\n          {\n            result: 'failed',\n            all: [\n              {\n                result: false,\n                data: 'pillock',\n                impact: 'cats'\n              }\n            ],\n            any: [],\n            none: [],\n            node: {\n              selector: ['q', 'r', 'pillock'],\n              source: '<pillock>george bush</pillock>'\n            },\n            impact: 'cats'\n          }\n        ],\n        incomplete: [\n          {\n            result: 'cantTell',\n            any: [\n              {\n                result: 0,\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ]\n      },\n      {\n        id: 'blinky',\n        description: 'something awesome',\n        tags: ['tag4'],\n        result: 'inapplicable',\n        passes: [\n          {\n            result: 'passed',\n            any: [\n              {\n                shouldIBeHere: 'no, this should be inapplicable!',\n                result: true,\n                data: 'minkey'\n              }\n            ],\n            all: [],\n            none: [],\n            node: {\n              selector: ['minkey'],\n              frames: [],\n              source: '<minkey>chimp</minky>'\n            }\n          }\n        ],\n        violations: [],\n        incomplete: []\n      }\n    ];\n\n  beforeEach(function () {\n    results = JSON.parse(JSON.stringify(_results));\n  });\n\n  it('creates an object with arrays as properties for each result', function () {\n    var resultObject = axe.utils.aggregateResult(results);\n\n    assert.isArray(resultObject.passes);\n    assert.isArray(resultObject.violations);\n    assert.isArray(resultObject.incomplete);\n    assert.isArray(resultObject.inapplicable);\n  });\n\n  it('copies failures and passes to their respective arrays on the result object', function () {\n    // insert 1 pass and 1 fail\n    var input = [results[0], results[1]];\n    var resultObject = axe.utils.aggregateResult(input);\n\n    assert.lengthOf(resultObject.passes, 1);\n    assert.lengthOf(resultObject.violations, 1);\n    assert.lengthOf(resultObject.incomplete, 0);\n    assert.lengthOf(resultObject.inapplicable, 0);\n\n    // Objects are the same\n    assert.deepEqual(resultObject.passes[0].nodes, input[0].passes);\n    assert.deepEqual(resultObject.violations[0].nodes, input[1].violations);\n\n    // Object is a copy\n    assert.notEqual(resultObject.passes[0].nodes, input[0].passes);\n    assert.notEqual(resultObject.violations[0].nodes, input[1].violations);\n  });\n\n  it('creates a duplicate of the result for each outcome it has', function () {\n    // insert 1 fail, containing a pass, a fail and a cantTell result\n    var input = [results[2]];\n    var resultObject = axe.utils.aggregateResult(input);\n\n    assert.lengthOf(resultObject.passes, 1);\n    assert.lengthOf(resultObject.violations, 1);\n    assert.lengthOf(resultObject.incomplete, 1);\n    assert.lengthOf(resultObject.inapplicable, 0);\n\n    // Objects are the same\n    assert.deepEqual(resultObject.passes[0].nodes, input[0].passes);\n    assert.deepEqual(resultObject.violations[0].nodes, input[0].violations);\n    assert.deepEqual(resultObject.incomplete[0].nodes, input[0].incomplete);\n  });\n\n  it('moves inapplicable results only to the inapplicable array', function () {\n    // insert 1 fail, containing a pass, a fail and a cantTell result\n    var input = [results[3]];\n    var resultObject = axe.utils.aggregateResult(input);\n\n    assert.lengthOf(resultObject.passes, 0);\n    assert.lengthOf(resultObject.violations, 0);\n    assert.lengthOf(resultObject.incomplete, 0);\n    assert.lengthOf(resultObject.inapplicable, 1);\n\n    assert.equal(resultObject.inapplicable[0].id, input[0].id);\n    assert.equal(\n      resultObject.inapplicable[0].description,\n      input[0].description\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/are-styles-set.js",
    "content": "/*global axe*/\ndescribe('axe.utils.areStylesSet', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.areStylesSet);\n  });\n\n  it('should return true if `display:none` is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"display:none;\">Display None</div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [{ property: 'display', value: 'none' }],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `display:none` is set', function () {\n    fixture.innerHTML =\n      '<div style=\"display:none;\"><div id=\"target\">Display None</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [{ property: 'display', value: 'none' }],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `visibility:hidden` is set', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility:hidden;\"><div id=\"target\">Display None</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [{ property: 'visibility', value: 'hidden' }],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `visibility:hidden` is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"visibility:hidden;\">Display None</div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [{ property: 'visibility', value: 'hidden' }],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `visibility:hidden` is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"visibility:hidden;\">Display None</div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [\n          {\n            property: 'display',\n            value: 'none'\n          },\n          {\n            property: 'visibility',\n            value: 'hidden'\n          }\n        ],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `visibility:hidden` is set', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility:hidden;\"><div id=\"target\">Display None</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [\n          {\n            property: 'display',\n            value: 'none'\n          },\n          {\n            property: 'visibility',\n            value: 'hidden'\n          }\n        ],\n        'body'\n      )\n    );\n  });\n\n  it('should return true if `display:none` is set', function () {\n    fixture.innerHTML =\n      '<div style=\"display:none;\"><div id=\"target\">Display None</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(\n      axe.utils.areStylesSet(\n        el,\n        [\n          {\n            property: 'display',\n            value: 'none'\n          },\n          {\n            property: 'visibility',\n            value: 'hidden'\n          }\n        ],\n        'body'\n      )\n    );\n  });\n\n  it('should return false if nothing is set', function () {\n    fixture.innerHTML =\n      '<div style=\"\"><div id=\"target\">Display None</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isFalse(\n      axe.utils.areStylesSet(\n        el,\n        [\n          {\n            property: 'display',\n            value: 'none'\n          },\n          {\n            property: 'visibility',\n            value: 'hidden'\n          }\n        ],\n        'body'\n      )\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/assert.js",
    "content": "describe('axe.utils.assert', function () {\n  it('does nothing when passed a truthy value', function () {\n    assert.doesNotThrow(function () {\n      axe.utils.assert(true);\n      axe.utils.assert('foo');\n      axe.utils.assert(123);\n      axe.utils.assert([]);\n      axe.utils.assert({});\n    });\n  });\n\n  it('throws an error when passed a falsey value', function () {\n    assert.throws(function () {\n      axe.utils.assert(false);\n    });\n    assert.throws(function () {\n      axe.utils.assert(0);\n    });\n    assert.throws(function () {\n      axe.utils.assert(null);\n    });\n    assert.throws(function () {\n      axe.utils.assert(undefined);\n    });\n  });\n\n  it('sets second argument as the error message', function () {\n    var message = 'Something went wrong';\n    try {\n      axe.utils.assert(false, message);\n    } catch (e) {\n      assert.instanceOf(e, Error);\n      assert.equal(e.message, message);\n    }\n  });\n});\n"
  },
  {
    "path": "test/core/utils/check-helper.js",
    "content": "describe('axe.utils.checkHelper', () => {\n  const { queryFixture, fixtureSetup } = axe.testUtils;\n  function noop() {}\n\n  it('should be a function', () => {\n    assert.isFunction(axe.utils.checkHelper);\n  });\n\n  it('should accept 4 named parameters', () => {\n    assert.lengthOf(axe.utils.checkHelper, 4);\n  });\n\n  it('should return an object', () => {\n    assert.isObject(axe.utils.checkHelper());\n  });\n\n  describe('return value', () => {\n    describe('async', () => {\n      it('should set isAsync property on returned object to `true` when called', () => {\n        const target = {},\n          helper = axe.utils.checkHelper(target, noop);\n\n        helper.async();\n        assert.isTrue(helper.isAsync);\n      });\n\n      it('should call the third parameter of `axe.utils.checkHelper` when invoked', () => {\n        function fn() {\n          success = true;\n        }\n        let success = false;\n        const helper = axe.utils.checkHelper({}, {}, fn);\n\n        const done = helper.async();\n        done();\n\n        assert.isTrue(success);\n      });\n\n      it('should call the fourth parameter of `axe.utils.checkHelper` when returning an error', () => {\n        let success = false;\n        function reject(e) {\n          success = true;\n          assert.equal(e.message, 'Concrete donkey!');\n        }\n\n        const helper = axe.utils.checkHelper({}, {}, noop, reject);\n        const done = helper.async();\n        done(new Error('Concrete donkey!'));\n\n        assert.isTrue(success);\n      });\n    });\n\n    describe('data', () => {\n      it('should set data property on target when called', () => {\n        const target = {},\n          expected = { monkeys: 'bananas' },\n          helper = axe.utils.checkHelper(target, noop);\n\n        assert.notProperty(target, 'data');\n        helper.data(expected);\n        assert.equal(target.data, expected);\n      });\n    });\n\n    describe('relatedNodes', () => {\n      const fixture = document.getElementById('fixture');\n      const getSelector = node => node.selector;\n\n      it('returns DqElements', () => {\n        fixtureSetup('<div id=\"t1\"></div><div id=\"t2\"></div>');\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(fixture.children);\n        assert.instanceOf(target.relatedNodes[0], axe.utils.DqElement);\n      });\n\n      it('should accept NodeList', () => {\n        fixtureSetup('<div id=\"t1\"></div><div id=\"t2\"></div>');\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(fixture.children);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#t1'], ['#t2']]);\n      });\n\n      it('should accept a single Node', () => {\n        fixtureSetup('<div id=\"t1\"></div><div id=\"t2\"></div>');\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(fixture.firstChild);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#t1']]);\n      });\n\n      it('should accept an Array', () => {\n        fixtureSetup('<div id=\"t1\"></div><div id=\"t2\"></div>');\n\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(Array.prototype.slice.call(fixture.children));\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#t1'], ['#t2']]);\n      });\n\n      it('should accept an array-like Object', () => {\n        fixtureSetup('<div id=\"t1\"></div><div id=\"t2\"></div>');\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        const nodes = {\n          0: fixture.children[0],\n          1: fixture.children[1],\n          length: 2\n        };\n        helper.relatedNodes(nodes);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#t1'], ['#t2']]);\n      });\n\n      it('should accept a VirtualNode', () => {\n        const vNode = queryFixture('<a id=\"target\"></a>');\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(vNode);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#target']]);\n      });\n\n      it('should accept an array of VirtualNodes', () => {\n        const vNode = queryFixture(`\n          <div id=\"target\"><a id=\"a\"></a><b id=\"b\"></b></div>\n        `);\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        helper.relatedNodes(vNode.children);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#a'], ['#b']]);\n      });\n\n      it('should filter out non-nodes', () => {\n        const vNode = queryFixture(`\n          <div><a id=\"target\"></a><b id=\"b\"></b></div>\n        `);\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        const nodes = [\n          null,\n          vNode,\n          true,\n          fixture.querySelector('b'),\n          'hello world',\n          new axe.SerialVirtualNode({\n            nodeName: 's'\n          })\n        ];\n        helper.relatedNodes(nodes);\n        const selectors = target.relatedNodes.map(getSelector);\n        assert.deepEqual(selectors, [['#target'], ['#b']]);\n      });\n\n      it('should noop for non-node-like objects', () => {\n        const target = {};\n        const helper = axe.utils.checkHelper(target, noop);\n        const nodes = new axe.SerialVirtualNode({\n          nodeName: 'div'\n        });\n        assert.doesNotThrow(() => {\n          helper.relatedNodes(nodes);\n        });\n        assert.lengthOf(target.relatedNodes, 0);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/clone.js",
    "content": "describe('utils.clone', () => {\n  const clone = axe.utils.clone;\n  const fixture = document.querySelector('#fixture');\n\n  it('should clone an object', () => {\n    const obj = {\n      cats: true,\n      dogs: 2,\n      fish: [0, 1, { one: 'two' }]\n    };\n    const c = clone(obj);\n\n    obj.cats = false;\n    obj.dogs = 1;\n    obj.fish[2].one = 'three';\n\n    assert.strictEqual(c.cats, true);\n    assert.strictEqual(c.dogs, 2);\n    assert.deepEqual(c.fish, [0, 1, { one: 'two' }]);\n  });\n\n  it('should clone nested objects', () => {\n    const obj = {\n      cats: {\n        fred: 1,\n        billy: 2,\n        meow: true\n      },\n      dogs: {\n        spot: 1,\n        max: 2,\n        woof: [0, 1, 2]\n      },\n      fish: [0, 1, 2]\n    };\n    const c = clone(obj);\n\n    obj.cats.fred = 47;\n    obj.dogs = 47;\n    obj.fish[0] = 'stuff';\n\n    assert.deepEqual(c.cats, {\n      fred: 1,\n      billy: 2,\n      meow: true\n    });\n\n    assert.deepEqual(c.dogs, {\n      spot: 1,\n      max: 2,\n      woof: [0, 1, 2]\n    });\n\n    assert.deepEqual(c.fish, [0, 1, 2]);\n  });\n\n  it('should clone objects with methods', () => {\n    const obj = {\n      cats: () => {\n        return 'meow';\n      },\n      dogs: () => {\n        return 'woof';\n      }\n    };\n    const c = clone(obj);\n\n    assert.strictEqual(obj.cats, c.cats);\n    assert.strictEqual(obj.dogs, c.dogs);\n\n    obj.cats = () => {};\n    obj.dogs = () => {};\n\n    assert.notStrictEqual(obj.cats, c.cats);\n    assert.notStrictEqual(obj.dogs, c.dogs);\n  });\n\n  it('should clone prototypes', () => {\n    function Cat(name) {\n      this.name = name;\n    }\n\n    Cat.prototype.meow = () => {\n      return 'meow';\n    };\n\n    Cat.prototype.bark = () => {\n      return 'cats dont bark';\n    };\n\n    const cat = new Cat('Fred'),\n      c = clone(cat);\n\n    assert.deepEqual(cat.name, c.name);\n    assert.deepEqual(Cat.prototype.bark, c.bark);\n    assert.deepEqual(Cat.prototype.meow, c.meow);\n  });\n\n  it('should clone circular objects while keeping the circular reference', () => {\n    const obj = { cats: true };\n    obj.child = obj;\n    const c = clone(obj);\n\n    obj.cats = false;\n\n    assert.deepEqual(c, {\n      cats: true,\n      child: c\n    });\n    assert.strictEqual(c, c.child);\n  });\n\n  it('should not return the same object when cloned twice', () => {\n    const obj = { cats: true };\n    const c1 = clone(obj);\n    const c2 = clone(obj);\n\n    assert.notStrictEqual(c1, c2);\n  });\n\n  it('should not return the same object when nested', () => {\n    const obj = { dogs: true };\n    const obj1 = { cats: true, child: { prop: obj } };\n    const obj2 = { fish: [0, 1, 2], child: { prop: obj } };\n\n    const c1 = clone(obj1);\n    const c2 = clone(obj2);\n\n    assert.notStrictEqual(c1.child.prop, c2.child.prop);\n  });\n\n  it('should not clone HTML elements', () => {\n    const obj = {\n      cats: true,\n      node: document.createElement('div')\n    };\n    const c = clone(obj);\n\n    obj.cats = false;\n\n    assert.equal(c.cats, true);\n    assert.strictEqual(c.node, obj.node);\n  });\n\n  it('should not clone HTML elements from different windows', () => {\n    fixture.innerHTML = '<iframe id=\"target\"></iframe>';\n    const iframe = fixture.querySelector('#target');\n\n    const obj = {\n      cats: true,\n      node: iframe.contentDocument\n    };\n    const c = clone(obj);\n\n    obj.cats = false;\n\n    assert.equal(c.cats, true);\n    assert.strictEqual(c.node, obj.node);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/closest.js",
    "content": "describe('utils.closest', function () {\n  var closest = axe.utils.closest;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should find the current node', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"parent\"><div id=\"target\">foo</div></div>'\n    );\n    var closestNode = closest(virtualNode, 'div');\n    assert.equal(closestNode, virtualNode);\n  });\n\n  it('should find a parent node', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"parent\"><span id=\"target\">foo</span></div>'\n    );\n    var closestNode = closest(virtualNode, 'div');\n    var parent = fixture.querySelector('#parent');\n    assert.equal(closestNode, axe.utils.getNodeFromTree(parent));\n  });\n\n  it('should find an ancestor node', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"parent\"><span><span><span><span id=\"target\">foo</span></span></span></div>'\n    );\n    var closestNode = closest(virtualNode, 'div');\n    var parent = fixture.querySelector('#parent');\n    assert.equal(closestNode, axe.utils.getNodeFromTree(parent));\n  });\n\n  it('should return null if no ancestor is found', function () {\n    var virtualNode = queryFixture(\n      '<div id=\"parent\"><div id=\"target\">foo</div></div>'\n    );\n    var closestNode = closest(virtualNode, 'h1');\n    assert.isNull(closestNode);\n  });\n\n  it('should error if tree is not complete', function () {\n    var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n    virtualNode.parent = undefined;\n\n    function fn() {\n      closest(virtualNode, 'h1');\n    }\n\n    assert.throws(fn);\n  });\n\n  it('should not error if tree is complete', function () {\n    var virtualNode = queryFixture('<div id=\"target\">foo</div>');\n    virtualNode.parent = null;\n\n    function fn() {\n      closest(virtualNode, 'h1');\n    }\n\n    assert.doesNotThrow(fn);\n  });\n\n  (shadowSupported ? it : xit)('should support shadow dom', function () {\n    fixture.innerHTML = '<div id=\"parent\"></div>';\n\n    var root = fixture.firstChild.attachShadow({ mode: 'open' });\n    var slotted = document.createElement('span');\n    slotted.innerHTML = '<span id=\"target\">foo</span>';\n    root.appendChild(slotted);\n    axe.utils.getFlattenedTree(fixture.firstChild);\n\n    var virtualNode = axe.utils.getNodeFromTree(slotted.firstChild);\n    var parent = fixture.querySelector('#parent');\n    var closestNode = closest(virtualNode, 'div');\n    assert.equal(closestNode, axe.utils.getNodeFromTree(parent));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/collect-results-from-frames.js",
    "content": "describe('axe.utils.collectResultsFromFrames', function () {\n  'use strict';\n\n  var Context = axe._thisWillBeDeletedDoNotUse.base.Context;\n  var fixture = document.getElementById('fixture');\n  var noop = function () {};\n  var origSetTimeout = window.setTimeout;\n\n  function contextSetup(scope) {\n    var context = new Context(scope);\n    axe._tree = context.flatTree;\n    axe._selectorData = axe.utils.getSelectorData(context.flatTree);\n\n    return context;\n  }\n\n  afterEach(function () {\n    window.setTimeout = origSetTimeout;\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n  });\n\n  it('should timeout the ping request after 500ms', function (done) {\n    this.timeout = 1000;\n\n    var timeoutSet = false;\n    window.setTimeout = function (fn, to) {\n      if (to === 500) {\n        timeoutSet = true;\n        fn();\n      } else {\n        // ping timeout\n        return origSetTimeout(fn, to);\n      }\n      return 'cats';\n    };\n\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n\n      axe.utils.collectResultsFromFrames(\n        context,\n        {},\n        'stuff',\n        'morestuff',\n        function () {\n          assert.isTrue(timeoutSet);\n          done();\n        },\n        noop\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/results-timeout.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should override the ping timeout with `options.pingWaitTime`, if provided', function (done) {\n    this.timeout = 1000;\n\n    var timeoutSet = false;\n    window.setTimeout = function (fn, to) {\n      if (to === 90000) {\n        timeoutSet = true;\n        fn();\n      } else {\n        // ping timeout\n        return origSetTimeout(fn, to);\n      }\n      return 'cats';\n    };\n\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n      var params = { pingWaitTime: 90000 };\n\n      axe.utils.collectResultsFromFrames(\n        context,\n        params,\n        'stuff',\n        'morestuff',\n        function () {\n          assert.isTrue(timeoutSet);\n          done();\n        },\n        noop\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/results-timeout.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should timeout the start request after 60s', function (done) {\n    window.setTimeout = function (fn, to) {\n      if (to === 60000) {\n        assert.ok('timeout set');\n        fn();\n      } else {\n        // ping timeout\n        return origSetTimeout(fn, to);\n      }\n      return 'cats';\n    };\n\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n      axe.utils.collectResultsFromFrames(\n        context,\n        {},\n        'stuff',\n        'morestuff',\n        noop,\n        function (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(err.message.split(/: /)[0], 'Axe in frame timed out');\n          done();\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/results-timeout.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should override the start timeout with `options.frameWaitTime`, if provided', function (done) {\n    window.setTimeout = function (fn, to) {\n      if (to === 90000) {\n        assert.ok('timeout set');\n        fn();\n      } else {\n        // ping timeout\n        return origSetTimeout(fn, to);\n      }\n      return 'cats';\n    };\n\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n      var params = { frameWaitTime: 90000 };\n\n      axe.utils.collectResultsFromFrames(\n        context,\n        params,\n        'stuff',\n        'morestuff',\n        noop,\n        function (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(err.message.split(/: /)[0], 'Axe in frame timed out');\n          done();\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/results-timeout.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should not throw given a recursive iframe', function (done) {\n    axe._load({\n      rules: [\n        {\n          id: 'iframe',\n          selector: 'iframe',\n          any: [\n            {\n              id: 'iframe',\n              evaluate: function () {\n                return true;\n              }\n            }\n          ]\n        }\n      ],\n      messages: {}\n    });\n\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n\n      axe.utils.collectResultsFromFrames(\n        context,\n        {},\n        'rules',\n        'morestuff',\n        function () {\n          done();\n        },\n        function (e) {\n          assert.ok(false, e);\n          done();\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/nested0.html';\n    fixture.appendChild(frame);\n  });\n\n  it('returns errors send from the frame', function (done) {\n    var frame = document.createElement('iframe');\n    frame.addEventListener('load', function () {\n      var context = contextSetup(document);\n      axe.utils.collectResultsFromFrames(\n        context,\n        {},\n        'rules',\n        'morestuff',\n        function () {\n          done(new Error('Should not be called'));\n        },\n        function (err) {\n          assert.instanceOf(err, Error);\n          assert.equal(err.message.split(/\\n/)[0], 'error in axe.throw');\n          done();\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/throwing.html';\n    fixture.appendChild(frame);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/contains.js",
    "content": "describe('axe.utils.contains', () => {\n  const fixture = document.getElementById('fixture');\n  const { fixtureSetup, createNestedShadowDom } = axe.testUtils;\n\n  it('is true when the first node is an ancestor', () => {\n    const tree = fixtureSetup(`<section> <img> </section>`);\n    const node1 = axe.utils.querySelectorAll(tree, 'section')[0];\n    const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n    assert.isTrue(axe.utils.contains(node1, node2));\n  });\n\n  it('is false when the first node is a descendant', () => {\n    const tree = fixtureSetup(`<section> <img> </section>`);\n    const node1 = axe.utils.querySelectorAll(tree, 'img')[0];\n    const node2 = axe.utils.querySelectorAll(tree, 'section')[0];\n    assert.isFalse(axe.utils.contains(node1, node2));\n  });\n\n  it('is false when the nodes are siblings', () => {\n    const tree = fixtureSetup(`<section></section> <img>`);\n    const node1 = axe.utils.querySelectorAll(tree, 'img')[0];\n    const node2 = axe.utils.querySelectorAll(tree, 'section')[0];\n    assert.isFalse(axe.utils.contains(node1, node2));\n    assert.isFalse(axe.utils.contains(node2, node1));\n  });\n\n  it('is true when the passed the same node', () => {\n    const tree = fixtureSetup(`<img>`);\n    const node = axe.utils.querySelectorAll(tree, 'img')[0];\n    assert.isTrue(axe.utils.contains(node, node));\n  });\n\n  it('is false when the nodes are cousins', () => {\n    const tree = fixtureSetup(`<section> <input> </section> <img>`);\n    const node1 = axe.utils.querySelectorAll(tree, 'input')[0];\n    const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n    assert.isFalse(axe.utils.contains(node1, node2));\n    assert.isFalse(axe.utils.contains(node2, node1));\n  });\n\n  describe('using fallbacks', () => {\n    it('should first check DOMNode.contains', () => {\n      let success = false;\n      const node2 = { actualNode: 'not really a node but it doesnt matter' };\n      const node1 = {\n        actualNode: {\n          contains: function (n2) {\n            success = true;\n            assert.deepEqual(n2, node2.actualNode);\n          },\n          compareDocumentPosition: () => {\n            success = false;\n            assert.ok(false, 'should not be called');\n          }\n        }\n      };\n\n      axe.utils.contains(node1, node2);\n      assert.isTrue(success);\n    });\n\n    it('should fallback to parent lookup', () => {\n      const node1 = {};\n      const node2 = {\n        parent: node1\n      };\n      assert.isTrue(axe.utils.contains(node1, node2));\n    });\n  });\n\n  describe('with shadow DOM', () => {\n    it('works when nodes are in the same tree', () => {\n      createNestedShadowDom(\n        fixture,\n        `<div id=\"shadowHost\"></div>`,\n        `<section> <img> </section>`\n      );\n      const tree = axe.setup(fixture);\n      const node1 = axe.utils.querySelectorAll(tree, 'section')[0];\n      const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n      assert.isTrue(axe.utils.contains(node1, node2));\n      assert.isFalse(axe.utils.contains(node2, node1));\n    });\n\n    it('works when the nodes are in nested trees', () => {\n      createNestedShadowDom(\n        fixture,\n        `<section id=\"shadowHost\"></section>`,\n        `<div> <img> </div>`\n      );\n      const tree = axe.setup(fixture);\n      const node1 = axe.utils.querySelectorAll(tree, 'section')[0];\n      const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n      assert.isTrue(axe.utils.contains(node1, node2));\n      assert.isFalse(axe.utils.contains(node2, node1));\n    });\n\n    it('works when the nodes are in nested multiple layers deep', () => {\n      createNestedShadowDom(\n        fixture,\n        `<main id=\"shadowHost\"></main>`,\n        '<article> <div id=\"shadowHost\"></div> </article>',\n        '<section> <div id=\"shadowHost\"></div> </section>',\n        `<div> <img> </div>`\n      );\n      const tree = axe.setup(fixture);\n      const node1 = axe.utils.querySelectorAll(tree, 'main')[0];\n      const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n      assert.isTrue(axe.utils.contains(node1, node2));\n      assert.isFalse(axe.utils.contains(node2, node1));\n    });\n\n    it('is false when the nodes are in adjacent trees', () => {\n      createNestedShadowDom(\n        fixture,\n        '<div id=\"firstHost\" class=\"shadowHost\"></div>',\n        `<img>`\n      );\n      createNestedShadowDom(\n        fixture,\n        '<div id=\"secondHost\" class=\"shadowHost\"></div>',\n        `<input>`\n      );\n      const tree = axe.setup(fixture);\n      const node1 = axe.utils.querySelectorAll(tree, 'img')[0];\n      const node2 = axe.utils.querySelectorAll(tree, 'input')[0];\n\n      assert.isFalse(axe.utils.contains(node1, node2));\n      assert.isFalse(axe.utils.contains(node2, node1));\n    });\n\n    it('works with slotted elements inside shadow DOM', () => {\n      createNestedShadowDom(\n        fixture,\n        `<div id=\"shadowHost\"> <img> </div>`,\n        `<section> <slot /> </section>`\n      );\n      const tree = axe.setup(fixture);\n      const node1 = axe.utils.querySelectorAll(tree, 'section')[0];\n      const node2 = axe.utils.querySelectorAll(tree, 'img')[0];\n\n      assert.isTrue(axe.utils.contains(node1, node2));\n      assert.isFalse(axe.utils.contains(node2, node1));\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/deep-merge.js",
    "content": "describe('utils.deepMerge', function () {\n  var deepMerge = axe.utils.deepMerge;\n\n  it('should merge two objects', function () {\n    var obj1 = { a: 'one' };\n    var obj2 = { b: 'two' };\n\n    assert.deepEqual(deepMerge(obj1, obj2), {\n      a: 'one',\n      b: 'two'\n    });\n  });\n\n  it('should not modify the objects', function () {\n    var obj1 = { a: 'one' };\n    var obj2 = { a: 'two' };\n    deepMerge(obj1, obj2);\n\n    assert.deepEqual(obj1, { a: 'one' });\n    assert.deepEqual(obj2, { a: 'two' });\n  });\n\n  it('should return a new object', function () {\n    var obj1 = { a: 'one' };\n    var obj2 = { a: 'two' };\n    var obj3 = deepMerge(obj1, obj2);\n\n    assert.notStrictEqual(obj1, obj3);\n    assert.notStrictEqual(obj2, obj3);\n  });\n\n  it('should not merge arrays', function () {\n    var obj1 = { a: ['one', 'two'] };\n    var obj2 = { a: ['three'] };\n\n    assert.deepEqual(deepMerge(obj1, obj2), { a: ['three'] });\n  });\n\n  it('should merge nested objects', function () {\n    var obj1 = { a: { a: ['one'] } };\n    var obj2 = { a: { a: ['one', 'two'], b: 'three' } };\n\n    assert.deepEqual(deepMerge(obj1, obj2), {\n      a: {\n        a: ['one', 'two'],\n        b: 'three'\n      }\n    });\n  });\n\n  it('should accept multiple objects', function () {\n    var obj1 = { a: { a: ['one'] } };\n    var obj2 = { a: { a: ['one', 'two'], b: 'three' } };\n    var obj3 = { a: { b: 'four' }, b: 'five' };\n\n    assert.deepEqual(deepMerge(obj1, obj2, obj3), {\n      a: {\n        a: ['one', 'two'],\n        b: 'four'\n      },\n      b: 'five'\n    });\n  });\n\n  it('should handle bad sources', function () {\n    var obj;\n\n    assert.doesNotThrow(function () {\n      obj = deepMerge(null, undefined, true, 'one', ['a', 'b'], 1, { a: 'b' });\n    });\n    assert.deepEqual(obj, { a: 'b' });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/dq-element.js",
    "content": "describe('DqElement', () => {\n  const DqElement = axe.utils.DqElement;\n  const fixture = document.getElementById('fixture');\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  const queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(() => {\n    axe.reset();\n  });\n\n  it('should be exposed to utils', () => {\n    assert.equal(axe.utils.DqElement, DqElement);\n  });\n\n  it('should take a virtual node as a parameter and return an object', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    const result = new DqElement(vNode);\n    assert.equal(result.element, vNode.actualNode);\n  });\n\n  it('should take an actual node as a parameter and return an object', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    const result = new DqElement(vNode.actualNode);\n    assert.equal(result.element, vNode.actualNode);\n  });\n\n  it('should return the same DqElement when instantiated with the same element', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    const result = new DqElement(vNode);\n    const result2 = new DqElement(vNode);\n    assert.equal(result, result2);\n  });\n\n  describe('element', () => {\n    it('should store reference to the element', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      const dqEl = new DqElement(vNode);\n      assert.equal(dqEl.element, vNode.actualNode);\n    });\n\n    it('should not be present in stringified version', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      const dqEl = new DqElement(vNode);\n      assert.isUndefined(JSON.parse(JSON.stringify(dqEl)).element);\n    });\n  });\n\n  describe('source', () => {\n    it('should use spec object over passed element', () => {\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const spec = { source: 'woot' };\n      const result = new DqElement(vNode, {}, spec);\n      assert.equal(result.source, 'woot');\n    });\n\n    it('should return null if audit.noHtml is set', () => {\n      axe.configure({ noHtml: true });\n      const vNode = queryFixture('<div class=\"bar\" id=\"target\">Hello!</div>');\n      const result = new DqElement(vNode);\n      assert.isNull(result.source);\n    });\n\n    it('should not use spec object over passed element if audit.noHtml is set', () => {\n      axe.configure({ noHtml: true });\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const spec = { source: 'woot' };\n      const result = new DqElement(vNode, {}, spec);\n      assert.isNull(result.source);\n    });\n  });\n\n  describe('selector', () => {\n    it('should prefer selector from spec object', () => {\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const spec = { selector: 'woot' };\n      const result = new DqElement(vNode, {}, spec);\n      assert.equal(result.selector, 'woot');\n    });\n  });\n\n  describe('ancestry', () => {\n    it('should prefer selector from spec object', () => {\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const spec = { ancestry: 'woot' };\n      const result = new DqElement(vNode, {}, spec);\n      assert.equal(result.ancestry, 'woot');\n    });\n  });\n\n  describe('xpath', () => {\n    it('should prefer selector from spec object', () => {\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const spec = { xpath: 'woot' };\n      const result = new DqElement(vNode, {}, spec);\n      assert.equal(result.xpath, 'woot');\n    });\n  });\n\n  describe('absolutePaths', () => {\n    it('creates a path all the way to root', () => {\n      const vNode = queryFixture('<div id=\"target\" class=\"bar\">Hello!</div>');\n      const result = new DqElement(vNode, {\n        absolutePaths: true\n      });\n      assert.include(result.selector[0], 'html > ');\n      assert.include(result.selector[0], '#fixture > ');\n      assert.include(result.selector[0], '#target');\n    });\n  });\n\n  describe('nodeIndexes', () => {\n    it('is taken from virtualNode', () => {\n      fixtureSetup('<i></i><b></b><s></s>');\n      assert.deepEqual(new DqElement(fixture.children[0]).nodeIndexes, [1]);\n      assert.deepEqual(new DqElement(fixture.children[1]).nodeIndexes, [2]);\n      assert.deepEqual(new DqElement(fixture.children[2]).nodeIndexes, [3]);\n    });\n\n    it('is taken from spec, over virtualNode', () => {\n      const vNode = queryFixture('<div id=\"target\"></div>');\n      const spec = { nodeIndexes: [123, 456] };\n      const dqElm = new DqElement(vNode, {}, spec);\n      assert.deepEqual(dqElm.nodeIndexes, [123, 456]);\n    });\n\n    it('is [] when the element is not in the virtual tree.', () => {\n      const div = document.createElement('div');\n      const dqElm = new DqElement(div);\n      assert.deepEqual(dqElm.nodeIndexes, []);\n    });\n  });\n\n  describe('toJSON', () => {\n    it('should only stringify selector and source', () => {\n      const spec = {\n        selector: ['foo > bar > joe'],\n        source: '<joe aria-required=\"true\">',\n        xpath: ['/foo/bar/joe'],\n        ancestry: ['foo > bar > joe'],\n        nodeIndexes: [123],\n        fromFrame: false\n      };\n\n      const div = document.createElement('div');\n      const result = new DqElement(div, {}, spec);\n      assert.deepEqual(result.toJSON(), spec);\n    });\n  });\n\n  describe('merging frames', () => {\n    let dqMain, dqIframe;\n    beforeEach(() => {\n      const tree = fixtureSetup(\n        '<main id=\"main\"></main><iframe id=\"iframe\"></iframe>'\n      );\n      const main = axe.utils.querySelectorAll(tree, 'main')[0];\n      const mainSpec = {\n        selector: ['#main'],\n        ancestry: ['html > body > main'],\n        xpath: ['/main']\n      };\n      dqMain = new DqElement(main, {}, mainSpec);\n\n      const iframe = axe.utils.querySelectorAll(tree, 'iframe')[0];\n      const iframeSpec = {\n        selector: ['#iframe'],\n        ancestry: ['html > body > iframe'],\n        xpath: ['/iframe']\n      };\n      dqIframe = new DqElement(iframe, {}, iframeSpec);\n    });\n\n    describe('.mergeSpecs', () => {\n      let mainSpec, iframeSpec;\n      beforeEach(() => {\n        mainSpec = dqMain.toJSON();\n        iframeSpec = dqIframe.toJSON();\n      });\n\n      it('merges node and frame selectors', () => {\n        const mergedSpec = DqElement.mergeSpecs(mainSpec, iframeSpec);\n        assert.deepEqual(mergedSpec.selector, [\n          iframeSpec.selector[0],\n          mainSpec.selector[0]\n        ]);\n        assert.deepEqual(mergedSpec.ancestry, [\n          iframeSpec.ancestry[0],\n          mainSpec.ancestry[0]\n        ]);\n        assert.deepEqual(mergedSpec.xpath, [\n          iframeSpec.xpath[0],\n          mainSpec.xpath[0]\n        ]);\n      });\n\n      it('merges nodeIndexes', () => {\n        const mergedSpec = DqElement.mergeSpecs(mainSpec, iframeSpec);\n        assert.deepEqual(mergedSpec.nodeIndexes, [\n          iframeSpec.nodeIndexes[0],\n          mainSpec.nodeIndexes[0]\n        ]);\n      });\n    });\n\n    describe('DqElement.fromFrame', () => {\n      it('returns a new DqElement', () => {\n        assert.instanceOf(DqElement.fromFrame(dqMain, {}, dqIframe), DqElement);\n      });\n\n      it('sets options for DqElement', () => {\n        const options = { absolutePaths: true };\n        const dqElm = DqElement.fromFrame(dqMain, options, dqIframe);\n        assert.isTrue(dqElm._options.toRoot);\n      });\n\n      it('has props as from mergeSpecs', () => {\n        const spec = DqElement.mergeSpecs(dqMain.toJSON(), dqIframe.toJSON());\n        const dqElm = DqElement.fromFrame(dqMain, {}, dqIframe);\n        assert.deepEqual(dqElm.toJSON(), spec);\n      });\n    });\n\n    describe('DqElement.prototype.fromFrame', () => {\n      it('is false when created without a spec', () => {\n        assert.isFalse(dqMain.fromFrame);\n      });\n\n      it('is false when spec is not from a frame', () => {\n        const specMain = dqMain.toJSON();\n        const dqElm = new DqElement(dqMain, {}, specMain);\n        assert.isFalse(dqElm.fromFrame);\n      });\n\n      it('is true when created with a spec', () => {\n        const dqElm = DqElement.fromFrame(dqMain, {}, dqIframe);\n        assert.isTrue(dqElm.fromFrame);\n      });\n    });\n  });\n\n  describe('DqElement.setRunOptions', () => {\n    it('sets options for DqElement', () => {\n      axe.setup();\n      const options = { absolutePaths: true, elementRef: true };\n      DqElement.setRunOptions(options);\n      const dqElm = new DqElement(document.body);\n\n      const { element, selector } = dqElm.toJSON();\n      assert.equal(element, document.body);\n      assert.equal(selector, 'html > body');\n    });\n\n    it('is reset by axe.teardown', () => {\n      const options = { absolutePaths: true, elementRef: true };\n      DqElement.setRunOptions(options);\n      axe.teardown();\n\n      axe.setup();\n      const dqElm = new DqElement(document.body);\n      const { element, selector } = dqElm.toJSON();\n      assert.isUndefined(element);\n      assert.equal(selector, 'body');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/element-matches.js",
    "content": "describe('utils.matchesSelector', function () {\n  'use strict';\n  var matchesSelector = axe.utils.matchesSelector;\n\n  function mockMethod(method, returnValue) {\n    var result = {};\n    result[method] = function () {\n      return returnValue;\n    };\n    result.ownerDocument = {\n      defaultView: {\n        Element: {\n          prototype: {}\n        }\n      }\n    };\n    result.ownerDocument.defaultView.Element.prototype[method] = function () {};\n\n    return result;\n  }\n\n  it('should check the prototype of the Element object for matching methods', function () {\n    assert.equal(matchesSelector(mockMethod('matches', 'test1')), 'test1');\n    assert.equal(\n      matchesSelector(mockMethod('matchesSelector', 'test2')),\n      'test2'\n    );\n    assert.equal(\n      matchesSelector(mockMethod('mozMatchesSelector', 'test3')),\n      'test3'\n    );\n    assert.equal(\n      matchesSelector(mockMethod('webkitMatchesSelector', 'test4')),\n      'test4'\n    );\n    assert.equal(\n      matchesSelector(mockMethod('msMatchesSelector', 'test5')),\n      'test5'\n    );\n  });\n\n  it('should actually work', function () {\n    var target,\n      fixture = document.getElementById('fixture');\n\n    fixture.innerHTML = '<div id=\"test\">Hi</div>';\n    target = document.getElementById('test');\n    assert.ok(matchesSelector(target, '#test'));\n\n    fixture.innerHTML = '';\n  });\n\n  it('should return false if the element does not have a matching method', function () {\n    var target,\n      fixture = document.getElementById('fixture');\n\n    fixture.innerHTML = '<div id=\"test\">Hi</div>';\n    target = document.getElementById('test');\n\n    target.matches = null;\n    target.matchesSelector = null;\n    target.mozMatchesSelector = null;\n    target.webkitMatchesSelector = null;\n    target.msMatchesSelector = null;\n\n    assert.isFalse(matchesSelector(target, '#test'));\n\n    fixture.innerHTML = '';\n  });\n});\n"
  },
  {
    "path": "test/core/utils/escape-selector.js",
    "content": "describe('utils.escapeSelector', function () {\n  'use strict';\n  var escapeSelector = axe.utils.escapeSelector;\n\n  it('leaves characters that do not need to escape alone', function () {\n    assert.equal(escapeSelector('a0b'), 'a0b');\n    assert.equal(escapeSelector('a1b'), 'a1b');\n    assert.equal(escapeSelector('a2b'), 'a2b');\n    assert.equal(escapeSelector('a3b'), 'a3b');\n    assert.equal(escapeSelector('a4b'), 'a4b');\n    assert.equal(escapeSelector('a5b'), 'a5b');\n    assert.equal(escapeSelector('a6b'), 'a6b');\n    assert.equal(escapeSelector('a7b'), 'a7b');\n    assert.equal(escapeSelector('a8b'), 'a8b');\n    assert.equal(escapeSelector('a9b'), 'a9b');\n    assert.equal(escapeSelector('a0123456789b'), 'a0123456789b');\n    assert.equal(\n      escapeSelector('abcdefghijklmnopqrstuvwxyz'),\n      'abcdefghijklmnopqrstuvwxyz'\n    );\n    assert.equal(\n      escapeSelector('ABCDEFGHIJKLMNOPQRSTUVWXYZ'),\n      'ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n    );\n  });\n\n  it('escapes null characters', function () {\n    assert.equal(escapeSelector('\\0'), '\\uFFFD');\n    assert.equal(escapeSelector('a\\0'), 'a\\uFFFD');\n    assert.equal(escapeSelector('a\\0b'), 'a\\uFFFDb');\n  });\n\n  it('stringifies non-string characters', function () {\n    assert.equal(escapeSelector(), 'undefined');\n    assert.equal(escapeSelector(true), 'true');\n    assert.equal(escapeSelector(false), 'false');\n    assert.equal(escapeSelector(null), 'null');\n    assert.equal(escapeSelector(''), '');\n  });\n\n  it('escapes strings starting with a number', function () {\n    assert.equal(escapeSelector('0a'), '\\\\30 a');\n    assert.equal(escapeSelector('1a'), '\\\\31 a');\n    assert.equal(escapeSelector('2a'), '\\\\32 a');\n    assert.equal(escapeSelector('3a'), '\\\\33 a');\n    assert.equal(escapeSelector('4a'), '\\\\34 a');\n    assert.equal(escapeSelector('5a'), '\\\\35 a');\n    assert.equal(escapeSelector('6a'), '\\\\36 a');\n    assert.equal(escapeSelector('7a'), '\\\\37 a');\n    assert.equal(escapeSelector('8a'), '\\\\38 a');\n    assert.equal(escapeSelector('9a'), '\\\\39 a');\n  });\n\n  it('only escapes \"-\" when before a number, or on its own', function () {\n    assert.equal(escapeSelector('-123'), '-\\\\31 23');\n    assert.equal(escapeSelector('-'), '\\\\-');\n    assert.equal(escapeSelector('--a'), '--a');\n  });\n\n  it('escapes characters staring with a negative number', function () {\n    assert.equal(escapeSelector('-0a'), '-\\\\30 a');\n    assert.equal(escapeSelector('-1a'), '-\\\\31 a');\n    assert.equal(escapeSelector('-2a'), '-\\\\32 a');\n    assert.equal(escapeSelector('-3a'), '-\\\\33 a');\n    assert.equal(escapeSelector('-4a'), '-\\\\34 a');\n    assert.equal(escapeSelector('-5a'), '-\\\\35 a');\n    assert.equal(escapeSelector('-6a'), '-\\\\36 a');\n    assert.equal(escapeSelector('-7a'), '-\\\\37 a');\n    assert.equal(escapeSelector('-8a'), '-\\\\38 a');\n    assert.equal(escapeSelector('-9a'), '-\\\\39 a');\n  });\n\n  it('escapes hex character codes', function () {\n    assert.equal(escapeSelector('\\x80\\x2D\\x5F\\xA9'), '\\x80\\x2D\\x5F\\xA9');\n    assert.equal(escapeSelector('\\xA0\\xA1\\xA2'), '\\xA0\\xA1\\xA2');\n\n    assert.equal(escapeSelector('\\x01\\x02\\x1E\\x1F'), '\\\\1 \\\\2 \\\\1e \\\\1f ');\n    assert.equal(escapeSelector('\\x20\\x21\\x78\\x79'), '\\\\ \\\\!xy');\n\n    // astral symbol (U+1D306 TETRAGRAM FOR CENTRE)\n    assert.equal(escapeSelector('\\uD834\\uDF06'), '\\uD834\\uDF06');\n    // lone surrogates\n    assert.equal(escapeSelector('\\uDF06'), '\\uDF06');\n    assert.equal(escapeSelector('\\uD834'), '\\uD834');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/extend-meta-data.js",
    "content": "describe('axe.utils.extend', function () {\n  'use strict';\n\n  it('should merge properties', function () {\n    var src = {\n      cats: 'fail',\n      dogs: 'fail'\n    };\n\n    axe.utils.extendMetaData(src, {\n      cats: 'meow',\n      dogs: 'woof'\n    });\n    assert.equal(Object.keys(src).length, 2);\n    assert.equal(src.cats, 'meow');\n    assert.equal(src.dogs, 'woof');\n  });\n\n  it('should execute any found functions', function () {\n    var src = {\n      cats: 'fail',\n      dogs: 'fail'\n    };\n    axe.utils.extendMetaData(src, {\n      cats: function (ctxt) {\n        assert.equal(ctxt, src);\n        return 'meow';\n      },\n      dogs: 'woof'\n    });\n    assert.equal(Object.keys(src).length, 2);\n    assert.equal(src.cats, 'meow');\n    assert.equal(src.dogs, 'woof');\n  });\n  it('should catch exceptions in functions and default to `null`', function () {\n    var src = {\n      cats: 'fail',\n      dogs: 'fail'\n    };\n    axe.utils.extendMetaData(src, {\n      cats: function () {\n        throw new Error('hehe');\n      },\n      dogs: 'woof'\n    });\n    assert.equal(Object.keys(src).length, 2);\n    assert.isNull(src.cats);\n    assert.equal(src.dogs, 'woof');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/filter-html-attrs.js",
    "content": "describe('axe.utils.filterHtmlAttrs', function () {\n  'use strict';\n  var fixture = document.querySelector('#fixture');\n\n  var filterHtmlAttrs = axe.utils.filterHtmlAttrs;\n  var html, expected;\n\n  beforeEach(function () {\n    fixture.innerHTML =\n      '<label> My Label <input type=\"text\" value=\"my value\" /></label>';\n    html = fixture.firstChild;\n    expected = '<label> My Label <input value=\"my value\"></label>';\n  });\n\n  it('should return string if no attributes are passed', function () {\n    assert.equal(filterHtmlAttrs(html), html);\n  });\n\n  it('should filter attribute if exists', function () {\n    assert.equal(filterHtmlAttrs(html, { type: true }).outerHTML, expected);\n  });\n\n  it('should filter attribute if matches CSS selector', function () {\n    assert.equal(filterHtmlAttrs(html, { type: 'input' }).outerHTML, expected);\n  });\n\n  it('should not filter attribute if does not match value', function () {\n    assert.equal(\n      filterHtmlAttrs(html, { type: 'div' }).outerHTML,\n      html.outerHTML\n    );\n  });\n\n  it('should not change the original element', function () {\n    var outerHTML = html.outerHTML;\n    assert.isTrue(filterHtmlAttrs(html, { type: true }) !== html);\n    assert.equal(html.outerHTML, outerHTML);\n  });\n\n  it('should filter multiple attributes', function () {\n    assert.equal(\n      filterHtmlAttrs(html, {\n        type: true,\n        value: '[value=\"my value\"]'\n      }).outerHTML,\n      '<label> My Label <input></label>'\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/finalize-result.js",
    "content": "describe('axe.utils.finalizeRuleResult', function () {\n  'use strict';\n  var original = axe._audit;\n\n  beforeEach(function () {\n    axe._audit = {\n      rules: []\n    };\n  });\n\n  after(function () {\n    axe._audit = original;\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.finalizeRuleResult);\n  });\n\n  it('returns the first param object', function () {\n    var goingIn = {\n      nodes: []\n    };\n    var comingOut = axe.utils.finalizeRuleResult(goingIn);\n\n    assert.equal(goingIn, comingOut);\n  });\n\n  it('assigns impact if rule.impact is defined', function () {\n    axe._audit = {\n      rules: [{ id: 'foo', impact: 'critical' }]\n    };\n\n    var output = axe.utils.finalizeRuleResult({\n      id: 'foo',\n      nodes: [\n        {\n          any: [\n            {\n              result: false,\n              impact: 'minor'\n            }\n          ],\n          all: [],\n          none: []\n        }\n      ]\n    });\n\n    assert.equal(output.impact, 'critical');\n    assert.equal(output.violations[0].impact, 'critical');\n    assert.equal(output.violations[0].any[0].impact, 'critical');\n  });\n\n  it('leaves impact as null when rule.impact is defined', function () {\n    axe._audit = {\n      rules: [{ id: 'foo', impact: 'critical' }]\n    };\n\n    var output = axe.utils.finalizeRuleResult({\n      id: 'foo',\n      nodes: [\n        {\n          any: [\n            {\n              result: true,\n              impact: 'minor'\n            }\n          ],\n          all: [],\n          none: []\n        }\n      ]\n    });\n\n    assert.isNull(output.impact);\n    assert.isNull(output.passes[0].impact);\n    assert.equal(output.passes[0].any[0].impact, 'critical');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/find-by.js",
    "content": "describe('axe.utils.findBy', function () {\n  'use strict';\n\n  it('should find the first matching object', function () {\n    var array = [\n      {\n        id: 'monkeys',\n        foo: 'bar'\n      },\n      {\n        id: 'bananas'\n      },\n      {\n        id: 'monkeys',\n        bar: 'baz'\n      }\n    ];\n\n    assert.equal(axe.utils.findBy(array, 'id', 'monkeys'), array[0]);\n  });\n\n  it('should return undefined with no match', function () {\n    var array = [\n      {\n        id: 'monkeys',\n        foo: 'bar'\n      },\n      {\n        id: 'bananas'\n      },\n      {\n        id: 'monkeys',\n        bar: 'baz'\n      }\n    ];\n\n    assert.isUndefined(axe.utils.findBy(array, 'id', 'macaque'));\n  });\n\n  it('should not throw if passed falsey first parameter', function () {\n    assert.isUndefined(axe.utils.findBy(null, 'id', 'macaque'));\n  });\n\n  it('ignores any non-object elements in the array', function () {\n    const obj = {\n      id: 'monkeys',\n      foo: 'bar'\n    };\n    const array = ['bananas', true, null, 123, obj];\n\n    assert.equal(axe.utils.findBy(array, 'id', 'monkeys'), obj);\n  });\n\n  it('only looks at owned properties', function () {\n    const obj1 = { id: 'monkeys', eat: 'bananas' };\n    const obj2 = Object.create(obj1);\n    obj2.id = 'gorillas';\n    assert.equal(axe.utils.findBy([obj2, obj1], 'eat', 'bananas'), obj1);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/flattened-tree.js",
    "content": "var fixture = document.getElementById('fixture');\nvar shadowSupport = axe.testUtils.shadowSupport;\n\ndescribe('axe.utils.getFlattenedTree', function () {\n  'use strict';\n  function createStyle(box) {\n    var style = document.createElement('style');\n    style.textContent =\n      'div.breaking { color: Red;font-size: 20px; border: 1px dashed Purple; }' +\n      (box ? 'slot { display: block; }' : '') +\n      'div.other { padding: 2px 0 0 0; border: 1px solid Cyan; }';\n    return style;\n  }\n\n  function flattenedTreeAssertions() {\n    var virtualDOM = axe.utils.getFlattenedTree(fixture.firstChild);\n    assert.equal(virtualDOM.length, 1); // host\n    assert.equal(virtualDOM[0].actualNode.nodeName, 'DIV');\n\n    var parentDOM = virtualDOM[0];\n    virtualDOM = virtualDOM[0].children;\n    assert.equal(virtualDOM.length, 3);\n    assert.equal(virtualDOM[0].actualNode.nodeName, 'STYLE');\n    assert.equal(virtualDOM[0].parent, parentDOM);\n\n    // breaking news stories\n    assert.equal(virtualDOM[1].actualNode.nodeName, 'DIV');\n    assert.equal(virtualDOM[1].actualNode.className, 'breaking');\n    assert.equal(virtualDOM[1].parent, parentDOM);\n\n    // other news stories\n    assert.equal(virtualDOM[2].actualNode.nodeName, 'DIV');\n    assert.equal(virtualDOM[2].actualNode.className, 'other');\n    assert.equal(virtualDOM[2].parent, parentDOM);\n\n    // breaking\n    assert.equal(virtualDOM[1].children.length, 1);\n    assert.equal(virtualDOM[1].children[0].actualNode.nodeName, 'UL');\n    assert.equal(virtualDOM[1].children[0].parent, virtualDOM[1]);\n    virtualDOM[1].children[0].children.forEach(function (child, index) {\n      assert.equal(child.actualNode.nodeName, 'LI');\n      assert.isTrue(child.actualNode.textContent === 3 * (index + 1) + '');\n    });\n    assert.equal(virtualDOM[1].children[0].children.length, 2);\n\n    // other\n    assert.equal(virtualDOM[2].children.length, 1);\n    assert.equal(virtualDOM[2].children[0].actualNode.nodeName, 'UL');\n    assert.equal(virtualDOM[2].children[0].parent, virtualDOM[2]);\n    assert.equal(virtualDOM[2].children[0].children.length, 4);\n  }\n\n  function shadowIdAssertions() {\n    var virtualDOM = axe.utils.getFlattenedTree(fixture);\n    assert.isUndefined(virtualDOM[0].shadowId); //fixture\n    assert.isUndefined(virtualDOM[0].children[0].shadowId); //host\n    assert.isDefined(virtualDOM[0].children[0].children[0].shadowId);\n    assert.isDefined(virtualDOM[0].children[0].children[1].shadowId);\n    assert.isDefined(virtualDOM[0].children[1].children[0].shadowId);\n    // shadow IDs in the same shadowRoot must be the same\n    assert.equal(\n      virtualDOM[0].children[0].children[0].shadowId,\n      virtualDOM[0].children[0].children[1].shadowId\n    );\n    // should cascade\n    assert.equal(\n      virtualDOM[0].children[0].children[1].shadowId,\n      virtualDOM[0].children[0].children[1].children[0].shadowId\n    );\n    // shadow IDs in different shadowRoots must be different\n    assert.notEqual(\n      virtualDOM[0].children[0].children[0].shadowId,\n      virtualDOM[0].children[1].children[0].shadowId\n    );\n  }\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should default to document', function () {\n    fixture.innerHTML = '';\n    var tree = axe.utils.getFlattenedTree();\n    assert(tree[0].actualNode === document.documentElement);\n  });\n\n  it('should set `null` on the parent for the root node', function () {\n    var tree = axe.utils.getFlattenedTree();\n    assert(tree[0].parent === null);\n  });\n\n  it('creates virtual nodes in the correct order', function () {\n    fixture.innerHTML = '<p><b><i></i></b></p><u><s></s></u>';\n\n    var vNode = axe.utils.getFlattenedTree(fixture)[0];\n    assert.equal(vNode.nodeIndex, 0);\n    assert.equal(vNode.props.nodeName, 'div');\n    assert.equal(vNode.children[0].nodeIndex, 1);\n    assert.equal(vNode.children[0].props.nodeName, 'p');\n    assert.equal(vNode.children[0].children[0].nodeIndex, 2);\n    assert.equal(vNode.children[0].children[0].props.nodeName, 'b');\n    assert.equal(vNode.children[0].children[0].children[0].nodeIndex, 3);\n    assert.equal(vNode.children[0].children[0].children[0].props.nodeName, 'i');\n    assert.equal(vNode.children[1].nodeIndex, 4);\n    assert.equal(vNode.children[1].props.nodeName, 'u');\n    assert.equal(vNode.children[1].children[0].nodeIndex, 5);\n    assert.equal(vNode.children[1].children[0].props.nodeName, 's');\n  });\n\n  it('should add selectorMap to root element', function () {\n    var tree = axe.utils.getFlattenedTree();\n    assert.exists(tree[0]._selectorMap);\n  });\n\n  if (shadowSupport.v0) {\n    describe('shadow DOM v0', function () {\n      beforeEach(function () {\n        function createStoryGroup(className, contentSelector) {\n          var group = document.createElement('div');\n          group.className = className;\n          group.innerHTML =\n            '<ul><content select=\"' + contentSelector + '\"></content></ul>';\n          return group;\n        }\n\n        function makeShadowTree(storyList) {\n          var root = storyList.createShadowRoot();\n          root.appendChild(createStyle());\n          root.appendChild(createStoryGroup('breaking', '.breaking'));\n          root.appendChild(createStoryGroup('other', ''));\n        }\n        var str =\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\">6</li></div>';\n        str +=\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\">6</li></div>';\n        fixture.innerHTML = str;\n\n        fixture.querySelectorAll('.stories').forEach(makeShadowTree);\n      });\n      it('it should support shadow DOM v0', function () {\n        assert.isDefined(fixture.firstChild.shadowRoot);\n      });\n      it('getFlattenedTree should return an array of stuff', function () {\n        assert.isTrue(\n          Array.isArray(axe.utils.getFlattenedTree(fixture.firstChild))\n        );\n      });\n      it(\n        \"getFlattenedTree's virtual DOM should represent the flattened tree\",\n        flattenedTreeAssertions\n      );\n      it(\n        \"getFlattenedTree's virtual DOM should give an ID to the shadow DOM\",\n        shadowIdAssertions\n      );\n    });\n  }\n\n  if (shadowSupport.v1) {\n    describe('shadow DOM v1', function () {\n      beforeEach(function () {\n        function createStoryGroup(className, slotName) {\n          var group = document.createElement('div');\n          group.className = className;\n          // Empty string in slot name attribute or absence thereof work the same, so no need for special handling.\n          group.innerHTML =\n            '<ul><slot name=\"' +\n            slotName +\n            '\">fallback content<li>one</li></slot></ul>';\n          return group;\n        }\n\n        function makeShadowTree(storyList) {\n          var root = storyList.attachShadow({ mode: 'open' });\n          root.appendChild(createStyle());\n          root.appendChild(createStoryGroup('breaking', 'breaking'));\n          root.appendChild(createStoryGroup('other', ''));\n        }\n        var str =\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str +=\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str += '<div class=\"stories\"></div>';\n        fixture.innerHTML = str;\n\n        fixture.querySelectorAll('.stories').forEach(makeShadowTree);\n      });\n      it('should support shadow DOM v1', function () {\n        assert.isDefined(fixture.firstChild.shadowRoot);\n      });\n      it('getFlattenedTree should return an array of stuff', function () {\n        assert.isTrue(\n          Array.isArray(axe.utils.getFlattenedTree(fixture.firstChild))\n        );\n      });\n      it(\n        \"getFlattenedTree's virtual DOM should represent the flattened tree\",\n        flattenedTreeAssertions\n      );\n      it(\n        \"getFlattenedTree's virtual DOM should give an ID to the shadow DOM\",\n        shadowIdAssertions\n      );\n      it(\"getFlattenedTree's virtual DOM should have the fallback content\", function () {\n        var virtualDOM = axe.utils.getFlattenedTree(fixture);\n        assert.isTrue(\n          virtualDOM[0].children[2].children[1].children[0].children.length ===\n            2\n        );\n        assert.isTrue(\n          virtualDOM[0].children[2].children[1].children[0].children[0]\n            .actualNode.nodeType === 3\n        );\n        assert.isTrue(\n          virtualDOM[0].children[2].children[1].children[0].children[0]\n            .actualNode.textContent === 'fallback content'\n        );\n        assert.isTrue(\n          virtualDOM[0].children[2].children[1].children[0].children[1]\n            .actualNode.nodeName === 'LI'\n        );\n      });\n    });\n    describe('shadow DOM v1: boxed slots', function () {\n      afterEach(function () {\n        fixture.innerHTML = '';\n      });\n      beforeEach(function () {\n        function createStoryGroup(className, slotName) {\n          var group = document.createElement('div');\n          group.className = className;\n          // Empty string in slot name attribute or absence thereof work the same, so no need for special handling.\n          group.innerHTML =\n            '<ul><slot name=\"' +\n            slotName +\n            '\">fallback content<li>one</li></slot></ul>';\n          return group;\n        }\n\n        function makeShadowTree(storyList) {\n          var root = storyList.attachShadow({ mode: 'open' });\n          root.appendChild(createStyle(true));\n          root.appendChild(createStoryGroup('breaking', 'breaking'));\n          root.appendChild(createStoryGroup('other', ''));\n        }\n        var str =\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str +=\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str += '<div class=\"stories\"></div>';\n        fixture.innerHTML = str;\n\n        fixture.querySelectorAll('.stories').forEach(makeShadowTree);\n      });\n      it(\"getFlattenedTree's virtual DOM should have the <slot> elements\", function () {\n        return; // Chrome's implementation of slot is broken\n        // var virtualDOM = axe.utils.getFlattenedTree(fixture);\n        // assert.isTrue(virtualDOM[0].children[1].children[0].children[0].actualNode.nodeName === 'SLOT');\n      });\n    });\n    describe('getNodeFromTree', function () {\n      afterEach(function () {\n        fixture.innerHTML = '';\n      });\n      beforeEach(function () {\n        function createStoryGroup(className, slotName) {\n          var group = document.createElement('div');\n          group.className = className;\n          // Empty string in slot name attribute or absence thereof work the same, so no need for special handling.\n          group.innerHTML =\n            '<ul><slot name=\"' +\n            slotName +\n            '\">fallback content<li>one</li></slot></ul>';\n          return group;\n        }\n\n        function makeShadowTree(storyList) {\n          var root = storyList.attachShadow({ mode: 'open' });\n          root.appendChild(createStyle());\n          root.appendChild(createStoryGroup('breaking', 'breaking'));\n          root.appendChild(createStoryGroup('other', ''));\n        }\n        var str =\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str +=\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str += '<div class=\"stories\"></div>';\n        fixture.innerHTML = str;\n\n        fixture.querySelectorAll('.stories').forEach(makeShadowTree);\n      });\n      it('should find the virtual node that matches the real node passed in', function () {\n        axe.utils.getFlattenedTree(fixture);\n        var node = document.querySelector('.stories li');\n        var vNode = axe.utils.getNodeFromTree(node);\n        assert.isDefined(vNode);\n        assert.equal(node, vNode.actualNode);\n        assert.equal(vNode.actualNode.textContent, '1');\n      });\n      it('should find the virtual node if it is the very top of the tree', function () {\n        var virtualDOM = axe.utils.getFlattenedTree(fixture);\n        var vNode = axe.utils.getNodeFromTree(\n          virtualDOM[0],\n          virtualDOM[0].actualNode\n        );\n        assert.isDefined(vNode);\n        assert.equal(virtualDOM[0].actualNode, vNode.actualNode);\n      });\n      it('should not throw if getDistributedNodes is missing', function () {\n        var getDistributedNodes = fixture.getDistributedNodes;\n        fixture.getDistributedNodes = undefined;\n        try {\n          var virtualDOM = axe.utils.getFlattenedTree(fixture);\n          var vNode = axe.utils.getNodeFromTree(\n            virtualDOM[0],\n            virtualDOM[0].actualNode\n          );\n          assert.isDefined(vNode);\n          assert.equal(virtualDOM[0].actualNode, vNode.actualNode);\n        } finally {\n          fixture.getDistributedNodes = getDistributedNodes;\n        }\n      });\n    });\n  } else {\n    it('does not throw when slot elements are used', function () {\n      fixture.innerHTML = '<button><slot></slot></button>';\n      assert.doesNotThrow(function () {\n        axe.utils.getFlattenedTree(fixture);\n      });\n    });\n  }\n\n  if (shadowSupport.undefined) {\n    describe('shadow dom undefined', function () {\n      it('SHADOW DOM TESTS DEFERRED, NO SUPPORT');\n    });\n  }\n});\n"
  },
  {
    "path": "test/core/utils/frame-messenger/frame-messenger.js",
    "content": "describe('frame-messenger', function () {\n  var fixture,\n    axeVersion,\n    axeApplication,\n    frame,\n    frameWin,\n    respondable,\n    frameSubscribe,\n    axeLog;\n  var postMessage = window.postMessage;\n  var captureError = axe.testUtils.captureError;\n\n  beforeEach(function (done) {\n    respondable = axe.utils.respondable;\n    axeVersion = axe.version;\n    axeLog = axe.log;\n    axeApplication = axe._audit.application;\n\n    frame = document.createElement('iframe');\n    frame.src = '../mock/frames/test.html';\n    frame.addEventListener('load', function () {\n      frameWin = frame.contentWindow;\n      frameSubscribe = frameWin.axe.utils.respondable.subscribe;\n      done();\n    });\n    frame.addEventListener('error', done);\n\n    fixture = document.querySelector('#fixture');\n    fixture.appendChild(frame);\n  });\n\n  afterEach(function () {\n    axe.version = axeVersion;\n    axe._audit.application = axeApplication;\n    axe.log = axeLog;\n    axe.reset();\n    window.postMessage = postMessage;\n  });\n\n  it('can be subscribed to', function (done) {\n    frameSubscribe('greeting', function () {\n      done();\n    });\n    respondable(frameWin, 'greeting', 'hello');\n  });\n\n  it('forwards the message', function (done) {\n    var expected = { hello: 'world' };\n    frameSubscribe(\n      'greeting',\n      captureError(function (actual) {\n        assert.deepEqual(actual, expected);\n        done();\n      }, done)\n    );\n    respondable(frameWin, 'greeting', expected);\n  });\n\n  it('passes a truthy keepalive value', function (done) {\n    frameSubscribe(\n      'greeting',\n      captureError(function (_, keepalive) {\n        assert.isTrue(keepalive);\n        done();\n      }, done)\n    );\n    respondable(frameWin, 'greeting', 'hello', 'truthy');\n  });\n\n  it('passes a falsy keepalive value', function (done) {\n    frameSubscribe(\n      'greeting',\n      captureError(function (_, keepalive) {\n        assert.isFalse(keepalive);\n        done();\n      }, done)\n    );\n    respondable(frameWin, 'greeting', 'hello', 0);\n  });\n\n  it('can not publish to a parent frame', function (done) {\n    var isCalled = false;\n    axe.utils.respondable.subscribe('greeting', function () {\n      isCalled = true;\n    });\n    assert.throws(function () {\n      frameWin.axe.utils.respondable(window, 'greeting', 'hello', 0);\n    });\n    setTimeout(\n      captureError(function () {\n        assert.isFalse(isCalled);\n        done();\n      }, done),\n      100\n    );\n  });\n\n  it('does not expose private methods', function () {\n    var methods = Object.keys(respondable).sort();\n    assert.deepEqual(\n      methods,\n      ['subscribe', 'isInFrame', 'updateMessenger'].sort()\n    );\n  });\n\n  it('passes serialized information only', function (done) {\n    var div = document.createElement('div');\n    frameSubscribe(\n      'greeting',\n      captureError(function (message) {\n        assert.deepEqual(message, {});\n        done();\n      }, done)\n    );\n\n    respondable(frameWin, 'greeting', div);\n  });\n\n  it('posts message to allowed origins', function () {\n    axe.configure({\n      allowedOrigins: [window.location.origin, 'http://customOrigin.com']\n    });\n\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isTrue(posted);\n    assert.equal(spy.callCount, 2);\n    assert.deepEqual(spy.firstCall.args[1], window.location.origin);\n    assert.deepEqual(spy.secondCall.args[1], 'http://customOrigin.com');\n  });\n\n  it('posts message to allowed origins using <same_origin>', function () {\n    axe.configure({\n      allowedOrigins: ['<same_origin>']\n    });\n\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isTrue(posted);\n    assert.equal(spy.callCount, 1);\n    assert.deepEqual(spy.firstCall.args[1], window.location.origin);\n  });\n\n  it('posts message to allowed origins using <unsafe_all_origins>', function () {\n    axe.configure({\n      allowedOrigins: ['http://customOrigin.com', '<unsafe_all_origins>']\n    });\n\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isTrue(posted);\n    assert.equal(spy.callCount, 1);\n    assert.equal(spy.firstCall.args[1], '*');\n  });\n\n  it('does not post message if no allowed origins', function () {\n    axe.configure({\n      allowedOrigins: []\n    });\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isFalse(posted);\n    assert.isFalse(spy.called);\n  });\n\n  it('does not post message if no allowed origins', function () {\n    axe._audit.allowedOrigins = null;\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isFalse(posted);\n    assert.isFalse(spy.called);\n  });\n\n  it('does not post message if allowed origins is empty', function () {\n    axe.configure({\n      allowedOrigins: []\n    });\n    var spy = sinon.spy(frameWin, 'postMessage');\n    var posted = respondable(frameWin, 'greeting');\n    assert.isFalse(posted);\n    assert.isFalse(spy.called);\n  });\n\n  it('throws error if origin is invalid', function () {\n    axe.configure({\n      allowedOrigins: ['foo.com']\n    });\n    assert.throws(function () {\n      respondable(frameWin, 'greeting');\n    }, 'allowedOrigins value \"foo.com\" is not a valid origin');\n  });\n\n  it('does not log error if message is null', function (done) {\n    axe.configure({\n      allowedOrigins: ['<unsafe_all_origins>']\n    });\n    var called = false;\n    frameWin.axe.log = function () {\n      called = true;\n    };\n\n    frameWin.postMessage(null, '*');\n\n    setTimeout(function () {\n      try {\n        assert.isFalse(called);\n        done();\n      } catch (e) {\n        done(e);\n      }\n    }, 500);\n  });\n\n  describe('isInFrame', function () {\n    it('is false for the page window', function () {\n      var frameRespondable = frameWin.axe.utils.respondable;\n      assert.isFalse(respondable.isInFrame());\n      assert.isFalse(frameRespondable.isInFrame(window));\n    });\n\n    it('is true for iframes', function () {\n      var frameRespondable = frameWin.axe.utils.respondable;\n      assert.isTrue(frameRespondable.isInFrame());\n      assert.isTrue(respondable.isInFrame(frameWin));\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/frame-messenger/subscribe.js",
    "content": "function afterMessage(win, callback) {\n  var handler = function () {\n    win.removeEventListener('message', handler);\n    // Wait one more tick for stuff to resolve\n    setTimeout(function () {\n      callback();\n    }, 10);\n  };\n  win.addEventListener('message', handler);\n}\n\nfunction once(callback) {\n  var called = false;\n  return function () {\n    if (!called) {\n      callback.apply(this, arguments);\n    }\n    called = true;\n  };\n}\n\ndescribe('frame-messenger', function () {\n  var fixture,\n    axeVersion,\n    axeApplication,\n    frame,\n    frameWin,\n    respondable,\n    frameSubscribe,\n    axeLog;\n  var postMessage = window.postMessage;\n  var captureError = axe.testUtils.captureError;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  beforeEach(function (done) {\n    respondable = axe.utils.respondable;\n    axeVersion = axe.version;\n    axeLog = axe.log;\n    axeApplication = axe._audit.application;\n\n    frame = document.createElement('iframe');\n    frame.src = '../mock/frames/test.html';\n    frame.addEventListener('load', function () {\n      frameWin = frame.contentWindow;\n      frameSubscribe = frameWin.axe.utils.respondable.subscribe;\n      done();\n    });\n    frame.addEventListener('error', done);\n\n    fixture = document.querySelector('#fixture');\n    fixture.appendChild(frame);\n  });\n\n  afterEach(function () {\n    axe.version = axeVersion;\n    axe._audit.application = axeApplication;\n    axe.log = axeLog;\n    axe.reset();\n    window.postMessage = postMessage;\n  });\n\n  describe('subscribe', function () {\n    it('is called with the same topic', function (done) {\n      var called = false;\n      frameSubscribe('greeting', function () {\n        called = true;\n      });\n      respondable(frameWin, 'greeting');\n      afterMessage(\n        frameWin,\n        captureError(function () {\n          assert.isTrue(called);\n          done();\n        }, done)\n      );\n    });\n\n    (shadowSupported ? it : xit)(\n      'works with frames in shadow DOM',\n      function (done) {\n        fixture.innerHTML = '<div id=\"shadow-root\"></div>';\n        var shadowRoot = fixture\n          .querySelector('#shadow-root')\n          .attachShadow({ mode: 'open' });\n        frame = document.createElement('iframe');\n        frame.src = '../mock/frames/test.html';\n\n        frame.addEventListener('load', function () {\n          var called = false;\n          frameWin = frame.contentWindow;\n          frameSubscribe = frameWin.axe.utils.respondable.subscribe;\n\n          frameSubscribe('greeting', function (msg) {\n            assert.equal(msg, 'hello');\n            called = true;\n          });\n          respondable(frameWin, 'greeting', 'hello');\n          afterMessage(\n            frameWin,\n            captureError(function () {\n              assert.isTrue(called);\n              done();\n            }, done)\n          );\n        });\n        shadowRoot.appendChild(frame);\n      }\n    );\n\n    it('is not called on a different topic', function (done) {\n      var called = false;\n      frameSubscribe('otherTopic', function () {\n        called = true;\n      });\n      respondable(frameWin, 'greeting');\n      afterMessage(\n        frameWin,\n        captureError(function () {\n          assert.isFalse(called);\n          done();\n        }, done)\n      );\n    });\n\n    it('is not called for different axe-core versions', function (done) {\n      var called = false;\n      axe.version = '1.0.0';\n      frameSubscribe('greeting', function () {\n        called = true;\n      });\n      respondable(frameWin, 'greeting');\n      afterMessage(\n        frameWin,\n        captureError(function () {\n          assert.isFalse(called);\n          done();\n        }, done)\n      );\n    });\n\n    it('is not called with the \"x.y.z\" wildcard', function (done) {\n      var called = false;\n      axe.version = 'x.y.z';\n      frameSubscribe('greeting', function () {\n        called = true;\n      });\n      respondable(frameWin, 'greeting');\n      afterMessage(\n        frameWin,\n        captureError(function () {\n          assert.isFalse(called);\n          done();\n        }, done)\n      );\n    });\n\n    it('is not called for different applications', function (done) {\n      var called = false;\n      axe._audit.application = 'Coconut';\n      frameSubscribe('greeting', function () {\n        called = true;\n      });\n      respondable(frameWin, 'greeting');\n      afterMessage(\n        frameWin,\n        captureError(function () {\n          assert.isFalse(called);\n          done();\n        }, done)\n      );\n    });\n\n    it('logs errors passed to respondable, rather than passing them on', function (done) {\n      axe.log = captureError(function (e) {\n        assert.equal(e.message, 'expected message');\n        done();\n      }, done);\n\n      frameSubscribe('greeting', function () {\n        done(new Error('subscribe should not be called'));\n      });\n      respondable(frameWin, 'greeting', new Error('expected message'));\n    });\n\n    it('throws if frame.parent is not the window', function () {\n      frameWin.parent = frameWin;\n      assert.throws(function () {\n        respondable(frameWin, 'greeting');\n      });\n    });\n\n    it('is not called when the source is not a frame in the page', function (done) {\n      var doneOnce = once(done);\n      var called = false;\n      frameWin.axe.log = function () {\n        called = true;\n      };\n\n      frameSubscribe('greeting', function () {\n        doneOnce(new Error('subscribe should not be called'));\n      });\n      respondable(frameWin, 'greeting');\n      // Swap parent after the message is sent, but before it is received:\n      frameWin.parent = frameWin;\n\n      setTimeout(\n        captureError(function () {\n          assert.isTrue(called);\n          doneOnce();\n        }, doneOnce),\n        100\n      );\n    });\n\n    it('throws when targeting itself', function () {\n      assert.throws(function () {\n        respondable(window, 'greeting');\n      });\n      assert.throws(function () {\n        frameWin.respondable(frameWin, 'greeting');\n      });\n    });\n\n    it('throws when targeting a window that is not a frame in the page', function () {\n      var blankPage = window.open('');\n      var frameCopy = window.open(frameWin.location.href);\n\n      // seems ie11 can't open new windows?\n      if (!blankPage) {\n        return;\n      }\n\n      // Cleanup\n      setTimeout(function () {\n        blankPage.close();\n        frameCopy.close();\n      });\n\n      assert.throws(function () {\n        respondable(blankPage, 'greeting');\n      });\n      assert.throws(function () {\n        respondable(frameCopy, 'greeting');\n      });\n    });\n\n    it('is not triggered by \"repeaters\"', function (done) {\n      var calls = 0;\n      frameSubscribe('greeting', function () {\n        calls++;\n      });\n      // Repeat fire the event\n      frameWin.addEventListener('message', function handler(evt) {\n        frameWin.postMessage(evt.data, '*');\n        frameWin.removeEventListener('message', handler);\n      });\n\n      respondable(frameWin, 'greeting', 'hello');\n      setTimeout(\n        captureError(function () {\n          assert.equal(calls, 1);\n          done();\n        }, done),\n        100\n      );\n    });\n\n    it('is not called if origin does not match', function (done) {\n      axe.configure({\n        allowedOrigins: ['http://customOrigin.com']\n      });\n      var spy = sinon.spy();\n\n      frameSubscribe('greeting', spy);\n      respondable(frameWin, 'greeting', 'hello');\n\n      setTimeout(function () {\n        assert.isFalse(spy.called);\n        done();\n      }, 500);\n    });\n\n    it('is called if origin is <unsafe_all_origins>', function (done) {\n      axe.configure({\n        allowedOrigins: ['<unsafe_all_origins>']\n      });\n      var spy = sinon.spy();\n\n      frameSubscribe('greeting', spy);\n      respondable(frameWin, 'greeting', 'hello');\n\n      setTimeout(function () {\n        assert.isTrue(spy.called);\n        done();\n      }, 500);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-all-checks.js",
    "content": "describe('axe.utils.getAllChecks', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.getAllChecks);\n  });\n\n  it('should concatenate all 3 check collections', function () {\n    var r = {\n      any: ['any:foo', 'any:bar'],\n      all: ['all:foo', 'all:bar'],\n      none: ['none:foo', 'none:bar']\n    };\n    assert.deepEqual(axe.utils.getAllChecks(r), [\n      'any:foo',\n      'any:bar',\n      'all:foo',\n      'all:bar',\n      'none:foo',\n      'none:bar'\n    ]);\n  });\n\n  it('should safely ignore missing collections - all', function () {\n    var r = {\n      any: ['any:foo', 'any:bar'],\n      none: ['none:foo', 'none:bar']\n    };\n    assert.deepEqual(axe.utils.getAllChecks(r), [\n      'any:foo',\n      'any:bar',\n      'none:foo',\n      'none:bar'\n    ]);\n  });\n\n  it('should safely ignore missing collections - any', function () {\n    var r = {\n      all: ['all:foo', 'all:bar'],\n      none: ['none:foo', 'none:bar']\n    };\n    assert.deepEqual(axe.utils.getAllChecks(r), [\n      'all:foo',\n      'all:bar',\n      'none:foo',\n      'none:bar'\n    ]);\n  });\n\n  it('should safely ignore missing collections - none', function () {\n    var r = {\n      any: ['any:foo', 'any:bar'],\n      all: ['all:foo', 'all:bar']\n    };\n    assert.deepEqual(axe.utils.getAllChecks(r), [\n      'any:foo',\n      'any:bar',\n      'all:foo',\n      'all:bar'\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-ancestry.js",
    "content": "describe('axe.utils.getAncestry', () => {\n  'use strict';\n  const fixture = document.getElementById('fixture');\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', () => {\n    assert.isFunction(axe.utils.getAncestry);\n  });\n\n  it('should generate an ancestry selector', () => {\n    fixture.innerHTML = '<div>1</div> <p>2</p> <p>3</p>';\n\n    const sel1 = axe.utils.getAncestry(fixture.children[0]);\n    assert.equal(sel1, 'html > body > div:nth-child(1) > div:nth-child(1)');\n    assert.isNotNull(document.querySelector(sel1));\n\n    const sel2 = axe.utils.getAncestry(fixture.children[1]);\n    assert.equal(sel2, 'html > body > div:nth-child(1) > p:nth-child(2)');\n    assert.isNotNull(document.querySelector(sel1));\n\n    const sel3 = axe.utils.getAncestry(fixture.children[2]);\n    assert.equal(sel3, 'html > body > div:nth-child(1) > p:nth-child(3)');\n    assert.isNotNull(document.querySelector(sel1));\n  });\n\n  it('should not throw when there is no parent', () => {\n    const node = document.createElement('section');\n    assert.doesNotThrow(() => axe.utils.getAncestry(node));\n  });\n\n  it('generates selectors of nested shadow trees', () => {\n    const node = document.createElement('section');\n    fixture.appendChild(node);\n\n    const shadowRoot1 = node.attachShadow({ mode: 'open' });\n    shadowRoot1.innerHTML = '<div><article><slot /></article</div>';\n\n    const shadowRoot2 = shadowRoot1\n      .querySelector('article')\n      .attachShadow({ mode: 'open' });\n    shadowRoot2.innerHTML = '<h1>Hello world</h1>';\n\n    const target = shadowRoot2.querySelector('h1');\n    const sel = axe.utils.getAncestry(target);\n    assert.deepEqual(sel, [\n      'html > body > div:nth-child(1) > section',\n      'div > article',\n      'h1'\n    ]);\n  });\n\n  it('generates selectors of siblings in shadow tree', () => {\n    const node = document.createElement('section');\n    fixture.appendChild(node);\n\n    const shadowRoot = node.attachShadow({ mode: 'open' });\n    shadowRoot.innerHTML = '<div>1</div> <div>2</div>';\n\n    const sel1 = axe.utils.getAncestry(shadowRoot.children[0]);\n    assert.deepEqual(sel1, [\n      'html > body > div:nth-child(1) > section',\n      'div:nth-child(1)'\n    ]);\n\n    const sel2 = axe.utils.getAncestry(shadowRoot.children[1]);\n    assert.deepEqual(sel2, [\n      'html > body > div:nth-child(1) > section',\n      'div:nth-child(2)'\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-base-lang.js",
    "content": "describe('axe.utils.getBaseLang', function () {\n  'use strict';\n\n  it('returns base lang as peanut for argument peanut-BUTTER', function () {\n    var actual = axe.utils.getBaseLang('peanut-BUTTER');\n    assert.equal(actual, 'peanut');\n  });\n\n  it('returns base lang as fr for argument FR-CA', function () {\n    var actual = axe.utils.getBaseLang('FR-CA');\n    assert.strictEqual(actual, 'fr');\n  });\n\n  it('returns base lang which is the prefix string before the first - (hyphen)', function () {\n    var actual = axe.utils.getBaseLang('en-GB');\n    assert.equal(actual, 'en');\n  });\n\n  it('returns primary language subtag as base lang for multi hyphenated argument', function () {\n    var actual = axe.utils.getBaseLang('SOME-random-lang');\n    assert.strictEqual(actual, 'some');\n  });\n\n  it('returns an empty string when argument is null or undefined', function () {\n    var actualNull = axe.utils.getBaseLang(null);\n    var actualUndefined = axe.utils.getBaseLang(undefined);\n    var actualEmpty = axe.utils.getBaseLang();\n    assert.strictEqual(actualNull, '');\n    assert.strictEqual(actualUndefined, '');\n    assert.strictEqual(actualEmpty, '');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-check-message.js",
    "content": "describe('axe.utils.getCheckMessage', function () {\n  var getCheckMessage = axe.utils.getCheckMessage;\n\n  beforeEach(function () {\n    axe._audit = {\n      data: {\n        checks: {\n          'my-check': {\n            messages: {\n              pass: 'Pass message',\n              fail: 'Fail message',\n              incomplete: 'Incomplete message'\n            }\n          }\n        }\n      }\n    };\n  });\n\n  afterEach(function () {\n    axe._audit = undefined;\n  });\n\n  it('should return the pass message', function () {\n    assert.equal(getCheckMessage('my-check', 'pass'), 'Pass message');\n  });\n\n  it('should return the fail message', function () {\n    assert.equal(getCheckMessage('my-check', 'fail'), 'Fail message');\n  });\n\n  it('should return the incomplete message', function () {\n    assert.equal(\n      getCheckMessage('my-check', 'incomplete'),\n      'Incomplete message'\n    );\n  });\n\n  it('should handle data', function () {\n    axe._audit.data.checks['my-check'].messages.pass =\n      'Pass message with ${data.message}';\n    assert.equal(\n      getCheckMessage('my-check', 'pass', { message: 'hello world!' }),\n      'Pass message with hello world!'\n    );\n  });\n\n  it('should error when check does not exist', function () {\n    assert.throws(function () {\n      getCheckMessage('invalid-check', 'pass');\n    });\n  });\n\n  it('should error when check message does not exist', function () {\n    assert.throws(function () {\n      getCheckMessage('invalid-check', 'invalid');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-check-option.js",
    "content": "describe('axe.utils.getCheckOption', function () {\n  'use strict';\n\n  it('should prefer options from rules', function () {\n    assert.deepEqual(\n      axe.utils.getCheckOption(\n        {\n          id: 'bananas',\n          enabled: 'fail',\n          options: 'fail'\n        },\n        'monkeys',\n        {\n          rules: {\n            monkeys: {\n              checks: {\n                bananas: {\n                  enabled: 'yes',\n                  options: 'please'\n                }\n              }\n            }\n          },\n          checks: {\n            bananas: {\n              enabled: 'nope',\n              options: 'jerk'\n            }\n          }\n        }\n      ),\n      {\n        enabled: 'yes',\n        options: 'please',\n        absolutePaths: undefined\n      }\n    );\n  });\n  it('should fallback to global check options if not defined on the rule', function () {\n    assert.deepEqual(\n      axe.utils.getCheckOption(\n        {\n          id: 'bananas',\n          enabled: 'fail',\n          options: 'fail'\n        },\n        'monkeys',\n        {\n          rules: {\n            monkeys: {\n              checks: {\n                bananas: {\n                  enabled: 'yes'\n                }\n              }\n            }\n          },\n          checks: {\n            bananas: {\n              enabled: 'nope',\n              options: 'please'\n            }\n          }\n        }\n      ),\n      {\n        enabled: 'yes',\n        options: 'please',\n        absolutePaths: undefined\n      }\n    );\n  });\n\n  it('should prefer fallback to global check options if not defined on the rule', function () {\n    assert.deepEqual(\n      axe.utils.getCheckOption(\n        {\n          id: 'bananas',\n          enabled: 'fail',\n          options: 'fail'\n        },\n        'monkeys',\n        {\n          checks: {\n            bananas: {\n              enabled: 'yes',\n              options: 'please'\n            }\n          }\n        }\n      ),\n      {\n        enabled: 'yes',\n        options: 'please',\n        absolutePaths: undefined\n      }\n    );\n  });\n\n  it('should otherwise use the check', function () {\n    assert.deepEqual(\n      axe.utils.getCheckOption(\n        {\n          id: 'bananas',\n          enabled: 'yes',\n          options: 'please'\n        },\n        'monkeys',\n        {}\n      ),\n      {\n        enabled: 'yes',\n        options: 'please',\n        absolutePaths: undefined\n      }\n    );\n  });\n\n  it('passes absolutePaths option along', function () {\n    assert.deepEqual(\n      axe.utils.getCheckOption(\n        {\n          id: 'bananas',\n          enabled: 'on',\n          options: 'many'\n        },\n        'monkeys',\n        {\n          absolutePaths: 'yep'\n        }\n      ),\n      {\n        enabled: 'on',\n        options: 'many',\n        absolutePaths: 'yep'\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-element-source.js",
    "content": "describe('axe.utils.getElementSource', () => {\n  const getElementSource = axe.utils.getElementSource;\n  const queryFixture = axe.testUtils.queryFixture;\n\n  afterEach(() => {\n    axe.reset();\n  });\n\n  it('should be exposed to utils', () => {\n    assert.equal(typeof axe.utils.getElementSource, 'function');\n  });\n\n  it('should return empty string for null/undefined element', () => {\n    assert.equal(getElementSource(null), '');\n    assert.equal(getElementSource(undefined), '');\n  });\n\n  it('should return nodeValue for text nodes', () => {\n    const vNode = queryFixture('<div id=\"target\">Hello world</div>');\n    const textNode = vNode.actualNode.firstChild;\n    assert.equal(textNode.nodeType, 3);\n    assert.equal(getElementSource(textNode), 'Hello world');\n  });\n\n  it('should return nodeValue for comment nodes', () => {\n    const vNode = queryFixture(\n      '<div id=\"target\"><!-- example comment --></div>'\n    );\n    const commentNode = vNode.actualNode.firstChild;\n    assert.equal(commentNode.nodeType, 8);\n    assert.equal(getElementSource(commentNode), ' example comment ');\n  });\n\n  it('should truncate long nodeValue for non-element nodes', () => {\n    const textNode = document.createTextNode('x'.repeat(500));\n    const result = getElementSource(textNode, { maxLength: 50 });\n    assert.equal(result, 'x'.repeat(50) + '...');\n  });\n\n  it('should work without the virtual tree (element not in axe context)', () => {\n    const el = document.createElement('div');\n    el.setAttribute('id', 'standalone');\n    el.textContent = 'Hello';\n    const result = getElementSource(el);\n    assert.equal(result, '<div id=\"standalone\">Hello</div>');\n  });\n\n  it('should include the outerHTML of the element', () => {\n    const vNode = queryFixture('<div class=\"bar\" id=\"target\">Hello!</div>');\n    const outerHTML = vNode.actualNode.outerHTML;\n    const result = getElementSource(vNode.actualNode);\n    assert.equal(result, outerHTML);\n  });\n\n  it('should work with SVG elements', () => {\n    const vNode = queryFixture('<svg aria-label=\"foo\" id=\"target\"></svg>');\n    const result = getElementSource(vNode.actualNode);\n    assert.equal(result, vNode.actualNode.outerHTML);\n  });\n\n  it('should work with MathML', () => {\n    const vNode = queryFixture(\n      '<math display=\"block\" id=\"target\">' +\n        '<mrow><msup><mi>x</mi><mn>2</mn></msup></mrow>' +\n        '</math>'\n    );\n\n    const result = getElementSource(vNode.actualNode);\n    assert.equal(result, vNode.actualNode.outerHTML);\n  });\n\n  describe('XML namespaces', () => {\n    it('should work with SVG and xlink:href attribute', () => {\n      const vNode = queryFixture(\n        '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">' +\n          '<a id=\"target\" xlink:href=\"#section\"><text>Link</text></a>' +\n          '</svg>'\n      );\n      const result = getElementSource(vNode.actualNode);\n\n      assert.include(result, 'xlink:href');\n      assert.include(result, '#section');\n      assert.equal(result, vNode.actualNode.outerHTML);\n    });\n\n    it('should truncate SVG with namespaced attributes correctly', () => {\n      const vNode = queryFixture(\n        '<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">' +\n          '<a id=\"target\" xlink:href=\"#section\" class=\"link\">' +\n          'x'.repeat(400) +\n          '</a>' +\n          '</svg>'\n      );\n      const result = getElementSource(vNode.actualNode, { maxLength: 50 });\n      assert.match(result, /<a\\s/);\n      assert.include(result, 'xlink:href');\n      assert.match(result, /\\.\\.\\.>$/);\n    });\n\n    it('should work with createElementNS for elements in SVG namespace', () => {\n      const svgNS = 'http://www.w3.org/2000/svg';\n      const el = document.createElementNS(svgNS, 'rect');\n      el.setAttributeNS(null, 'id', 'target');\n      el.setAttribute('width', '100');\n      el.setAttribute('height', '50');\n      const result = getElementSource(el);\n      assert.include(result, 'rect');\n      assert.include(result, 'id=\"target\"');\n      assert.include(result, 'width=\"100\"');\n      assert.include(result, 'height=\"50\"');\n      assert.equal(result, el.outerHTML);\n    });\n\n    it('should work with setAttributeNS for namespaced attributes', () => {\n      const xlinkNS = 'http://www.w3.org/1999/xlink';\n      const svgNS = 'http://www.w3.org/2000/svg';\n      const el = document.createElementNS(svgNS, 'image');\n      el.setAttributeNS(xlinkNS, 'xlink:href', 'test.png');\n      el.setAttributeNS(null, 'id', 'target');\n      const result = getElementSource(el);\n      assert.include(result, 'xlink:href');\n      assert.include(result, 'test.png');\n      assert.include(result, 'id=\"target\"');\n      assert.equal(result, el.outerHTML);\n    });\n  });\n\n  it('should truncate large elements', () => {\n    let div = '<div class=\"foo\" id=\"target\">';\n    for (let i = 0; i < 300; i++) {\n      div += i;\n    }\n    div += '</div>';\n    const vNode = queryFixture(div);\n    const result = getElementSource(vNode.actualNode);\n    assert.equal(result, '<div class=\"foo\" id=\"target\">');\n  });\n\n  it('should truncate large attributes of large element', () => {\n    const el = document.createElement('div');\n    let attributeName = 'data-';\n    let attributeValue = '';\n    for (let i = 0; i < 500; i++) {\n      attributeName += 'foo';\n      attributeValue += i;\n    }\n    el.setAttribute(attributeName, attributeValue);\n\n    const result = getElementSource(el);\n    assert.equal(\n      result,\n      `<div ${attributeName.substring(0, 20)}...=\"${attributeValue.substring(0, 20)}...\">`\n    );\n  });\n\n  it('should remove attributes for a large element having a large number of attributes', () => {\n    let customElement = '<div id=\"target\"';\n    for (let i = 0; i < 100; i++) {\n      customElement += ` attr${i}=\"value${i}\"`;\n    }\n    customElement += `><div>`;\n\n    const vNode = queryFixture(customElement);\n    const result = getElementSource(vNode.actualNode);\n    const truncatedAttrCount = (result.match(/attr/g) || []).length;\n    assert.isBelow(truncatedAttrCount, 100);\n    assert.isAtLeast(truncatedAttrCount, 10);\n  });\n\n  it('should truncate a large element with long custom tag name', () => {\n    let longCustomElementTagName = new Array(300).join('b');\n    let customElement = `<${longCustomElementTagName} id=\"target\">A</${longCustomElementTagName}>`;\n    const vNode = queryFixture(customElement);\n    const result = getElementSource(vNode.actualNode);\n    assert.equal(result, `${customElement.substring(0, 300)} ...>`);\n  });\n\n  it('should not truncate attributes if children are long but attribute itself is within limits', () => {\n    let el = document.createElement('div');\n    let attributeValue = '';\n    let innerHtml = '';\n    for (let i = 0; i < 50; i++) {\n      attributeValue += 'a';\n      innerHtml += 'foobar';\n    }\n    el.setAttribute('long-attribute', attributeValue);\n    el.innerHTML = innerHtml;\n\n    const result = getElementSource(el);\n    assert.equal(result, `<div long-attribute=\"${attributeValue}\">`);\n  });\n\n  describe('options', () => {\n    it('should respect custom maxLength', () => {\n      const vNode = queryFixture(\n        '<div class=\"foo\" id=\"target\">' + 'x'.repeat(200) + '</div>'\n      );\n      const result = getElementSource(vNode.actualNode, { maxLength: 25 });\n      assert.equal(result, '<div class=\"foo\" ...>');\n    });\n\n    it('should respect custom attrLimit', () => {\n      const el = document.createElement('div');\n      const longName = 'data-' + 'x'.repeat(300);\n      const longValue = 'y'.repeat(300);\n      el.setAttribute(longName, longValue);\n\n      const result = getElementSource(el, { attrLimit: 10 });\n      assert.equal(result, '<div data-xxxxx...=\"yyyyyyyyyy...\">');\n    });\n\n    it('should include later attributes that fit after skipping long ones', () => {\n      const vNode = queryFixture(\n        '<div id=\"target\" data-very-long-attr=\"' +\n          'x'.repeat(200) +\n          '\" class=\"foo\">content</div>'\n      );\n      const result = getElementSource(vNode.actualNode, { maxLength: 50 });\n      assert.equal(result, '<div id=\"target\" class=\"foo\" ...>');\n    });\n\n    it('should use defaults when options is empty', () => {\n      let div = '<div class=\"foo\" id=\"target\">';\n      for (let i = 0; i < 300; i++) {\n        div += i;\n      }\n      div += '</div>';\n      const vNode = queryFixture(div);\n      const result = getElementSource(vNode.actualNode, {});\n      assert.equal(result, '<div class=\"foo\" id=\"target\">');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-environment-data.js",
    "content": "describe('utils.getEnvironmentData', function () {\n  'use strict';\n  var __audit;\n  var getEnvironmentData = axe.utils.getEnvironmentData;\n  before(function () {\n    __audit = axe._audit;\n    axe._audit = { brand: 'Deque' };\n  });\n\n  after(function () {\n    axe._audit = __audit;\n  });\n\n  it('returns the first argument, if it is truthy', function () {\n    var input = {\n      testEngine: {\n        name: 'axe-core',\n        version: axe.version\n      }\n    };\n    var output = getEnvironmentData(input);\n    assert.equal(input, output);\n  });\n\n  it('should return a `testEngine` property', function () {\n    var data = getEnvironmentData();\n    assert.isObject(data.testEngine);\n    assert.equal(data.testEngine.name, 'axe-core');\n    assert.equal(data.testEngine.version, axe.version);\n  });\n\n  it('should return a `testRunner` property', function () {\n    var data = getEnvironmentData();\n    assert.isObject(data.testRunner);\n    assert.equal(data.testRunner.name, axe._audit.brand);\n  });\n\n  it('should return a `testEnvironment` property', function () {\n    var data = getEnvironmentData();\n    assert.isObject(data.testEnvironment);\n    assert.ok(data.testEnvironment.userAgent);\n    assert.ok(data.testEnvironment.windowWidth);\n    assert.ok(data.testEnvironment.windowHeight);\n    assert.isNotNull(data.testEnvironment.orientationAngle);\n    assert.isNotNull(data.testEnvironment.orientationType);\n  });\n\n  it('should return a `timestamp` property`', function () {\n    var data = getEnvironmentData();\n    assert.isDefined(data.timestamp);\n  });\n\n  it('should return a `url` property', function () {\n    var data = getEnvironmentData();\n    assert.isDefined(data.url);\n  });\n\n  // TODO: remove or update test once we are testing axe-core in jsdom and\n  // other supported environments as what this is testing should be done in\n  // those environment tests\n  it('gets data from the `win` parameter when passed', function () {\n    var data = getEnvironmentData(null, {\n      screen: {\n        orientation: {\n          type: 'fictional',\n          angle: 'slanted'\n        }\n      },\n      navigator: {\n        userAgent: 'foo'\n      },\n      location: {\n        href: 'foo://'\n      },\n      innerWidth: 321,\n      innerHeight: 123\n    });\n\n    delete data.timestamp;\n    assert.deepEqual(data, {\n      testEngine: {\n        name: 'axe-core',\n        version: axe.version\n      },\n      testRunner: {\n        name: axe._audit.brand\n      },\n      testEnvironment: {\n        userAgent: 'foo',\n        windowWidth: 321,\n        windowHeight: 123,\n        orientationAngle: 'slanted',\n        orientationType: 'fictional'\n      },\n      url: 'foo://'\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-frame-contexts.js",
    "content": "describe('utils.getFrameContexts', function () {\n  var getFrameContexts = axe.utils.getFrameContexts;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var fixture = document.querySelector('#fixture');\n\n  it('returns an empty array if the page has no frames', function () {\n    var frameContext = getFrameContexts();\n    assert.isArray(frameContext);\n    assert.lengthOf(frameContext, 0);\n  });\n\n  it('returns a `frameSelector` for each included frame', function () {\n    fixture.innerHTML =\n      '<iframe></iframe>' + '<iframe></iframe>' + '<iframe></iframe>';\n\n    var selectors = getFrameContexts().map(function (frameData) {\n      return frameData.frameSelector;\n    });\n    assert.lengthOf(selectors, 3);\n    assert.include(selectors[0], 'iframe:nth-child(1)');\n    assert.include(selectors[1], 'iframe:nth-child(2)');\n    assert.include(selectors[2], 'iframe:nth-child(3)');\n  });\n\n  it('sets frameContext.initiator to false for each included frame', function () {\n    fixture.innerHTML =\n      '<iframe></iframe>' + '<iframe></iframe>' + '<iframe></iframe>';\n\n    var contexts = getFrameContexts().map(function (frameData) {\n      return frameData.frameContext;\n    });\n\n    assert.lengthOf(contexts, 3);\n    assert.isFalse(contexts[0].initiator);\n    assert.isFalse(contexts[1].initiator);\n    assert.isFalse(contexts[2].initiator);\n  });\n\n  it('sets frameContext.focusable depending on the frame', function () {\n    fixture.innerHTML =\n      '<iframe></iframe>' +\n      '<iframe tabindex=\"0\"></iframe>' +\n      '<iframe tabindex=\"-1\"></iframe>';\n\n    var contexts = getFrameContexts().map(function (frameData) {\n      return frameData.frameContext;\n    });\n    assert.lengthOf(contexts, 3);\n    assert.isTrue(contexts[0].focusable);\n    assert.isTrue(contexts[1].focusable);\n    assert.isFalse(contexts[2].focusable);\n  });\n\n  it('sets frameContext.size based on frame size', function () {\n    fixture.innerHTML =\n      '<iframe width=\"1\" height=\"1\"></iframe>' +\n      '<iframe width=\"10\" height=\"10\"></iframe>' +\n      '<iframe width=\"100\" height=\"100\"></iframe>';\n\n    var frameSize = getFrameContexts().map(function (frameData) {\n      return frameData.frameContext.size;\n    });\n    assert.lengthOf(frameSize, 3);\n    assert.deepEqual(frameSize[0], {\n      width: 1,\n      height: 1\n    });\n    assert.deepEqual(frameSize[1], {\n      width: 10,\n      height: 10\n    });\n    assert.deepEqual(frameSize[2], {\n      width: 100,\n      height: 100\n    });\n  });\n\n  describe('include / exclude', function () {\n    it('returns a `frameContext` for each included frame', function () {\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' +\n        '<iframe id=\"f2\"></iframe>' +\n        '<iframe id=\"f3\"></iframe>';\n      var context = {\n        include: [\n          ['#f1', 'header'],\n          ['#f2', 'main']\n        ],\n        exclude: [['#f3', 'footer']]\n      };\n      var contexts = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameContext;\n      });\n\n      assert.lengthOf(contexts, 3);\n      assert.deepEqual(contexts[0].include, [['header']]);\n      assert.deepEqual(contexts[0].exclude, []);\n      assert.deepEqual(contexts[1].include, [['main']]);\n      assert.deepEqual(contexts[1].exclude, []);\n      assert.deepEqual(contexts[2].include, []);\n      assert.deepEqual(contexts[2].exclude, [['footer']]);\n    });\n\n    it('excludes non-frame contexts', function () {\n      fixture.innerHTML = '<iframe id=\"f1\"></iframe>';\n      var context = {\n        include: [['#header'], ['a'], ['#f1', 'header']]\n      };\n      var contexts = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameContext;\n      });\n\n      assert.lengthOf(contexts, 1);\n      assert.deepEqual(contexts[0].include, [['header']]);\n      assert.deepEqual(contexts[0].exclude, []);\n    });\n\n    it('mixes contexts if the frame is selected twice', function () {\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' + '<iframe id=\"f2\"></iframe>';\n      var context = {\n        include: [\n          ['#f1', 'header'],\n          ['#f2', 'footer']\n        ],\n        exclude: [['iframe', 'main']]\n      };\n      var contexts = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameContext;\n      });\n      assert.lengthOf(contexts, 2);\n      assert.deepEqual(contexts[0].include, [['header']]);\n      assert.deepEqual(contexts[0].exclude, [['main']]);\n      assert.deepEqual(contexts[1].include, [['footer']]);\n      assert.deepEqual(contexts[1].exclude, [['main']]);\n    });\n\n    it('combines include/exclude arrays of frames selected twice', function () {\n      fixture.innerHTML = '<iframe></iframe>';\n      var context = {\n        include: [\n          ['iframe', 'header'],\n          ['iframe', 'main']\n        ],\n        exclude: [\n          ['iframe', 'aside'],\n          ['iframe', 'footer']\n        ]\n      };\n      var contexts = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameContext;\n      });\n\n      assert.lengthOf(contexts, 1);\n      assert.deepEqual(contexts[0].include, [['header'], ['main']]);\n      assert.deepEqual(contexts[0].exclude, [['aside'], ['footer']]);\n    });\n\n    it('skips excluded frames', function () {\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' +\n        '<iframe id=\"f2\"></iframe>' +\n        '<iframe id=\"f3\"></iframe>';\n      var context = {\n        exclude: [[['#f2']]]\n      };\n      var selectors = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameSelector;\n      });\n      assert.lengthOf(selectors, 2);\n      assert.include(selectors[0], 'iframe:nth-child(1)');\n      assert.include(selectors[1], 'iframe:nth-child(3)');\n    });\n\n    it('skips frames excluded by a parent', function () {\n      fixture.innerHTML = '<iframe></iframe>';\n      var frameContexts = getFrameContexts({\n        exclude: [['#fixture']]\n      });\n      assert.lengthOf(frameContexts, 0);\n    });\n\n    it('normalizes the context', function () {\n      var frameContexts;\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' + '<iframe id=\"f2\"></iframe>';\n      frameContexts = getFrameContexts('#f1');\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n\n      frameContexts = getFrameContexts({ include: [['#f1']] });\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n\n      frameContexts = getFrameContexts({ exclude: [['#f2']] });\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n    });\n\n    it('accepts elements', function () {\n      var frameContexts;\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' + '<iframe id=\"f2\"></iframe>';\n      var f1 = fixture.querySelector('#f1');\n      var f2 = fixture.querySelector('#f2');\n      frameContexts = getFrameContexts(f1);\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n\n      frameContexts = getFrameContexts({ include: [f1] });\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n\n      frameContexts = getFrameContexts({ exclude: [f2] });\n      assert.lengthOf(frameContexts, 1);\n      assert.include(frameContexts[0].frameSelector, 'iframe:nth-child(1)');\n      assert.deepEqual(frameContexts[0].frameContext.include, []);\n      assert.deepEqual(frameContexts[0].frameContext.exclude, []);\n    });\n\n    it('works with nested frames', function () {\n      fixture.innerHTML =\n        '<iframe id=\"f1\"></iframe>' + '<iframe id=\"f2\"></iframe>';\n      var context = {\n        include: [\n          ['#f1', '#f3', 'header'],\n          ['#f2', '#f4', '#f5', 'footer']\n        ],\n        exclude: [['#f2', '#f6', '#f7', '#f7', 'main']]\n      };\n      var contexts = getFrameContexts(context).map(function (frameData) {\n        return frameData.frameContext;\n      });\n\n      assert.lengthOf(contexts, 2);\n      assert.deepEqual(contexts[0].include, [['#f3', 'header']]);\n      assert.deepEqual(contexts[0].exclude, []);\n      assert.deepEqual(contexts[1].include, [['#f4', '#f5', 'footer']]);\n      assert.deepEqual(contexts[1].exclude, [['#f6', '#f7', '#f7', 'main']]);\n    });\n\n    (shadowSupported ? it : xit)('works on iframes in shadow dom', function () {\n      fixture.innerHTML = '<div id=\"shadow\"></div>';\n      var div = fixture.querySelector('div');\n      var shadowRoot = div.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<main><iframe id=\"f1\" width=\"100\" height=\"100\"></iframe></main>';\n\n      var frameContext = getFrameContexts();\n\n      assert.lengthOf(frameContext, 1);\n      assert.lengthOf(frameContext[0].frameSelector, 2);\n      assert.equal(frameContext[0].frameSelector[1], 'main > iframe');\n      assert.deepEqual(frameContext[0].frameContext.include, []);\n      assert.deepEqual(frameContext[0].frameContext.exclude, []);\n    });\n  });\n\n  describe('options.iframes', function () {\n    it('returns a non-empty array with `iframes: true`', function () {\n      fixture.innerHTML = '<iframe></iframe>';\n      var contexts = getFrameContexts({}, { iframes: true });\n      assert.lengthOf(contexts, 1);\n    });\n\n    it('returns an empty array with `iframes: false`', function () {\n      fixture.innerHTML = '<iframe></iframe>';\n      var contexts = getFrameContexts({}, { iframes: false });\n      assert.lengthOf(contexts, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-friendly-uri-end.js",
    "content": "describe('axe.utils.getFriendlyUriEnd', function () {\n  'use strict';\n  var getFriendlyUriEnd = axe.utils.getFriendlyUriEnd;\n\n  it('returns a domain name', function () {\n    assert.equal('deque.com', getFriendlyUriEnd('http://deque.com'));\n    assert.equal('deque.com/', getFriendlyUriEnd('https://www.deque.com/'));\n    assert.equal('docs.deque.com/', getFriendlyUriEnd('//docs.deque.com/'));\n  });\n\n  it('returns a filename', function () {\n    assert.equal('contact/', getFriendlyUriEnd('../../contact/'));\n    assert.equal('contact/', getFriendlyUriEnd('http://deque.com/contact/'));\n    assert.equal('contact', getFriendlyUriEnd('/contact'));\n    assert.equal('contact.html', getFriendlyUriEnd('/contact.html'));\n  });\n\n  it('trims whitespace', function () {\n    assert.equal(undefined, getFriendlyUriEnd('  '));\n    assert.equal('start page', getFriendlyUriEnd('start page\\t'));\n    assert.equal('home#heading', getFriendlyUriEnd('home#heading  '));\n  });\n\n  it('returns a hash URI', function () {\n    assert.equal('#footer', getFriendlyUriEnd('#footer'));\n    assert.equal(\n      'contact.html#footer',\n      getFriendlyUriEnd('/contact.html#footer')\n    );\n    assert.equal('home.html#main', getFriendlyUriEnd('/home.html#main '));\n  });\n\n  it('returns undef when there is a query', function () {\n    assert.isUndefined(getFriendlyUriEnd('/contact?'));\n    assert.isUndefined(getFriendlyUriEnd('/contact?foo=bar'));\n  });\n\n  it('returns undef for index files', function () {\n    assert.isUndefined(getFriendlyUriEnd('/index.cfs'));\n    assert.isUndefined(getFriendlyUriEnd('/index'));\n  });\n\n  it('returns undef when the result is too short', function () {\n    assert.isUndefined(getFriendlyUriEnd('/i.html'));\n    assert.isUndefined(getFriendlyUriEnd('/dq'));\n  });\n\n  it('returns undef when the result is too long', function () {\n    assert.isDefined(getFriendlyUriEnd('/abcd.html', { maxLength: 50 }));\n    assert.isDefined(getFriendlyUriEnd('#foo-bar-baz', { maxLength: 50 }));\n    assert.isDefined(getFriendlyUriEnd('//deque.com', { maxLength: 50 }));\n\n    assert.isUndefined(getFriendlyUriEnd('/abcd.html', { maxLength: 5 }));\n    assert.isUndefined(getFriendlyUriEnd('#foo-bar-baz', { maxLength: 5 }));\n    assert.isUndefined(getFriendlyUriEnd('//deque.com', { maxLength: 5 }));\n  });\n\n  it('returns undef when the result has too many numbers', function () {\n    assert.isUndefined(getFriendlyUriEnd('123456.html'));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-node-attributes.js",
    "content": "describe('axe.utils.getNodeAttributes', function () {\n  'use strict';\n\n  it('should return the list of attributes', function () {\n    var node = document.createElement('div');\n    node.setAttribute('class', 'foo bar');\n    var actual = axe.utils.getNodeAttributes(node);\n    assert.isTrue(actual instanceof window.NamedNodeMap);\n    assert.equal(actual.length, 1);\n    assert.equal(actual[0].name, 'class');\n  });\n\n  it('should return the list of attributes when the DOM is clobbered', function () {\n    var node = document.createElement('form');\n    node.setAttribute('id', '123');\n    node.innerHTML = '<select name=\"attributes\"></select>';\n\n    // eslint-disable-next-line no-restricted-syntax\n    assert.isFalse(node.attributes instanceof window.NamedNodeMap);\n\n    var actual = axe.utils.getNodeAttributes(node);\n    assert.isTrue(actual instanceof window.NamedNodeMap);\n    assert.equal(actual.length, 1);\n    assert.equal(actual[0].name, 'id');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-root-node.js",
    "content": "function makeShadowTreeGRN(node) {\n  'use strict';\n  var root = node.attachShadow({ mode: 'open' });\n  var div = document.createElement('div');\n  div.className = 'parent';\n  root.appendChild(div);\n}\n\ndescribe('axe.utils.getRootNode', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return the document when the node is just a normal node', function () {\n    fixture.innerHTML = '<div id=\"target\"></div>';\n    var node = document.getElementById('target');\n    assert.isTrue(axe.utils.getRootNode(node) === document);\n  });\n  it('should return the document when the node is disconnected', function () {\n    var node = document.createElement('div');\n    assert.isTrue(axe.utils.getRootNode(node) === document);\n  });\n  (shadowSupported ? it : xit)(\n    'should return the shadow root when it is inside the shadow DOM',\n    function () {\n      var shadEl;\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div></div>';\n      makeShadowTreeGRN(fixture.firstChild);\n      shadEl = fixture.firstChild.shadowRoot.querySelector('div');\n      assert.isTrue(axe.utils.getRootNode(shadEl) !== document);\n      assert.isTrue(\n        axe.utils.getRootNode(shadEl) === fixture.firstChild.shadowRoot\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/core/utils/get-rule.js",
    "content": "describe('axe.utils.getRule', function () {\n  beforeEach(function () {\n    axe._load({\n      rules: [\n        {\n          id: 'rule1'\n        },\n        {\n          id: 'rule2'\n        }\n      ]\n    });\n  });\n\n  it('should return the rule by the id', function () {\n    var rule = axe.utils.getRule('rule1');\n    assert.isTrue(rule.id === 'rule1');\n  });\n\n  it(\"should throw error if the rule doesn't exist\", function () {\n    assert.throws(function () {\n      axe.utils.getRule('no-id');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-scroll.js",
    "content": "describe('axe.utils.getScroll', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(axe.utils.getScroll);\n  });\n\n  it('returns undefined when element is not scrollable', function () {\n    var target = queryFixture(\n      '<section id=\"target\">This element is not scrollable</section>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when element does not overflow', function () {\n    var target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px;\">' +\n        '<div style=\"height: 10px; width: 10px; background-color: pink;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when element overflow is hidden', function () {\n    var target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: hidden\">' +\n        '<div style=\"height: 2000px; width: 100px; background-color: pink;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns undefined when element overflow is clip', function () {\n    var target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: clip\">' +\n        '<div style=\"height: 2000px; width: 100px; background-color: pink;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns scroll offset when element overflow is auto', function () {\n    var target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: auto\">' +\n        '<div style=\"height: 10px; width: 2000px; background-color: red;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isDefined(actual);\n    assert.hasAllKeys(actual, ['elm', 'top', 'left']);\n    assert.equal(actual.top, 0);\n    assert.equal(actual.left, 0);\n  });\n\n  it('returns undefined when element overflow is visible', function () {\n    var target = queryFixture(\n      '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: visible;\">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isUndefined(actual);\n  });\n\n  it('returns scroll offset when element overflow is scroll', function () {\n    var target = queryFixture(\n      '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>'\n    );\n    var actual = axe.utils.getScroll(target.actualNode);\n    assert.isDefined(actual);\n    assert.hasAllKeys(actual, ['elm', 'top', 'left']);\n    assert.equal(actual.top, 0);\n    assert.equal(actual.left, 0);\n  });\n\n  describe('shadowDOM - axe.utils.getScroll', function () {\n    before(function () {\n      if (!shadowSupported) {\n        this.skip();\n      }\n    });\n\n    it('returns undefined when shadowDOM element does not overflow', function () {\n      fixture.innerHTML = '<div></div>';\n\n      var root = fixture.firstChild.attachShadow({ mode: 'open' });\n      var slotted = document.createElement('div');\n      slotted.innerHTML =\n        '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted;\">Sed.</p>';\n      root.appendChild(slotted);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var target = axe.utils.querySelectorAll(tree, 'p')[0];\n      var actual = axe.utils.getScroll(target.actualNode);\n      assert.isUndefined(actual);\n    });\n\n    it('returns scroll offset when shadowDOM element has overflow', function () {\n      fixture.innerHTML = '<div></div>';\n\n      var root = fixture.firstChild.attachShadow({ mode: 'open' });\n      var slotted = document.createElement('div');\n      slotted.innerHTML =\n        '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: auto;\">This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence.</p>';\n      root.appendChild(slotted);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var target = axe.utils.querySelectorAll(tree, 'p')[0];\n      var actual = axe.utils.getScroll(target.actualNode);\n      assert.isDefined(actual);\n      assert.hasAllKeys(actual, ['elm', 'top', 'left']);\n      assert.equal(actual.top, 0);\n      assert.equal(actual.left, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-selector.js",
    "content": "function createContentGetSelector() {\n  'use strict';\n  var group = document.createElement('div');\n  group.innerHTML =\n    '<label id=\"mylabel\">Label</label><input id=\"myinput\" aria-labelledby=\"mylabel\" type=\"text\" />';\n  return group;\n}\n\nfunction makeShadowTreeGetSelector(node) {\n  'use strict';\n  var root = node.attachShadow({ mode: 'open' });\n  var div = document.createElement('div');\n  div.className = 'parent';\n  root.appendChild(div);\n  div.appendChild(createContentGetSelector());\n}\n\nfunction makeNonunique(fixture) {\n  'use strict';\n  var nonUnique = '<div><div><div></div></div></div>';\n  fixture.innerHTML =\n    '<main>' + nonUnique + nonUnique + nonUnique + '<div><div></div></div>';\n  var node = document.createElement('div');\n  var parent = fixture.querySelector('div:nth-child(4) > div');\n  parent.appendChild(node);\n  return node;\n}\n\nfunction makeNonuniqueLongAttributes(fixture) {\n  'use strict';\n  var nonUnique = '<div><div><div></div></div></div>';\n  fixture.innerHTML =\n    '<main>' + nonUnique + nonUnique + nonUnique + '<div><div></div></div>';\n  var node = document.createElement('div');\n  node.setAttribute('data-att', 'ddfkjghlkdddfkjghlkdddfkjghlkdddfkjghlkd');\n  var parent = fixture.querySelector('div:nth-child(4) > div');\n  parent.appendChild(node);\n  return node;\n}\n\ndescribe('axe.utils.getSelector', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n  var fixtureSetup = axe.testUtils.fixtureSetup;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.getSelector);\n  });\n\n  it('throws if axe._selectorData is undefined', function () {\n    assert.throws(function () {\n      var node = document.createElement('div');\n      fixture.appendChild(node);\n      axe.utils.getSelector(node);\n    });\n  });\n\n  it('should generate a unique CSS selector', function () {\n    var node = document.createElement('div');\n    fixtureSetup(node);\n    var sel = axe.utils.getSelector(node);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should still work if an element has nothing but whitespace as a className', function () {\n    var node = document.createElement('div');\n    node.className = '    ';\n    fixtureSetup(node);\n    var sel = axe.utils.getSelector(node);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should handle special characters in IDs', function () {\n    var node = document.createElement('div');\n    node.id = 'monkeys#are.animals\\\\ok';\n    fixtureSetup(node);\n\n    var result = document.querySelectorAll(axe.utils.getSelector(node));\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should handle special characters in classNames', function () {\n    var node = document.createElement('div');\n    node.className = '.  bb-required';\n    fixtureSetup(node);\n\n    var result = document.querySelectorAll(axe.utils.getSelector(node));\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should be able to fall back to positional selectors', function () {\n    var node, expected;\n    var nodes = [];\n    for (var i = 0; i < 10; i++) {\n      node = document.createElement('div');\n      nodes.push(node);\n      if (i === 5) {\n        expected = node;\n      }\n    }\n    fixtureSetup(nodes);\n    var result = document.querySelectorAll(axe.utils.getSelector(expected));\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], expected);\n  });\n\n  it('should use a unique ID', function () {\n    var node = document.createElement('div');\n    node.id = 'monkeys';\n    fixtureSetup(node);\n\n    var sel = axe.utils.getSelector(node);\n\n    assert.equal(sel, '#monkeys');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should not use ids if they are not unique', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n    node1.id = 'monkeys';\n    node2.id = 'monkeys';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.notInclude(sel, '#monkeys');\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use classes if available and unique', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n    node1.className = 'monkeys simian';\n    node2.className = 'dogs cats';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.equal(sel, '.dogs');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use classes if more unique than the tag', function () {\n    var node1 = document.createElement('p');\n    var node2 = document.createElement('p');\n    node1.className = 'monkeys simian cats';\n    node2.className = 'dogs cats';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    assert.equal(sel, '.dogs');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should NOT use classes if they are more common than the tag', function () {\n    var node1 = document.createElement('p');\n    var node2 = document.createElement('p');\n    node1.className = 'dogs cats';\n    node2.className = 'dogs cats';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.isTrue(sel.indexOf('.dogs') === -1);\n    assert.isTrue(sel.indexOf('p') === 0);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use the most unique class', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n    node1.className = 'dogs';\n    node2.className = 'dogs cats';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    assert.equal(sel, '.cats');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use the most unique class and not the unique attribute', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n\n    node1.className = 'dogs';\n    node2.className = 'dogs cats';\n    node2.setAttribute('data-axe', 'hello');\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.equal(sel, '.cats');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use only a single unique attribute', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n\n    node1.setAttribute('data-thing', 'hello');\n    node2.setAttribute('data-thing', 'hello');\n    node2.setAttribute('data-axe', 'hello');\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.equal(sel, 'div[data-axe=\"hello\"]');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use three uncommon but not unique features', function () {\n    var node1 = document.createElement('div');\n    node1.setAttribute('data-axe', 'hello');\n    node1.setAttribute('data-thing', 'hello');\n    node1.className = 'thing';\n\n    var node2 = document.createElement('div');\n    node2.setAttribute('data-axe', 'hello');\n    node2.setAttribute('data-thing', 'hello');\n    node2.className = 'thing';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    var clsIndex = sel.indexOf('.thing');\n    var attIndex = Math.min(\n      sel.indexOf('[data-axe=\"hello\"]'),\n      sel.indexOf('[data-thing=\"hello\"]')\n    );\n\n    assert.isTrue(clsIndex !== -1);\n    assert.isTrue(sel.indexOf('[data-axe=\"hello\"]') !== -1);\n    assert.isTrue(sel.indexOf('[data-thing=\"hello\"]') !== -1);\n\n    assert.isTrue(clsIndex < attIndex, 'classes first');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use only three uncommon but not unique features', function () {\n    var node1 = document.createElement('div');\n    node1.setAttribute('data-axe', 'hello');\n    node1.setAttribute('data-thing', 'hello');\n    node1.setAttribute('data-thang', 'hello');\n    node1.className = 'thing thang';\n\n    var node2 = document.createElement('div');\n    node2.setAttribute('data-axe', 'hello');\n    node2.setAttribute('data-thing', 'hello');\n    node2.setAttribute('data-thang', 'hello');\n    node2.className = 'thing thang';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    var parts = sel.split('.');\n    parts = parts\n      .reduce(function (val, item) {\n        var its = item.split('[');\n        return val.concat(its);\n      }, [])\n      .filter(function (item) {\n        return item !== '';\n      });\n    assert.equal(parts.length, 3);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use only three uncommon but not unique classes', function () {\n    var node1 = document.createElement('div');\n    var node2 = document.createElement('div');\n    node1.className = 'thing thang thug thick';\n    node2.className = 'thing thang thug thick';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    var parts = sel.split('.');\n    parts = parts\n      .reduce(function (val, item) {\n        var its = item.split('[');\n        return val.concat(its);\n      }, [])\n      .filter(function (item) {\n        return item !== '';\n      });\n    assert.equal(parts.length, 3);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should use only three uncommon but not unique attributes', function () {\n    var node1 = document.createElement('div');\n    node1.setAttribute('data-axe', 'hello');\n    node1.setAttribute('data-thug', 'hello');\n    node1.setAttribute('data-thing', 'hello');\n    node1.setAttribute('data-thang', 'hello');\n\n    var node2 = document.createElement('div');\n    node2.setAttribute('data-axe', 'hello');\n    node2.setAttribute('data-thing', 'hello');\n    node2.setAttribute('data-thang', 'hello');\n    node2.setAttribute('data-thug', 'hello');\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n    var parts = sel.split('.');\n    parts = parts\n      .reduce(function (val, item) {\n        var its = item.split('[');\n        return val.concat(its);\n      }, [])\n      .filter(function (item) {\n        return item !== '';\n      });\n    assert.equal(parts.length, 4);\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should not use long attributes', function () {\n    var node = makeNonuniqueLongAttributes(fixture);\n    fixtureSetup();\n    var sel = axe.utils.getSelector(node, {});\n    assert.isTrue(sel.indexOf('data-att') === -1);\n  });\n\n  it('should use :root when not unique html element', function () {\n    var node = document.createElement('html');\n    node.setAttribute('lang', 'en');\n    fixtureSetup(node);\n\n    var sel = axe.utils.getSelector(document.documentElement, {});\n    assert.equal(sel, ':root');\n  });\n\n  it('should use position if classes are not unique', function () {\n    var node1 = document.createElement('div');\n    node1.className = 'monkeys simian';\n\n    var node2 = document.createElement('div');\n    node2.className = 'monkeys simian';\n\n    fixtureSetup([node1, node2]);\n    var sel = axe.utils.getSelector(node2);\n\n    assert.equal(sel, '.monkeys.simian:nth-child(2)');\n\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node2);\n  });\n\n  it('should work on the documentElement', function () {\n    fixtureSetup();\n\n    var sel = axe.utils.getSelector(document.documentElement);\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], document.documentElement);\n  });\n\n  it('should work on the documentElement with classes', function () {\n    var orig = document.documentElement.className;\n    document.documentElement.className = 'stuff and other things';\n    fixtureSetup();\n\n    var sel = axe.utils.getSelector(document.documentElement);\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], document.documentElement);\n    document.documentElement.className = orig;\n  });\n\n  it('should work on the body', function () {\n    fixtureSetup();\n    var sel = axe.utils.getSelector(document.body);\n    var result = document.querySelectorAll(sel);\n\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], document.body);\n  });\n\n  it('should work on namespaced elements', function () {\n    fixtureSetup('<hx:include>Hello</hx:include>');\n    var node = fixture.firstChild;\n\n    var sel = axe.utils.getSelector(node);\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should work on complex namespaced elements', function () {\n    fixtureSetup(\n      '<m:math xmlns:m=\"http://www.w3.org/1998/Math/MathML\">' +\n        '<m:mi>x</m:mi>' +\n        '<m:annotation-xml encoding=\"MathML-Content\">' +\n        '<m:ci>x</m:ci>' +\n        '</m:annotation-xml>' +\n        '</m:math>'\n    );\n\n    var node = fixture.querySelector('m\\\\:ci');\n    var sel = axe.utils.getSelector(node);\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n\n  it('should not use ignored attributes', function () {\n    var node = document.createElement('div');\n    var ignoredAttributes = [\n      'style',\n      'selected',\n      'checked',\n      'disabled',\n      'tabindex',\n      'aria-checked',\n      'aria-selected',\n      'aria-invalid',\n      'aria-activedescendant',\n      'aria-busy',\n      'aria-disabled',\n      'aria-expanded',\n      'aria-grabbed',\n      'aria-pressed',\n      'aria-valuenow',\n      'xmlns'\n    ];\n    ignoredAttributes.forEach(function (att) {\n      node.setAttribute(att, 'true');\n    });\n    fixtureSetup(node);\n\n    assert.isTrue(axe.utils.getSelector(node).indexOf('[') === -1);\n  });\n\n  it('should use href and src attributes, shortened', function () {\n    var link1 = document.createElement('a');\n    link1.setAttribute('href', '//deque.com/thang/');\n\n    var link2 = document.createElement('a');\n    link2.setAttribute('href', '//deque.com/about/');\n\n    var img1 = document.createElement('img');\n    img1.setAttribute('src', '//deque.com/thang.png');\n    var img2 = document.createElement('img');\n    img2.setAttribute('src', '//deque.com/logo.png');\n\n    fixtureSetup([link1, link2, img1, img2]);\n    assert.equal(axe.utils.getSelector(link2), 'a[href$=\"about/\"]');\n    assert.equal(axe.utils.getSelector(img2), 'img[src$=\"logo.png\"]');\n  });\n\n  it('should escape href attributes', function () {\n    var link1 = document.createElement('a');\n    link1.setAttribute('href', '//deque.com/about/');\n\n    var link2 = document.createElement('a');\n    link2.setAttribute('href', '//deque.com/child/ \\n\\n\\n');\n\n    fixtureSetup([link1, link2]);\n    assert.equal(\n      axe.utils.getSelector(link2),\n      'a[href=\"//deque.com/child/ \\\\a \\\\a \\\\a \"]'\n    );\n  });\n\n  it('should not URL encode or token escape href attribute', function () {\n    var link1 = document.createElement('a');\n    link1.setAttribute('href', '3 Seater');\n\n    var link2 = document.createElement('a');\n    link2.setAttribute('href', '1 Seater');\n\n    var expected = 'a[href$=\"1 Seater\"]';\n    fixtureSetup([link1, link2]);\n    assert.equal(axe.utils.getSelector(link2), expected);\n    assert.isTrue(axe.utils.matchesSelector(link2, expected));\n  });\n\n  it('should escape certain special characters in attribute', function () {\n    var div1 = document.createElement('div');\n    div1.setAttribute('data-thing', 'foobar');\n\n    var div2 = document.createElement('div');\n    div2.setAttribute('data-thing', '!@#$%^&*()_+[]\\\\;\\',./{}|:\"<>?');\n\n    var expected = 'div[data-thing=\"!@#$%^&*()_+[]\\\\\\\\;\\',./{}|:\\\\\"<>?\"]';\n    fixtureSetup([div1, div2]);\n    assert.equal(axe.utils.getSelector(div2), expected);\n    assert.isTrue(axe.utils.matchesSelector(div2, expected));\n  });\n\n  it('should escape newline characters in attribute', function () {\n    var div1 = document.createElement('div');\n    div1.setAttribute('data-thing', 'foobar');\n\n    var div2 = document.createElement('div');\n    div2.setAttribute('data-thing', '  \\n\\n\\n');\n\n    var expected = 'div[data-thing=\"  \\\\a \\\\a \\\\a \"]';\n    fixtureSetup([div1, div2]);\n    assert.equal(axe.utils.getSelector(div2), expected);\n    assert.isTrue(axe.utils.matchesSelector(div2, expected));\n  });\n\n  it('should not generate universal selectors', function () {\n    var node = document.createElement('div');\n    node.setAttribute('role', 'menuitem');\n    fixtureSetup(node);\n\n    assert.equal(axe.utils.getSelector(node), 'div[role=\"menuitem\"]');\n  });\n\n  it('should work correctly when a URL attribute cannot be shortened', function () {\n    var href1 = 'mars2.html?a=be_bold';\n    var node1 = document.createElement('a');\n    node1.setAttribute('href', href1);\n\n    var href2 = 'mars2.html?a=be_italic';\n    var node2 = document.createElement('a');\n    node2.setAttribute('href', href2);\n    fixtureSetup([node1, node2]);\n\n    assert.include(axe.utils.getSelector(node1), 'mars2.html?a=be_bold');\n    assert.include(axe.utils.getSelector(node2), 'mars2.html?a=be_italic');\n  });\n\n  // shadow DOM v1 - note: v0 is compatible with this code, so no need\n  // to specifically test this\n  (shadowSupported ? it : xit)(\n    'no options: should work with shadow DOM',\n    function () {\n      var shadEl;\n      fixture.innerHTML = '<div></div>';\n      makeShadowTreeGetSelector(fixture.firstChild);\n      fixtureSetup();\n\n      shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput');\n      assert.deepEqual(axe.utils.getSelector(shadEl), [\n        '#fixture > div',\n        '#myinput'\n      ]);\n    }\n  );\n\n  // shadow DOM v1 - note: v0 is compatible with this code, so no need\n  // to specifically test this\n  (shadowSupported ? it : xit)(\n    'toRoot: should work with shadow DOM',\n    function () {\n      var shadEl;\n      fixture.innerHTML = '<div></div>';\n      makeShadowTreeGetSelector(fixture.firstChild);\n      axe._tree = axe.utils.getFlattenedTree(document);\n      axe._selectorData = axe.utils.getSelectorData(axe._tree);\n\n      shadEl = fixture.firstChild.shadowRoot.querySelector('input#myinput');\n      assert.deepEqual(axe.utils.getSelector(shadEl, { toRoot: true }), [\n        'html > body > #fixture > div',\n        '.parent > div > #myinput'\n      ]);\n    }\n  );\n\n  it('should correctly calculate unique selector when no discernable features', function () {\n    var node = makeNonunique(fixture);\n    fixtureSetup();\n\n    var sel = axe.utils.getSelector(node, {});\n    var mine = document.querySelector(sel);\n    assert.isTrue(mine === node);\n  });\n\n  it('should not traverse further up than required when no discernable features', function () {\n    var node = makeNonunique(fixture);\n    fixtureSetup();\n\n    var top = fixture.querySelector('div:nth-child(4)');\n    var sel = axe.utils.getSelector(node, {});\n    sel = sel.substring(0, sel.indexOf(' >'));\n    var test = document.querySelector(sel);\n    assert.isTrue(test === top);\n  });\n\n  it('should not error if fragment is no longer in the DOM', function () {\n    var fragment = document.createDocumentFragment();\n    var node = document.createElement('div');\n    fragment.appendChild(node);\n    fixtureSetup();\n    assert.doesNotThrow(function () {\n      axe.utils.getSelector(node);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-shadow-selector.js",
    "content": "describe('axe.utils.getShadowSelector', () => {\n  const fixture = document.getElementById('fixture');\n  const shadowTest = axe.testUtils.shadowSupport.v1 ? it : xit;\n  const getShadowSelector = axe.utils.getShadowSelector;\n\n  function generator(node) {\n    return node.nodeName.toLowerCase();\n  }\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  it('returns generated output for light DOM nodes', () => {\n    const h1 = document.createElement('h1');\n    fixture.appendChild(h1);\n\n    const selector = getShadowSelector(generator, h1);\n    assert.equal(selector, 'h1');\n  });\n\n  it('passes node and options to generator', () => {\n    let called = false;\n    const node = document.createElement('h1');\n    const options = { hello: 'world' };\n    function generatorFn(arg1, arg2) {\n      called = true;\n      assert.equal(arg1, node);\n      assert.equal(arg2, options);\n    }\n\n    getShadowSelector(generatorFn, node, options);\n    assert.isTrue(called);\n  });\n\n  it('passes am empty object if no options are provided', () => {\n    let called = false;\n    const node = document.createElement('h1');\n    function generatorFn(_, arg2) {\n      called = true;\n      assert.deepEqual(arg2, {});\n    }\n\n    getShadowSelector(generatorFn, node);\n    assert.isTrue(called);\n  });\n\n  shadowTest('returns the output of the generator for light DOM', () => {\n    fixture.innerHTML = '<div><h1>Hello world</h1></div>';\n    const div = fixture.querySelector('div');\n    const h1 = fixture.querySelector('h1');\n    const shadowHost = div.attachShadow({ mode: 'open' });\n    shadowHost.innerHTML = '<span><slot /></span>';\n\n    assert.equal(getShadowSelector(generator, h1), 'h1');\n  });\n\n  shadowTest('returns an array of outputs for each shadow tree host', () => {\n    const node = document.createElement('section');\n    fixture.appendChild(node);\n\n    const shadowRoot1 = node.attachShadow({ mode: 'open' });\n    shadowRoot1.innerHTML = '<div><article><slot /></article</div>';\n\n    const shadowRoot2 = shadowRoot1\n      .querySelector('article')\n      .attachShadow({ mode: 'open' });\n    shadowRoot2.innerHTML = '<h1>Hello world</h1>';\n\n    const target = shadowRoot2.querySelector('h1');\n    const sel = getShadowSelector(generator, target);\n    assert.deepEqual(sel, ['section', 'article', 'h1']);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-standards.js",
    "content": "describe('axe.utils.getStandards', function () {\n  it('returns the standards object', function () {\n    var standards = axe.utils.getStandards();\n    assert.hasAnyKeys(standards, [\n      'ariaAttrs',\n      'ariaRoles',\n      'htmlElms',\n      'cssColors'\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-stylesheet-factory.js",
    "content": "describe('axe.utils.getStyleSheetFactory', function () {\n  'use strict';\n\n  var dynamicDoc = document.implementation.createHTMLDocument(\n    'Dynamic document for testing axe.utils.getStyleSheetFactory'\n  );\n\n  it('throws if there is no argument of dynamicDocument', function () {\n    assert.throws(function () {\n      axe.utils.getStyleSheetFactory();\n    });\n  });\n\n  it('returns a function when passed argument of dynamicDocument', function () {\n    var actual = axe.utils.getStyleSheetFactory(dynamicDoc);\n    assert.isFunction(actual);\n  });\n\n  it('returns a CSSOM stylesheet, when invoked with data (text)', function () {\n    var stylesheetFactory = axe.utils.getStyleSheetFactory(dynamicDoc);\n    var actual = stylesheetFactory({\n      data: '.someStyle{background-color:red;}',\n      root: document,\n      priority: [1, 0]\n    });\n\n    assert.isDefined(actual);\n    assert.hasAllKeys(actual, [\n      'sheet',\n      'isCrossOrigin',\n      'shadowId',\n      'root',\n      'priority'\n    ]);\n    assert.deepEqual(actual.priority, [1, 0]);\n    axe.testUtils.assertStylesheet(\n      actual.sheet,\n      '.someStyle',\n      '.someStyle{background-color:red;}'\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/get-xpath.js",
    "content": "describe('axe.utils.getXpath', () => {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n\n  // @see https://stackoverflow.com/a/14284815/2124254\n  function getElementByXPath(path) {\n    return document.evaluate(\n      path,\n      document,\n      () => 'http://www.w3.org/1998/Math/MathML',\n      XPathResult.FIRST_ORDERED_NODE_TYPE,\n      null\n    ).singleNodeValue;\n  }\n\n  it('should be a function', () => {\n    assert.isFunction(axe.utils.getXpath);\n  });\n\n  it('should generate an XPath selector', () => {\n    const node = document.createElement('div');\n    fixture.appendChild(node);\n\n    const sel = axe.utils.getXpath(node);\n\n    assert.equal(sel, \"//div[@id='fixture']/div\");\n    assert.equal(node, getElementByXPath(sel));\n  });\n\n  it('should handle special characters', () => {\n    const node = document.createElement('div');\n    node.id = 'monkeys#are.animals\\\\ok';\n    fixture.appendChild(node);\n\n    const sel = axe.utils.getXpath(node);\n\n    assert.equal(sel, \"//div[@id='monkeys#are.animals\\\\ok']\");\n\n    assert.equal(node, getElementByXPath(sel));\n  });\n\n  it('should stop on unique ID', () => {\n    const node = document.createElement('div');\n    node.id = 'monkeys';\n    fixture.appendChild(node);\n\n    const sel = axe.utils.getXpath(node);\n    assert.equal(sel, \"//div[@id='monkeys']\");\n    assert.equal(node, getElementByXPath(sel));\n  });\n\n  it('should use the nearest unique ID', () => {\n    fixture.innerHTML = `\n      <div id=\"dogs\">\n        <div>\n          <div>\n            <div id=\"monkeys\">\n              <div></div>\n            </div>\n          </div>\n        </div>\n      </div>\n    `;\n    const node = fixture.querySelector('#monkeys > div');\n\n    const sel = axe.utils.getXpath(node);\n    assert.equal(sel, \"//div[@id='monkeys']/div\");\n    assert.equal(node, getElementByXPath(sel));\n  });\n\n  it('should not use ids if they are not unique', () => {\n    let node = document.createElement('div');\n    node.id = 'monkeys';\n    fixture.appendChild(node);\n\n    node = document.createElement('div');\n    node.id = 'monkeys';\n    fixture.appendChild(node);\n\n    const sel = axe.utils.getXpath(node);\n\n    assert.equal(sel, \"//div[@id='fixture']/div[2]\");\n    assert.equal(node, getElementByXPath(sel));\n  });\n\n  it('should properly calculate number when siblings are of different type', () => {\n    let node, target;\n    node = document.createElement('span');\n    fixture.appendChild(node);\n\n    node = document.createElement('span');\n    fixture.appendChild(node);\n\n    node = document.createElement('div');\n    fixture.appendChild(node);\n\n    node = document.createElement('div');\n    target = node;\n    fixture.appendChild(node);\n\n    node = document.createElement('div');\n    fixture.appendChild(node);\n\n    node = document.createElement('span');\n    fixture.appendChild(node);\n\n    const sel = axe.utils.getXpath(target);\n\n    assert.equal(sel, \"//div[@id='fixture']/div[2]\");\n    assert.equal(target, getElementByXPath(sel));\n  });\n\n  it('should work on the documentElement', () => {\n    const sel = axe.utils.getXpath(document.documentElement);\n    assert.equal(sel, '/html');\n    assert.equal(document.documentElement, getElementByXPath(sel));\n  });\n\n  it('should work on the body', () => {\n    const sel = axe.utils.getXpath(document.body);\n    assert.equal(sel, '/html/body');\n    assert.equal(document.body, getElementByXPath(sel));\n  });\n\n  it('should work on namespaced elements', function () {\n    fixture.innerHTML = '<hx:include>Hello</hx:include>';\n    var node = fixture.firstChild;\n    var sel = axe.utils.getXpath(node);\n\n    assert.equal(sel, \"//div[@id='fixture']/hx:include\");\n    // couldn't figure out how to use document.evaluate to select an element with namespace\n  });\n});\n"
  },
  {
    "path": "test/core/utils/is-array-like.js",
    "content": "describe('axe.utils.isArrayLike', () => {\n  const isArrayLike = axe.utils.isArrayLike;\n\n  it('is true for an array', () => {\n    assert.isTrue(isArrayLike([]));\n  });\n\n  it('is true for an array-like object', () => {\n    assert.isTrue(isArrayLike({ length: 1 }));\n  });\n\n  it('is false for strings (which also have .length)', () => {\n    assert.isFalse(isArrayLike('string'));\n  });\n\n  it('is false for a Node with .length', () => {\n    const div = document.createElement('div');\n    div.length = 123;\n    assert.isFalse(isArrayLike(div));\n  });\n\n  it('is false for non-array-like objects', () => {\n    assert.isFalse(isArrayLike({}));\n    assert.isFalse(isArrayLike(null));\n    assert.isFalse(isArrayLike(undefined));\n    assert.isFalse(isArrayLike(1));\n    assert.isFalse(isArrayLike(true));\n    assert.isFalse(isArrayLike(false));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/is-context.js",
    "content": "describe('axe.utils isContext* methods', () => {\n  const { isContextProp, isContextObject, isContextSpec } = axe.utils;\n\n  const methods = [\n    { name: 'isLabelledShadowDomSelector', prop: 'fromShadowDom' },\n    { name: 'isLabelledFramesSelector', prop: 'fromFrames' }\n  ];\n\n  methods.forEach(({ name, prop }) => {\n    describe(name, () => {\n      const method = axe.utils[name];\n      it(`is true for an object with '${prop}'`, () => {\n        assert.isTrue(method({ [prop]: true }));\n      });\n\n      it('is false for an object without `fromShadowDom`', () => {\n        assert.isFalse(method({}));\n      });\n\n      it('is false for non-objects', () => {\n        assert.isFalse(method('string'));\n        assert.isFalse(method(1));\n        assert.isFalse(method([]));\n        assert.isFalse(method(null));\n      });\n\n      it('is false if the property comes from the prototype', () => {\n        assert.isFalse(method(Object.create({ [prop]: true })));\n      });\n    });\n  });\n\n  describe('isContextProp', () => {\n    it('is true for a string', () => {\n      assert.isTrue(isContextProp('string'));\n    });\n\n    it('is true for a Node', () => {\n      assert.isTrue(isContextProp(document.createElement('div')));\n    });\n\n    it('is true for an array', () => {\n      assert.isTrue(isContextProp([]));\n    });\n\n    it('is true for an object with .length', () => {\n      assert.isTrue(isContextProp({ length: 1 }));\n    });\n\n    it('is true for an object with `fromFrames`', () => {\n      assert.isTrue(isContextProp({ fromFrames: true }));\n    });\n\n    it('is true for an object with `fromShadowDom`', () => {\n      assert.isTrue(isContextProp({ fromShadowDom: true }));\n    });\n\n    it('is false for other objects', () => {\n      assert.isFalse(isContextProp({}));\n      assert.isFalse(isContextProp({ exclude: [] }));\n      assert.isFalse(isContextProp({ include: true }));\n      assert.isFalse(isContextProp({ runOnly: 'rules' }));\n    });\n\n    it('is false for other types', () => {\n      assert.isFalse(isContextProp(1));\n      assert.isFalse(isContextProp(null));\n      assert.isFalse(isContextProp(undefined));\n    });\n  });\n\n  describe('isContextObject', () => {\n    it('is false if not an object `include` or `exclude`', () => {\n      assert.isFalse(isContextObject(true));\n      assert.isFalse(isContextObject(null));\n      assert.isFalse(isContextObject(1));\n      assert.isFalse(isContextObject({ foo: 'bar' }));\n    });\n\n    it('is true for an object with `include` with a context prop', () => {\n      assert.isTrue(isContextObject({ include: 'string' }));\n      assert.isTrue(\n        isContextObject({ include: document.createElement('div') })\n      );\n      assert.isTrue(isContextObject({ include: [] }));\n      assert.isTrue(isContextObject({ include: { length: 1 } }));\n      assert.isTrue(isContextObject({ include: { fromFrames: true } }));\n      assert.isTrue(isContextObject({ include: { fromShadowDom: true } }));\n    });\n\n    it('is false for an object with `include` that is not a context prop', () => {\n      assert.isFalse(isContextObject({ include: false }));\n      assert.isFalse(isContextObject({ include: null }));\n      assert.isFalse(isContextObject({ include: 123 }));\n      assert.isFalse(isContextObject({ include: { something: 'else' } }));\n    });\n\n    it('is true for an object with `exclude` with a context prop', () => {\n      assert.isTrue(isContextObject({ exclude: 'string' }));\n      assert.isTrue(\n        isContextObject({ exclude: document.createElement('div') })\n      );\n      assert.isTrue(isContextObject({ exclude: [] }));\n      assert.isTrue(isContextObject({ exclude: { length: 1 } }));\n      assert.isTrue(isContextObject({ exclude: { fromFrames: true } }));\n      assert.isTrue(isContextObject({ exclude: { fromShadowDom: true } }));\n    });\n\n    it('is false for an object with `exclude` that is not a context prop', () => {\n      assert.isFalse(isContextObject({ exclud: false }));\n      assert.isFalse(isContextObject({ exclud: null }));\n      assert.isFalse(isContextObject({ exclud: 123 }));\n      assert.isFalse(isContextObject({ exclude: { something: 'else' } }));\n    });\n\n    it('is false if `include` is on the prototype', () => {\n      assert.isFalse(isContextObject(Object.create({ include: 'string' })));\n    });\n\n    it('is false if `exclude` is on the prototype', () => {\n      assert.isFalse(isContextObject(Object.create({ exclude: 'string' })));\n    });\n\n    it('is true for an object with both `include` and `exclude`', () => {\n      assert.isTrue(isContextObject({ include: 'string', exclude: 'string' }));\n    });\n\n    it('is true for an object with a valid `include` and invalid `exclude`', () => {\n      assert.isTrue(isContextObject({ include: [], exclude: 1 }));\n    });\n\n    it('is true for an object with a valid `exclude` and invalid `include`', () => {\n      assert.isTrue(isContextObject({ exclude: [], include: 1 }));\n    });\n  });\n\n  describe('isContextSpec', () => {\n    it('is true for a context object', () => {\n      assert.isTrue(isContextSpec({ include: 'string' }));\n      assert.isTrue(isContextSpec({ exclude: ['string'] }));\n    });\n\n    it('is true for a context prop', () => {\n      assert.isTrue(isContextSpec('string'));\n    });\n\n    it('is false for other types', () => {\n      assert.isFalse(isContextSpec(true));\n      assert.isFalse(isContextSpec(null));\n      assert.isFalse(isContextSpec(1));\n      assert.isFalse(isContextSpec({}));\n      assert.isFalse(isContextSpec({ include: null }));\n      assert.isFalse(isContextSpec({ runOnly: 'foo' }));\n      assert.isFalse(isContextSpec(Object.create({ include: 'foo' })));\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/is-hidden.js",
    "content": "function createContentHidden() {\n  'use strict';\n  var group = document.createElement('div');\n  group.innerHTML =\n    '<label id=\"mylabel\">Label</label><input aria-labelledby=\"mylabel\" type=\"text\" />';\n  return group;\n}\n\nfunction makeShadowTreeHidden(node) {\n  'use strict';\n  var root = node.attachShadow({ mode: 'open' });\n  var div = document.createElement('div');\n  div.className = 'parent';\n  root.appendChild(div);\n  div.appendChild(createContentHidden());\n}\n\ndescribe('axe.utils.isHidden', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.isHidden);\n  });\n\n  it('should return false on detached elements', function () {\n    var el = document.createElement('div');\n    el.innerHTML = 'I am not visible because I am detached!';\n\n    assert.isTrue(axe.utils.isHidden(el));\n  });\n\n  it('should return false on a document', function () {\n    assert.isFalse(axe.utils.isHidden(document));\n  });\n\n  it('should return true if `aria-hidden` is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" aria-hidden=\"true\">Hidden from screen readers</div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(axe.utils.isHidden(el));\n  });\n\n  it('should return true if `display: none` is set', function () {\n    fixture.innerHTML =\n      '<div id=\"target\" style=\"display: none\">Hidden from screen readers</div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(axe.utils.isHidden(el));\n  });\n\n  it('should return true if `aria-hidden` is set on parent', function () {\n    fixture.innerHTML =\n      '<div aria-hidden=\"true\"><div id=\"target\">Hidden from screen readers</div></div>';\n\n    var el = document.getElementById('target');\n    assert.isTrue(axe.utils.isHidden(el));\n  });\n\n  it('should know how `visibility` works', function () {\n    fixture.innerHTML =\n      '<div style=\"visibility: hidden;\">' +\n      '<div id=\"target\" style=\"visibility: visible;\">Hi</div>' +\n      '</div>';\n\n    var el = document.getElementById('target');\n    assert.isFalse(axe.utils.isHidden(el));\n  });\n\n  (shadowSupported ? it : xit)(\n    'not hidden: should work when the element is inside shadow DOM',\n    function () {\n      var tree, node;\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div></div>';\n      makeShadowTreeHidden(fixture.firstChild);\n      tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      node = axe.utils.querySelectorAll(tree, 'input')[0];\n      assert.isFalse(axe.utils.isHidden(node.actualNode));\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'hidden: should work when the element is inside shadow DOM',\n    function () {\n      var tree, node;\n      // shadow DOM v1 - note: v0 is compatible with this code, so no need\n      // to specifically test this\n      fixture.innerHTML = '<div style=\"display:none\"></div>';\n      makeShadowTreeHidden(fixture.firstChild);\n      tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      node = axe.utils.querySelectorAll(tree, 'input')[0];\n      assert.isTrue(axe.utils.isHidden(node.actualNode));\n    }\n  );\n\n  (shadowSupported ? it : xit)(\n    'should work with hidden slotted elements',\n    function () {\n      function createContentSlotted() {\n        var group = document.createElement('div');\n        group.innerHTML =\n          '<div id=\"target\" style=\"display:none;\">Stuff<slot></slot></div>';\n        return group;\n      }\n      function makeShadowTree(node) {\n        var root = node.attachShadow({ mode: 'open' });\n        var div = document.createElement('div');\n        root.appendChild(div);\n        div.appendChild(createContentSlotted());\n      }\n      fixture.innerHTML = '<div><p><a>hello</a></p></div>';\n      makeShadowTree(fixture.firstChild);\n      var tree = axe.utils.getFlattenedTree(fixture.firstChild);\n      var el = axe.utils.querySelectorAll(tree, 'a')[0];\n      assert.isTrue(axe.utils.isHidden(el.actualNode));\n    }\n  );\n});\n"
  },
  {
    "path": "test/core/utils/is-html-element.js",
    "content": "/* global axe */\ndescribe('axe.utils.isHtmlElement', function () {\n  var queryFixture = axe.testUtils.queryFixture;\n  var isHtmlElement = axe.utils.isHtmlElement;\n\n  it('returns true if given ul', function () {\n    var node = document.createElement('ul');\n    assert.isTrue(isHtmlElement(node));\n  });\n\n  it('returns true if given nav', function () {\n    var node = document.createElement('nav');\n    assert.isTrue(isHtmlElement(node));\n  });\n\n  it('returns true if given iframe', function () {\n    var node = document.createElement('iframe');\n    assert.isTrue(isHtmlElement(node));\n  });\n\n  it('returns false if given custom element', function () {\n    var node = document.createElement('myElement');\n    assert.isFalse(isHtmlElement(node));\n  });\n\n  it('returns false if given svg namespace', function () {\n    var node = document.createElementNS('http://www.w3.org/2000/svg', 'a');\n    assert.isFalse(isHtmlElement(node));\n  });\n\n  it('returns false if node has inherited svg namespace', function () {\n    var svgNameSpace = 'http://www.w3.org/2000/svg';\n    var node = document.createElementNS(svgNameSpace, 'svg');\n    var child = document.createElementNS(svgNameSpace, 'a');\n    child.setAttribute('href', '');\n    child.textContent = 'Child Node';\n    node.appendChild(child);\n\n    var childNode = node.querySelector('a');\n    assert.isFalse(isHtmlElement(childNode));\n  });\n\n  it('works with VirtualNodes', function () {\n    var vNode = queryFixture('<ul id=\"target\"></ul>');\n    assert.isTrue(isHtmlElement(vNode));\n  });\n\n  it('works with SerialVirtualNode', function () {\n    var vNode = new axe.SerialVirtualNode({ nodeName: 'ul' });\n    assert.isTrue(isHtmlElement(vNode));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/is-node-in-context.js",
    "content": "describe('axe.utils.isNodeInContext', () => {\n  const { queryFixture } = axe.testUtils;\n  const { Context } = axe._thisWillBeDeletedDoNotUse.base;\n  const { isNodeInContext } = axe.utils;\n\n  it('is true when the node is included', () => {\n    const node = queryFixture(\n      `<article> <section> <img id=\"target\"> </section> </article>`\n    );\n    const context = new Context({ include: [['article']] }, axe._tree);\n    assert.isTrue(isNodeInContext(node, context));\n  });\n\n  it('is false when the node is not included', () => {\n    const node = queryFixture(\n      `<article id=\"target\"> <section> <img> </section> </article>`\n    );\n    const context = new Context({ include: [['section']] }, axe._tree);\n    assert.isFalse(isNodeInContext(node, context));\n  });\n\n  describe('when the node is excluded', () => {\n    it('is false when exclude is closer to the node than include', () => {\n      const node = queryFixture(\n        `<main> <article> <section> <img id=\"target\"> </section> </article> </main>`\n      );\n      const context = new Context(\n        {\n          include: [['article']],\n          exclude: [['main'], ['section']]\n        },\n        axe._tree\n      );\n      assert.isFalse(isNodeInContext(node, context));\n    });\n\n    it('is true when include is closer to the node than exclude', () => {\n      const node = queryFixture(\n        `<main> <article> <section> <img id=\"target\"> </section> </article> </main>`\n      );\n      const context = new Context(\n        {\n          include: [['main'], ['section']],\n          exclude: [['article']]\n        },\n        axe._tree\n      );\n      assert.isTrue(isNodeInContext(node, context));\n    });\n  });\n\n  describe('when nodeIndex is undefined', () => {\n    it('is true when the node is included', () => {\n      const node = queryFixture(`<article> <img id=\"target\">  </article>`);\n      delete node.nodeIndex;\n      delete node.parent.nodeIndex;\n      const context = new Context({ include: [['article']] }, axe._tree);\n      assert.isTrue(isNodeInContext(node, context));\n    });\n\n    it('is false when the node is not included', () => {\n      const node = queryFixture(\n        `<article id=\"target\"> <section> <img> </section> </article>`\n      );\n\n      delete node.nodeIndex;\n      delete node.children[0].nodeIndex;\n      const context = new Context({ include: [['section']] }, axe._tree);\n      assert.isFalse(isNodeInContext(node, context));\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/is-shadow-root.js",
    "content": "var fixture = document.getElementById('fixture');\nvar shadowSupport = axe.testUtils.shadowSupport;\n\ndescribe('axe.utils.isShadowRoot', function () {\n  'use strict';\n\n  function createStyle(box) {\n    var style = document.createElement('style');\n    style.textContent =\n      'div.breaking { color: Red;font-size: 20px; border: 1px dashed Purple; }' +\n      (box ? 'slot { display: block; }' : '') +\n      'div.other { padding: 2px 0 0 0; border: 1px solid Cyan; }';\n    return style;\n  }\n\n  var isShadowRoot = axe.utils.isShadowRoot;\n\n  it('returns false if the node has no shadowRoot', function () {\n    assert.isFalse(isShadowRoot({ nodeName: 'DIV', shadowRoot: undefined }));\n  });\n  it('returns true if the native element allows shadow DOM', function () {\n    assert.isTrue(isShadowRoot({ nodeName: 'DIV', shadowRoot: {} }));\n    assert.isTrue(isShadowRoot({ nodeName: 'H1', shadowRoot: {} }));\n    assert.isTrue(isShadowRoot({ nodeName: 'ASIDE', shadowRoot: {} }));\n  });\n  it('returns true if a custom element with shadowRoot', function () {\n    assert.isTrue(isShadowRoot({ nodeName: 'X-BUTTON', shadowRoot: {} }));\n    assert.isTrue(\n      isShadowRoot({ nodeName: 'T1000-SCHWARZENEGGER', shadowRoot: {} })\n    );\n  });\n  it('returns true if an invalid custom element with shadowRoot', function () {\n    assert.isFalse(isShadowRoot({ nodeName: '0-BUZZ', shadowRoot: {} }));\n    assert.isFalse(isShadowRoot({ nodeName: '--ELM--', shadowRoot: {} }));\n  });\n  it('returns false if the native element does not allow shadow DOM', function () {\n    assert.isFalse(isShadowRoot({ nodeName: 'IFRAME', shadowRoot: {} }));\n    assert.isFalse(isShadowRoot({ nodeName: 'STRONG', shadowRoot: {} }));\n  });\n\n  if (shadowSupport.v1) {\n    describe('shadow DOM v1', function () {\n      afterEach(function () {\n        fixture.innerHTML = '';\n      });\n      beforeEach(function () {\n        function createStoryGroup(className, slotName) {\n          var group = document.createElement('div');\n          group.className = className;\n          // Empty string in slot name attribute or absence thereof work the same, so no need for special handling.\n          group.innerHTML =\n            '<ul><slot name=\"' +\n            slotName +\n            '\">fallback content<li>one</li></slot></ul>';\n          return group;\n        }\n\n        function makeShadowTree(storyList) {\n          var root = storyList.attachShadow({ mode: 'open' });\n          root.appendChild(createStyle());\n          root.appendChild(createStoryGroup('breaking', 'breaking'));\n          root.appendChild(createStoryGroup('other', ''));\n        }\n        var str =\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str +=\n          '<div class=\"stories\"><li>1</li>' +\n          '<li>2</li><li class=\"breaking\" slot=\"breaking\">3</li>' +\n          '<li>4</li><li>5</li><li class=\"breaking\" slot=\"breaking\">6</li></div>';\n        str += '<div class=\"stories\"></div>';\n        fixture.innerHTML = str;\n\n        fixture.querySelectorAll('.stories').forEach(makeShadowTree);\n      });\n      it('should support shadow DOM v1', function () {\n        assert.isDefined(fixture.firstChild.shadowRoot);\n      });\n    });\n  }\n});\n"
  },
  {
    "path": "test/core/utils/is-xhtml.js",
    "content": "describe('axe.utils.isXHTML', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.isXHTML);\n  });\n\n  it('should return true on any document that is XHTML', function () {\n    var doc = document.implementation.createDocument(\n      'http://www.w3.org/1999/xhtml',\n      'html',\n      null\n    );\n    assert.isTrue(axe.utils.isXHTML(doc));\n  });\n\n  it('should return false on any document that is HTML', function () {\n    var doc = document.implementation.createHTMLDocument('Monkeys');\n    assert.isFalse(axe.utils.isXHTML(doc));\n  });\n\n  it('should return false on any document that is HTML - fixture', function () {\n    assert.isFalse(axe.utils.isXHTML(document));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/matchAncestry.js",
    "content": "describe('axe.utils.matchAncestry', function () {\n  'use strict';\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.matchAncestry);\n  });\n\n  it('should match when ancestry is the same and one level', function () {\n    var result = axe.utils.matchAncestry(\n      ['html > body > div:nth-child(1)'],\n      ['html > body > div:nth-child(1)']\n    );\n    assert.isTrue(result);\n  });\n\n  it('should not match when ancestry is different and one level', function () {\n    var result = axe.utils.matchAncestry(\n      ['html > body > div:nth-child(3)'],\n      ['html > body > div:nth-child(1)']\n    );\n    assert.isFalse(result);\n  });\n\n  it('should not match when ancestries have different numbers of elements', function () {\n    var result = axe.utils.matchAncestry(\n      ['iframe', 'html > body > div:nth-child(1)'],\n      ['html > body > div:nth-child(1)']\n    );\n    assert.isFalse(result);\n  });\n\n  it('should not match when first level is different and second level is the same', function () {\n    var result = axe.utils.matchAncestry(\n      ['iframe', 'html > body > div:nth-child(1)'],\n      ['otherIframe', 'html > body > div:nth-child(1)']\n    );\n    assert.isFalse(result);\n  });\n\n  it('should not match when second level is different and first level is the same', function () {\n    var result = axe.utils.matchAncestry(\n      ['iframe', 'html > body > div:nth-child(1)'],\n      ['iframe', 'html > body > div:nth-child(2)']\n    );\n    assert.isFalse(result);\n  });\n\n  it('should match when all levels are the same', function () {\n    var result = axe.utils.matchAncestry(\n      ['iframe', 'iframe2', 'html > body > div:nth-child(1)'],\n      ['iframe', 'iframe2', 'html > body > div:nth-child(1)']\n    );\n    assert.isTrue(result);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/matches.js",
    "content": "describe('utils.matches', function () {\n  var matches = axe.utils.matches;\n  var fixture = document.querySelector('#fixture');\n  var queryFixture = axe.testUtils.queryFixture;\n  var convertSelector = axe._thisWillBeDeletedDoNotUse.utils.convertSelector;\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  describe('tag', function () {\n    it('returns true if tag matches', function () {\n      var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n      assert.isTrue(matches(virtualNode, 'h1'));\n    });\n\n    it('returns false if the tag does not match', function () {\n      var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n      assert.isFalse(matches(virtualNode, 'div'));\n    });\n\n    it('is case sensitive for XHTML', function () {\n      var virtualNode = queryFixture('<H1 id=\"target\">foo</H1>');\n      delete virtualNode._cache.props;\n      virtualNode._isXHTML = true;\n      assert.isFalse(matches(virtualNode, 'h1'));\n    });\n\n    it('is case insensitive for HTML, but not for XHTML', function () {\n      var virtualNode = queryFixture('<H1 id=\"target\">foo</H1>');\n      delete virtualNode._cache.props;\n      virtualNode._isXHTML = true;\n      assert.isFalse(matches(virtualNode, 'h1'));\n    });\n  });\n\n  describe('classes', function () {\n    it('returns true if all classes match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar baz\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '.foo.bar.baz'));\n    });\n\n    it('returns false if some classes do not match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar baz\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, '.foo.bar.bazz'));\n    });\n\n    it('returns false if any classes are missing', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, '.foo.bar.baz'));\n    });\n  });\n\n  describe('attributes', function () {\n    it('returns true if attribute exists', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo]'));\n    });\n\n    it('returns true if attribute matches', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo=baz]'));\n    });\n\n    it('returns true if all attributes match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo=\"baz\"][bar=\"foo\"][baz=\"bar\"]'));\n    });\n\n    it('returns false if some attributes do not match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, '[foo=\"baz\"][bar=\"foo\"][baz=\"baz\"]'));\n    });\n\n    it('returns false if any attributes are missing', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"baz\" baz=\"bar\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, '[foo=\"baz\"][bar=\"foo\"][baz=\"bar\"]'));\n    });\n\n    it('returns true if attribute starts with value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"bazaphone\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo^=\"baz\"]'));\n    });\n\n    it('returns true if attribute ends with value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"bazaphone\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo$=\"hone\"]'));\n    });\n\n    it('returns true if attribute contains value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"bazaphone\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo*=\"baz\"]'));\n    });\n\n    it('returns true if attribute has value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"bar baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo~=\"baz\"]'));\n    });\n\n    it('returns true if attribute matches having an empty value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo=\"\"]'));\n    });\n\n    it('returns true if attribute matches not having value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo=\"\"]'));\n    });\n\n    it('returns false if attribute should not have value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo=\"bar baz\" bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, '[foo=\"\"]'));\n    });\n\n    it('should return true if attribute exists without value', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" foo bar=\"foo\" baz=\"bar\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, '[foo]'));\n    });\n  });\n\n  describe('id', function () {\n    it('returns true if id matches', function () {\n      var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n      assert.isTrue(matches(virtualNode, '#target'));\n    });\n\n    it('returns false if the id does not match', function () {\n      var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n      assert.isFalse(matches(virtualNode, '#notTarget'));\n    });\n  });\n\n  describe('pseudos', function () {\n    it('throws error if pseudo is not implemented', function () {\n      var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n      assert.throws(function () {\n        matches(virtualNode, 'h1:empty');\n      });\n      assert.throws(function () {\n        matches(virtualNode, 'h1::before');\n      });\n    });\n\n    describe(':not', function () {\n      it('returns true if :not matches using tag', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:not(span)'));\n      });\n\n      it('returns true if :not matches using class', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:not(.foo)'));\n      });\n\n      it('returns true if :not matches using attribute', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:not([class])'));\n      });\n\n      it('returns true if :not matches using id', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:not(#foo)'));\n      });\n\n      it('returns true if :not matches none of the selectors', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:not([role=heading], span)'));\n      });\n\n      it('returns false if :not matches element', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isFalse(matches(virtualNode, 'h1:not([id])'));\n      });\n\n      it('returns false if :not matches one of the selectors', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isFalse(matches(virtualNode, 'h1:not([role=heading], [id])'));\n      });\n    });\n\n    describe(':is', function () {\n      it('returns true if :is matches using tag', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, ':is(h1)'));\n      });\n\n      it('returns true if :is matches using class', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\" class=\"foo\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:is(.foo)'));\n      });\n\n      it('returns true if :is matches using attribute', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\" class=\"foo\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:is([class])'));\n      });\n\n      it('returns true if :is matches using id', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, 'h1:is(#target)'));\n      });\n\n      it('returns true if :is matches one of the selectors', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isTrue(matches(virtualNode, ':is([role=heading], h1)'));\n      });\n\n      it('returns true if :is matches complex selector', function () {\n        var virtualNode = queryFixture('<div><h1 id=\"target\">foo</h1></div>');\n        assert.isTrue(matches(virtualNode, 'h1:is(div > #target)'));\n      });\n\n      it('returns false if :is does not match element', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isFalse(matches(virtualNode, 'h1:is([class])'));\n      });\n\n      it('returns false if :is matches none of the selectors', function () {\n        var virtualNode = queryFixture('<h1 id=\"target\">foo</h1>');\n        assert.isFalse(\n          matches(virtualNode, 'h1:is([class], span, #foo, .bar)')\n        );\n      });\n    });\n  });\n\n  describe('complex', function () {\n    it('returns true for complex selector', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar baz\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, 'span.foo[id=\"target\"]:not(div)'));\n    });\n\n    it('returns false if any part of the selector does not match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar baz\"></span>'\n      );\n      assert.isFalse(matches(virtualNode, 'span.foo[id=\"target\"]:not(span)'));\n    });\n\n    it('returns true if a comma-separated list of selectors match', function () {\n      var virtualNode = queryFixture(\n        '<span id=\"target\" class=\"foo bar baz\"></span>'\n      );\n      assert.isTrue(matches(virtualNode, 'div, p, span'));\n    });\n  });\n\n  describe('combinator', function () {\n    it('returns true if parent selector matches', function () {\n      var virtualNode = queryFixture('<div><h1 id=\"target\">foo</h1></div>');\n      assert.isTrue(matches(virtualNode, 'div > h1'));\n    });\n\n    it('returns true if nested parent selector matches', function () {\n      var virtualNode = queryFixture(\n        '<main><div><h1 id=\"target\">foo</h1></div><main>'\n      );\n      assert.isTrue(matches(virtualNode, 'main > div > h1'));\n    });\n\n    it('returns true if hierarchical selector matches', function () {\n      var virtualNode = queryFixture('<div><h1 id=\"target\">foo</h1></div>');\n      assert.isTrue(matches(virtualNode, 'div h1'));\n    });\n\n    it('returns true if nested hierarchical selector matches', function () {\n      var virtualNode = queryFixture(\n        '<div><table><tr><td><h1 id=\"target\">foo</h1></td></tr></table></div>'\n      );\n      assert.isTrue(matches(virtualNode, 'div tr h1'));\n    });\n\n    it('returns true if mixed parent and hierarchical selector matches', function () {\n      var virtualNode = queryFixture(\n        '<div><table><tr><td><h1 id=\"target\">foo</h1></td></tr></table></div>'\n      );\n      assert.isTrue(matches(virtualNode, 'div tr > td h1'));\n    });\n\n    it('returns false if parent selector does not match', function () {\n      var virtualNode = queryFixture('<div><h1 id=\"target\">foo</h1></div>');\n      assert.isFalse(matches(virtualNode, 'span > h1'));\n    });\n\n    it('returns false if nested parent selector does not match', function () {\n      var virtualNode = queryFixture(\n        '<main><div><h1 id=\"target\">foo</h1></div><main>'\n      );\n      assert.isFalse(matches(virtualNode, 'span > div > h1'));\n    });\n\n    it('returns false if hierarchical selector does not match', function () {\n      var virtualNode = queryFixture('<div><h1 id=\"target\">foo</h1></div>');\n      assert.isFalse(matches(virtualNode, 'span h1'));\n    });\n\n    it('returns false if nested hierarchical selector does not match', function () {\n      var virtualNode = queryFixture(\n        '<div><table><tr><td><h1 id=\"target\">foo</h1></td></tr></table></div>'\n      );\n      assert.isFalse(matches(virtualNode, 'div span h1'));\n    });\n\n    it('returns false if mixed parent and hierarchical selector does not match', function () {\n      var virtualNode = queryFixture(\n        '<div><table><tr><td><h1 id=\"target\">foo</h1></td></tr></table></div>'\n      );\n      assert.isFalse(matches(virtualNode, 'div span > td h1'));\n    });\n\n    it('throws error if combinator is not implemented', function () {\n      var virtualNode = queryFixture('<div></div><h1 id=\"target\">foo</h1>');\n      assert.throws(function () {\n        matches(virtualNode, 'div + h1');\n      });\n      assert.throws(function () {\n        matches(virtualNode, 'div ~ h1');\n      });\n    });\n  });\n\n  describe('convertSelector', function () {\n    it('should set type to attrExist for attribute selector', function () {\n      var expression = convertSelector('[disabled]');\n      assert.equal(expression[0][0].attributes[0].type, 'attrExist');\n    });\n\n    it('should set type to attrValue for attribute value selector', function () {\n      var expression = convertSelector('[aria-pressed=\"true\"]');\n      assert.equal(expression[0][0].attributes[0].type, 'attrValue');\n    });\n\n    it('should set type to attrValue for empty attribute value selector', function () {\n      var expression = convertSelector('[aria-pressed=\"\"]');\n      assert.equal(expression[0][0].attributes[0].type, 'attrValue');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/memoize.js",
    "content": "describe('axe.utils.memoize', function () {\n  'use strict';\n\n  it('should add the function to axe._memoizedFns', function () {\n    const length = axe._memoizedFns.length;\n\n    axe.utils.memoize(function myFn() {});\n    assert.equal(axe._memoizedFns.length, length + 1);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/merge-results.js",
    "content": "describe('axe.utils.mergeResults', () => {\n  'use strict';\n  var queryFixture = axe.testUtils.queryFixture;\n  var RuleError = axe.utils.RuleError;\n\n  it('should normalize empty results', () => {\n    var result = axe.utils.mergeResults([\n      { results: [] },\n      { results: [{ id: 'a', result: 'b' }] }\n    ]);\n    assert.deepEqual(result, [\n      {\n        id: 'a',\n        result: 'b'\n      }\n    ]);\n  });\n\n  it('merges frame content, including all selector types', () => {\n    var iframe = queryFixture('<iframe id=\"target\"></iframe>').actualNode;\n    var node = {\n      selector: ['#foo'],\n      xpath: ['html/#foo'],\n      ancestry: ['html > div'],\n      nodeIndexes: [123]\n    };\n    var result = axe.utils.mergeResults([\n      {\n        frameElement: iframe,\n        results: [\n          {\n            id: 'a',\n            result: 'b',\n            nodes: [{ node: node }]\n          }\n        ]\n      }\n    ]);\n\n    assert.lengthOf(result, 1);\n    assert.lengthOf(result[0].nodes, 1);\n\n    var node = result[0].nodes[0].node;\n    assert.deepEqual(node.selector, ['#target', '#foo']);\n    assert.deepEqual(node.xpath, [\"//iframe[@id='target']\", 'html/#foo']);\n    assert.deepEqual(node.ancestry, [\n      'html > body > div:nth-child(1) > iframe',\n      'html > div'\n    ]);\n    assert.deepEqual(node.nodeIndexes, [1, 123]);\n  });\n\n  it('merges frame specs', () => {\n    var iframe = queryFixture('<iframe id=\"target\"></iframe>').actualNode;\n    var frameSpec = new axe.utils.DqElement(iframe).toJSON();\n    var node = {\n      selector: ['#foo'],\n      xpath: ['html/#foo'],\n      ancestry: ['html > div'],\n      nodeIndexes: [123]\n    };\n    var result = axe.utils.mergeResults([\n      {\n        frameSpec: frameSpec,\n        results: [\n          {\n            id: 'a',\n            result: 'b',\n            nodes: [{ node: node }]\n          }\n        ]\n      }\n    ]);\n\n    assert.lengthOf(result, 1);\n    assert.lengthOf(result[0].nodes, 1);\n\n    var node = result[0].nodes[0].node;\n    assert.deepEqual(node.selector, ['#target', '#foo']);\n    assert.deepEqual(node.xpath, [\"//iframe[@id='target']\", 'html/#foo']);\n    assert.deepEqual(node.ancestry, [\n      'html > body > div:nth-child(1) > iframe',\n      'html > div'\n    ]);\n    assert.deepEqual(node.nodeIndexes, [1, 123]);\n  });\n\n  it('sorts results from iframes into their correct DOM position', () => {\n    var result = axe.utils.mergeResults([\n      {\n        results: [\n          {\n            id: 'a',\n            result: 'a',\n            nodes: [\n              {\n                node: {\n                  selector: ['h1'],\n                  nodeIndexes: [1]\n                }\n              }\n            ]\n          },\n          {\n            id: 'a',\n            result: 'd',\n            nodes: [\n              {\n                node: {\n                  selector: ['h4'],\n                  nodeIndexes: [4]\n                }\n              }\n            ]\n          },\n          {\n            id: 'a',\n            result: 'b',\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'h2'],\n                  nodeIndexes: [2, 1],\n                  fromFrame: true\n                }\n              }\n            ]\n          },\n          {\n            id: 'a',\n            result: 'c',\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'h3'],\n                  nodeIndexes: [2, 2],\n                  fromFrame: true\n                }\n              }\n            ]\n          }\n        ]\n      }\n    ]);\n\n    var ids = result[0].nodes.map(function (el) {\n      return el.node.selector.join(' >> ');\n    });\n    assert.deepEqual(ids, ['h1', 'iframe1 >> h2', 'iframe1 >> h3', 'h4']);\n  });\n\n  it('sorts nested iframes', () => {\n    var result = axe.utils.mergeResults([\n      {\n        results: [\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['h1'],\n                  nodeIndexes: [1]\n                }\n              },\n              {\n                node: {\n                  selector: ['h5'],\n                  nodeIndexes: [3]\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'h2'],\n                  nodeIndexes: [2, 1],\n                  fromFrame: true\n                }\n              },\n              {\n                node: {\n                  selector: ['iframe1', 'h4'],\n                  nodeIndexes: [2, 3],\n                  fromFrame: true\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'iframe2', 'h3'],\n                  nodeIndexes: [2, 2, 1],\n                  fromFrame: true\n                }\n              }\n            ]\n          }\n        ]\n      }\n    ]);\n\n    var ids = result[0].nodes.map(function (el) {\n      return el.node.selector.join(' >> ');\n    });\n    assert.deepEqual(ids, [\n      'h1',\n      'iframe1 >> h2',\n      'iframe1 >> iframe2 >> h3',\n      'iframe1 >> h4',\n      'h5'\n    ]);\n  });\n\n  it('sorts results even if nodeIndexes are empty', () => {\n    var result = axe.utils.mergeResults([\n      {\n        results: [\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['h1'],\n                  nodeIndexes: [1]\n                }\n              },\n              {\n                node: {\n                  selector: ['nill'],\n                  nodeIndexes: []\n                }\n              },\n              {\n                node: {\n                  selector: ['h3'],\n                  nodeIndexes: [3]\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['nill'],\n                  nodeIndexes: []\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'h2'],\n                  nodeIndexes: [2, 1],\n                  fromFrame: true\n                }\n              },\n              {\n                node: {\n                  selector: ['nill'],\n                  nodeIndexes: []\n                }\n              }\n            ]\n          }\n        ]\n      }\n    ]);\n\n    var ids = result[0].nodes.map(function (el) {\n      return el.node.selector.join(' >> ');\n    });\n    // Order of \"nill\" varies in IE\n    assert.deepEqual(ids, [\n      'h1',\n      'iframe1 >> h2',\n      'h3',\n      'nill',\n      'nill',\n      'nill'\n    ]);\n  });\n\n  it('sorts results even if nodeIndexes are undefined', () => {\n    var result = axe.utils.mergeResults([\n      {\n        results: [\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['h1'],\n                  nodeIndexes: [1]\n                }\n              },\n              {\n                node: {\n                  selector: ['nill']\n                }\n              },\n              {\n                node: {\n                  selector: ['h3'],\n                  nodeIndexes: [3]\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['nill']\n                }\n              }\n            ]\n          },\n          {\n            id: 'heading-order',\n            result: true,\n            nodes: [\n              {\n                node: {\n                  selector: ['iframe1', 'h2'],\n                  nodeIndexes: [2, 1],\n                  fromFrame: true\n                }\n              },\n              {\n                node: {\n                  selector: ['nill']\n                }\n              }\n            ]\n          }\n        ]\n      }\n    ]);\n\n    var ids = result[0].nodes.map(function (el) {\n      return el.node.selector.join(' >> ');\n    });\n    // Order of \"nill\" varies in IE\n    assert.deepEqual(ids, [\n      'h1',\n      'iframe1 >> h2',\n      'h3',\n      'nill',\n      'nill',\n      'nill'\n    ]);\n  });\n\n  it('sorts nodes all placed on the same result', () => {\n    var result = axe.utils.mergeResults([\n      {\n        results: [\n          {\n            id: 'iframe',\n            result: 'inapplicable',\n            nodes: [\n              {\n                node: {\n                  selector: ['#level0', '#level1', '#level2a'],\n                  nodeIndexes: [12, 14, 14]\n                }\n              },\n              {\n                node: {\n                  selector: ['#level0', '#level1', '#level2b'],\n                  nodeIndexes: [12, 14, 16]\n                }\n              },\n              {\n                node: {\n                  selector: ['#level0', '#level1'],\n                  nodeIndexes: [12, 14]\n                }\n              },\n              {\n                node: {\n                  selector: ['#level0'],\n                  nodeIndexes: [12]\n                }\n              }\n            ]\n          }\n        ]\n      }\n    ]);\n\n    var ids = result[0].nodes.map(function (el) {\n      return el.node.selector.join(' >> ');\n    });\n\n    assert.deepEqual(ids, [\n      '#level0',\n      '#level0 >> #level1',\n      '#level0 >> #level1 >> #level2a',\n      '#level0 >> #level1 >> #level2b'\n    ]);\n  });\n\n  describe('errors', () => {\n    it('sets error if it is present', () => {\n      const result = axe.utils.mergeResults([\n        { results: [{ id: 'a', result: 'b', error: new Error('test') }] }\n      ]);\n      assert.equal(result[0].error.message, 'test');\n    });\n\n    it('picks the first error if there are multiple', () => {\n      const result = axe.utils.mergeResults([\n        {\n          results: [\n            {\n              id: 'error-occurred',\n              result: undefined,\n              nodes: [{ node: { selector: ['h1'], nodeIndexes: [1] } }]\n            },\n            {\n              id: 'error-occurred',\n              result: undefined,\n              error: new RuleError({ error: new Error('test 1') }),\n              nodes: [\n                {\n                  node: {\n                    selector: ['iframe1', 'h2'],\n                    nodeIndexes: [2, 1],\n                    fromFrame: true\n                  }\n                }\n              ]\n            },\n            {\n              id: 'error-occurred',\n              result: undefined,\n              error: new RuleError({ error: new Error('test 2') }),\n              nodes: [\n                {\n                  node: {\n                    selector: ['iframe2', 'h3'],\n                    nodeIndexes: [3, 1],\n                    fromFrame: true\n                  }\n                }\n              ]\n            }\n          ]\n        }\n      ]);\n\n      assert.equal(result[0].error.message, 'test 1');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/node-lookup.js",
    "content": "describe('axe.utils.nodeLookup', () => {\n  const nodeLookup = axe.utils.nodeLookup;\n  const queryFixture = axe.testUtils.queryFixture;\n\n  it('works with virtual nodes', () => {\n    const element = queryFixture('<div id=\"target\">Hello</div>');\n\n    const { vNode, domNode } = nodeLookup(element);\n    assert.equal(element, vNode);\n    assert.equal(element.actualNode, domNode);\n  });\n\n  it('works with serial virtual nodes', () => {\n    const element = new axe.SerialVirtualNode({ nodeName: 'div' });\n\n    const { vNode, domNode } = nodeLookup(element);\n    assert.equal(element, vNode);\n    assert.isUndefined(domNode);\n  });\n\n  it('works with elements', () => {\n    const element = queryFixture('<div id=\"target\">Hello</div>');\n\n    const { vNode, domNode } = nodeLookup(element.actualNode);\n    assert.equal(element, vNode);\n    assert.equal(element.actualNode, domNode);\n  });\n\n  it('works with non-elements', () => {\n    const element = document.createTextNode('my text node');\n\n    const { vNode, domNode } = nodeLookup(element);\n    assert.isNull(vNode);\n    assert.equal(element, domNode);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/node-serializer.js",
    "content": "describe('nodeSerializer', () => {\n  const { nodeSerializer, DqElement } = axe.utils;\n  const fixture = document.querySelector('#fixture');\n  beforeEach(() => {\n    axe.setup();\n  });\n\n  afterEach(() => {\n    nodeSerializer.update(null);\n  });\n\n  describe('.toSpec()', () => {\n    it('returns DqElement.toJSON() by default', () => {\n      const spec = nodeSerializer.toSpec(fixture);\n      const dqElm = new DqElement(fixture);\n      assert.deepEqual(spec, dqElm.toJSON());\n    });\n\n    it('can be replaced with nodeSerializer.update({ toSpec: fn })', () => {\n      nodeSerializer.update({\n        toSpec(dqElm) {\n          const json = dqElm.toJSON();\n          json.source = 'Replaced';\n          return json;\n        }\n      });\n\n      const spec = nodeSerializer.toSpec(fixture);\n      const dqElm = new DqElement(fixture);\n      assert.deepEqual(spec, { ...dqElm.toJSON(), source: 'Replaced' });\n    });\n  });\n\n  describe('.dqElmToSpec()', () => {\n    it('returns DqElement.toJSON() by default', () => {\n      const dqElm = new DqElement(fixture);\n      const spec = nodeSerializer.dqElmToSpec(dqElm);\n      assert.deepEqual(spec, dqElm.toJSON());\n    });\n\n    it('can be replaced with nodeSerializer.update({ toSpec: fn })', () => {\n      nodeSerializer.update({\n        toSpec(dqElm) {\n          const json = dqElm.toJSON();\n          json.source = 'Replaced';\n          return json;\n        }\n      });\n\n      const dqElm = new DqElement(fixture);\n      const spec = nodeSerializer.dqElmToSpec(dqElm);\n      assert.deepEqual(spec, { ...dqElm.toJSON(), source: 'Replaced' });\n    });\n\n    it('optionally accepts runOptions, replacing real values with dummy values', () => {\n      const dqElm = new DqElement(fixture);\n      const spec = nodeSerializer.dqElmToSpec(dqElm, {\n        selectors: false,\n        xpath: false,\n        ancestry: false\n      });\n      assert.deepEqual(spec, {\n        ...dqElm.toJSON(),\n        selector: [':root'],\n        ancestry: [':root'],\n        xpath: '/'\n      });\n    });\n\n    it('returns selector when falsey but not false', () => {\n      const dqElm = new DqElement(fixture);\n      const spec = nodeSerializer.dqElmToSpec(dqElm, { selectors: null });\n      assert.deepEqual(spec, {\n        ...dqElm.toJSON(),\n        ancestry: [':root'],\n        xpath: '/'\n      });\n    });\n\n    it('returns selector if fromFrame, even if runOptions.selectors is false', () => {\n      const dqElm = new DqElement(fixture);\n      dqElm.fromFrame = true;\n      const spec = nodeSerializer.dqElmToSpec(dqElm, {\n        selectors: false\n      });\n      assert.deepEqual(spec, {\n        ...dqElm.toJSON(),\n        ancestry: [':root'],\n        xpath: '/'\n      });\n    });\n\n    it('skips computing props turned off with runOptions', () => {\n      const throws = () => {\n        throw new Error('Should not be called');\n      };\n\n      const dqElm = new DqElement(\n        fixture,\n        {},\n        {\n          selector: { get: throws },\n          ancestry: { get: throws },\n          xpath: { get: throws }\n        }\n      );\n\n      assert.doesNotThrow(() => {\n        nodeSerializer.dqElmToSpec(dqElm, {\n          selectors: false,\n          xpath: false,\n          ancestry: false\n        });\n      });\n    });\n  });\n\n  describe('.mergeSpecs()', () => {\n    const nodeSpec = {\n      source: '<div id=\"fixture\"></div>',\n      selector: ['#fixture'],\n      ancestry: ['html > body > #fixture'],\n      nodeIndexes: [3],\n      xpath: ['html/body/div[1]']\n    };\n    const frameSpec = {\n      source: '<iframe></iframe>',\n      selector: ['#frame'],\n      ancestry: ['html > body > #frame'],\n      nodeIndexes: [3],\n      xpath: ['html/body/iframe[1]']\n    };\n\n    it('returns DqElement.mergeSpecs() by default', () => {\n      const combinedSpec = nodeSerializer.mergeSpecs(nodeSpec, frameSpec);\n      assert.deepEqual(combinedSpec, DqElement.mergeSpecs(nodeSpec, frameSpec));\n    });\n\n    it('can be replaced with nodeSerializer.update({ mergeSpecs: fn })', () => {\n      nodeSerializer.update({\n        mergeSpecs(childSpec, parentSpec) {\n          const spec = DqElement.mergeSpecs(childSpec, parentSpec);\n          spec.source = 'Replaced';\n          return spec;\n        }\n      });\n      const spec = nodeSerializer.mergeSpecs(nodeSpec, frameSpec);\n      assert.deepEqual(spec, {\n        ...DqElement.mergeSpecs(nodeSpec, frameSpec),\n        source: 'Replaced'\n      });\n    });\n  });\n\n  describe('.mapRawNodeResults()', () => {\n    it('returns undefined when passed undefined', () => {\n      assert.isUndefined(nodeSerializer.mapRawNodeResults(undefined));\n    });\n\n    it('converts DqElements node to specs', () => {\n      const dqElm = new DqElement(fixture);\n      const rawNodeResults = [\n        {\n          any: [],\n          all: [],\n          none: [\n            {\n              id: 'nope',\n              data: null,\n              relatedNodes: []\n            }\n          ],\n          node: dqElm,\n          result: 'failed'\n        }\n      ];\n\n      const serialized = nodeSerializer.mapRawNodeResults(rawNodeResults);\n      assert.deepEqual(serialized, [\n        {\n          ...rawNodeResults[0],\n          node: dqElm.toJSON()\n        }\n      ]);\n    });\n\n    it('converts DqElements relatedNodes to specs', () => {\n      const dqElm = new DqElement(fixture);\n      const related = new DqElement(fixture.querySelector('p'));\n      const rawNodeResults = [\n        {\n          any: [\n            {\n              id: 'something',\n              data: null,\n              relatedNodes: [related]\n            }\n          ],\n          all: [\n            {\n              id: 'everything',\n              data: null,\n              relatedNodes: [related]\n            }\n          ],\n          none: [\n            {\n              id: 'nope',\n              data: null,\n              relatedNodes: [related]\n            }\n          ],\n          node: dqElm,\n          result: 'failed'\n        }\n      ];\n\n      const serialized = nodeSerializer.mapRawNodeResults(rawNodeResults);\n      assert.deepEqual(serialized[0].any, [\n        {\n          ...rawNodeResults[0].any[0],\n          relatedNodes: [related.toJSON()]\n        }\n      ]);\n      assert.deepEqual(serialized[0].all, [\n        {\n          ...rawNodeResults[0].all[0],\n          relatedNodes: [related.toJSON()]\n        }\n      ]);\n      assert.deepEqual(serialized[0].none, [\n        {\n          ...rawNodeResults[0].none[0],\n          relatedNodes: [related.toJSON()]\n        }\n      ]);\n    });\n  });\n\n  describe('.mapRawResults()', () => {\n    it('converts DqElements to specs', () => {\n      const dqElm = new DqElement(fixture);\n      const rawNodeResults = [\n        {\n          any: [\n            {\n              id: 'nope',\n              data: null,\n              relatedNodes: [dqElm]\n            }\n          ],\n          all: [],\n          none: [],\n          node: dqElm,\n          result: 'failed'\n        }\n      ];\n      const rawResults = [\n        {\n          id: 'test',\n          nodes: rawNodeResults\n        }\n      ];\n\n      const serialized = nodeSerializer.mapRawResults(rawResults);\n      assert.deepEqual(serialized, [\n        {\n          ...rawResults[0],\n          nodes: [\n            {\n              ...rawNodeResults[0],\n              node: dqElm.toJSON(),\n              any: [\n                {\n                  ...rawNodeResults[0].any[0],\n                  relatedNodes: [dqElm.toJSON()]\n                }\n              ]\n            }\n          ]\n        }\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/node-sorter.js",
    "content": "describe('axe.utils.nodeSorter', function () {\n  'use strict';\n\n  function $id(id) {\n    return document.getElementById(id);\n  }\n\n  var fixture = document.getElementById('fixture');\n\n  it('should exist', function () {\n    assert.isFunction(axe.utils.nodeSorter);\n  });\n\n  it('should return -1 if a comes before b', function () {\n    fixture.innerHTML = '<div id=\"a\"></div><div id=\"b\"></div>';\n\n    assert.equal(\n      axe.utils.nodeSorter({ actualNode: $id('a') }, { actualNode: $id('b') }),\n      -1\n    );\n  });\n\n  it('should return -1 if a comes before b - nested', function () {\n    fixture.innerHTML = '<div id=\"a\"><div id=\"b\"></div></div>';\n\n    assert.equal(\n      axe.utils.nodeSorter({ actualNode: $id('a') }, { actualNode: $id('b') }),\n      -1\n    );\n  });\n\n  it('should return 1 if b comes before a', function () {\n    fixture.innerHTML = '<div id=\"b\"></div><div id=\"a\"></div>';\n\n    assert.equal(\n      axe.utils.nodeSorter({ actualNode: $id('a') }, { actualNode: $id('b') }),\n      1\n    );\n  });\n\n  it('should return 1 if b comes before a - nested', function () {\n    fixture.innerHTML = '<div id=\"b\"><div id=\"a\"></div></div>';\n\n    assert.equal(\n      axe.utils.nodeSorter({ actualNode: $id('a') }, { actualNode: $id('b') }),\n      1\n    );\n  });\n\n  it('should return 0 if a === b', function () {\n    fixture.innerHTML = '<div id=\"a\"></div>';\n\n    assert.equal(\n      axe.utils.nodeSorter({ actualNode: $id('a') }, { actualNode: $id('a') }),\n      0\n    );\n  });\n});\n"
  },
  {
    "path": "test/core/utils/normalize-run-options.js",
    "content": "describe('axe.utils.normalizeRunOptions', () => {\n  const mockChecks = [\n    {\n      id: 'positive1-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'positive2-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'negative1-check1',\n      evaluate: () => true\n    },\n    {\n      id: 'positive3-check1',\n      evaluate: () => true\n    }\n  ];\n\n  const mockRules = [\n    {\n      id: 'positive1',\n      selector: 'input',\n      tags: ['positive'],\n      any: [\n        {\n          id: 'positive1-check1'\n        }\n      ]\n    },\n    {\n      id: 'positive2',\n      selector: '#monkeys',\n      tags: ['positive'],\n      any: ['positive2-check1']\n    },\n    {\n      id: 'negative1',\n      selector: 'div',\n      tags: ['negative'],\n      none: ['negative1-check1']\n    },\n    {\n      id: 'positive3',\n      selector: 'blink',\n      tags: ['positive'],\n      any: ['positive3-check1']\n    }\n  ];\n  const Audit = axe._thisWillBeDeletedDoNotUse.base.Audit;\n  let axeLog;\n  let axeAudit;\n  let audit;\n  beforeEach(() => {\n    axeLog = axe.log;\n    axeAudit = axe._audit;\n    audit = new Audit();\n    axe._audit = audit;\n    mockRules.forEach(function (r) {\n      audit.addRule(r);\n    });\n    mockChecks.forEach(function (c) {\n      audit.addCheck(c);\n    });\n  });\n  afterEach(() => {\n    axe.log = axeLog;\n    axe._audit = axeAudit;\n  });\n\n  it('returns the options object when it is valid', () => {\n    const opt = {\n      runOnly: {\n        type: 'rule',\n        values: ['positive1', 'positive2']\n      },\n      rules: {\n        negative1: { enabled: false }\n      }\n    };\n    assert.equal(axe.utils.normalizeRunOptions(opt), opt);\n  });\n\n  it('allows `value` as alternative to `values`', () => {\n    const opt = {\n      runOnly: {\n        type: 'rule',\n        value: ['positive1', 'positive2']\n      }\n    };\n    const out = axe.utils.normalizeRunOptions(opt);\n    assert.deepEqual(out.runOnly.values, ['positive1', 'positive2']);\n    assert.isUndefined(out.runOnly.value);\n  });\n\n  it('allows type: rules as an alternative to type: rule', () => {\n    const opt = {\n      runOnly: {\n        type: 'rules',\n        values: ['positive1', 'positive2']\n      }\n    };\n    assert.equal(axe.utils.normalizeRunOptions(opt).runOnly.type, 'rule');\n  });\n\n  it('allows type: tags as an alternative to type: tag', () => {\n    const opt = {\n      runOnly: {\n        type: 'tags',\n        values: ['positive']\n      }\n    };\n    assert.equal(axe.utils.normalizeRunOptions(opt).runOnly.type, 'tag');\n  });\n\n  it('allows type: undefined as an alternative to type: tag', () => {\n    const opt = {\n      runOnly: {\n        values: ['positive']\n      }\n    };\n    assert.equal(axe.utils.normalizeRunOptions(opt).runOnly.type, 'tag');\n  });\n\n  it('allows runOnly as an array as an alternative to type: tag', () => {\n    const opt = { runOnly: ['positive', 'negative'] };\n    const out = axe.utils.normalizeRunOptions(opt);\n    assert.equal(out.runOnly.type, 'tag');\n    assert.deepEqual(out.runOnly.values, ['positive', 'negative']);\n  });\n\n  it('allows runOnly as an array as an alternative to type: rule', () => {\n    const opt = { runOnly: ['positive1', 'negative1'] };\n    const out = axe.utils.normalizeRunOptions(opt);\n    assert.equal(out.runOnly.type, 'rule');\n    assert.deepEqual(out.runOnly.values, ['positive1', 'negative1']);\n  });\n\n  it('allows runOnly as a string as an alternative to an array', () => {\n    const opt = { runOnly: 'positive1' };\n    const out = axe.utils.normalizeRunOptions(opt);\n    assert.equal(out.runOnly.type, 'rule');\n    assert.deepEqual(out.runOnly.values, ['positive1']);\n  });\n\n  it('throws an error if runOnly contains both rules and tags', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: ['positive', 'negative1']\n      });\n    });\n  });\n\n  it('defaults runOnly to type: tag', () => {\n    const opt = { runOnly: ['fakeTag'] };\n    const out = axe.utils.normalizeRunOptions(opt);\n    assert.equal(out.runOnly.type, 'tag');\n    assert.deepEqual(out.runOnly.values, ['fakeTag']);\n  });\n\n  it('throws an error runOnly.values not an array', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: {\n          type: 'rule',\n          values: { badProp: 'badValue' }\n        }\n      });\n    });\n  });\n\n  it('throws an error runOnly.values an empty', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: {\n          type: 'rule',\n          values: []\n        }\n      });\n    });\n  });\n\n  it('throws an error runOnly.type is unknown', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: {\n          type: 'something-else',\n          values: ['wcag2aa']\n        }\n      });\n    });\n  });\n\n  it('throws an error when option.runOnly has an unknown rule', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: {\n          type: 'rule',\n          values: ['frakeRule']\n        }\n      });\n    });\n  });\n\n  it(\"doesn't throw an error when option.runOnly has an unknown tag\", () => {\n    assert.doesNotThrow(() => {\n      axe.utils.normalizeRunOptions({\n        runOnly: {\n          type: 'tags',\n          values: ['fakeTag']\n        }\n      });\n    });\n  });\n\n  it('throws an error when option.rules has an unknown rule', () => {\n    assert.throws(() => {\n      axe.utils.normalizeRunOptions({\n        rules: {\n          fakeRule: { enabled: false }\n        }\n      });\n    });\n  });\n\n  it('logs an issue when a tag is unknown', () => {\n    let message = '';\n    axe.log = function (m) {\n      message = m;\n    };\n    axe.utils.normalizeRunOptions({\n      runOnly: {\n        type: 'tags',\n        values: ['unknown-tag']\n      }\n    });\n    assert.include(message, 'Could not find tags');\n  });\n\n  it('logs no issues for unknown WCAG level tags', () => {\n    let message = '';\n    axe.log = function (m) {\n      message = m;\n    };\n    axe.utils.normalizeRunOptions({\n      runOnly: {\n        type: 'tags',\n        values: ['wcag23aaa']\n      }\n    });\n    assert.isEmpty(message);\n  });\n\n  it('logs an issue when a tag is unknown, together with a wcag level tag', () => {\n    let message = '';\n    axe.log = function (m) {\n      message = m;\n    };\n    axe.utils.normalizeRunOptions({\n      runOnly: {\n        type: 'tags',\n        values: ['wcag23aaa', 'unknwon-tag']\n      }\n    });\n    assert.include(message, 'Could not find tags');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/object-has-own.js",
    "content": "describe('axe.utils.objectHasOwn', () => {\n  const objectHasOwn = axe.utils.objectHasOwn;\n\n  it('is true for an object with a property', () => {\n    assert.isTrue(objectHasOwn({ prop: true }, 'prop'));\n  });\n\n  it('is false for an object without a property', () => {\n    assert.isFalse(objectHasOwn({}, 'prop'));\n  });\n\n  it('is false for non-objects', () => {\n    assert.isFalse(objectHasOwn('string', 'prop'));\n    assert.isFalse(objectHasOwn(1, 'prop'));\n    assert.isFalse(objectHasOwn([], 'prop'));\n    assert.isFalse(objectHasOwn(null, 'prop'));\n  });\n\n  it('is false if the property comes from the prototype', () => {\n    assert.isFalse(objectHasOwn(Object.create({ prop: true }), 'prop'));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/parse-crossorigin-stylesheet.js",
    "content": "describe('axe.utils.parseCrossOriginStylesheet', function () {\n  'use strict';\n\n  var dynamicDoc;\n  var convertDataToStylesheet;\n\n  beforeEach(function () {\n    dynamicDoc = document.implementation.createHTMLDocument(\n      'Dynamic document for testing axe.utils.parseCrossOriginStylesheet'\n    );\n    convertDataToStylesheet = axe.utils.getStyleSheetFactory(dynamicDoc);\n  });\n\n  afterEach(function () {\n    dynamicDoc = undefined;\n    convertDataToStylesheet = undefined;\n  });\n\n  it('returns cross-origin stylesheet', function (done) {\n    var importUrl =\n      'https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css';\n    var options = {\n      rootNode: document,\n      shadowId: undefined,\n      convertDataToStylesheet: convertDataToStylesheet,\n      rootIndex: 1\n    };\n    var priority = [1, 0];\n    var importedUrls = [];\n    var isCrossOriginRequest = true;\n\n    axe.utils\n      .parseCrossOriginStylesheet(\n        importUrl,\n        options,\n        priority,\n        importedUrls,\n        isCrossOriginRequest\n      )\n      .then(function (data) {\n        assert.isDefined(data);\n        assert.isDefined(data.sheet);\n\n        assert.equal(data.isCrossOrigin, isCrossOriginRequest);\n        assert.deepEqual(data.priority, priority);\n\n        assert.property(data.sheet, 'cssRules');\n        assert.isAtLeast(data.sheet.cssRules.length, 1);\n\n        axe.testUtils.assertStylesheet(\n          data.sheet,\n          '.container',\n          '.container { position: relative; width: 100%; max-width: 960px; margin: 0px auto; padding: 0px 20px; box-sizing: border-box; }'\n        );\n        done();\n      })\n      .catch(function (err) {\n        done(err);\n      });\n  });\n\n  it('rejects when given url to fetch is not found', function (done) {\n    this.timeout(axe.constants.preload.timeout + 1000);\n\n    var importUrl =\n      'https://make-up-a-website-that-does-not-exist.com/style.css';\n    var options = {\n      rootNode: document,\n      shadowId: undefined,\n      convertDataToStylesheet: convertDataToStylesheet,\n      rootIndex: 1\n    };\n    var priority = [1, 0];\n    var importedUrls = [];\n    var isCrossOriginRequest = true;\n    axe.utils\n      .parseCrossOriginStylesheet(\n        importUrl,\n        options,\n        priority,\n        importedUrls,\n        isCrossOriginRequest\n      )\n      .then(function () {\n        done(\n          new Error('Expected axe.utils.parseCrossOriginStylesheet to reject.')\n        );\n      })\n      .catch(function (err) {\n        assert.isNotNull(err);\n        done();\n      });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/parse-sameorigin-stylesheet.js",
    "content": "describe('axe.utils.parseSameOriginStylesheet', () => {\n  let stylesForPage;\n  const styleSheets = {\n    emptyStyleTag: {\n      id: 'emptyStyleTag',\n      text: ''\n    },\n    styleTagWithOneImport: {\n      id: 'styleTagWithOneImport',\n      text: '@import \"../integration/full/preload-cssom/base.css\";'\n    },\n    inlineStyle: {\n      id: 'inlineStyle',\n      text: '.inline-css { font-weight:normal; }'\n    }\n  };\n  let dynamicDoc;\n  let convertDataToStylesheet;\n\n  beforeEach(() => {\n    dynamicDoc = document.implementation.createHTMLDocument(\n      'Dynamic document for testing axe.utils.parseSameOriginStylesheet'\n    );\n    convertDataToStylesheet = axe.utils.getStyleSheetFactory(dynamicDoc);\n  });\n\n  afterEach(done => {\n    dynamicDoc = undefined;\n    convertDataToStylesheet = undefined;\n    axe.testUtils.removeStyleSheets(stylesForPage).then(() => {\n      done();\n      stylesForPage = undefined;\n    });\n  });\n\n  it('returns empty results when given sheet has no cssRules', done => {\n    // add style that has no styles\n    stylesForPage = [styleSheets.emptyStyleTag];\n\n    axe.testUtils.addStyleSheets(stylesForPage).then(() => {\n      // get recently added sheet\n      const sheet = Array.from(document.styleSheets).filter(styleSheet => {\n        return styleSheet.ownerNode.id === styleSheets.emptyStyleTag.id;\n      })[0];\n      // parse sheet\n      const options = {\n        rootNode: document,\n        shadowId: undefined,\n        convertDataToStylesheet: convertDataToStylesheet\n      };\n      const priority = [1, 0];\n      const importedUrls = [];\n      const isCrossOriginRequest = false;\n      axe.utils\n        .parseSameOriginStylesheet(\n          sheet,\n          options,\n          priority,\n          importedUrls,\n          false\n        )\n        .then(function (data) {\n          assert.isDefined(data);\n          assert.isDefined(data.sheet);\n          assert.equal(data.isCrossOrigin, isCrossOriginRequest);\n          assert.deepEqual(data.priority, priority);\n          assert.property(data.sheet, 'cssRules');\n          assert.isTrue(data.sheet.cssRules.length === 0);\n          done();\n        });\n    });\n  });\n\n  it('returns @import rule specified in the stylesheet', done => {\n    // add style that has @import style\n    stylesForPage = [styleSheets.styleTagWithOneImport];\n\n    axe.testUtils.addStyleSheets(stylesForPage).then(() => {\n      // get recently added sheet\n      const sheet = Array.from(document.styleSheets).filter(styleSheet => {\n        return styleSheet.ownerNode.id === styleSheets.styleTagWithOneImport.id;\n      })[0];\n      // parse sheet\n      const options = {\n        rootNode: document,\n        shadowId: undefined,\n        convertDataToStylesheet: convertDataToStylesheet\n      };\n      const priority = [1, 0];\n      const importedUrls = [];\n      const isCrossOriginRequest = false;\n      axe.utils\n        .parseSameOriginStylesheet(\n          sheet,\n          options,\n          priority,\n          importedUrls,\n          false\n        )\n        .then(function (data) {\n          assert.isDefined(data);\n\n          const parsedImportData = data[0];\n          assert.isDefined(parsedImportData.sheet);\n          assert.equal(parsedImportData.isCrossOrigin, isCrossOriginRequest);\n          // as @import is a style with in @imported sheet, an additional priority is appended.\n          assert.deepEqual(parsedImportData.priority, [1, 0, 0]);\n          assert.property(parsedImportData.sheet, 'cssRules');\n          assert.isAtLeast(parsedImportData.sheet.cssRules.length, 1);\n          axe.testUtils.assertStylesheet(\n            parsedImportData.sheet,\n            '.style-from-base-css',\n            '.style-from-base-css {font-size: 100%; }'\n          );\n          done();\n        });\n    });\n  });\n\n  it('returns inline style specified in the stylesheet', done => {\n    // add style that has @import style\n    stylesForPage = [styleSheets.inlineStyle];\n\n    axe.testUtils.addStyleSheets(stylesForPage).then(() => {\n      // get recently added sheet\n      const sheet = Array.from(document.styleSheets).filter(styleSheet => {\n        return styleSheet.ownerNode.id === styleSheets.inlineStyle.id;\n      })[0];\n      // parse sheet\n      const options = {\n        rootNode: document,\n        shadowId: undefined,\n        convertDataToStylesheet: convertDataToStylesheet\n      };\n      const priority = [1, 0];\n      const importedUrls = [];\n      const isCrossOriginRequest = false;\n      axe.utils\n        .parseSameOriginStylesheet(\n          sheet,\n          options,\n          priority,\n          importedUrls,\n          false\n        )\n        .then(function (data) {\n          assert.isDefined(data);\n          assert.isDefined(data.sheet);\n          assert.equal(data.isCrossOrigin, isCrossOriginRequest);\n          assert.deepEqual(data.priority, [1, 0]);\n          assert.property(data.sheet, 'cssRules');\n          assert.isAtLeast(data.sheet.cssRules.length, 1);\n          axe.testUtils.assertStylesheet(\n            data.sheet,\n            '.inline-css',\n            '.inline-css { font-weight:normal; }'\n          );\n          done();\n        });\n    });\n  });\n\n  /**\n   * Note:\n   * Only single workflow of resolving either the `@import` or `inline` styles can be tested here.\n   * Multiple resolutions from a given stylesheet containing a combination of styles are test as integration tests.\n   * See: `/tests/full/integration/preload-cssom.html`\n   */\n});\n"
  },
  {
    "path": "test/core/utils/parse-tabindex.js",
    "content": "describe('axe.utils.parseTabindex', function () {\n  'use strict';\n\n  it('should return 0 for \"0\"', function () {\n    assert.strictEqual(axe.utils.parseTabindex('0'), 0);\n  });\n\n  it('should return 1 for \"+1\"', function () {\n    assert.strictEqual(axe.utils.parseTabindex('+1'), 1);\n  });\n\n  it('should return -1 for \"-1\"', function () {\n    assert.strictEqual(axe.utils.parseTabindex('-1'), -1);\n  });\n\n  it('should return null for null', function () {\n    assert.strictEqual(axe.utils.parseTabindex(null), null);\n  });\n\n  it('should return null for an empty string', function () {\n    assert.strictEqual(axe.utils.parseTabindex(''), null);\n  });\n\n  it('should return null for a whitespace string', function () {\n    assert.strictEqual(axe.utils.parseTabindex('   '), null);\n  });\n\n  it('should return null for non-numeric strings', function () {\n    assert.strictEqual(axe.utils.parseTabindex('abc'), null);\n  });\n\n  it('should return the first valid digit(s) for decimal numbers', function () {\n    assert.strictEqual(axe.utils.parseTabindex('2.5'), 2);\n  });\n\n  it('should return 123 for \"123abc\"', function () {\n    assert.strictEqual(axe.utils.parseTabindex('123abc'), 123);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/performance-timer.js",
    "content": "describe('performance timer', () => {\n  const { performanceTimer } = axe.utils;\n  const originalLog = performanceTimer._log;\n  let messages = [];\n  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));\n\n  // Browser sleeps can slop a bit (in either direction) to sync with other work on the main thread.\n  const ANIMATION_FRAME_TOLERANCE_MS = 17; // Math.ceil(time per frame at 60Hz)\n\n  const getNumber = msg => {\n    const match = msg.match(/([0-9.]+)ms/);\n    return match ? parseFloat(match[1]) : 0;\n  };\n\n  beforeEach(() => {\n    performanceTimer._log = msg => {\n      messages.push(msg);\n    };\n  });\n\n  afterEach(async () => {\n    try {\n      performanceTimer.end();\n    } catch {\n      // ignore - performanceTimer.end() was called in a test instead\n    }\n    try {\n      performanceTimer.auditEnd();\n    } catch {\n      // ignore - performanceTimer.auditEnd() was called in a test instead\n    }\n    performanceTimer._log = originalLog;\n    messages = [];\n    // Force a next tick in the test to avoid flakiness\n    await sleep(0);\n  });\n\n  it('warns using performanceTimer before axe started', () => {\n    performanceTimer.mark('foo_start');\n    performanceTimer.mark('foo_end');\n    performanceTimer.measure('foo', 'foo_start', 'foo_end');\n    performanceTimer.logMeasures('foo');\n    assert.equal(messages.length, 1);\n    assert.match(\n      messages[0],\n      /Axe must be started before using performanceTimer/\n    );\n  });\n\n  it('measures time elapsed between marks', async () => {\n    performanceTimer.start();\n\n    const timestampPreMarkStart = performance.now();\n    performanceTimer.mark('foo_start');\n    const timestampPostMarkStart = performance.now();\n\n    await sleep(100);\n\n    const timestampPreMarkEnd = performance.now();\n    performanceTimer.mark('foo_end');\n    const timestampPostMarkEnd = performance.now();\n\n    performanceTimer.measure('foo', 'foo_start', 'foo_end');\n    performanceTimer.logMeasures('foo');\n\n    assert.equal(messages.length, 1);\n    const actual = getNumber(messages[0]);\n\n    assert.isAtLeast(actual, 100 - ANIMATION_FRAME_TOLERANCE_MS);\n\n    // The actual value might be significantly >100ms in a browser, especially on a\n    // CI agent with a slow CPU, but should be consistent with performance.now()\n    const maxExpected = timestampPostMarkEnd - timestampPreMarkStart;\n    const minExpected = timestampPreMarkEnd - timestampPostMarkStart;\n    assert.isAtLeast(actual, minExpected);\n    assert.isAtMost(actual, maxExpected);\n  });\n\n  it('measures time elapsed if auditStart() was called', () => {\n    performanceTimer.auditStart();\n    performanceTimer.mark('foo_start');\n    performanceTimer.mark('foo_end');\n    performanceTimer.measure('foo', 'foo_start', 'foo_end');\n    performanceTimer.logMeasures('foo');\n\n    assert.equal(messages.length, 1);\n    assert.match(messages[0], /Measure foo took [0-9.]+ms/);\n  });\n\n  it('measures the time axe takes from .start() to .end()', () => {\n    performanceTimer.start();\n    performanceTimer.end();\n    assert.equal(messages.length, 1);\n    assert.match(messages[0], /Measure axe took [0-9.]+ms/);\n  });\n\n  it('measures the time axe takes from .auditStart() to .auditEnd()', () => {\n    performanceTimer.auditStart();\n    performanceTimer.auditEnd();\n    assert.equal(messages.length, 1);\n    assert.match(messages[0], /Measure audit_start_to_end took [0-9.]+ms/);\n  });\n\n  describe('.measure', () => {\n    it('logs an error if the start mark is not present', () => {\n      performanceTimer.mark('foo_end');\n      performanceTimer.measure('foo', 'foo_start', 'foo_end');\n      assert.equal(messages.length, 1);\n      // non-specific message, Firefox has a different message from Chromium\n      assert.match(messages[0], /foo_start/);\n    });\n\n    it('logs an error if the end mark is not present', () => {\n      performanceTimer.mark('foo_start');\n      performanceTimer.measure('foo', 'foo_start', 'foo_end');\n      assert.equal(messages.length, 1);\n      // non-specific message, Firefox has a different message from Chromium\n      assert.match(messages[0], /foo_end/);\n    });\n  });\n\n  describe('logMeasures', () => {\n    beforeEach(() => {\n      performanceTimer.start();\n    });\n\n    it('logs a specific measure when provided', () => {\n      performanceTimer.mark('foo_start');\n      performanceTimer.mark('foo_end');\n      performanceTimer.measure('foo', 'foo_start', 'foo_end');\n\n      performanceTimer.mark('bar_start');\n      performanceTimer.mark('bar_end');\n      performanceTimer.measure('bar', 'bar_start', 'bar_end');\n\n      // Mot measured, should not show up\n      performanceTimer.mark('baz_start');\n      performanceTimer.mark('baz_end');\n\n      performanceTimer.logMeasures('foo');\n      assert.equal(messages.length, 1);\n      assert.match(messages[0], /Measure foo took [0-9.]+ms/);\n    });\n\n    it('logs all measures if no measure name is provided', () => {\n      performanceTimer.mark('foo_start');\n      performanceTimer.mark('foo_end');\n      performanceTimer.measure('foo', 'foo_start', 'foo_end');\n\n      performanceTimer.mark('bar_start');\n      performanceTimer.mark('bar_end');\n      performanceTimer.measure('bar', 'bar_start', 'bar_end');\n\n      // Mot measured, should not show up\n      performanceTimer.mark('baz_start');\n      performanceTimer.mark('baz_end');\n\n      performanceTimer.logMeasures();\n      assert.equal(messages.length, 2);\n      assert.match(messages[0], /Measure foo took [0-9.]+ms/);\n      assert.match(messages[1], /Measure bar took [0-9.]+ms/);\n    });\n  });\n\n  describe('timeElapsed', () => {\n    it('returns the time elapsed since axe started', async () => {\n      const timestampPreStart = performance.now();\n      performanceTimer.start();\n      const timestampPostStart = performance.now();\n\n      await sleep(100);\n\n      const timestampPreTimeElapsed = performance.now();\n      const actual = performanceTimer.timeElapsed();\n      const timestampPostTimeElapsed = performance.now();\n\n      assert.isAtLeast(actual, 100 - ANIMATION_FRAME_TOLERANCE_MS);\n      const maxExpected = timestampPostTimeElapsed - timestampPreStart;\n      const minExpected = timestampPreTimeElapsed - timestampPostStart;\n      assert.isAtLeast(actual, minExpected);\n      assert.isAtMost(actual, maxExpected);\n    });\n\n    it('returns the time elapsed since auditStart() was called', async () => {\n      const timestampPreStart = performance.now();\n      performanceTimer.auditStart();\n      const timestampPostStart = performance.now();\n\n      await sleep(100);\n\n      const timestampPreTimeElapsed = performance.now();\n      const actual = performanceTimer.timeElapsed();\n      const timestampPostTimeElapsed = performance.now();\n\n      assert.isAtLeast(actual, 100 - ANIMATION_FRAME_TOLERANCE_MS);\n      const maxExpected = timestampPostTimeElapsed - timestampPreStart;\n      const minExpected = timestampPreTimeElapsed - timestampPostStart;\n      assert.isAtLeast(actual, minExpected);\n      assert.isAtMost(actual, maxExpected);\n    });\n\n    it('does not use auditStart if axe started', async () => {\n      const timestampPreStart = performance.now();\n      performanceTimer.start();\n      const timestampPostStart = performance.now();\n\n      await sleep(100);\n      performanceTimer.auditStart(); // Should be ignored\n\n      const timestampPreTimeElapsed = performance.now();\n      const actual = performanceTimer.timeElapsed();\n      const timestampPostTimeElapsed = performance.now();\n\n      assert.isAtLeast(actual, 100 - ANIMATION_FRAME_TOLERANCE_MS);\n      const maxExpected = timestampPostTimeElapsed - timestampPreStart;\n      const minExpected = timestampPreTimeElapsed - timestampPostStart;\n      assert.isAtLeast(actual, minExpected);\n      assert.isAtMost(actual, maxExpected);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/pollyfills.elements-from-point.js",
    "content": "describe('document.elementsFromPoint pollyfills', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  afterEach(function () {\n    document.getElementById('fixture').innerHTML = '';\n  });\n\n  it('ensures document.elementsFromPoint is always there', function () {\n    assert.isFunction(document.elementsFromPoint);\n  });\n\n  it('returns document.elementsFromPoint if it is set', function () {\n    var orig = document.elementsFromPoint;\n    document.elementsFromPoint = function () {\n      return 123;\n    };\n\n    var elmFromPt = axe.utils.pollyfillElementsFromPoint();\n    assert.equal(elmFromPt(), 123);\n    document.elementsFromPoint = orig;\n  });\n\n  it('returns document.msElementsFromPoint if elementsFromPoint is undefined', function () {\n    var orig = document.elementsFromPoint;\n    var msOrig = document.msElementsFromPoint;\n\n    document.elementsFromPoint = undefined;\n    document.msElementsFromPoint = function () {\n      return 123;\n    };\n\n    var elmFromPt = axe.utils.pollyfillElementsFromPoint();\n    assert.equal(elmFromPt(), 123);\n\n    document.elementsFromPoint = orig;\n    document.msElementsFromPoint = msOrig;\n  });\n\n  it('returns the pollyfill no native function is available', function () {\n    var orig = document.elementsFromPoint;\n    var msOrig = document.msElementsFromPoint;\n\n    document.elementsFromPoint = undefined;\n    document.msElementsFromPoint = undefined;\n\n    var elmFromPt = axe.utils.pollyfillElementsFromPoint();\n    assert.isFunction(elmFromPt);\n\n    document.elementsFromPoint = orig;\n    document.msElementsFromPoint = msOrig;\n  });\n\n  describe('pollyfill function', function () {\n    var orig, msOrig;\n    before(function () {\n      orig = document.elementsFromPoint;\n      msOrig = document.msElementsFromPoint;\n\n      document.elementsFromPoint = undefined;\n      document.msElementsFromPoint = undefined;\n\n      document.elementsFromPoint = axe.utils.pollyfillElementsFromPoint();\n    });\n\n    after(function () {\n      document.elementsFromPoint = orig;\n      document.msElementsFromPoint = msOrig;\n    });\n\n    it('should return positioned elements properly', function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"position: absolute; top: 0px; left: 0px; height: 100px; ' +\n        'width: 90px; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '<div id=\"pos\" style=\"position: absolute; top: 50px; left: 40px; height: 40px; ' +\n        'width: 30px; background-color: rgba(0, 128, 0, 0.5);\"></div>' +\n        '<div id=\"parent\" style=\"position: absolute; top: 0px; left: 0px; height: 40px; ' +\n        'width: 30px; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '<div id=\"target\" style=\"position: absolute; top: 60px; left: 45px; height: 20px; ' +\n        'width: 15px; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '</div></div></div>';\n      var target = fixture.querySelector('#target');\n      var pos = fixture.querySelector('#pos');\n      var container = fixture.querySelector('#container');\n\n      target.scrollIntoView();\n      var rect = target.getBoundingClientRect();\n\n      var visualParents = document.elementsFromPoint(\n        Math.ceil(rect.left + 1),\n        Math.ceil(rect.top + 1)\n      );\n      assert.deepEqual(visualParents.slice(0, 3), [target, pos, container]);\n    });\n\n    it('should return inline elements properly', function () {\n      fixture.innerHTML =\n        '<div id=\"container\" style=\"position: absolute; top: 0px; left: 0px; height: 100px; ' +\n        'width: 90px; background-color: rgba(0, 128, 0, 0.5);\">' +\n        '<span id=\"pos\" style=\"position: absolute; top: 60px; left: 45px;' +\n        'background-color: rgba(0, 128, 0, 0.5);\">Text goes here</span>' +\n        '<span id=\"parent\" style=\"position: absolute; top: 0px; left: 0px;' +\n        'background-color: rgba(0, 128, 0, 0.5);\">' +\n        '<span id=\"target\" style=\"position: absolute; top: 60px; left: 45px;' +\n        'background-color: rgba(0, 128, 0, 0.5);\">Text goes here' +\n        '</span></span></div>';\n      var target = fixture.querySelector('#target');\n      var pos = fixture.querySelector('#pos');\n      var container = fixture.querySelector('#container');\n\n      target.scrollIntoView();\n      var rect = target.getBoundingClientRect();\n\n      var visualParents = document.elementsFromPoint(\n        Math.ceil(rect.left + 1),\n        Math.ceil(rect.top + 1)\n      );\n      assert.deepEqual(visualParents.slice(0, 3), [target, pos, container]);\n    });\n\n    it('should return normal flow elements properly', function () {\n      fixture.innerHTML =\n        '<div id=\"parent\" style=\"background-color: rgba(0, 128, 0, 0.5); height: 40px; width: 30px;\">' +\n        '<div id=\"target\" style=\"background-color: rgba(0, 128, 0, 0.5); height: 20px; width: 15px;\">' +\n        '</div></div>';\n      var target = fixture.querySelector('#target');\n      var parent = fixture.querySelector('#parent');\n\n      target.scrollIntoView();\n      var rect = target.getBoundingClientRect();\n\n      var visualParents = document.elementsFromPoint(\n        Math.ceil(rect.left),\n        Math.ceil(rect.top)\n      );\n      assert.deepEqual(visualParents.slice(0, 3), [target, parent, fixture]);\n    });\n\n    it('returns elements with negative z-index after the body', function () {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"z-index:-1; position:absolute;\">Target!</div>' +\n        '<div id=\"sibling\">Some text</div>';\n      var target = fixture.querySelector('#target');\n\n      target.scrollIntoView();\n      var rect = target.getBoundingClientRect();\n\n      var visualParents = document.elementsFromPoint(\n        Math.ceil(rect.left),\n        Math.ceil(rect.top)\n      );\n\n      // Last two element should be: body, target (due to z-index:-1), html\n      assert.deepEqual(visualParents.slice(-3), [\n        document.body,\n        target,\n        document.documentElement\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/preload-cssom.js",
    "content": "/**\n * NOTE:\n * `document.styleSheets` does not recognize dynamically injected stylesheets after `load` via `beforeEach`/ `before`,\n * so tests for disabled and external stylesheets are done in `integration` tests\n * Refer Directory: `./test/full/preload-cssom/**.*`\n */\ndescribe('axe.utils.preloadCssom', function () {\n  'use strict';\n\n  var treeRoot;\n\n  function addStyleToHead() {\n    var css = 'html {font-size: inherit;}';\n    var head = document.head || document.getElementsByTagName('head')[0];\n    var style = document.createElement('style');\n    style.id = 'preloadCssomTestHeadSheet';\n    style.type = 'text/css';\n    style.appendChild(document.createTextNode(css));\n    head.appendChild(style);\n  }\n\n  function removeStyleFromHead() {\n    var s = document.getElementById('preloadCssomTestHeadSheet');\n    if (s) {\n      s.parentNode.removeChild(s);\n    }\n  }\n\n  beforeEach(function () {\n    addStyleToHead();\n    treeRoot = axe._tree = axe.utils.getFlattenedTree(document);\n  });\n\n  afterEach(function () {\n    removeStyleFromHead();\n  });\n\n  it('returns CSSOM object containing an array of sheets', function (done) {\n    var actual = axe.utils.preloadCssom({ treeRoot: treeRoot });\n    actual\n      .then(function (cssom) {\n        assert.isAtLeast(cssom.length, 2);\n        done();\n      })\n      .catch(function (error) {\n        done(error);\n      });\n  });\n\n  it('returns CSSOM and ensure that each object have defined properties', function (done) {\n    var actual = axe.utils.preloadCssom({ treeRoot: treeRoot });\n    actual\n      .then(function (cssom) {\n        assert.isAtLeast(cssom.length, 2);\n        cssom.forEach(function (o) {\n          assert.hasAllKeys(o, [\n            'root',\n            'shadowId',\n            'sheet',\n            'isCrossOrigin',\n            'priority'\n          ]);\n        });\n        done();\n      })\n      .catch(function (error) {\n        done(error);\n      });\n  });\n\n  it('returns false if number of sheets returned does not match stylesheets defined in document', function (done) {\n    var actual = axe.utils.preloadCssom({ treeRoot: treeRoot });\n    actual\n      .then(function (cssom) {\n        assert.isFalse(cssom.length <= 1);\n        done();\n      })\n      .catch(function (error) {\n        done(error);\n      });\n  });\n\n  it('returns all stylesheets and ensure each sheet has property cssRules', function (done) {\n    var actual = axe.utils.preloadCssom({ treeRoot: treeRoot });\n    actual\n      .then(function (cssom) {\n        cssom.forEach(function (s) {\n          assert.isDefined(s.sheet);\n          assert.property(s.sheet, 'cssRules');\n        });\n        done();\n      })\n      .catch(function (error) {\n        done(error);\n      });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/preload-media.js",
    "content": "describe('axe.utils.preloadMedia', () => {\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n\n  it('returns empty array when there are no media nodes to be preloaded', async () => {\n    axe._tree = axe.utils.getFlattenedTree(document);\n\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns empty array when <audio> has no source', async () => {\n    fixtureSetup('<audio autoplay=\"true\" controls></audio>');\n\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns empty array when <video> has no source', async () => {\n    fixtureSetup('<video id=\"target\" autoplay=\"true\"><source src=\"\"/></video>');\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns empty array when media node does not preload', async () => {\n    fixtureSetup(`\n      <video id=\"target\" autoplay=\"true\" preload=\"none\">\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    `);\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns empty array when media node is muted', async () => {\n    fixtureSetup(`\n      <video id=\"target\" autoplay=\"true\" muted>\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    `);\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns empty array when media node is paused', async () => {\n    fixtureSetup(`\n      <video id=\"target\" autoplay=\"true\" paused>\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    `);\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 0);\n  });\n\n  it('returns media node (audio) after their metadata has been preloaded', async () => {\n    fixtureSetup(\n      '<audio src=\"/test/assets/moon-speech.mp3\" autoplay=\"true\" controls></audio>'\n    );\n\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 1);\n    assert.isTrue(result[0].readyState > 0);\n    assert.equal(Math.round(result[0].duration), 27);\n  });\n\n  it('returns media nodes (audio, video) after their metadata has been preloaded', async () => {\n    fixtureSetup(\n      // 1 audio elm\n      '<audio src=\"/test/assets/moon-speech.mp3\" autoplay=\"true\"></audio>' +\n        // 1 video elm\n        '<video autoplay=\"true\">' +\n        '<source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />' +\n        '<source src=\"/test/assets/video.webm\" type=\"video/webm\" />' +\n        '</video>'\n    );\n\n    const result = await axe.utils.preloadMedia({ treeRoot: axe._tree[0] });\n    assert.equal(result.length, 2);\n    assert.isTrue(result[0].readyState > 0);\n    assert.equal(Math.round(result[0].duration), 27);\n\n    assert.isTrue(result[1].readyState > 0);\n    assert.equal(Math.round(result[1].duration), 14);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/preload.js",
    "content": "describe('axe.utils.preload', function () {\n  'use strict';\n\n  var fixture = document.getElementById('fixture');\n\n  beforeEach(function () {\n    axe.setup(fixture);\n  });\n\n  it('returns `undefined` when `preload` option is set to false.', function (done) {\n    var options = {\n      preload: false\n    };\n    var actual = axe.utils.preload(options);\n    actual\n      .then(function (results) {\n        assert.isUndefined(results);\n        done();\n      })\n      .catch(function (error) {\n        done(error);\n      });\n  });\n\n  it('returns assets with `cssom`, verify result is same output from `preloadCssom` fn', function (done) {\n    var options = {\n      preload: {\n        assets: ['cssom']\n      }\n    };\n    var actual = axe.utils.preload(options);\n    actual\n      .then(function (results) {\n        assert.isDefined(results);\n        assert.property(results, 'cssom');\n\n        axe.utils.preloadCssom(options).then(function (resultFromPreloadCssom) {\n          assert.deepEqual(results.cssom, resultFromPreloadCssom);\n          done();\n        });\n      })\n      .catch(done);\n  });\n\n  describe('axe.utils.shouldPreload', function () {\n    it('should return true if preload configuration is valid', function () {\n      var actual = axe.utils.shouldPreload({\n        preload: {\n          assets: ['cssom']\n        }\n      });\n      assert.isTrue(actual);\n    });\n\n    it('should return true if preload is undefined', function () {\n      var actual = axe.utils.shouldPreload({\n        preload: undefined\n      });\n      assert.isTrue(actual);\n    });\n\n    it('should return true if preload is null', function () {\n      var actual = axe.utils.shouldPreload({\n        preload: null\n      });\n      assert.isTrue(actual);\n    });\n\n    it('should return true if preload is not set', function () {\n      var actual = axe.utils.shouldPreload({});\n      assert.isTrue(actual);\n    });\n\n    it('should return false if preload configuration is invalid', function () {\n      var options = {\n        preload: {\n          errorProperty: ['cssom']\n        }\n      };\n      var actual = axe.utils.shouldPreload(options);\n      assert.isFalse(actual);\n    });\n  });\n\n  describe('axe.utils.getPreloadConfig', function () {\n    it('should return default assets if preload configuration is not set', function () {\n      var actual = axe.utils.getPreloadConfig({}).assets;\n      var expected = ['cssom', 'media'];\n      assert.deepEqual(actual, expected);\n    });\n\n    it('should return default assets if preload options is set to true', function () {\n      var actual = axe.utils.getPreloadConfig({}).assets;\n      var expected = ['cssom', 'media'];\n      assert.deepEqual(actual, expected);\n    });\n\n    it('should return default timeout value if not configured', function () {\n      var actual = axe.utils.getPreloadConfig({}).timeout;\n      var expected = 10000;\n      assert.equal(actual, expected);\n    });\n\n    it('should throw error if requested asset type is not supported', function () {\n      var options = {\n        preload: {\n          assets: ['some-unsupported-asset']\n        }\n      };\n      var actual = function () {\n        axe.utils.getPreloadConfig(options);\n      };\n      var expected = Error;\n      assert.throws(actual, expected);\n    });\n\n    it('should remove any duplicate assets passed via preload configuration', function () {\n      var options = {\n        preload: {\n          assets: ['cssom', 'cssom']\n        }\n      };\n      var actual = axe.utils.getPreloadConfig(options);\n      assert.property(actual, 'assets');\n      assert.containsAllKeys(actual, ['assets', 'timeout']);\n      assert.lengthOf(actual.assets, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/process-message.js",
    "content": "describe('axe.utils.processMessage', function () {\n  'use strict';\n\n  var original = axe._audit;\n\n  beforeEach(function () {\n    axe._audit = {\n      data: {\n        incompleteFallbackMessage: 'fallback message'\n      }\n    };\n  });\n\n  after(function () {\n    axe._audit = original;\n  });\n\n  it('should replace a ${data}', function () {\n    var message = 'Hello ${data}';\n    var output = axe.utils.processMessage(message, 'World!');\n    assert.equal(output, 'Hello World!');\n  });\n\n  it('should replace a ${ data } (with whitespace)', function () {\n    var message = 'Hello ${ data }';\n    var output = axe.utils.processMessage(message, 'World!');\n    assert.equal(output, 'Hello World!');\n  });\n\n  it('should replace ${data.prop}', function () {\n    var message = 'Hello ${data.world}';\n    var output = axe.utils.processMessage(message, { world: 'World!' });\n    assert.equal(output, 'Hello World!');\n  });\n\n  it('should replace ${data.prop}', function () {\n    var message = 'Hello ${data.world}';\n    var output = axe.utils.processMessage(message, { world: undefined });\n    assert.equal(output, 'Hello ');\n  });\n\n  it('should replace ${ data.prop } (with whitespace)', function () {\n    var message = 'Hello ${ data.world }';\n    var output = axe.utils.processMessage(message, { world: 'World!' });\n    assert.equal(output, 'Hello World!');\n  });\n\n  describe('data is array', function () {\n    it('should replace ${data.values} with comma separated list of values', function () {\n      var message = 'Output: ${data.values}';\n      var output = axe.utils.processMessage(message, ['one', 'two']);\n      assert.equal(output, 'Output: one, two');\n    });\n\n    it('should handle singular message', function () {\n      var message = {\n        singular: 'Singular message: ${data.values}',\n        plural: 'Plural messages: ${ data.values }'\n      };\n      var output = axe.utils.processMessage(message, ['one']);\n      assert.equal(output, 'Singular message: one');\n    });\n\n    it('should handle plural message', function () {\n      var message = {\n        singular: 'Singular message: ${data.values}',\n        plural: 'Plural messages: ${ data.values }'\n      };\n      var output = axe.utils.processMessage(message, ['one', 'two']);\n      assert.equal(output, 'Plural messages: one, two');\n    });\n  });\n\n  describe('message is object', function () {\n    it('should handle message based on messageKey', function () {\n      var message = {\n        prop1: 'prop1 message',\n        prop2: 'prop2 message'\n      };\n      var output = axe.utils.processMessage(message, { messageKey: 'prop2' });\n      assert.equal(output, 'prop2 message');\n    });\n\n    it('should replace ${data}', function () {\n      var message = {\n        prop1: 'prop1 message',\n        prop2: '${data.world} message'\n      };\n      var output = axe.utils.processMessage(message, {\n        messageKey: 'prop2',\n        world: 'World!'\n      });\n      assert.equal(output, 'World! message');\n    });\n\n    it('should use default message', function () {\n      var message = {\n        default: 'default message',\n        prop1: 'prop1 message',\n        prop2: 'prop2 message'\n      };\n      var output = axe.utils.processMessage(message);\n      assert.equal(output, 'default message');\n    });\n\n    it('should use fallback message if no default', function () {\n      var message = {\n        prop1: 'prop1 message',\n        prop2: 'prop2 message'\n      };\n      var output = axe.utils.processMessage(message);\n      assert.equal(output, 'fallback message');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/publish-metadata.js",
    "content": "describe('axe.utils.publishMetaData', function () {\n  'use strict';\n\n  afterEach(function () {\n    axe._audit = null;\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.publishMetaData);\n  });\n\n  it('should pull data from rules from axe._audit.data', function () {\n    var expected = {\n      foo: 'bar',\n      bob: 'loblaw'\n    };\n\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: expected\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: []\n    };\n    axe.utils.publishMetaData(result);\n\n    assert.equal(result.foo, expected.foo);\n    assert.equal(result.bob, expected.bob);\n  });\n\n  it('should pull data from checks from axe._audit.data', function () {\n    var expected = {\n      foo: 'bar',\n      bob: 'loblaw'\n    };\n\n    axe._load({\n      rules: [],\n      data: {\n        checks: {\n          cats: expected\n        }\n      }\n    });\n\n    var result = {\n      id: 'foo',\n      nodes: [\n        {\n          any: [\n            {\n              id: 'cats'\n            }\n          ],\n          all: [],\n          none: []\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.equal(result.nodes[0].any[0].foo, expected.foo);\n    assert.equal(result.nodes[0].any[0].bar, expected.bar);\n  });\n\n  it('should execute messages', function () {\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              }\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              }\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              }\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: false,\n              id: 'cats-ANY'\n            }\n          ],\n          none: [\n            {\n              result: true,\n              id: 'cats-NONE'\n            }\n          ],\n          all: [\n            {\n              result: false,\n              id: 'cats-ALL'\n            }\n          ]\n        },\n        {\n          any: [\n            {\n              result: true,\n              id: 'cats-ANY'\n            }\n          ],\n          none: [\n            {\n              result: false,\n              id: 'cats-NONE'\n            }\n          ],\n          all: [\n            {\n              result: true,\n              id: 'cats-ALL'\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result, {\n      id: 'cats',\n      help: 'cats-rule',\n      tags: [],\n      nodes: [\n        {\n          any: [\n            {\n              result: false,\n              id: 'cats-ANY',\n              message: 'fail-ANY'\n            }\n          ],\n          none: [\n            {\n              result: true,\n              id: 'cats-NONE',\n              message: 'fail-NONE'\n            }\n          ],\n          all: [\n            {\n              result: false,\n              id: 'cats-ALL',\n              message: 'fail-ALL'\n            }\n          ]\n        },\n        {\n          any: [\n            {\n              result: true,\n              id: 'cats-ANY',\n              message: 'pass-ANY'\n            }\n          ],\n          none: [\n            {\n              result: false,\n              id: 'cats-NONE',\n              message: 'pass-NONE'\n            }\n          ],\n          all: [\n            {\n              result: true,\n              id: 'cats-ALL',\n              message: 'pass-ALL'\n            }\n          ]\n        }\n      ]\n    });\n  });\n\n  it('should return default incomplete message with no reason specified by the check', function () {\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              },\n              incomplete: {\n                'incomplete-NONE-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-NONE-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              },\n              incomplete: {\n                'incomplete-ANY-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-ANY-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              },\n              incomplete: {\n                'incomplete-ALL-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-ALL-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              data: {}\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              data: {}\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              data: {}\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result, {\n      id: 'cats',\n      help: 'cats-rule',\n      tags: [],\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              message: 'Fallback message for no reason',\n              data: {}\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              message: 'Fallback message for no reason',\n              data: {}\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              message: 'Fallback message for no reason',\n              data: {}\n            }\n          ]\n        }\n      ]\n    });\n  });\n\n  it('should fall back to a generic message if incomplete object fails', function () {\n    axe._load({\n      rules: [],\n      data: {\n        incompleteFallbackMessage: function () {\n          return 'Dogs are the best';\n        },\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              },\n              incomplete: {}\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              },\n              incomplete: {}\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              },\n              incomplete: {}\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              data: {}\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              data: {}\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              data: {}\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result, {\n      id: 'cats',\n      help: 'cats-rule',\n      tags: [],\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              message: 'Dogs are the best',\n              data: {}\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              message: 'Dogs are the best',\n              data: {}\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              message: 'Dogs are the best',\n              data: {}\n            }\n          ]\n        }\n      ]\n    });\n  });\n\n  it('should handle incomplete reasons', function () {\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              },\n              incomplete: {\n                'incomplete-NONE-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-NONE-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              },\n              incomplete: {\n                'incomplete-ANY-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-ANY-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              },\n              incomplete: {\n                'incomplete-ALL-reason1':\n                  \"We couldn't tell because of some reason\",\n                'incomplete-ALL-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              data: {\n                missingData: 'incomplete-ANY-reason1'\n              }\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              data: {\n                missingData: 'incomplete-NONE-reason1'\n              }\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              data: {\n                missingData: 'incomplete-ALL-reason1'\n              }\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result, {\n      id: 'cats',\n      help: 'cats-rule',\n      tags: [],\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              message: \"We couldn't tell because of some reason\",\n              data: {\n                missingData: 'incomplete-ANY-reason1'\n              }\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              message: \"We couldn't tell because of some reason\",\n              data: {\n                missingData: 'incomplete-NONE-reason1'\n              }\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              message: \"We couldn't tell because of some reason\",\n              data: {\n                missingData: 'incomplete-ALL-reason1'\n              }\n            }\n          ]\n        }\n      ]\n    });\n  });\n\n  it('should handle incomplete reasons with backwards compatibility', function () {\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              },\n              incomplete: {\n                'incomplete-NONE-reason1':\n                  \"We couldn't tell because of reason #1\",\n                'incomplete-NONE-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              },\n              incomplete: {\n                'incomplete-ANY-reason1':\n                  \"We couldn't tell because of reason #1\",\n                'incomplete-ANY-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              },\n              incomplete: {\n                'incomplete-ALL-reason1':\n                  \"We couldn't tell because of reason #1\",\n                'incomplete-ALL-reason2': 'Some other reason',\n                default: 'Fallback message for no reason'\n              }\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-ANY-reason1'\n                  }\n                ]\n              }\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-NONE-reason1'\n                  }\n                ]\n              }\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-ALL-reason1'\n                  }\n                ]\n              }\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result, {\n      id: 'cats',\n      help: 'cats-rule',\n      tags: [],\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY',\n              message: \"We couldn't tell because of reason #1\",\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-ANY-reason1'\n                  }\n                ]\n              }\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE',\n              message: \"We couldn't tell because of reason #1\",\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-NONE-reason1'\n                  }\n                ]\n              }\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL',\n              message: \"We couldn't tell because of reason #1\",\n              data: {\n                missingData: [\n                  {\n                    reason: 'incomplete-ALL-reason1'\n                  }\n                ]\n              }\n            }\n          ]\n        }\n      ]\n    });\n  });\n\n  it('should not modify base configuration', function () {\n    axe._load({\n      rules: [],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-PASS': {\n            failureMessage: function () {\n              return 'cats-check';\n            }\n          },\n          'cats-ANY': {\n            failureMessage: function () {\n              return 'cats-check2';\n            }\n          },\n          'cats-ALL': {\n            failureMessage: function () {\n              return 'cats-check2';\n            }\n          }\n        }\n      }\n    });\n    axe.utils.publishMetaData({\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: false,\n              id: 'cats-PASS'\n            }\n          ],\n          none: [\n            {\n              result: true,\n              id: 'cats-FAIL'\n            }\n          ],\n          all: [\n            {\n              result: false,\n              id: 'cats-ALL'\n            }\n          ]\n        }\n      ]\n    });\n\n    assert.isNotNull(axe._audit.data.checks['cats-PASS'].failureMessage);\n    assert.isNotNull(axe._audit.data.checks['cats-ANY'].failureMessage);\n    assert.isNotNull(axe._audit.data.checks['cats-ALL'].failureMessage);\n  });\n\n  it('should pull tags off rule object', function () {\n    var expected = {\n      foo: 'bar',\n      bob: 'loblaw'\n    };\n\n    axe._load({\n      rules: [\n        {\n          id: 'foo',\n          tags: ['hai']\n        }\n      ],\n      data: {\n        checks: {\n          cats: expected\n        }\n      }\n    });\n\n    var result = {\n      id: 'foo',\n      nodes: [\n        {\n          any: [\n            {\n              id: 'cats'\n            }\n          ],\n          all: [],\n          none: []\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result.tags, ['hai']);\n  });\n\n  describe('non-doT syntax', function () {\n    it('should process ${data} syntax', function () {\n      axe._load({\n        rules: [],\n        data: {\n          rules: {\n            cats: {\n              help: 'cats-rule'\n            }\n          },\n          checks: {\n            'cats-ANY': {\n              messages: {\n                pass: 'pass-ANY ${data}',\n                fail: 'fail-ANY ${data}'\n              }\n            }\n          }\n        }\n      });\n\n      var result = {\n        id: 'cats',\n        nodes: [\n          {\n            any: [\n              {\n                result: true,\n                id: 'cats-ANY',\n                data: 'cats'\n              }\n            ],\n            none: [],\n            all: []\n          }\n        ]\n      };\n\n      axe.utils.publishMetaData(result);\n      assert.deepEqual(result, {\n        id: 'cats',\n        help: 'cats-rule',\n        tags: [],\n        nodes: [\n          {\n            any: [\n              {\n                result: true,\n                id: 'cats-ANY',\n                message: 'pass-ANY cats',\n                data: 'cats'\n              }\n            ],\n            none: [],\n            all: []\n          }\n        ]\n      });\n    });\n\n    it('should return default incomplete message with no reason specified by the check', function () {\n      axe._load({\n        rules: [],\n        data: {\n          rules: {\n            cats: {\n              help: 'cats-rule'\n            }\n          },\n          checks: {\n            'cats-NONE': {\n              messages: {\n                fail: 'fail-NONE',\n                pass: 'pass-NONE',\n                incomplete: {\n                  'incomplete-NONE-reason1':\n                    \"We couldn't tell because of some reason\",\n                  'incomplete-NONE-reason2': 'Some other reason',\n                  default: 'Fallback message for no reason'\n                }\n              }\n            },\n            'cats-ANY': {\n              messages: {\n                fail: 'fail-ANY',\n                pass: 'pass-ANY',\n                incomplete: {\n                  'incomplete-ANY-reason1':\n                    \"We couldn't tell because of some reason\",\n                  'incomplete-ANY-reason2': 'Some other reason',\n                  default: 'Fallback message for no reason'\n                }\n              }\n            },\n            'cats-ALL': {\n              messages: {\n                fail: 'fail-ALL',\n                pass: 'pass-ALL',\n                incomplete: {\n                  'incomplete-ALL-reason1':\n                    \"We couldn't tell because of some reason\",\n                  'incomplete-ALL-reason2': 'Some other reason',\n                  default: 'Fallback message for no reason'\n                }\n              }\n            }\n          }\n        }\n      });\n\n      var result = {\n        id: 'cats',\n        nodes: [\n          {\n            any: [\n              {\n                result: undefined,\n                id: 'cats-ANY',\n                data: {}\n              }\n            ],\n            none: [\n              {\n                result: undefined,\n                id: 'cats-NONE',\n                data: {}\n              }\n            ],\n            all: [\n              {\n                result: undefined,\n                id: 'cats-ALL',\n                data: {}\n              }\n            ]\n          }\n        ]\n      };\n      axe.utils.publishMetaData(result);\n      assert.deepEqual(result, {\n        id: 'cats',\n        help: 'cats-rule',\n        tags: [],\n        nodes: [\n          {\n            any: [\n              {\n                result: undefined,\n                id: 'cats-ANY',\n                message: 'Fallback message for no reason',\n                data: {}\n              }\n            ],\n            none: [\n              {\n                result: undefined,\n                id: 'cats-NONE',\n                message: 'Fallback message for no reason',\n                data: {}\n              }\n            ],\n            all: [\n              {\n                result: undefined,\n                id: 'cats-ALL',\n                message: 'Fallback message for no reason',\n                data: {}\n              }\n            ]\n          }\n        ]\n      });\n    });\n\n    it('should fall back to a generic message if incomplete object fails', function () {\n      axe._load({\n        rules: [],\n        data: {\n          incompleteFallbackMessage: 'Dogs are the best',\n          rules: {\n            cats: {\n              help: 'cats-rule'\n            }\n          },\n          checks: {\n            'cats-NONE': {\n              messages: {\n                faiL: 'fail-NONE',\n                pass: 'pass-NONE',\n                incomplete: {}\n              }\n            },\n            'cats-ANY': {\n              messages: {\n                faiL: 'fail-ANY',\n                pass: 'pass-ANY',\n                incomplete: {}\n              }\n            },\n            'cats-ALL': {\n              messages: {\n                faiL: 'fail-ALL',\n                pass: 'pass-ALL',\n                incomplete: {}\n              }\n            }\n          }\n        }\n      });\n\n      var result = {\n        id: 'cats',\n        nodes: [\n          {\n            any: [\n              {\n                result: undefined,\n                id: 'cats-ANY',\n                data: {}\n              }\n            ],\n            none: [\n              {\n                result: undefined,\n                id: 'cats-NONE',\n                data: {}\n              }\n            ],\n            all: [\n              {\n                result: undefined,\n                id: 'cats-ALL',\n                data: {}\n              }\n            ]\n          }\n        ]\n      };\n      axe.utils.publishMetaData(result);\n      assert.deepEqual(result, {\n        id: 'cats',\n        help: 'cats-rule',\n        tags: [],\n        nodes: [\n          {\n            any: [\n              {\n                result: undefined,\n                id: 'cats-ANY',\n                message: 'Dogs are the best',\n                data: {}\n              }\n            ],\n            none: [\n              {\n                result: undefined,\n                id: 'cats-NONE',\n                message: 'Dogs are the best',\n                data: {}\n              }\n            ],\n            all: [\n              {\n                result: undefined,\n                id: 'cats-ALL',\n                message: 'Dogs are the best',\n                data: {}\n              }\n            ]\n          }\n        ]\n      });\n    });\n  });\n\n  it('should use fail message for rules with \"reviewOnFaill: true\"', function () {\n    axe._load({\n      rules: [\n        {\n          id: 'cats',\n          reviewOnFail: true\n        }\n      ],\n      data: {\n        rules: {\n          cats: {\n            help: function () {\n              return 'cats-rule';\n            }\n          }\n        },\n        checks: {\n          'cats-NONE': {\n            messages: {\n              fail: function () {\n                return 'fail-NONE';\n              },\n              pass: function () {\n                return 'pass-NONE';\n              }\n            }\n          },\n          'cats-ANY': {\n            messages: {\n              fail: function () {\n                return 'fail-ANY';\n              },\n              pass: function () {\n                return 'pass-ANY';\n              }\n            }\n          },\n          'cats-ALL': {\n            messages: {\n              fail: function () {\n                return 'fail-ALL';\n              },\n              pass: function () {\n                return 'pass-ALL';\n              }\n            }\n          }\n        }\n      }\n    });\n\n    var result = {\n      id: 'cats',\n      nodes: [\n        {\n          any: [\n            {\n              result: undefined,\n              id: 'cats-ANY'\n            }\n          ],\n          none: [\n            {\n              result: undefined,\n              id: 'cats-NONE'\n            }\n          ],\n          all: [\n            {\n              result: undefined,\n              id: 'cats-ALL'\n            }\n          ]\n        }\n      ]\n    };\n    axe.utils.publishMetaData(result);\n    assert.deepEqual(result.nodes, [\n      {\n        any: [\n          {\n            result: undefined,\n            id: 'cats-ANY',\n            message: 'fail-ANY'\n          }\n        ],\n        none: [\n          {\n            result: undefined,\n            id: 'cats-NONE',\n            message: 'fail-NONE'\n          }\n        ],\n        all: [\n          {\n            result: undefined,\n            id: 'cats-ALL',\n            message: 'fail-ALL'\n          }\n        ]\n      }\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/qsa.js",
    "content": "function setShadowId(vNode, shadowId) {\n  vNode.shadowId = shadowId;\n  for (var i = 0; i < vNode.children.length; i++) {\n    setShadowId(vNode.children[i], shadowId);\n  }\n}\n\nfunction getTestDom() {\n  'use strict';\n  var html = document.createElement('html');\n  html.innerHTML =\n    '' +\n    '<body>' +\n    '<div class=\"first\" data-a11yhero=\"faulkner\">' +\n    '<ul>' +\n    '<li class=\"breaking\"></li>' +\n    '<li class=\"breaking\"></li>' +\n    '</ul>' +\n    '</div>' +\n    '<div id=\"one\"></div>' +\n    '<div class=\"second third\">' +\n    '<ul>' +\n    '<li role=\"tab\" id=\"one\"></li>' +\n    '<li role=\"button\" id=\"one\"></li>' +\n    '</ul>' +\n    '</div>' +\n    '<span class=\"fourth\">' +\n    '<span>' +\n    '<span>' +\n    '<span></span>' +\n    '<span></span>' +\n    '</span>' +\n    '</span>' +\n    '</span>' +\n    '</body>';\n\n  // remove the head node\n  var head = html.querySelector('head');\n  if (head) {\n    head.parentNode.removeChild(head);\n  }\n\n  var tree = axe.utils.getFlattenedTree(html);\n\n  // setup shadowIds for testing\n  var first = axe.utils.getNodeFromTree(html.querySelector('.first'));\n  var second = axe.utils.getNodeFromTree(html.querySelector('.second'));\n  setShadowId(first, 'a');\n  setShadowId(second, 'b');\n  axe.utils.getNodeFromTree(html.querySelector('[role=\"button\"]')).shadowId =\n    'c';\n\n  return tree;\n}\n\ndescribe('axe.utils.querySelectorAllFilter', function () {\n  'use strict';\n  var dom;\n  afterEach(function () {});\n\n  var tests = ['without cache', 'with cache'];\n  for (var i = 0; i < tests.length; i++) {\n    var describeName = tests[i];\n    describe(describeName, function () {\n      afterEach(function () {});\n\n      if (describeName === 'without cache') {\n        beforeEach(function () {\n          dom = getTestDom();\n\n          // prove we're using the DOM by deleting the cache\n          delete dom[0]._selectorCache;\n        });\n\n        it('should not have a primed cache', function () {\n          assert.isUndefined(dom[0]._selectorCache);\n        });\n      } else {\n        beforeEach(function () {\n          dom = getTestDom();\n\n          // prove we're using the cache by deleting all the children\n          dom[0].children = [];\n        });\n\n        it('should not use the cache if not using the top-level node', function () {\n          var nodes = axe.utils.querySelectorAllFilter(dom, 'ul');\n\n          // this would return 4 nodes if we were still using the\n          // top-level cache\n          var result = axe.utils.querySelectorAllFilter(nodes[0], 'li');\n          assert.equal(result.length, 2);\n        });\n      }\n\n      it('should find nodes using just the tag', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'li');\n        assert.equal(result.length, 4);\n      });\n      it('should find nodes using parent selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'ul > li');\n        assert.equal(result.length, 4);\n      });\n      it('should NOT find nodes using parent selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div > li');\n        assert.equal(result.length, 0);\n      });\n      it('should find nodes using nested parent selectors', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'span > span > span > span'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using hierarchical selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div li');\n        assert.equal(result.length, 4);\n      });\n      it('should find nodes using class selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '.breaking');\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using hierarchical class selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '.first .breaking');\n        assert.equal(result.length, 2);\n      });\n      it('should NOT find nodes using hierarchical class selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '.second .breaking');\n        assert.equal(result.length, 0);\n      });\n      it('should find nodes using multiple class selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '.second.third');\n        assert.equal(result.length, 1);\n      });\n      it('should find nodes using id', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '#one');\n        assert.equal(result.length, 1);\n      });\n\n      // can only select shadow dom nodes when we're not using the\n      // top-level node. but since the top-level node is the one\n      // with the cache, this only works when we are testing the full\n      // tree (i.e. without cache)\n      if (describeName === 'without cache') {\n        it('should find nodes using id, but not in shadow DOM', function () {\n          var result = axe.utils.querySelectorAllFilter(\n            dom[0].children[0],\n            '#one'\n          );\n          assert.equal(result.length, 1);\n        });\n        it('should find nodes using id, within a shadow DOM', function () {\n          var result = axe.utils.querySelectorAllFilter(\n            dom[0].children[0].children[2],\n            '#one'\n          );\n          assert.equal(result.length, 1);\n        });\n      }\n\n      it('should find nodes using attribute', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[role]');\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using attribute with value', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[role=tab]');\n        assert.equal(result.length, 1);\n      });\n      it('should find nodes using attribute with value', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[role=\"button\"]');\n        assert.equal(result.length, 1);\n      });\n      it('should find nodes using parent attribute with value', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          '[data-a11yhero=\"faulkner\"] > ul'\n        );\n        assert.equal(result.length, 1);\n      });\n      it('should find nodes using hierarchical attribute with value', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          '[data-a11yhero=\"faulkner\"] li'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using :not selector with class', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div:not(.first)');\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using :not selector with matching id', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div:not(#one)');\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using :not selector with matching attribute selector', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not([data-a11yhero])'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using :not selector with matching attribute selector with value', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not([data-a11yhero=faulkner])'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes using :not selector with bogus attribute selector with value', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not([data-a11yhero=wilco])'\n        );\n        assert.equal(result.length, 3);\n      });\n      it('should find nodes using :not selector with bogus id', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div:not(#thangy)');\n        assert.equal(result.length, 3);\n      });\n      it('should find nodes using :not selector with attribute', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'div:not([id])');\n        assert.equal(result.length, 2);\n      });\n      it('should find nodes hierarchically using :not selector', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not(.first) li'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find same nodes hierarchically using more :not selector', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not(.first) li:not(.breaking)'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should NOT find nodes hierarchically using :not selector', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'div:not(.second) li:not(.breaking)'\n        );\n        assert.equal(result.length, 0);\n      });\n      it('should find nodes using ^= attribute selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[class^=\"sec\"]');\n        assert.equal(result.length, 1);\n      });\n      it('should find nodes using $= attribute selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[id$=\"ne\"]');\n        assert.equal(result.length, 3);\n      });\n      it('should find nodes using *= attribute selector', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, '[role*=\"t\"]');\n        assert.equal(result.length, 2);\n      });\n      it('should put it all together', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          '.first[data-a11yhero=\"faulkner\"] > ul li.breaking'\n        );\n        assert.equal(result.length, 2);\n      });\n      it('should find an element only once', function () {\n        var divs = axe.utils.querySelectorAllFilter(dom, 'div');\n        var ones = axe.utils.querySelectorAllFilter(dom, '#one');\n        var divOnes = axe.utils.querySelectorAllFilter(dom, 'div, #one');\n\n        assert.isBelow(\n          divOnes.length,\n          divs.length + ones.length,\n          'Elements matching both parts of a selector should not be included twice'\n        );\n      });\n      it('should return nodes sorted by document position', function () {\n        var result = axe.utils.querySelectorAllFilter(dom, 'ul, #one');\n        assert.equal(result[0].actualNode.nodeName, 'UL');\n        assert.equal(result[1].actualNode.nodeName, 'DIV');\n        assert.equal(result[2].actualNode.nodeName, 'UL');\n      });\n      it('should filter the returned nodes when passed a filter function', function () {\n        var result = axe.utils.querySelectorAllFilter(\n          dom,\n          'ul, #one',\n          function (node) {\n            return node.actualNode.nodeName !== 'UL';\n          }\n        );\n        assert.equal(result[0].actualNode.nodeName, 'DIV');\n        assert.equal(result.length, 1);\n      });\n    });\n  }\n});\n\ndescribe('axe.utils.querySelectorAll', function () {\n  'use strict';\n  var dom;\n  afterEach(function () {});\n  beforeEach(function () {\n    dom = getTestDom();\n  });\n  it('should find nodes using just the tag', function () {\n    var result = axe.utils.querySelectorAll(dom, 'li');\n    assert.equal(result.length, 4);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/queue.js",
    "content": "describe('axe.utils.queue', function () {\n  'use strict';\n\n  it('should be a function', function () {\n    assert.isFunction(axe.utils.queue);\n  });\n\n  describe('defer', function () {\n    it('should be a function', function () {\n      var q = axe.utils.queue();\n      assert.isFunction(q.defer);\n    });\n\n    it('should push onto the \"axe.utils.queue\"', function (done) {\n      var q = axe.utils.queue();\n\n      q.defer(function (resolve) {\n        setTimeout(function () {\n          resolve(1);\n        }, 0);\n      });\n\n      q.defer(function (resolve) {\n        setTimeout(function () {\n          resolve(2);\n        }, 0);\n      });\n\n      q.then(function (data) {\n        assert.deepEqual(data, [1, 2]);\n        done();\n      });\n    });\n\n    it('should execute resolve immediately if defered functions are already complete', function () {\n      var q = axe.utils.queue(),\n        complete = false;\n\n      q.defer(function (resolve) {\n        resolve(1);\n      });\n\n      q.defer(function (resolve) {\n        resolve(2);\n      });\n\n      q.then(function (data) {\n        complete = true;\n        assert.deepEqual(data, [1, 2]);\n      });\n\n      assert.isTrue(complete);\n    });\n\n    it('is chainable', function () {\n      var q = axe.utils.queue();\n      assert.equal(\n        q,\n        q.defer(function () {})\n      );\n    });\n\n    it('throws if then was already called', function () {\n      assert.throws(function () {\n        var q = axe.utils.queue();\n        q.defer(function (resolve) {\n          resolve();\n        });\n\n        q.then(function () {});\n\n        q.defer(function (resolve) {\n          resolve();\n        });\n      });\n    });\n\n    it('can await another queue', function (done) {\n      var q1 = axe.utils.queue();\n      var q2 = axe.utils.queue();\n\n      q1.defer(function (resolve) {\n        setTimeout(function () {\n          resolve(123);\n        }, 10);\n      });\n\n      q2.defer(q1);\n      q2.then(function (res) {\n        // unwrap both queue results\n        assert.equal(res[0][0], 123);\n        done();\n      });\n    });\n  });\n\n  describe('then', function () {\n    it('should be a function', function () {\n      var q = axe.utils.queue();\n      assert.isFunction(q.then);\n    });\n\n    it('should execute immediately if axe.utils.queue is complete', function () {\n      var q = axe.utils.queue();\n      var result = false;\n\n      q.then(function () {\n        result = true;\n      });\n\n      assert.isTrue(result);\n    });\n\n    it('is chainable', function () {\n      var q = axe.utils.queue();\n      assert.equal(\n        q,\n        q.then(function () {})\n      );\n    });\n\n    it('throws when called more than once', function () {\n      assert.throws(function () {\n        var q = axe.utils.queue();\n        q.defer(function () {});\n        q.then(function () {});\n        q.then(function () {});\n      });\n    });\n  });\n\n  describe('abort', function () {\n    it('should be a function', function () {\n      var q = axe.utils.queue();\n      assert.isFunction(q.abort);\n    });\n\n    it('stops `then` from being called', function (done) {\n      var q = axe.utils.queue();\n\n      q.defer(function (resolve) {\n        setTimeout(function () {\n          resolve(true);\n        }, 100);\n      });\n\n      q.then(function () {\n        assert.ok(false, 'should not execute');\n      });\n      q.catch(function () {});\n\n      setTimeout(function () {\n        var data = q.abort();\n        assert.ok(true, 'Queue aborted');\n        assert.isFunction(data[0]);\n        done();\n      }, 1);\n    });\n\n    it('sends a message to `catch`', function (done) {\n      var q = axe.utils.queue();\n      q.defer(function () {});\n\n      q.then(function () {});\n      q.catch(function (err) {\n        assert.equal(err, 'Super sheep');\n        done();\n      });\n\n      q.abort('Super sheep');\n    });\n  });\n\n  describe('catch', function () {\n    it('is called when defer throws an error', function (done) {\n      var q = axe.utils.queue();\n      q.defer(function () {\n        throw 'error! 1';\n      });\n\n      q.catch(function (e) {\n        assert.equal(e, 'error! 1');\n        done();\n      });\n    });\n\n    it('can catch error synchronously', function (done) {\n      var q = axe.utils.queue();\n      var sync = true;\n      q.defer(function () {\n        throw 'error! 2';\n      });\n\n      q.catch(function (e) {\n        assert.equal(e, 'error! 2');\n        assert.ok(sync, 'error caught in sync');\n        done();\n      });\n      sync = false;\n    });\n\n    it('is called when the reject method is called', function (done) {\n      /*eslint no-unused-vars: 0*/\n      var q = axe.utils.queue();\n      var errorsCaught = 0;\n\n      q.defer(function (resolve, reject) {\n        setTimeout(function () {\n          reject('error! 2');\n        }, 1);\n      });\n\n      q.catch(function (e) {\n        assert.equal(e, 'error! 2');\n        errorsCaught += 1;\n        done();\n      });\n    });\n\n    it('will not run `then` if an error is thrown', function (done) {\n      var q = axe.utils.queue();\n      q.defer(function () {\n        throw 'error! 3';\n      });\n\n      q.then(function () {\n        assert.ok(false, 'Should not be called');\n      });\n      q.catch(function (e) {\n        assert.equal(e, 'error! 3');\n        done();\n      });\n    });\n\n    it('does not continue other tasks if an error occurs', function (done) {\n      var q = axe.utils.queue();\n      var aborted;\n      q.defer(function () {\n        throw 'error! 3';\n      });\n      q.defer(function () {\n        aborted = false;\n      });\n\n      q.then(function () {\n        assert.ok(false, 'Should not be called');\n      });\n      q.catch(function (e) {\n        assert.equal(e, 'error! 3');\n      });\n      setTimeout(function () {\n        assert.notEqual(aborted, false);\n        done();\n      }, 30);\n    });\n\n    it('is chainable', function () {\n      var q = axe.utils.queue();\n      assert.equal(\n        q,\n        q.catch(function () {})\n      );\n    });\n\n    it('throws when called more than once', function () {\n      assert.throws(function () {\n        var q = axe.utils.queue();\n        q.defer(function () {});\n        q.catch(function () {});\n        q.catch(function () {});\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/respondable.js",
    "content": "describe('axe.utils.respondable', function () {\n  var fixture = document.querySelector('#fixture');\n  var respondable = axe.utils.respondable;\n  var noop = sinon.spy();\n  var frameWin;\n\n  beforeEach(function (done) {\n    var frame = document.createElement('iframe');\n    frame.src = '../mock/frames/test.html';\n    frame.addEventListener('load', function () {\n      frameWin = frame.contentWindow;\n      done();\n    });\n    frame.addEventListener('error', done);\n\n    fixture.appendChild(frame);\n  });\n\n  afterEach(function () {\n    axe._thisWillBeDeletedDoNotUse.utils.setDefaultFrameMessenger(respondable);\n  });\n\n  it('should error if open is not a function', function () {\n    assert.throws(function () {\n      respondable.updateMessenger({\n        post: noop,\n        close: noop\n      });\n    });\n  });\n\n  it('should error if post is not a function', function () {\n    assert.throws(function () {\n      respondable.updateMessenger({\n        open: noop\n      });\n    });\n  });\n\n  it('should error if open function return is not a function', function () {\n    assert.throws(function () {\n      respondable.updateMessenger({\n        post: noop,\n        open: function () {\n          return 1;\n        }\n      });\n    });\n  });\n\n  it('should call the open function and pass the listener', function () {\n    var open = sinon.spy();\n    respondable.updateMessenger({\n      open: open,\n      post: noop\n    });\n\n    assert.isTrue(open.called);\n    assert.isTrue(typeof open.args[0][0] === 'function');\n  });\n\n  it('should call previous close function', function () {\n    var close = sinon.spy();\n    respondable.updateMessenger({\n      open: function () {\n        return close;\n      },\n      post: noop\n    });\n\n    respondable.updateMessenger({\n      open: noop,\n      post: noop\n    });\n\n    assert.isTrue(close.called);\n  });\n\n  it('should use the post function when making a frame post', function () {\n    var post = sinon.spy();\n    respondable.updateMessenger({\n      open: noop,\n      post: post\n    });\n\n    respondable(frameWin, 'greeting');\n    assert.isTrue(post.called);\n  });\n\n  it('should pass the post function the correct parameters', function () {\n    var post = sinon.spy();\n    var callback = sinon.spy();\n\n    respondable.updateMessenger({\n      open: noop,\n      post: post\n    });\n\n    respondable(frameWin, 'greeting', 'hello', true, callback);\n    assert.isTrue(\n      post.calledWith(\n        frameWin,\n        sinon.match({\n          topic: 'greeting',\n          message: 'hello',\n          keepalive: true\n        }),\n        callback\n      )\n    );\n  });\n\n  it('should work as a full integration', function () {\n    var listeners = {};\n    var listener = sinon.spy();\n\n    respondable.updateMessenger({\n      open: function () {\n        listeners.greeting = listener;\n      },\n      post: function (win, data) {\n        if (listeners[data.topic]) {\n          listeners[data.topic]();\n        }\n      },\n      close: noop\n    });\n\n    respondable(frameWin, 'greeting', 'hello');\n    assert.isTrue(listener.called);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/rule-error.js",
    "content": "describe('utils.RuleError', () => {\n  const RuleError = axe.utils.RuleError;\n\n  it('returns a serializable error', () => {\n    const error = new Error('test');\n    const ruleError = new RuleError({ error });\n    assert.ownInclude(ruleError, {\n      message: error.message,\n      stack: error.stack,\n      name: error.name\n    });\n  });\n\n  it('returns a instanceof Error', () => {\n    const error = new Error('test');\n    const ruleError = new RuleError({ error });\n    assert.instanceOf(ruleError, Error);\n  });\n\n  it('includes the ruleId if provided', () => {\n    const error = new Error('test');\n    const ruleError = new RuleError({ error, ruleId: 'aria' });\n    assert.equal(ruleError.ruleId, 'aria');\n    assert.include(ruleError.message, 'Skipping aria rule.');\n  });\n\n  it('includes the method if provided', () => {\n    const error = new Error('test');\n    const ruleError = new RuleError({ error, method: '#matches' });\n    assert.equal(ruleError.method, '#matches');\n  });\n\n  it('includes the errorNode if provided', () => {\n    const error = new Error('test');\n    const ruleError = new RuleError({ error, errorNode: 'err' });\n    assert.equal(ruleError.errorNode, 'err');\n  });\n\n  it('includes a serialized cause if provided', () => {\n    const error = new Error('test');\n    error.cause = new Error('cause');\n    const ruleError = new RuleError({ error });\n    assert.deepEqual(ruleError.cause, axe.utils.serializeError(error.cause));\n  });\n});\n"
  },
  {
    "path": "test/core/utils/rule-should-run.js",
    "content": "describe('axe.utils.ruleShouldRun', function () {\n  'use strict';\n\n  it('should return false if rule.pageOnly and !context.page', function () {\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          pageLevel: true\n        },\n        {\n          page: false\n        },\n        {}\n      )\n    );\n  });\n\n  it('should return false if rule.enabled is false, option.enabled is false and ruleID is not present runOnly', function () {\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: false\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: false\n            }\n          },\n          runOnly: {\n            type: 'rule',\n            values: ['apples']\n          }\n        }\n      )\n    );\n  });\n\n  it('should return true if rule.enabled is false, option.enabled is false and ruleID is present in runOnly', function () {\n    assert.isTrue(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: false\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: false\n            }\n          },\n          runOnly: {\n            type: 'rule',\n            values: ['bananas']\n          }\n        }\n      )\n    );\n  });\n\n  it('should return true if rule.enabled is false, option is undefined and ruleID is present in runOnly', function () {\n    assert.isTrue(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: false\n        },\n        {},\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['bananas']\n          }\n        }\n      )\n    );\n  });\n\n  it('should return false even if enabled is set to true if ruleID is not present in runOnly', function () {\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: true\n        },\n        {},\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['apples']\n          }\n        }\n      )\n    );\n  });\n\n  it('should return false if rule.enabled is false', function () {\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: false,\n          tags: ['fruit']\n        },\n        {},\n        {}\n      )\n    );\n  });\n\n  it('should return true if rule.enabled is true', function () {\n    assert.isTrue(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: true,\n          tags: ['fruit']\n        },\n        {},\n        {}\n      )\n    );\n  });\n\n  it('should return true if option is set to true but rule is set to false', function () {\n    assert.isTrue(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: false\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: true\n            }\n          }\n        }\n      )\n    );\n  });\n\n  it('should return false if option is set to false but rule is set to true', function () {\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: true\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: false\n            }\n          }\n        }\n      )\n    );\n  });\n\n  it('should use option.rules.enabled over option.runOnly tags', function () {\n    assert.isTrue(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: true,\n          tags: ['fruit']\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: true\n            }\n          },\n          runOnly: {\n            type: 'tag',\n            values: ['meat']\n          }\n        }\n      )\n    );\n\n    assert.isFalse(\n      axe.utils.ruleShouldRun(\n        {\n          id: 'bananas',\n          enabled: true,\n          tags: ['fruit']\n        },\n        {},\n        {\n          rules: {\n            bananas: {\n              enabled: false\n            }\n          },\n          runOnly: {\n            type: 'tag',\n            values: ['fruit']\n          }\n        }\n      )\n    );\n  });\n\n  describe('default axe._tagExclude', function () {\n    var origTagExclude;\n    before(function () {\n      axe._load({});\n      origTagExclude = axe._audit.tagExclude;\n    });\n    after(function () {\n      axe._audit.tagExclude = origTagExclude;\n    });\n\n    beforeEach(function () {\n      axe._audit.tagExclude = [];\n    });\n\n    it('excludes rules with a tag put in axe._tagExclude', function () {\n      axe._audit.tagExclude = ['the-cheat'];\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'e-mail',\n            enabled: true,\n            tags: ['strongbad']\n          },\n          {},\n          {}\n        )\n      );\n\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'party',\n            enabled: true,\n            tags: ['the-cheat']\n          },\n          {},\n          {}\n        )\n      );\n    });\n\n    it('adds axe.tagExclude to the existing exclude tags', function () {\n      axe._audit.tagExclude = ['the-cheat'];\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'e-mail',\n            enabled: true,\n            tags: ['the-cheat']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: { exclude: ['strongbad'] }\n            }\n          }\n        )\n      );\n    });\n\n    it('does not exclude tags explicitly included', function () {\n      axe._audit.tagExclude = ['the-cheat'];\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'e-mail',\n            enabled: false,\n            tags: ['the-cheat']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: { include: ['the-cheat'] }\n            }\n          }\n        )\n      );\n\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'e-mail',\n            enabled: false,\n            tags: ['the-cheat']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'rule',\n              values: ['e-mail']\n            }\n          }\n        )\n      );\n\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'e-mail',\n            enabled: false,\n            tags: ['the-cheat']\n          },\n          {},\n          {\n            rules: {\n              'e-mail': {\n                enabled: true\n              }\n            }\n          }\n        )\n      );\n    });\n  });\n\n  describe('runOnly type:tag', function () {\n    it('should return true if passed an array with a matching tag', function () {\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: ['fruit']\n            }\n          }\n        )\n      );\n    });\n\n    it('should return false if passed an array with a matching tag', function () {\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: true,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: ['meat']\n            }\n          }\n        )\n      );\n    });\n\n    it('should accept string as an include value', function () {\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                include: 'fruit'\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should accept array as an include value', function () {\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                include: ['fruit', 'veggie']\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should accept string as an exclude value', function () {\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit', 'tasty']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                exclude: 'tasty'\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should accept array as an exclude value', function () {\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                exclude: ['fruit', 'tasty']\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should return true if it matches include but not exclude', function () {\n      assert.isTrue(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'cabbage',\n            enabled: false,\n            tags: ['veggie']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                include: ['fruit', 'veggie'],\n                exclude: ['tasty']\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should return false if it matches no include', function () {\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                include: ['veggies'],\n                exclude: ['fruit', 'tasty']\n              }\n            }\n          }\n        )\n      );\n    });\n\n    it('should return false if it matches include and exclude', function () {\n      assert.isFalse(\n        axe.utils.ruleShouldRun(\n          {\n            id: 'bananas',\n            enabled: false,\n            tags: ['fruit', 'tasty']\n          },\n          {},\n          {\n            runOnly: {\n              type: 'tag',\n              values: {\n                include: ['fruit', 'veggies'],\n                exclude: ['tasty']\n              }\n            }\n          }\n        )\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/scroll-state.js",
    "content": "describe('axe.utils.getScrollState', function () {\n  'use strict';\n  var mockWin;\n  var getScrollState = axe.utils.getScrollState;\n\n  var fixture = document.getElementById('fixture');\n\n  beforeEach(function () {\n    mockWin = {\n      pageXOffset: 1,\n      pageYOffset: 3,\n      document: {\n        documentElement: {\n          children: [],\n          scrollTop: 3,\n          scrollHeight: 4\n        }\n      },\n      body: { children: [] }\n    };\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(getScrollState);\n  });\n\n  it('takes the window object as an optional argument', function () {\n    assert.deepEqual(getScrollState(), getScrollState(window));\n  });\n\n  it('returns the window as the first item, if pageXOffset is supported', function () {\n    assert.deepEqual(getScrollState(mockWin)[0], {\n      elm: mockWin,\n      top: mockWin.pageYOffset,\n      left: mockWin.pageXOffset\n    });\n  });\n\n  it('returns the html as the first item, if pageXOffset is not supported', function () {\n    mockWin.pageYOffset = undefined;\n    mockWin.pageXOffset = undefined;\n    var html = mockWin.document.documentElement;\n\n    assert.deepEqual(getScrollState(mockWin)[0], {\n      elm: html,\n      top: html.scrollTop,\n      left: html.scrollLeft\n    });\n  });\n\n  it('grabs scrollTop and scrollLeft from all descendants of body', function () {\n    fixture.innerHTML =\n      '<div style=\"overflow:auto; height: 50px\" id=\"tgt1\">' +\n      '<div style=\"height: 100px\"> Han Solo </div>' +\n      '<div style=\"overflow: auto; height: 50px\" id=\"tgt2\">' +\n      '<div style=\"height: 100px\"> Chewbacca </div>' +\n      '</div>' +\n      '</div>';\n\n    var tgt1 = document.getElementById('tgt1');\n    var tgt2 = document.getElementById('tgt2');\n    tgt1.scrollTop = 10;\n    tgt2.scrollTop = 20;\n\n    var scrollState = getScrollState();\n\n    assert.deepEqual(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt1;\n      }),\n      { elm: tgt1, top: 10, left: 0 }\n    );\n    assert.deepEqual(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt2;\n      }),\n      { elm: tgt2, top: 20, left: 0 }\n    );\n  });\n\n  it('ignores elements with overflow visible', function () {\n    fixture.innerHTML =\n      '<div style=\"overflow:visible; height: 50px\" id=\"tgt1\">' +\n      '<div style=\"height: 100px\" id=\"tgt2\"> Han Solo </div>' +\n      '</div>';\n\n    var tgt1 = document.getElementById('tgt1');\n    var tgt2 = document.getElementById('tgt2');\n    var scrollState = getScrollState();\n\n    assert.isUndefined(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt1;\n      })\n    );\n    assert.isUndefined(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt2;\n      })\n    );\n  });\n\n  it('ignores elements that do not overflow', function () {\n    fixture.innerHTML =\n      '<div style=\"overflow:auto; height: 300px\" id=\"tgt1\">' +\n      '<div style=\"height: 100px\"> Han Solo </div>' +\n      '<div style=\"overflow: hidden; height: 150px\" id=\"tgt2\">' +\n      '<div style=\"height: 100px\"> Chewbacca </div>' +\n      '</div>' +\n      '</div>';\n\n    var tgt1 = document.getElementById('tgt1');\n    var tgt2 = document.getElementById('tgt2');\n    var scrollState = getScrollState();\n\n    assert.isUndefined(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt1;\n      })\n    );\n    assert.isUndefined(\n      scrollState.find(function (scroll) {\n        return scroll.elm === tgt2;\n      })\n    );\n  });\n\n  it('does not fail with svg elements', function () {\n    fixture.innerHTML =\n      '<svg class=\"svg\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"17\" viewBox=\"0 0 13 17\">' +\n      '<path fill=\"currentColor\" d=\"M6.5 0L0 6.5 1.4 8l4-4v12.7h2V4l4.3 4L13 6.4z\"></path>' +\n      '</svg>';\n\n    assert.doesNotThrow(function () {\n      getScrollState();\n    });\n  });\n});\n\ndescribe('axe.utils.setScrollState', function () {\n  'use strict';\n  var setScrollState = axe.utils.setScrollState;\n\n  var fixture = document.getElementById('fixture');\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should be a function', function () {\n    assert.isFunction(setScrollState);\n  });\n\n  it('sets scrollTop and scrollLeft for regular nodes', function () {\n    var elm1 = {},\n      elm2 = {};\n    setScrollState([\n      { elm: elm1, top: 10, left: 20 },\n      { elm: elm2, top: 30, left: 40 }\n    ]);\n\n    assert.deepEqual(elm1, { scrollTop: 10, scrollLeft: 20 });\n    assert.deepEqual(elm2, { scrollTop: 30, scrollLeft: 40 });\n  });\n\n  it('calls scroll() for the window element', function () {\n    var called;\n    var winScroll = window.scroll;\n    window.scroll = function (left, top) {\n      called = { top: top, left: left };\n    };\n    setScrollState([{ elm: window, top: 10, left: 20 }]);\n    assert.deepEqual(called, { top: 10, left: 20 });\n    window.scroll = winScroll;\n  });\n});\n"
  },
  {
    "path": "test/core/utils/select.js",
    "content": "describe('axe.utils.select', () => {\n  const $id = id => document.getElementById(id);\n  const { Context } = axe._thisWillBeDeletedDoNotUse.base;\n  const { fixtureSetup } = axe.testUtils;\n\n  it('should be a function', () => {\n    assert.isFunction(axe.utils.select);\n  });\n\n  it('should return an array', () => {\n    assert.isArray(axe.utils.select('div', { include: [] }));\n  });\n\n  describe('selector', () => {\n    it('should accept a selector', () => {\n      fixtureSetup('<div id=\"monkeys\"></div>');\n      const context = new Context(document, axe._tree);\n      const result = axe.utils.select('#monkeys', context);\n      assert.equal(result[0].actualNode, $id('monkeys'));\n    });\n  });\n\n  describe('context', () => {\n    it('should include', () => {\n      fixtureSetup(\n        '<div id=\"monkeys\"><div id=\"bananas\" class=\"bananas\"></div></div>'\n      );\n      const context = new Context('#monkeys', axe._tree);\n      const result = axe.utils.select('.bananas', context);\n      assert.deepEqual(result[0].actualNode, $id('bananas'));\n    });\n\n    it('should exclude', () => {\n      fixtureSetup(\n        '<div id=\"monkeys\"><div id=\"bananas\" class=\"bananas\"></div></div>'\n      );\n      const context = new Context(\n        {\n          include: [['#fixture']],\n          exclude: [['#monkeys']]\n        },\n        axe._tree\n      );\n      const result = axe.utils.select('.bananas', context);\n      assert.isEmpty(result);\n    });\n\n    it('should pick the deepest exclude/include - exclude winning', () => {\n      fixtureSetup(\n        `<div id=\"include1\">\n        \t<div id=\"exclude1\">\n        \t\t<div id=\"include2\">\n        \t\t\t<div id=\"exclude2\">\n        \t\t\t\t<div class=\"bananas\"></div>\n        \t\t\t</div>\n        \t\t</div>\n        \t</div>\n        </div>`\n      );\n      const context = new Context(\n        {\n          include: [['#include1'], ['#include2']],\n          exclude: [['#exclude1'], ['#exclude2']]\n        },\n        axe._tree\n      );\n      const result = axe.utils.select('.bananas', context);\n      assert.deepEqual(result, []);\n    });\n\n    it('should pick the deepest exclude/include - include winning', () => {\n      fixtureSetup(\n        `<div id=\"include1\"> \n        \t<div id=\"exclude1\"> \n        \t\t<div id=\"include2\"> \n        \t\t\t<div id=\"exclude2\"> \n        \t\t\t\t<div id=\"include3\"> \n        \t\t\t\t\t<div id=\"bananas\" class=\"bananas\"></div> \n        \t\t\t\t</div> \n        \t\t\t</div> \n        \t\t</div> \n        \t</div> \n        </div>`\n      );\n      const context = new Context(\n        {\n          include: [['#include3'], ['#include2'], ['#include1']],\n          exclude: [['#exclude1'], ['#exclude2']]\n        },\n        axe._tree\n      );\n      const result = axe.utils.select('.bananas', context);\n      assert.deepEqual(result[0].actualNode, $id('bananas'));\n    });\n  });\n\n  it('should only contain unique elements', () => {\n    fixtureSetup(\n      '<div id=\"monkeys\"><div id=\"bananas\" class=\"bananas\"></div></div>'\n    );\n    const context = new Context(\n      {\n        include: [['#fixture'], ['#monkeys']]\n      },\n      axe._tree\n    );\n\n    const result = axe.utils.select('.bananas', context);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0].actualNode, $id('bananas'));\n  });\n\n  it('should not return duplicates on overlapping includes', () => {\n    fixtureSetup(\n      '<div id=\"zero\"><div id=\"one\"><div id=\"target1\" class=\"bananas\"></div></div>' +\n        '<div id=\"two\"><div id=\"target2\" class=\"bananas\"></div></div></div>'\n    );\n    const context = new Context(\n      {\n        include: [['#zero'], ['#one']]\n      },\n      axe._tree\n    );\n\n    const result = axe.utils.select('.bananas', context);\n    assert.deepEqual(\n      result.map(n => n.actualNode),\n      [$id('target1'), $id('target2')]\n    );\n    assert.equal(result.length, 2);\n  });\n\n  it('should return the cached result if one exists', () => {\n    fixtureSetup(\n      '<div id=\"zero\"><div id=\"one\"><div id=\"target1\" class=\"bananas\"></div></div>' +\n        '<div id=\"two\"><div id=\"target2\" class=\"bananas\"></div></div></div>'\n    );\n\n    axe._selectCache = [\n      {\n        selector: '.bananas',\n        result: 'fruit bat'\n      }\n    ];\n    const context = new Context([['#zero']], axe._tree);\n    const result = axe.utils.select('.bananas', context);\n    assert.equal(result, 'fruit bat');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/selector-cache.js",
    "content": "describe('utils.selector-cache', () => {\n  const fixture = document.querySelector('#fixture');\n  const cacheNodeSelectors =\n    axe._thisWillBeDeletedDoNotUse.utils.cacheNodeSelectors;\n  const getNodesMatchingExpression =\n    axe._thisWillBeDeletedDoNotUse.utils.getNodesMatchingExpression;\n  const convertSelector = axe.utils.convertSelector;\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  let vNode;\n  beforeEach(() => {\n    fixture.innerHTML = '<div id=\"target\" class=\"foo\" aria-label=\"bar\"></div>';\n    vNode = new axe.VirtualNode(fixture.firstChild);\n  });\n\n  describe('cacheNodeSelectors', () => {\n    it('should add the node to the global selector', () => {\n      const map = {};\n      cacheNodeSelectors(vNode, map);\n      assert.deepEqual(map['*'], [vNode]);\n    });\n\n    it('should add the node to the nodeName', () => {\n      const map = {};\n      cacheNodeSelectors(vNode, map);\n      assert.deepEqual(map.div, [vNode]);\n    });\n\n    it('should add the node to all attribute selectors', () => {\n      const map = {};\n      cacheNodeSelectors(vNode, map);\n      assert.deepEqual(map['[id]'], [vNode]);\n      assert.deepEqual(map['[class]'], [vNode]);\n      assert.deepEqual(map['[aria-label]'], [vNode]);\n    });\n\n    it('should add the node to the id map', () => {\n      const map = {};\n      cacheNodeSelectors(vNode, map);\n      assert.deepEqual(map[' [idsMap]'].target, [vNode]);\n    });\n\n    it('should not add the node to selectors it does not match', () => {\n      const map = {};\n      cacheNodeSelectors(vNode, map);\n      assert.isUndefined(map['[for]']);\n      assert.isUndefined(map.h1);\n    });\n\n    it('should ignore non-element nodes', () => {\n      const map = {};\n      fixture.innerHTML = 'Hello';\n      vNode = new axe.VirtualNode(fixture.firstChild);\n      cacheNodeSelectors(vNode, map);\n\n      assert.lengthOf(Object.keys(map), 0);\n    });\n\n    describe('with javascripty attribute selectors', () => {\n      const terms = [\n        'prototype',\n        'constructor',\n        '__proto__',\n        'Element',\n        'nodeName',\n        'valueOf',\n        'toString'\n      ];\n      for (const term of terms) {\n        it(`works with ${term}`, () => {\n          fixture.innerHTML = `<div id=\"${term}\" class=\"${term}\" aria-label=\"${term}\"></div>`;\n          vNode = new axe.VirtualNode(fixture.firstChild);\n          const map = {};\n          cacheNodeSelectors(vNode, map);\n          assert.deepEqual(map['[id]'], [vNode]);\n          assert.deepEqual(map['[class]'], [vNode]);\n          assert.deepEqual(map['[aria-label]'], [vNode]);\n        });\n      }\n    });\n  });\n\n  describe('getNodesMatchingExpression', () => {\n    let tree;\n    let spanVNode;\n    let headingVNode;\n\n    function createTree() {\n      for (let i = 0; i < fixture.children.length; i++) {\n        const child = fixture.children[i];\n        const isShadow = child.hasAttribute('data-shadow');\n        const html = child.innerHTML;\n        if (isShadow) {\n          const shadowRoot = child.attachShadow({ mode: 'open' });\n          shadowRoot.innerHTML = html;\n          child.innerHTML = '';\n        }\n      }\n\n      return axe.utils.getFlattenedTree(fixture);\n    }\n\n    beforeEach(() => {\n      fixture.firstChild.innerHTML =\n        '<h1><span class=\"bar\" id=\"not-target\" aria-labelledby=\"target\"></span></h1>';\n      tree = axe.utils.getFlattenedTree(fixture.firstChild);\n\n      vNode = tree[0];\n      headingVNode = vNode.children[0];\n      spanVNode = headingVNode.children[0];\n    });\n\n    it('should return undefined if the cache is not primed', () => {\n      tree[0]._selectorMap = null;\n      const expression = convertSelector('div');\n      assert.isUndefined(getNodesMatchingExpression(tree, expression));\n    });\n\n    it('should return a list of matching nodes by global selector', () => {\n      const expression = convertSelector('*');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [\n        vNode,\n        headingVNode,\n        spanVNode\n      ]);\n    });\n\n    it('should return a list of matching nodes by nodeName', () => {\n      const expression = convertSelector('div');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    it('should return a list of matching nodes by id', () => {\n      const expression = convertSelector('#target');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    (shadowSupported ? it : xit)(\n      'should only return nodes matching shadowId when matching by id',\n      () => {\n        fixture.innerHTML =\n          '<div id=\"target\"></div><div data-shadow><div id=\"target\"></div></div>';\n        tree = createTree();\n        const expression = convertSelector('#target');\n        const expected = [tree[0].children[0]];\n        assert.deepEqual(\n          getNodesMatchingExpression(tree, expression),\n          expected\n        );\n      }\n    );\n\n    it('should return a list of matching nodes by class', () => {\n      const expression = convertSelector('.foo');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    it('should return a list of matching nodes by attribute', () => {\n      const expression = convertSelector('[aria-label]');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    it('should return an empty array if selector does not match', () => {\n      const expression = convertSelector('main');\n      assert.lengthOf(getNodesMatchingExpression(tree, expression), 0);\n    });\n\n    it('should return an empty array for complex selector that does not match', () => {\n      const expression = convertSelector('span.missingClass[id]');\n      assert.lengthOf(getNodesMatchingExpression(tree, expression), 0);\n    });\n\n    it('should return an empty array for a non-complex selector that does not match', () => {\n      const expression = convertSelector('div#not-target[id]');\n      assert.lengthOf(getNodesMatchingExpression(tree, expression), 0);\n    });\n\n    it('should return nodes for each expression', () => {\n      fixture.innerHTML =\n        '<div role=\"button\"></div><span aria-label=\"other\"></span>';\n      tree = createTree();\n      const expression = convertSelector('[role], [aria-label]');\n      const expected = [tree[0].children[0], tree[0].children[1]];\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), expected);\n    });\n\n    it('should return nodes for child combinator selector', () => {\n      const expression = convertSelector('div span');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [\n        spanVNode\n      ]);\n    });\n\n    it('should return nodes for direct child combinator selector', () => {\n      const expression = convertSelector('div > h1');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [\n        headingVNode\n      ]);\n    });\n\n    it('should not return nodes for direct child combinator selector that does not match', () => {\n      const expression = convertSelector('div > span');\n      assert.lengthOf(getNodesMatchingExpression(tree, expression), 0);\n    });\n\n    it('should return nodes for attribute value selector', () => {\n      const expression = convertSelector('[id=\"target\"]');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    it('should return undefined for combinator selector with global selector', () => {\n      const expression = convertSelector('body *');\n      assert.isUndefined(getNodesMatchingExpression(tree, expression));\n    });\n\n    it('should return nodes for multipart selectors', () => {\n      const expression = convertSelector('div.foo[id]');\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), [vNode]);\n    });\n\n    it('should remove duplicates', () => {\n      fixture.innerHTML = '<div role=\"button\" aria-label=\"other\"></div>';\n      tree = createTree();\n      const expression = convertSelector('div[role], [aria-label]');\n      const expected = [tree[0].children[0]];\n      assert.deepEqual(getNodesMatchingExpression(tree, expression), expected);\n    });\n\n    it('should sort nodes by added order', () => {\n      fixture.innerHTML =\n        '<div id=\"id0\"></div>' +\n        '<span id=\"id1\"></span>' +\n        '<div id=\"id2\"></div>' +\n        '<span id=\"id3\"></span>' +\n        '<div id=\"id4\"></div>' +\n        '<span id=\"id5\"></span>' +\n        '<div id=\"id6\"></div>' +\n        '<span id=\"id7\"></span>' +\n        '<div id=\"id8\"></div>' +\n        '<span id=\"id9\"></span>';\n      tree = createTree();\n\n      const expression = convertSelector('div, span');\n      const nodes = getNodesMatchingExpression(tree, expression);\n      const ids = [];\n      for (let i = 0; i < nodes.length; i++) {\n        ids.push(nodes[i].attr('id'));\n      }\n\n      assert.deepEqual(ids, [\n        'fixture',\n        'id0',\n        'id1',\n        'id2',\n        'id3',\n        'id4',\n        'id5',\n        'id6',\n        'id7',\n        'id8',\n        'id9'\n      ]);\n    });\n\n    it('should filter nodes', () => {\n      fixture.innerHTML =\n        '<div role=\"button\" aria-label=\"other\"></div><div></div>';\n      tree = createTree();\n\n      function filter(node) {\n        return node.hasAttr('role');\n      }\n\n      const nonFilteredNodes = getNodesMatchingExpression(\n        tree,\n        convertSelector('div, [aria-label]')\n      );\n      const nonFilteredExpected = [\n        tree[0],\n        tree[0].children[0],\n        tree[0].children[1]\n      ];\n\n      const filteredNodes = getNodesMatchingExpression(\n        tree,\n        convertSelector('div, [aria-label]'),\n        filter\n      );\n      const filteredExpected = [tree[0].children[0]];\n\n      assert.deepEqual(nonFilteredNodes, nonFilteredExpected);\n      assert.deepEqual(filteredNodes, filteredExpected);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/send-command-to-frame.js",
    "content": "describe('axe.utils.sendCommandToFrame', () => {\n  const fixture = document.getElementById('fixture');\n  let params;\n  const captureError = axe.testUtils.captureError;\n\n  beforeEach(() => {\n    params = { command: 'rules' };\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n    axe._selectorData = undefined;\n  });\n\n  const assertNotCalled = () => {\n    assert.ok(false, 'should not be called');\n  };\n\n  it('should return results from frames', done => {\n    const frame = document.createElement('iframe');\n    frame.addEventListener('load', () => {\n      axe.utils.sendCommandToFrame(\n        frame,\n        params,\n        captureError(function (res) {\n          assert.lengthOf(res, 1);\n          assert.equal(res[0].id, 'html');\n          done();\n        }, done),\n        () => {\n          done(new Error('sendCommandToFrame should not error'));\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/test.html';\n    fixture.appendChild(frame);\n  });\n\n  it('adjusts skips ping with options.pingWaitTime=0', done => {\n    const frame = document.createElement('iframe');\n    params = {\n      command: 'rules',\n      options: { pingWaitTime: 0 }\n    };\n\n    frame.addEventListener('load', () => {\n      const topics = [];\n      frame.contentWindow.addEventListener('message', function (event) {\n        try {\n          topics.push(JSON.parse(event.data).topic);\n        } catch {\n          /* ignore */\n        }\n      });\n      axe.utils.sendCommandToFrame(\n        frame,\n        params,\n        captureError(() => {\n          try {\n            assert.deepEqual(topics, ['axe.start']);\n            done();\n          } catch (e) {\n            done(e);\n          }\n        }, done),\n        () => {\n          done(new Error('sendCommandToFrame should not error'));\n        }\n      );\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/test.html';\n    fixture.appendChild(frame);\n  });\n\n  it('should timeout if there is no response from frame', done => {\n    const orig = window.setTimeout;\n    window.setTimeout = function (fn, to) {\n      if (to === 30000) {\n        assert.ok('timeout set');\n        fn();\n      } else {\n        // ping timeout\n        return orig(fn, to);\n      }\n      return 'cats';\n    };\n\n    const frame = document.createElement('iframe');\n    frame.addEventListener('load', () => {\n      axe._tree = axe.utils.getFlattenedTree(document.documentElement);\n      axe.utils.sendCommandToFrame(\n        frame,\n        params,\n        function (result) {\n          assert.equal(result, null);\n          done();\n        },\n        assertNotCalled\n      );\n      window.setTimeout = orig;\n    });\n\n    frame.id = 'level0';\n    frame.src = '../mock/frames/zombie-frame.html';\n    fixture.appendChild(frame);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/serialize-error.js",
    "content": "describe('utils.serializeError', function () {\n  const serializeError = axe.utils.serializeError;\n\n  it('should serialize an error', () => {\n    const error = new Error('test');\n    const serialized = serializeError(error);\n    assert.ownInclude(serialized, {\n      message: error.message,\n      stack: error.stack,\n      name: error.name\n    });\n  });\n\n  it('should serialize known serializable properties', () => {\n    const error = new Error('test');\n    error.code = 3;\n    error.ruleId = 'test1';\n    error.method = 'test2';\n    const serialized = serializeError(error);\n    assert.ownInclude(serialized, {\n      code: error.code,\n      ruleId: error.ruleId,\n      method: error.method\n    });\n  });\n\n  it('should not include nullish properties', () => {\n    const error = new Error('test');\n\n    // Neither an explicitly undefined nor an omitted property should be included\n    error.code = null;\n    error.method = undefined;\n    // error.ruleId = undefined;\n\n    const serialized = serializeError(error);\n    assert.doesNotHaveAnyKeys(serialized, ['code', 'method', 'ruleId']);\n  });\n\n  it('should not include non-scalar values even in allow-listed properties', () => {\n    const error = new Error('test');\n    error.code = { foo: 'bar' };\n    error.ruleId = ['baz', 'qux'];\n    const serialized = serializeError(error);\n    assert.doesNotHaveAnyKeys(serialized, ['method', 'ruleId']);\n  });\n\n  it('should not include non-allow-listed properties', () => {\n    const error = new Error('test');\n    error.someUnknownProp = 'test';\n    error.errorNode = 'test';\n    const serialized = serializeError(error);\n    assert.doesNotHaveAnyKeys(serialized, ['someUnknownProp', 'errorNode']);\n  });\n\n  it('should serialize an error with a cause', () => {\n    const error = new Error('test');\n    error.cause = new Error('cause');\n    const serialized = serializeError(error);\n    assert.ownInclude(serialized.cause, {\n      message: error.cause.message,\n      stack: error.cause.stack,\n      name: error.cause.name\n    });\n  });\n\n  it('should serialize recursively', () => {\n    const error = new Error('test');\n    error.cause = new Error('cause');\n    error.cause.cause = new Error('cause2');\n    const serialized = serializeError(error);\n    assert.ownInclude(serialized.cause.cause, {\n      message: error.cause.cause.message,\n      stack: error.cause.cause.stack,\n      name: error.cause.cause.name\n    });\n  });\n\n  it('should not serialize the cause if the stack exceeds 10 levels', () => {\n    const error = new Error('test');\n    error.cause = new Error('cause');\n    error.cause.cause = new Error('cause2');\n    error.cause.cause.cause = new Error('cause3');\n    const serialized = serializeError(error, 9);\n    assert.equal(serialized.cause.cause, '...');\n  });\n});\n"
  },
  {
    "path": "test/core/utils/shadow-select-all.js",
    "content": "describe('utils.shadowSelectAll', () => {\n  const shadowSelectAll = axe.utils.shadowSelectAll;\n  const fixture = document.querySelector('#fixture');\n  const mapNodeName = elms => elms.map(elm => elm.nodeName.toLowerCase());\n\n  it('throws when not passed a string or array', () => {\n    assert.throws(() => {\n      shadowSelectAll(123);\n    });\n  });\n\n  it('throws when passed an array with non-string values', () => {\n    assert.throws(() => {\n      shadowSelectAll([123]);\n    });\n  });\n\n  describe('given a string', () => {\n    it('returns [] if no node is found', () => {\n      fixture.innerHTML = '<b class=\"hello\"></b>';\n      assert.deepEqual(shadowSelectAll('.goodbye'), []);\n    });\n\n    it('returns the each matching element in the document', () => {\n      fixture.innerHTML = `<b class=\"hello\"></b>\n        <s class=\"goodbye\"></s>\n        <i class=\"hello\"></i>`;\n      const nodes = shadowSelectAll('#fixture > .hello');\n      assert.deepEqual(mapNodeName(nodes), ['b', 'i']);\n    });\n  });\n\n  describe('given an array of string', () => {\n    function addShadowTree(host, html) {\n      const root = host.attachShadow({ mode: 'open' });\n      root.innerHTML = html;\n      return root;\n    }\n\n    it('returns [] given an empty array', () => {\n      assert.deepEqual(shadowSelectAll([]), []);\n    });\n\n    it('returns [] if the shadow host does not exist', () => {\n      fixture.innerHTML = '<div></div>';\n      addShadowTree(fixture.children[0], `<b></b>`);\n      assert.deepEqual(shadowSelectAll(['#fixture > span', 'b']), []);\n    });\n\n    it('returns [] if the no final element exists', () => {\n      fixture.innerHTML = '<span></span>';\n      addShadowTree(fixture.children[0], `<i></i>`);\n      assert.deepEqual(shadowSelectAll(['span', 'b']), []);\n    });\n\n    it('returns nodes from a shadow tree', () => {\n      fixture.innerHTML = '<span></span>';\n      addShadowTree(fixture.children[0], `<b></b><i></i>`);\n      const nodeNames = mapNodeName(shadowSelectAll(['#fixture > span', '*']));\n      assert.deepEqual(nodeNames, ['b', 'i']);\n    });\n\n    it('returns nodes from multiple shadow trees', () => {\n      fixture.innerHTML = '<span></span><span></span>';\n      addShadowTree(fixture.children[0], `<a></a><b></b>`);\n      addShadowTree(fixture.children[1], `<i></i><s></s>`);\n      const nodeNames = mapNodeName(shadowSelectAll(['#fixture > span', '*']));\n      assert.deepEqual(nodeNames, ['a', 'b', 'i', 's']);\n    });\n\n    it('returns nodes from multiple trees deep', () => {\n      fixture.innerHTML = '<div></div><div></div>';\n      const root1 = addShadowTree(\n        fixture.children[0],\n        '<span></span><span></span>'\n      );\n      const root2 = addShadowTree(\n        fixture.children[1],\n        '<div></div><span></span>'\n      );\n\n      addShadowTree(root1.children[0], '<a></a>');\n      addShadowTree(root1.children[1], '<b></b>');\n      addShadowTree(root2.children[0], '<i></i>');\n      addShadowTree(root2.children[1], '<s></s>');\n\n      const nodeNames = mapNodeName(\n        shadowSelectAll(['#fixture > div', 'span', '*'])\n      );\n      assert.deepEqual(nodeNames, ['a', 'b', 's']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/shadow-select.js",
    "content": "var shadowSupported = axe.testUtils.shadowSupport.v1;\nvar testSuite = shadowSupported ? describe : describe.skip;\n\ntestSuite('utils.shadowSelect', function () {\n  var shadowSelect = axe.utils.shadowSelect;\n  var fixture = document.querySelector('#fixture');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('throws when not passed a string or array', function () {\n    assert.throws(function () {\n      shadowSelect(123);\n    });\n  });\n\n  it('throws when passed an array with non-string values', function () {\n    assert.throws(function () {\n      shadowSelect([123]);\n    });\n  });\n\n  describe('given a string', function () {\n    it('returns null if no node is found', function () {\n      fixture.innerHTML = '<b class=\"hello\"></b>';\n      assert.isNull(shadowSelect('.goodbye'));\n    });\n\n    it('returns the first matching element in the document', function () {\n      fixture.innerHTML = '<b class=\"hello\"></b><i class=\"hello\"></i>';\n      var node = shadowSelect('.hello');\n      assert.equal(node.nodeName.toLowerCase(), 'b');\n    });\n  });\n\n  describe('given an array of string', function () {\n    function appendShadowTree(parentNode, nodeName) {\n      var node = document.createElement(nodeName);\n      parentNode.appendChild(node);\n      return node.attachShadow({ mode: 'open' });\n    }\n\n    it('returns null given an empty array', function () {\n      assert.isNull(shadowSelect([]));\n    });\n\n    it('returns null if the node does not exist in the shadow tree', function () {\n      var shadowRoot = appendShadowTree(fixture, 'div');\n      shadowRoot.innerHTML = '<b class=\"hello\"></b>';\n      assert.isNull(shadowSelect(['#fixture > div', '.goodbye']));\n    });\n\n    it('returns null if an intermediate node is not a shadow root', function () {\n      var shadowRoot = appendShadowTree(fixture, 'article');\n      shadowRoot.innerHTML = '<section><p class=\"hello\"></p></section>';\n      assert.isNull(shadowSelect(['#fixture > article', 'section', 'p']));\n    });\n\n    it('returns from Document with a length of 1', function () {\n      fixture.innerHTML = '<b class=\"hello\"></b><i class=\"hello\"></i>';\n      var node = shadowSelect(['.hello']);\n      assert.equal(node.nodeName.toLowerCase(), 'b');\n    });\n\n    it('returns from a shadow tree with length 2', function () {\n      var shadowRoot = appendShadowTree(fixture, 'div');\n      shadowRoot.innerHTML = '<b class=\"hello\"></b><i class=\"hello\"></i>';\n\n      var node = shadowSelect(['#fixture > div', '.hello']);\n      assert.equal(node.nodeName.toLowerCase(), 'b');\n    });\n\n    it('returns a node from multiple trees deep', function () {\n      var root = fixture;\n      var nodes = ['article', 'section', 'div', 'p'];\n      nodes.forEach(function (nodeName) {\n        root = appendShadowTree(root, nodeName);\n      });\n      root.innerHTML = '<b class=\"hello\"></b><i class=\"hello\"></i>';\n\n      var node = shadowSelect([\n        '#fixture > article',\n        'section',\n        'div',\n        'p',\n        '.hello'\n      ]);\n      assert.equal(node.nodeName.toLowerCase(), 'b');\n    });\n  });\n});\n"
  },
  {
    "path": "test/core/utils/to-array.js",
    "content": "describe('axe.utils.toArray', function () {\n  'use strict';\n  it('should call Array.prototype.slice', function () {\n    var orig = Array.prototype.slice,\n      called = false,\n      arrayLike = { 0: 'cats', length: 1 };\n\n    Array.prototype.slice = function () {\n      called = true;\n      assert.equal(this, arrayLike);\n    };\n\n    axe.utils.toArray(arrayLike);\n\n    assert.isTrue(called);\n\n    Array.prototype.slice = orig;\n  });\n\n  it('should return an array', function () {\n    var arrayLike = { 0: 'cats', length: 1 };\n\n    var result = axe.utils.toArray(arrayLike);\n    assert.isArray(result);\n  });\n});\n\ndescribe('axe.utils.uniqueArray', function () {\n  'use strict';\n\n  it('should filter duplicate values', function () {\n    var array1 = [1, 2, 3, 4, 5];\n    var array2 = [1, 3, 7];\n\n    var result = axe.utils.uniqueArray(array1, array2);\n    assert.isArray(result);\n    assert.includeMembers(result, [1, 2, 3, 4, 5, 7]);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/token-list.js",
    "content": "describe('axe.utils.tokenList', function () {\n  'use strict';\n\n  it('should split by space', function () {\n    assert.deepEqual(axe.utils.tokenList('bananas monkeys 42'), [\n      'bananas',\n      'monkeys',\n      '42'\n    ]);\n  });\n\n  it('should trim first', function () {\n    assert.deepEqual(axe.utils.tokenList(' \\r   bananas monkeys 42\t\\n  '), [\n      'bananas',\n      'monkeys',\n      '42'\n    ]);\n  });\n\n  it('should collapse whitespace', function () {\n    assert.deepEqual(axe.utils.tokenList(' \\r   bananas \\r \\n\tmonkeys\t\t42\t\\n  '), [\n      'bananas',\n      'monkeys',\n      '42'\n    ]);\n  });\n\n  it('should return empty string array for null value', function () {\n    assert.deepEqual(axe.utils.tokenList(null), ['']);\n  });\n});\n"
  },
  {
    "path": "test/core/utils/valid-langs.js",
    "content": "describe('axe.utils.isValidLang', function () {\n  'use strict';\n\n  describe('isValidLang', function () {\n    it('should return true for valid 3-character lang', function () {\n      assert.isTrue(axe.utils.isValidLang('bbb'));\n    });\n\n    it('should return true for valid 2-character lang', function () {\n      assert.isTrue(axe.utils.isValidLang('aa'));\n    });\n\n    it('should return false for invalid lang', function () {\n      assert.isFalse(axe.utils.isValidLang('xyz'));\n    });\n\n    it('should return false for invalid 2-character lang', function () {\n      assert.isFalse(axe.utils.isValidLang('bb'));\n    });\n\n    it('should return false for invalid 1-character lang code', function () {\n      assert.isFalse(axe.utils.isValidLang('a'));\n    });\n\n    it('should return false for invalid 4-character lang code', function () {\n      assert.isFalse(axe.utils.isValidLang('abcd'));\n    });\n\n    it('should return false for empty string', function () {\n      assert.isFalse(axe.utils.isValidLang(''));\n    });\n\n    it('should return false for invalid lang code', function () {\n      assert.isFalse(axe.utils.isValidLang('123'));\n    });\n  });\n\n  describe('validLangs', function () {\n    it('should return an array of langs', function () {\n      assert.isTrue(Array.isArray(axe.utils.validLangs()));\n    });\n\n    it('should include valid langs', function () {\n      var langs = axe.utils.validLangs();\n      assert.isTrue(langs.indexOf('aaa') !== -1);\n      assert.isTrue(langs.indexOf('aa') !== -1);\n      assert.isTrue(langs.indexOf('en') !== -1);\n      assert.isTrue(langs.indexOf('zzj') !== -1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/get-webdriver.js",
    "content": "const { Builder } = require('selenium-webdriver');\nconst chrome = require('selenium-webdriver/chrome');\nconst chromedriverPath =\n  process.env.CHROMEDRIVER_BIN ?? require('chromedriver').path;\n\nconst getWebdriver = () => {\n  const service = new chrome.ServiceBuilder(chromedriverPath);\n  const options = new chrome.Options().addArguments(\n    '--headless',\n    '--no-sandbox',\n    '--disable-dev-shm-usage',\n    '--disable-gpu'\n  );\n\n  if (process.env.CHROME_BIN) {\n    options.setBinaryPath(process.env.CHROME_BIN);\n  }\n\n  return new Builder()\n    .setChromeOptions(options)\n    .forBrowser('chrome')\n    .setChromeService(service)\n    .build();\n};\n\nmodule.exports.getWebdriver = getWebdriver;\n"
  },
  {
    "path": "test/integration/adapter.js",
    "content": "/*global mocha */\nconst failedTests = [];\nfunction flattenTitles(test) {\n  const titles = [];\n  while (test.parent.title) {\n    titles.push(test.parent.title);\n    test = test.parent;\n  }\n  return titles.reverse();\n}\n\n(function () {\n  'use strict';\n\n  var runner = mocha.run();\n  runner.on('end', function () {\n    window.mochaResults = runner.stats;\n    window.mochaResults.reports = failedTests;\n  });\n  runner.on('fail', function logFailure(test, err) {\n    failedTests.push({\n      name: test.title,\n      result: false,\n      message: err.message,\n      stack: err.stack,\n      titles: flattenTitles(test)\n    });\n  });\n})();\n"
  },
  {
    "path": "test/integration/api/external/index.js",
    "content": "// describes the API used by axe Pro\ndescribe('external API', () => {\n  afterEach(() => {\n    // setup _tree as needed, always reset\n    axe._tree = null;\n  });\n\n  describe('axe.commons.text.sanitize', () => {\n    it('must be a function with the signature String -> String', () => {\n      assert.isString(axe.commons.text.sanitize(''));\n      assert.isString(axe.commons.text.sanitize('not empty'));\n    });\n  });\n\n  describe('axe.utils.getBaseLang', () => {\n    it('must be a function with the signature String -> String', () => {\n      assert.isString(axe.utils.getBaseLang(''));\n      assert.isString(axe.utils.getBaseLang('not empty'));\n    });\n  });\n\n  describe('axe.utils.validLangs', () => {\n    it('be a function with the signature * -> [String]', () => {\n      const langs = axe.utils.validLangs();\n      assert.isArray(langs);\n      langs.forEach(assert.isString);\n      assert.isArray(axe.utils.validLangs(document));\n    });\n  });\n\n  describe('axe.commons.dom.isVisible', () => {\n    it('must be a function with the signature Element -> Boolean', () => {\n      const el = randomNodeInTree(isElement);\n      assert.isBoolean(axe.commons.dom.isVisible(el));\n      assert.isBoolean(axe.commons.dom.isVisible(randomNodeInTree(isElement)));\n    });\n  });\n\n  describe('axe.commons.aria.implicitRole', () => {\n    it('must be a function with the signature Element -> String|null', () => {\n      axe.utils.getFlattenedTree(document.documentElement);\n      const implicitRolesOrNull = getEntries(\n        axe.commons.aria.lookupTable.role\n      ).reduce(\n        function (roles, entry) {\n          const role = entry[0];\n          const val = entry[1];\n          if (val.implicit) {\n            roles.push(role);\n          }\n          return roles;\n        },\n        [null]\n      );\n      const role = axe.commons.aria.implicitRole(randomNodeInTree());\n      assert.oneOf(role, implicitRolesOrNull);\n    });\n  });\n\n  describe('axe.commons.aria.lookupTable.role', () => {\n    it('must be an object (dict)', () => {\n      assert.isObject(axe.commons.aria.lookupTable.role);\n    });\n    it('must have the signature String -> String {role.type}', () => {\n      const keys = getKeys(axe.commons.aria.lookupTable.role);\n      const types = getValues(axe.commons.aria.lookupTable.role).map(\n        function (role) {\n          return role.type;\n        }\n      );\n      keys.forEach(assert.isString);\n      types.forEach(assert.isString);\n    });\n  });\n\n  describe('axe.utils.getFlattenedTree', () => {\n    it('must be a function with the signature Element -> [vnode]', () => {\n      assert.isArray(axe.utils.getFlattenedTree(document.body));\n      assert.lengthOf(\n        axe.utils.getFlattenedTree(randomNodeInTree(isElement)),\n        1\n      );\n    });\n  });\n\n  describe('axe.utils.getNodeFromTree', () => {\n    it('must be a function with the signature Node -> vnode', () => {\n      axe._tree = axe.utils.getFlattenedTree(document.body);\n      assert.oneOf(\n        axe.utils.getNodeFromTree(randomNodeInTree()),\n        flat(axe._tree[0])\n      );\n    });\n    it('must return null for nodes not in the axe._tree', () => {\n      assert.isNull(axe.utils.getNodeFromTree(randomElement()));\n    });\n  });\n\n  describe('axe.commons.dom.isOpaque', () => {\n    it('must be a function with the signature Element -> Boolean', () => {\n      assert.isBoolean(axe.commons.dom.isOpaque(randomNodeInTree(isElement)));\n    });\n  });\n\n  describe('axe.commons.text.accessibleTextVirtual', () => {\n    it('must be a function with the signature Element vnode -> String', () => {\n      axe._tree = axe.utils.getFlattenedTree(document.body);\n      const vnode = axe.utils.getNodeFromTree(randomNodeInTree(isElement));\n      assert.isString(axe.commons.text.accessibleTextVirtual(vnode));\n    });\n  });\n});\n\nconst elements = [\n  document.createElement('div'),\n  document.createElement('button'),\n  document.createElement('article')\n];\n\nconst inTree = [];\nconst treeWalker = collectNodes();\nlet next = treeWalker.iterate().next();\nwhile (!next.done) {\n  if (next.value.nodeType === 1) {\n    inTree.push(next.value);\n  }\n  next = treeWalker.iterate().next();\n}\n\nfunction isElement(el) {\n  return el.nodeType === Node.ELEMENT_NODE;\n}\n\nfunction random(fromArr) {\n  return function (filter) {\n    filter = filter || isTrue;\n    const arr = fromArr.filter(filter);\n    const seed = Math.random();\n    return arr[Math.floor(seed * arr.length)];\n  };\n}\n\nfunction randomNodeInTree(filter) {\n  return random(inTree)(filter);\n}\n\nfunction randomElement(filter) {\n  return random(elements)(filter);\n}\n\n// mimic tree: body and all element and text children\nfunction collectNodes() {\n  const walker = document.createTreeWalker(\n    document,\n    NodeFilter.SHOW_ALL,\n    function (node) {\n      if (!document.body.contains(node)) {\n        return NodeFilter.FILTER_SKIP;\n      }\n      return NodeFilter.FILTER_ACCEPT;\n    },\n    false\n  );\n  const nextNode = () => {\n    const value = walker.nextNode();\n    return {\n      value: value,\n      done: !value\n    };\n  };\n  walker.iterate = () => {\n    return { next: nextNode };\n  };\n  return walker;\n}\n\nfunction flat(tree) {\n  const result = [];\n  const insert = function (node) {\n    result.push(node);\n    (node.children || []).forEach(insert);\n  };\n  if (tree) {\n    insert(tree);\n  }\n  return result;\n}\n\nfunction isTrue() {\n  return true;\n}\n\nfunction getEntries(obj) {\n  const results = [];\n  let key;\n  for (key in obj) {\n    if (obj.hasOwnProperty(key)) {\n      results.push([key, obj[key]]);\n    }\n  }\n  return results;\n}\n\nfunction getValues(obj) {\n  return getEntries(obj).map(function (entry) {\n    return entry[1];\n  });\n}\n\nfunction getKeys(obj) {\n  return getEntries(obj).map(function (entry) {\n    return entry[0];\n  });\n}\n"
  },
  {
    "path": "test/integration/full/README.md",
    "content": "# Full Integration Tests\n\nFull Integration tests are tests that require a complete HTML page rather than testing content inside of a fixture element. Full integration tests are run using a non-headless browser and requires a page navigation to the relevant HTML test file.\n\nTo run the full integration tests, run `npm run test:integration`. Different browsers can be used using various npm scripts:\n\n- `npm run test:integration:chrome` - Run the tests using Chrome (default when using `test:integration`)\n- `npm run test:integration:firefox` - Run the tests using Firefox\n- `npm run test:integration:ie` - Run the tests using Internet Explorer (requires running on Windows machine)\n\nBecause the full integration tests are not run using Mocha, Mocha styles and scripts, Chai, axe-core, [testutils.js](../../tesstutils.js), and the [adapter.js](../adapter.js) file will need to be loaded on the page.\n\nIf the Mocha output would interfere with the axe-core results, you can load the [no-ui-reporter.js](../no-ui-reporter.js) to hide the Mocha test output in the browser UI and instead report the results in the browsers devtools console.\n"
  },
  {
    "path": "test/integration/full/all-rules/all-rules.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"en\">\n  <head>\n    <title>all rules test</title>\n    <meta charset=\"utf8\" />\n    <meta http-equiv=\"refresh\" content=\"foo\" />\n    <meta name=\"viewport\" content=\"maximum-scale=2\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div>\n      <a href=\"#fail1-tgt\" style=\"position: absolute; margin: -10000px\"\n        >bad link 1</a\n      >\n      <banner></banner>\n      <main>\n        <div accesskey=\"B\" id=\"__proto__\"></div>\n        <map>\n          <area href=\"#\" id=\"pass1\" alt=\"monkeys\" />\n        </map>\n        <div aria-label=\"foo\" id=\"constructor\">Foo</div>\n        <div role=\"contentinfo\"></div>\n        <div role=\"link\">Home</div>\n        <div role=\"dialog\" aria-label=\"Cookies\"></div>\n        <p aria-hidden=\"true\">Some text</p>\n        <div role=\"spinbutton\" aria-label=\"foo\"></div>\n        <div role=\"meter\" title=\"foo\"></div>\n        <div role=\"progressbar\" title=\"foo\"></div>\n        <div role=\"list\">\n          <div role=\"listitem\">Item 1</div>\n        </div>\n        <button aria-roledescription=\"my button\">button</button>\n        <div role=\"text\">Some text<span> and some more text</span></div>\n        <div role=\"checkbox\">Newspaper</div>\n        <div role=\"tooltip\">Copy this content</div>\n        <div role=\"tree\">\n          <div role=\"treeitem\">Item</div>\n        </div>\n        <div role=\"tab\">Tab Name</div>\n        <audio id=\"caption\"><track kind=\"captions\" /></audio>\n        <input autocomplete=\"username\" id=\"toString\" />\n        <p id=\"fail1\" style=\"line-height: 1.5 !important\">Banana error</p>\n        <p><blink>text</blink></p>\n        <button id=\"text\">Name</button>\n        <dl>\n          <dt>foo</dt>\n          <dd>bar</dd>\n        </dl>\n        <div id=\"foo\"></div>\n        <h1>Ok</h1>\n        <h2>Ok</h2>\n        <table>\n          <tr>\n            <th scope=\"col\">Ok</th>\n          </tr>\n        </table>\n\n        <img src=\"img.jpg\" alt=\"\" aria-braillelabel=\"image\" />\n        <video><track kind=\"captions\" /></video>\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          role=\"img\"\n          width=\"100\"\n          height=\"100\"\n        >\n          <title>I am a circle</title>\n          <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n        </svg>\n        <div lang=\"en\">English</div>\n        <object\n          title=\"This object has text\"\n          data=\"data:text/html,Object%20content\"\n        ></object>\n        <li role=\"presentation\" aria-label=\"My Heading\">Hello</li>\n        <div role=\"img\" aria-label=\"blah\"></div>\n        <div style=\"overflow-y: scroll; height: 5px\">\n          <input type=\"text\" />\n        </div>\n        <select aria-label=\"foo\"></select>\n        <img ismap src=\"image.jpg\" />\n        <p tabindex=\"-1\">Paragraph.</p>\n        <input type=\"button\" />\n        <input type=\"image\" src=\"img.jpg\" />\n        <marquee scrollamount=\"0\">This content is inside a marquee.</marquee>\n        <div role=\"navigation\">\n          <div role=\"banner\"></div>\n          <div role=\"complementary\"></div>\n        </div>\n        <span id=\"fail1\" class=\"fail1\"></span>\n        <button id=\"fail1\"></button>\n        <span id=\"pass1\"></span>\n        <button id=\"pass2\"></button>\n        <div aria-labelledby=\"fail1 pass1 pass2\"></div>\n        <audio\n          id=\"incomplete1\"\n          src=\"/test/assets/moon-speech.mp3\"\n          autoplay=\"true\"\n        ></audio>\n      </main>\n      <footer></footer>\n\n      <iframe\n        id=\"focusable-iframe\"\n        title=\"foo\"\n        src=\"frames/focusable.html\"\n      ></iframe>\n      <iframe\n        id=\"focusable-iframe\"\n        title=\"bar\"\n        src=\"frames/focusable.html\"\n        tabindex=\"-1\"\n      ></iframe>\n\n      <p>Paragraph with a <a href=\"#\">link</a>.</p>\n      <ul>\n        <li>Hello</li>\n        <li>World</li>\n      </ul>\n      <details>\n        <summary>pass</summary>\n        <p>Hello world</p>\n      </details>\n\n      <div style=\"height: 100vh\">Large scroll area</div>\n      <button id=\"end-of-page\">End of page</button>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"all-rules.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/all-rules/all-rules.js",
    "content": "describe('all rules test', () => {\n  let results;\n  before(done => {\n    axe.testUtils.awaitNestedLoad(async () => {\n      results = await axe.run();\n      done();\n    });\n  });\n\n  it('should run all rules', () => {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find same results when scrolled', async () => {\n    const endButton = document.querySelector('#end-of-page');\n    endButton.focus();\n    const scrollResults = await axe.run();\n    scrollResults.testEnvironment = results.testEnvironment;\n    scrollResults.timestamp = results.timestamp;\n    assert.deepEqual(results, scrollResults);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/all-rules/frames/focusable.html",
    "content": "<!doctype html>\n<html id=\"focusable\">\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <button>Click</button>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/aria-hidden-body/fail.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>aria-hidden on body Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body aria-hidden=\"true\">\n    <h2>Some title.</h2>\n    <a href=\"http://www.deque.com\">Deque</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/aria-hidden-body/fail.js",
    "content": "describe('aria-hidden on body test ' + window.location.pathname, function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['aria-hidden-body'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find some', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/aria-hidden-body/frames/frame-hidden-body.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body aria-hidden=\"true\"></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/aria-hidden-body/pass.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>aria-hidden on body Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body aria-hidden=\"false\">\n    <h2>Some title.</h2>\n    <a href=\"http://www.deque.com\">Deque</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n    <iframe src=\"frames/frame-hidden-body.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/aria-hidden-body/pass.js",
    "content": "describe('aria-hidden on body test ' + window.location.pathname, function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['aria-hidden-body'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/aria-hidden-focus/modal.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>html-has-lang test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <style>\n      .a {\n        position: fixed;\n        top: 0;\n        left: 0;\n        width: 100vw;\n        height: 100vh;\n        background: rgba(0, 0, 0, 0.5);\n        z-index: 99999;\n      }\n\n      .b {\n        position: fixed;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        top: 35%;\n        bottom: 35%;\n        left: 35%;\n        right: 35%;\n        background: white;\n        z-index: 999999;\n      }\n    </style>\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <main aria-hidden=\"true\" id=\"incomplete1\">\n      <p>Contents of page</p>\n      <button>Click me</button>\n    </main>\n    <div class=\"a\"></div>\n    <div class=\"b\">Subscribe to our newsletter!</div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"modal.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/aria-hidden-focus/modal.js",
    "content": "describe('aria-hidden-focus test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['aria-hidden-focus'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0 violations', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0 passes', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete[0].nodes, 1);\n    });\n\n    it('should find #incomplete1', function () {\n      assert.deepEqual(results.incomplete[0].nodes[0].target, ['#incomplete1']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/async/async.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Async Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <div class=\"async\" data-out=\"true\" id=\"pass\"></div>\n    <div class=\"async\" data-out=\"false\" id=\"violation\"></div>\n    <div class=\"async\" data-out=\"undefined\" id=\"incomplete\"></div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"async.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/async/async.js",
    "content": "describe('async rule test', function () {\n  'use strict';\n  var results;\n\n  function AsyncCheck(node) {\n    /*eslint indent: 0*/\n    var check = this;\n    var done = check.async();\n    setTimeout(function () {\n      var dataOut = node.getAttribute('data-out');\n      check.data(dataOut);\n      switch (dataOut) {\n        case 'true':\n          return done(true);\n\n        case 'false':\n          return done(false);\n\n        default:\n          return done(undefined);\n      }\n    }, 10);\n  }\n\n  before(function (done) {\n    axe.configure({\n      rules: [\n        {\n          id: 'my-async',\n          metadata: {\n            description: '',\n            help: '',\n            helpUrl: 'https://example.com/dylang'\n          },\n          selector: '.async',\n          any: ['my-async'],\n          all: [],\n          none: [],\n          tags: ['wcag2aa']\n        }\n      ],\n      checks: [\n        {\n          id: 'my-async',\n          options: [],\n          evaluate: AsyncCheck.toString(),\n          metadata: {\n            impact: 'critical',\n            messages: {\n              pass: 'function (out) { return \"passed with \" + out.data }',\n              fail: 'function (out) { return \"failed with \" + out.data }',\n              incomplete:\n                'function (out) { return \"incomplete with \" + out.data }'\n            }\n          }\n        }\n      ]\n    });\n\n    axe.run(\n      { runOnly: { type: 'rule', values: ['my-async'] } },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n\n    it('should find #violation', function () {\n      assert.equal(\n        results.violations[0].nodes[0].any[0].message,\n        'failed with false'\n      );\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#violation']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes, 1);\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass', function () {\n      assert.equal(\n        results.passes[0].nodes[0].any[0].message,\n        'passed with true'\n      );\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass']);\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete, 1);\n      assert.lengthOf(results.incomplete[0].nodes, 1);\n    });\n\n    it('should find #incomplete', function () {\n      assert.equal(\n        results.incomplete[0].nodes[0].any[0].message,\n        'incomplete with undefined'\n      );\n      assert.deepEqual(results.incomplete[0].nodes[0].target, ['#incomplete']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/bypass/aria-header.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"heading\">This header will make the test pass.</div>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass-tests.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/fail.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/fail.js",
    "content": "describe('bypass fail test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    var mocha = document.getElementById('mocha'),\n      html = mocha.innerHTML;\n    mocha.innerHTML = '';\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['bypass'] } },\n        function (err, r) {\n          assert.isNull(err);\n\n          results = r;\n          mocha.innerHTML = html;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete, 1);\n    });\n\n    it('should find html', function () {\n      assert.deepEqual(results.incomplete[0].nodes[0].target, ['html']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/bypass/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 here either</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 here either</p>\n    <iframe id=\"frame2\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/frames/level2-a.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1>This page has an h1</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 content in this iframe</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/header-iframe-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"fail1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No h1 content</p>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"header-iframe-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/header-iframe-fail.js",
    "content": "describe('bypass iframe test fail', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<div><b>bypass iframe test fail</b></div>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['bypass'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete[0].nodes, 1);\n    });\n\n    it('should find #frame1', function () {\n      assert.deepEqual(results.incomplete[0].nodes[0].target, ['#fail1']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/bypass/header-iframe-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No h1 content</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"header-iframe-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/header-iframe-pass.js",
    "content": "describe('bypass iframe test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    this.timeout = 50000;\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<div><b>bypass pass test fail</b></div>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['bypass'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/bypass/header1.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <h2>This header will make the test pass.</h2>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass-tests.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/header2.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <h1>This header will make the test pass.</h1>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass-tests.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/pass-tests.js",
    "content": "describe('bypass aria header test ' + window.location.pathname, function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['bypass'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find html', function () {\n      assert.isTrue(results.passes[0].nodes[0].target[0].startsWith('html'));\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/bypass/region.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <div role=\"main\">This header will make the test pass.</div>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass-tests.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/bypass/skip-link.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML+ARIA 1.0//EN\" \"http://www.w3.org/WAI/ARIA/schemata/html4-aria-1.dtd\">\n<html lang=\"en\">\n  <head>\n    <title>Bypass Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <a href=\"#mocha\">Skip</a>\n    <div>Test.</div>\n    <a href=\"http://www.deque.com\">stuff</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"pass-tests.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/configuration/tag-exclude.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"en\">\n  <head>\n    <title>axe.configure({ tagExclude }) test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <img alt=\"\" />\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"tag-exclude.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/configuration/tag-exclude.js",
    "content": "describe('all rules test', () => {\n  const experimentalRuleId = 'img-alt-experimental';\n  const deprecatedRuleId = 'img-alt-deprecated';\n\n  beforeEach(() => {\n    axe.configure({\n      rules: [\n        {\n          id: experimentalRuleId,\n          impact: 'critical',\n          selector: 'img',\n          tags: ['wcag2a', 'experimental'],\n          enabled: false,\n          metadata: {\n            description:\n              'Ensures <img> elements have alternate text or a role of none or presentation',\n            help: 'Images must have alternate text'\n          },\n          all: [],\n          any: ['has-alt'],\n          none: []\n        },\n        {\n          id: deprecatedRuleId,\n          impact: 'critical',\n          selector: 'img',\n          tags: ['wcag2a', 'deprecated'],\n          enabled: false,\n          metadata: {\n            description:\n              'Ensures <img> elements have alternate text or a role of none or presentation',\n            help: 'Images must have alternate text'\n          },\n          all: [],\n          any: ['has-alt'],\n          none: []\n        }\n      ]\n    });\n  });\n\n  after(() => {\n    axe.reset();\n  });\n\n  function findResult(results, ruleId) {\n    return [\n      ...results.violations,\n      ...results.passes,\n      ...results.incomplete,\n      ...results.inapplicable\n    ].find(result => result.id === ruleId);\n  }\n\n  it('does not run experimental rules by default', async () => {\n    const results = await axe.run({\n      runOnly: {\n        type: 'tags',\n        values: ['wcag2a']\n      }\n    });\n    assert.isUndefined(findResult(results, experimentalRuleId));\n  });\n\n  it('does not run deprecated rules by default', async () => {\n    const results = await axe.run({\n      runOnly: {\n        type: 'tags',\n        values: ['wcag2a']\n      }\n    });\n    assert.isUndefined(findResult(results, deprecatedRuleId));\n  });\n\n  it('runs tagExclude rules when enabled with { rules }', async () => {\n    const results = await axe.run({\n      runOnly: {\n        type: 'tags',\n        values: ['wcag2a']\n      },\n      rules: {\n        [experimentalRuleId]: { enabled: true },\n        [deprecatedRuleId]: { enabled: true }\n      }\n    });\n\n    assert.isDefined(findResult(results, experimentalRuleId));\n    assert.isDefined(findResult(results, deprecatedRuleId));\n  });\n\n  it('runs tagExclude rules when enabled with { runOnly: { type: rule } }', async () => {\n    const results = await axe.run({\n      runOnly: {\n        type: 'rule',\n        values: [experimentalRuleId, deprecatedRuleId]\n      }\n    });\n    assert.isDefined(findResult(results, experimentalRuleId));\n    assert.isDefined(findResult(results, deprecatedRuleId));\n  });\n\n  it('runs tagExclude rules when enabled with { runOnly: { type: tag } }', async () => {\n    const results = await axe.run({\n      runOnly: {\n        type: 'tag',\n        values: ['wcag2a', 'experimental', 'deprecated']\n      }\n    });\n    assert.isDefined(findResult(results, experimentalRuleId));\n    assert.isDefined(findResult(results, deprecatedRuleId));\n  });\n});\n"
  },
  {
    "path": "test/integration/full/configure-options/configure-options.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title></title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"target\"></div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"configure-options.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/configure-options/configure-options.js",
    "content": "describe('Configure Options', () => {\n  const target = document.querySelector('#target');\n\n  afterEach(() => {\n    axe.reset();\n    target.innerHTML = '';\n  });\n\n  describe('Check', () => {\n    describe('aria-allowed-attr', () => {\n      it('should allow an attribute supplied in options', done => {\n        target.setAttribute('role', 'separator');\n        target.setAttribute('aria-valuenow', '0');\n\n        axe.configure({\n          checks: [\n            {\n              id: 'aria-allowed-attr',\n              options: { separator: ['aria-valuenow'] }\n            }\n          ]\n        });\n        axe.run(\n          target,\n          {\n            runOnly: {\n              type: 'rule',\n              values: ['aria-allowed-attr']\n            }\n          },\n          function (error, results) {\n            assert.lengthOf(results.violations, 0, 'violations');\n            done();\n          }\n        );\n      });\n\n      it('should not normalize external check options', done => {\n        target.setAttribute('lang', 'en');\n\n        axe.configure({\n          checks: [\n            {\n              id: 'dylang',\n              options: ['dylan'],\n              evaluate:\n                'function (node, options) {\\n        const lang = (node.getAttribute(\"lang\") || \"\").trim().toLowerCase();\\n        const xmlLang = (node.getAttribute(\"xml:lang\") || \"\").trim().toLowerCase();\\n        const invalid = [];\\n        (options || []).forEach(function(cc) {\\n          cc = cc.toLowerCase();\\n          if (lang && (lang === cc || lang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\\n            lang = null;\\n          }\\n          if (xmlLang && (xmlLang === cc || xmlLang.indexOf(cc.toLowerCase() + \"-\") === 0)) {\\n            xmlLang = null;\\n          }\\n        });\\n        if (xmlLang) {\\n          invalid.push(\\'xml:lang=\"\\' + xmlLang + \\'\"\\');\\n        }\\n        if (lang) {\\n          invalid.push(\\'lang=\"\\' + lang + \\'\"\\');\\n        }\\n        if (invalid.length) {\\n          this.data(invalid);\\n          return true;\\n        }\\n        return false;\\n      }',\n              messages: {\n                pass: 'Good language',\n                fail: 'You mst use the DYLAN language'\n              }\n            }\n          ],\n          rules: [\n            {\n              id: 'dylang',\n              metadata: {\n                description:\n                  \"Ensures lang attributes have the value of 'dylan'\",\n                help: \"lang attribute must have the value of 'dylan'\"\n              },\n              selector: '#target',\n              any: [],\n              all: [],\n              none: ['dylang'],\n              tags: ['wcag2aa']\n            }\n          ],\n          data: {\n            rules: {\n              dylang: {\n                description:\n                  \"Ensures lang attributes have the value of 'dylan'\",\n                help: \"lang attribute must have the value of 'dylan'\"\n              }\n            }\n          }\n        });\n\n        axe.run(\n          '#target',\n          {\n            runOnly: {\n              type: 'rule',\n              values: ['dylang']\n            }\n          },\n          function (err, results) {\n            try {\n              assert.isNull(err);\n              assert.lengthOf(results.violations, 1, 'violations');\n              done();\n            } catch (e) {\n              done(e);\n            }\n          }\n        );\n      });\n    });\n\n    describe('aria-required-attr', () => {\n      it('should report unique attributes when supplied from options', done => {\n        target.setAttribute('role', 'slider');\n        axe.configure({\n          checks: [\n            {\n              id: 'aria-required-attr',\n              options: { slider: ['aria-snuggles'] }\n            }\n          ]\n        });\n        axe.run(\n          '#target',\n          {\n            runOnly: {\n              type: 'rule',\n              values: ['aria-required-attr']\n            }\n          },\n          function (error, results) {\n            assert.lengthOf(results.violations, 1, 'violations');\n            assert.sameMembers(results.violations[0].nodes[0].any[0].data, [\n              'aria-snuggles'\n            ]);\n            done();\n          }\n        );\n      });\n    });\n  });\n\n  describe('disableOtherRules', () => {\n    it('disables rules that are not in the `rules` array', done => {\n      axe.configure({\n        disableOtherRules: true,\n        rules: [\n          {\n            id: 'html-has-lang',\n            enabled: true\n          },\n          {\n            id: 'html-lang-valid',\n            enabled: false\n          }\n        ]\n      });\n\n      axe.run(function (error, results) {\n        assert.isNull(error);\n        assert.lengthOf(results.passes, 1, 'passes');\n        assert.equal(results.passes[0].id, 'html-has-lang');\n\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.incomplete, 0, 'incomplete');\n        assert.lengthOf(results.inapplicable, 0, 'inapplicable');\n        done();\n      });\n    });\n  });\n\n  describe('noHtml', () => {\n    const captureError = axe.testUtils.captureError;\n    it('prevents html property on nodes', done => {\n      target.setAttribute('role', 'slider');\n      axe.configure({\n        noHtml: true,\n        checks: [\n          {\n            id: 'aria-required-attr',\n            options: { slider: ['aria-snuggles'] }\n          }\n        ]\n      });\n      axe.run(\n        '#target',\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['aria-required-attr']\n          }\n        },\n        captureError(function (error, results) {\n          assert.isNull(error);\n          assert.isNull(results.violations[0].nodes[0].html);\n          done();\n        }, done)\n      );\n    });\n\n    it('prevents html property on nodes from iframes', done => {\n      const config = {\n        noHtml: true,\n        rules: [\n          {\n            id: 'div#target',\n            // purposefully don't match so the first result is from\n            // the iframe\n            selector: 'foo'\n          }\n        ]\n      };\n\n      const iframe = document.createElement('iframe');\n      iframe.src = '/test/mock/frames/noHtml-config.html';\n      iframe.onload = () => {\n        axe.configure(config);\n        axe.run(\n          '#target',\n          {\n            runOnly: {\n              type: 'rule',\n              values: ['div#target']\n            }\n          },\n          captureError(function (error, results) {\n            assert.isNull(error);\n            assert.deepEqual(results.passes[0].nodes[0].target, [\n              'iframe',\n              '#target'\n            ]);\n            assert.isNull(results.passes[0].nodes[0].html);\n            done();\n          }, done)\n        );\n      };\n      target.appendChild(iframe);\n    });\n\n    it('prevents html property in postMesage', done => {\n      const config = {\n        noHtml: true,\n        rules: [\n          {\n            id: 'div#target',\n            // purposefully don't match so the first result is from\n            // the iframe\n            selector: 'foo'\n          }\n        ]\n      };\n\n      const iframe = document.createElement('iframe');\n      iframe.src = '/test/mock/frames/noHtml-config.html';\n      iframe.onload = () => {\n        axe.configure(config);\n\n        axe.run('#target', {\n          runOnly: {\n            type: 'rule',\n            values: ['div#target']\n          }\n        });\n      };\n      target.appendChild(iframe);\n\n      window.addEventListener('message', function (evt) {\n        const data = JSON.parse(evt.data);\n        if (Array.isArray(data.payload)) {\n          try {\n            assert.isNull(data.payload[0].nodes[0].node.source);\n            done();\n          } catch (err) {\n            done(err);\n          }\n        }\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/context/context.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"frame-container\">\n      <iframe id=\"myframe\" src=\"frames/level1.html\"></iframe>\n    </div>\n    <ul style=\"display: none\">\n      <p>Text</p>\n      <p>Text 2</p>\n    </ul>\n    <div id=\"shadow-container\">\n      <p id=\"test-passes\">Passing Text</p>\n      <div id=\"shadow-host\" aria-sort=\"both\"></div>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script>\n      if (axe.testUtils.shadowSupport.v1) {\n        var host = document.getElementById('shadow-host');\n        var list = document.getElementsByTagName('ul')[0];\n        var shadowRoot = host.attachShadow({ mode: 'open' });\n        var shadowContent = document.createElement('div');\n        shadowContent.setAttribute('style', 'background-color: #333');\n        var content = document.createElement('p');\n        content.style.color = '#000';\n        content.textContent = 'Failing Text';\n        shadowContent.appendChild(content);\n        var shadowList = shadowContent.appendChild(list);\n        shadowList.style.display = 'block';\n        shadowRoot.appendChild(shadowContent);\n      }\n    </script>\n    <script src=\"context.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/context.js",
    "content": "describe('context test', function () {\n  'use strict';\n\n  var config = { runOnly: { type: 'rule', values: ['html-lang-valid'] } };\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n    axe._tree = undefined;\n  });\n\n  describe('direct exclude', function () {\n    describe('no include', function () {\n      it('should find no violations given a selector array', function (done) {\n        axe.run({ exclude: [['iframe']] }, config, function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(\n            results.passes[0].nodes,\n            1,\n            'context.html has a lang attribute'\n          );\n          done();\n        });\n      });\n\n      it('should find one violation given a multi-level selector array', function (done) {\n        axe.run(\n          { exclude: [['iframe', 'iframe']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 1, 'violations');\n            assert.lengthOf(\n              results.violations[0].nodes,\n              1,\n              'level1.html; 2-a & 2-b excluded'\n            );\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html (main doc) not excluded'\n            );\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a direct reference', function (done) {\n        axe.run(\n          { exclude: [document.querySelector('iframe')] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html has a lang attribute'\n            );\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a NodeList', function (done) {\n        axe.run(\n          { exclude: document.getElementsByTagName('iframe') },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html has a lang attribute'\n            );\n            done();\n          }\n        );\n      });\n    });\n\n    describe('body include', function () {\n      it('should find no violations given a selector array', function (done) {\n        axe.run(\n          { include: [document.body], exclude: [['iframe']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find one violation given a multi-level selector array', function (done) {\n        axe.run(\n          { include: [document.body], exclude: [['iframe', 'iframe']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 1, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a direct reference', function (done) {\n        axe.run(\n          {\n            include: [document.body],\n            exclude: [document.querySelector('iframe')]\n          },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a NodeList', function (done) {\n        axe.run(\n          {\n            include: [document.body],\n            exclude: document.getElementsByTagName('iframe')\n          },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n    });\n  });\n\n  describe('indirect exclude', function () {\n    it('should find no nodes', function (done) {\n      axe.run(\n        { include: [document.body], exclude: [['#myframe']] },\n        config,\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 0, 'passes');\n          done();\n        }\n      );\n    });\n\n    (shadowSupported ? it : xit)(\n      'should find no nodes in Shadow DOM',\n      function (done) {\n        var sConfig = { runOnly: { type: 'rule', values: ['color-contrast'] } };\n        axe.run(\n          { include: [['#shadow-container']], exclude: [['#shadow-host']] },\n          sConfig,\n          function (err, results) {\n            try {\n              assert.isNull(err);\n              assert.lengthOf(results.violations, 0, 'violations');\n              assert.lengthOf(results.passes, 1, 'passes');\n              done();\n            } catch (e) {\n              done(e);\n            }\n          }\n        );\n      }\n    );\n\n    describe('no include', function () {\n      it('should find no violations given a selector array', function (done) {\n        axe.run(\n          { exclude: [['#frame-container']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html has a lang attribute'\n            );\n            done();\n          }\n        );\n      });\n\n      it('should find one violation given a multi-level selector array', function (done) {\n        axe.run(\n          { exclude: [['iframe', 'body']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 1, 'violations');\n            assert.lengthOf(\n              results.violations[0].nodes,\n              1,\n              'level1.html; 2-a & 2-b excluded'\n            );\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html (main doc) not excluded'\n            );\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a direct reference', function (done) {\n        axe.run(\n          { exclude: [document.querySelector('#frame-container')] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html has a lang attribute'\n            );\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a NodeList', function (done) {\n        axe.run(\n          { exclude: document.getElementsByTagName('div') },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 1, 'passes');\n            assert.lengthOf(\n              results.passes[0].nodes,\n              1,\n              'context.html has a lang attribute'\n            );\n            done();\n          }\n        );\n      });\n    });\n\n    describe('body include', function () {\n      it('should find no violations given a selector array', function (done) {\n        axe.run(\n          { include: [document.body], exclude: [['#frame-container']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find one violation given a multi-level selector array', function (done) {\n        axe.run(\n          { include: [document.body], exclude: [['iframe', 'body']] },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 1, 'violations');\n            assert.lengthOf(\n              results.violations[0].nodes,\n              1,\n              'level1.html; 2-a & 2-b excluded'\n            );\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a direct reference', function (done) {\n        axe.run(\n          {\n            include: [document.body],\n            exclude: [document.querySelector('#frame-container')]\n          },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n\n      it('should find no violations given a NodeList', function (done) {\n        axe.run(\n          {\n            include: [document.body],\n            exclude: document.getElementsByTagName('div')\n          },\n          config,\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 0, 'violations');\n            assert.lengthOf(results.passes, 0, 'passes');\n            done();\n          }\n        );\n      });\n    });\n  });\n\n  describe('direct include', function () {\n    it('should find the frames given a context object', function (done) {\n      axe.run({ include: [['#myframe']] }, config, function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      });\n    });\n    it('should find the frames given a direct reference', function (done) {\n      axe.run(\n        document.getElementById('myframe'),\n        config,\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 1, 'violations');\n          assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n          assert.lengthOf(results.passes, 0, 'passes');\n          done();\n        }\n      );\n    });\n    it('should find the frames given a NodeList', function (done) {\n      axe.run(\n        document.getElementsByTagName('iframe'),\n        config,\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 1, 'violations');\n          assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n          assert.lengthOf(results.passes, 0, 'passes');\n          done();\n        }\n      );\n    });\n\n    describe('Shadow DOM', function () {\n      var sConfig = {\n        runOnly: {\n          type: 'rule',\n          values: ['aria-allowed-attr', 'color-contrast']\n        }\n      };\n      it('when passed a shadow host, reports issues both on itself and in the shadow DOM', function (done) {\n        axe.run('#shadow-host', sConfig, function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 2, 'violations');\n          var allowedAttrsViolations = results.violations.filter(\n            function (violation) {\n              return violation.id === 'aria-allowed-attr';\n            }\n          );\n          assert.lengthOf(\n            allowedAttrsViolations,\n            1,\n            'aria allowed attrs violations'\n          );\n          done();\n        });\n      });\n      it('when passed a shadow root, reports issues in the shadow DOM, but not on the host', function (done) {\n        var host = document.querySelector('#shadow-host');\n        var shadowRoot = host.shadowRoot;\n        axe.run(shadowRoot, sConfig, function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 1, 'violations');\n          var allowedAttrsViolations = results.violations.filter(\n            function (violation) {\n              return violation.id === 'aria-allowed-attr';\n            }\n          );\n          assert.lengthOf(\n            allowedAttrsViolations,\n            0,\n            'aria allowed attrs violations'\n          );\n          done();\n        });\n      });\n    });\n  });\n\n  describe('indirect include', function () {\n    it('should find the frames given context object with a node reference', function (done) {\n      axe.run({ include: [document.body] }, config, function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      });\n    });\n    it('should find the frames give a node', function (done) {\n      axe.run(document.body, config, function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      });\n    });\n    it('should find the frames give a NodeList', function (done) {\n      axe.run(\n        document.getElementsByTagName('body'),\n        config,\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 1, 'violations');\n          assert.lengthOf(results.violations[0].nodes, 3, 'violation nodes');\n          assert.lengthOf(results.passes, 0, 'passes');\n          done();\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/context/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"level1\" lang=\"@(#$*\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame2-a\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame2-b\" src=\"level2-b.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"level2-a\" lang=\"!@£\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/frames/level2-b.html",
    "content": "<!doctype html>\n<html id=\"level2-b\" xml:lang=\"$%^\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/frames/shadow-frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Shadow frame</title>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <input id=\"outOfContext3\" />\n    <div id=\"shadowFrameHost\">\n      <input id=\"outOfContext4\" />\n    </div>\n    <script>\n      const shadowRoot = shadowFrameHost.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = `<main>\n        <div> <input id=\"fail\" value=\"fail\" /> </div>\n        <aside> <input id=\"outOfContext5\" /> </aside>\n      </main>\n      <slot />`;\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/shadow-dom.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      const assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <input id=\"outOfContext1\" />\n    <div id=\"shadowHost\">\n      <input id=\"outOfContext2\" />\n    </div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script>\n      const shadowRoot = shadowHost.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = `<iframe id=\"shadowFrame1\" src=\"frames/shadow-frame.html\"></iframe>\n        <iframe id=\"shadowFrame2\" src=\"frames/shadow-frame.html\"></iframe>\n        <br>\n        <slot />`;\n    </script>\n\n    <script src=\"shadow-dom.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/context/shadow-dom.js",
    "content": "describe('context test', () => {\n  before(done => {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  it('is able to include & exclude from frames in shadow DOM trees', async () => {\n    const { violations } = await axe.run(\n      {\n        include: [\n          [\n            ['#shadowHost', '#shadowFrame1'],\n            ['#shadowFrameHost', 'main']\n          ],\n          [\n            ['#shadowHost', '#shadowFrame2'],\n            ['#shadowFrameHost', 'main']\n          ]\n        ],\n        exclude: [\n          [\n            ['#shadowHost', '#shadowFrame1'],\n            ['#shadowFrameHost', 'main aside']\n          ],\n          [\n            ['#shadowHost', '#shadowFrame2'],\n            ['#shadowFrameHost', 'main aside']\n          ]\n        ]\n      },\n      { runOnly: 'label' }\n    );\n\n    const targets = violations[0].nodes.map(({ target }) => target);\n    assert.deepEqual(targets, [\n      [\n        ['#shadowHost', '#shadowFrame1'],\n        ['#shadowFrameHost', '#fail']\n      ],\n      [\n        ['#shadowHost', '#shadowFrame2'],\n        ['#shadowFrameHost', '#fail']\n      ]\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/blending.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Color Contrast Blending Verification Tests</title>\n\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <style>\n      body {\n        margin: 4rem 2rem;\n      }\n\n      .test-group {\n        position: relative;\n        z-index: 1;\n        display: grid;\n        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n      }\n\n      .test-group > div {\n        display: flex;\n        flex-direction: row;\n        border: 1px solid white;\n      }\n\n      .test-group * {\n        width: 100px;\n        height: 100px;\n        flex-shrink: 0;\n      }\n    </style>\n  </head>\n\n  <body>\n    <p>\n      Use this page to verify that axe-core produces the correct colors for each\n      blended background. <b>If using Chrome</b>, please ensure it is using the\n      sRGB color profile by navigating to chrome://flags/, searching for \"Force\n      color profile\" and setting it to \"sRGB\" (otherwise it uses the OS color\n      profile which for Mac, which we believe is \"Display P3 D65\" and will\n      produce the incorrect result color when blending).\n    </p>\n    <p>\n      For more information, see\n      <a href=\"https://github.com/dequelabs/axe-core/issues/2924\"\n        >https://github.com/dequelabs/axe-core/issues/2924</a\n      >\n    </p>\n    <div id=\"fixture\">\n      <h2>normal</h2>\n      <div class=\"test-group\">\n        <div id=\"test1\">\n          <div style=\"background-color: rgba(255, 255, 255, 1)\">\n            <div style=\"background-color: rgba(0, 128, 0, 0.25)\">\n              <div\n                id=\"test1-target\"\n                style=\"background-color: rgba(255, 0, 0, 0.5)\"\n              >\n                Test1\n              </div>\n            </div>\n          </div>\n          <div id=\"test1-result\">Test1 result</div>\n        </div>\n\n        <div id=\"test2\">\n          <div style=\"background-color: rgba(255, 255, 255, 1)\">\n            <div\n              id=\"test2-target\"\n              style=\"background-color: rgba(255, 0, 0, 0.5)\"\n            >\n              Test2\n            </div>\n          </div>\n          <div id=\"test2-result\">Test2 result</div>\n        </div>\n\n        <div id=\"test3\">\n          <div style=\"background-color: rgba(255, 255, 255, 1)\">\n            <div\n              id=\"test3-target\"\n              style=\"background-color: rgba(0, 128, 0, 0.25)\"\n            >\n              Test3\n            </div>\n          </div>\n          <div id=\"test3-result\">Test3 result</div>\n        </div>\n\n        <div id=\"test4\">\n          <div style=\"background-color: red\">\n            <div style=\"background-color: rgba(0, 0, 255, 0.3)\">\n              <div\n                id=\"test4-target\"\n                style=\"background-color: rgba(0, 128, 0, 0.3)\"\n              >\n                Test4\n              </div>\n            </div>\n          </div>\n          <div id=\"test4-result\">Test4 result</div>\n        </div>\n\n        <div id=\"test5\">\n          <div style=\"background-color: red\">\n            <div\n              id=\"test5-target\"\n              style=\"background-color: rgba(0, 128, 0, 0.3)\"\n            >\n              Test5\n            </div>\n          </div>\n          <div id=\"test5-result\">Test5 result</div>\n        </div>\n\n        <div id=\"test6\">\n          <div style=\"background-color: red\">\n            <div\n              id=\"test6-target\"\n              style=\"background-color: rgba(0, 0, 255, 0.3)\"\n            >\n              Test6\n            </div>\n          </div>\n          <div id=\"test6-result\">Test6 result</div>\n        </div>\n\n        <div id=\"test7\">\n          <div style=\"background-color: rgba(255, 0, 0, 0.25)\">\n            <div style=\"background-color: rgba(255, 0, 0, 0.25)\">\n              <div style=\"background-color: rgba(0, 255, 0, 0.25)\">\n                <div\n                  id=\"test7-target\"\n                  style=\"background-color: rgba(0, 255, 0, 0.25)\"\n                >\n                  Test7\n                </div>\n              </div>\n            </div>\n          </div>\n          <div id=\"test7-result\">Test7 result</div>\n        </div>\n\n        <div id=\"test8\">\n          <div style=\"background-color: rgba(255, 0, 0, 0.25)\">\n            <div style=\"background-color: rgba(255, 0, 0, 0.25)\">\n              <div style=\"background-color: rgba(0, 255, 0, 0.25)\">\n                <div style=\"background-color: rgba(0, 255, 0, 0.1)\">\n                  <div style=\"background-color: rgba(0, 0, 255, 0.1)\">\n                    <div\n                      id=\"test8-target\"\n                      style=\"background-color: rgba(0, 0, 255, 0.05)\"\n                    >\n                      Test8\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div id=\"test8-result\">Test8 result</div>\n        </div>\n\n        <div id=\"test9\">\n          <div style=\"background-color: rgba(204, 204, 204, 0.43)\">\n            <div\n              id=\"test9-target\"\n              style=\"background-color: rgba(204, 204, 204, 0.43)\"\n            >\n              Test9\n            </div>\n          </div>\n          <div id=\"test9-result\">Test9 result</div>\n        </div>\n      </div>\n    </div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"blending.js\"></script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/blending.js",
    "content": "describe('color-contrast blending test', () => {\n  const include = [];\n  const resultElms = [];\n  const expected = [\n    // normal\n    'rgb(223, 112, 96)',\n    'rgb(255, 128, 128)',\n    'rgb(191, 223, 191)',\n    'rgb(125, 38, 54)',\n    'rgb(179, 38, 0)',\n    'rgb(179, 0, 77)',\n    'rgb(144, 192, 81)',\n    'rgb(147, 154, 120)',\n    'rgb(221, 221, 221)',\n    // multiply\n    'rgb(191, 112, 96)',\n    'rgb(255, 128, 128)',\n    'rgb(191, 223, 191)',\n    'rgb(125, 0, 54)',\n    'rgb(179, 0, 0)',\n    'rgb(179, 0, 0)',\n    'rgb(144, 171, 81)',\n    'rgb(147, 154, 112)',\n    'rgb(213, 213, 213)',\n    // screen\n    'rgb(223, 223, 191)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(179, 38, 77)',\n    'rgb(255, 38, 0)',\n    'rgb(255, 0, 77)',\n    'rgb(165, 192, 81)',\n    'rgb(150, 157, 120)',\n    'rgb(228, 228, 228)',\n    // overlay\n    'rgb(223, 207, 159)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(156, 0, 54)',\n    'rgb(255, 0, 0)',\n    'rgb(255, 0, 0)',\n    'rgb(148, 187, 81)',\n    'rgb(147, 154, 114)',\n    'rgb(226, 226, 226)',\n    // darken\n    'rgb(191, 112, 96)',\n    'rgb(255, 128, 128)',\n    'rgb(191, 223, 191)',\n    'rgb(125, 0, 54)',\n    'rgb(179, 0, 0)',\n    'rgb(179, 0, 0)',\n    'rgb(144, 171, 81)',\n    'rgb(147, 154, 112)',\n    'rgb(221, 221, 221)',\n    // lighten\n    'rgb(223, 223, 191)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(179, 38, 77)',\n    'rgb(255, 38, 0)',\n    'rgb(255, 0, 77)',\n    'rgb(165, 192, 81)',\n    'rgb(150, 157, 120)',\n    'rgb(221, 221, 221)',\n    // color-dodge\n    'rgb(223, 223, 191)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(179, 0, 77)',\n    'rgb(255, 0, 0)',\n    'rgb(255, 0, 0)',\n    'rgb(165, 192, 81)',\n    'rgb(150, 157, 120)',\n    'rgb(230, 230, 230)',\n    // color-burn\n    'rgb(191, 112, 96)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(125, 0, 54)',\n    'rgb(255, 0, 0)',\n    'rgb(255, 0, 0)',\n    'rgb(144, 171, 81)',\n    'rgb(147, 154, 112)',\n    'rgb(219, 219, 219)',\n    // hard-light\n    'rgb(223, 112, 96)',\n    'rgb(255, 128, 128)',\n    'rgb(191, 255, 191)',\n    'rgb(125, 0, 54)',\n    'rgb(179, 0, 0)',\n    'rgb(179, 0, 77)',\n    'rgb(144, 192, 81)',\n    'rgb(147, 154, 120)',\n    'rgb(226, 226, 226)',\n    // soft-light\n    'rgb(206, 209, 167)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(163, 0, 61)',\n    'rgb(255, 0, 0)',\n    'rgb(255, 0, 0)',\n    'rgb(155, 180, 81)',\n    'rgb(148, 155, 115)',\n    'rgb(223, 223, 223)',\n    // difference\n    'rgb(128, 223, 191)',\n    'rgb(128, 255, 255)',\n    'rgb(255, 223, 255)',\n    'rgb(179, 38, 77)',\n    'rgb(255, 38, 0)',\n    'rgb(255, 0, 77)',\n    'rgb(165, 176, 81)',\n    'rgb(150, 157, 119)',\n    'rgb(183, 183, 183)',\n    // exclusion\n    'rgb(128, 223, 191)',\n    'rgb(128, 255, 255)',\n    'rgb(255, 223, 255)',\n    'rgb(179, 38, 77)',\n    'rgb(255, 38, 0)',\n    'rgb(255, 0, 77)',\n    'rgb(165, 176, 81)',\n    'rgb(150, 157, 119)',\n    'rgb(198, 198, 198)',\n    // hue\n    'rgb(212, 212, 196)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(125, 32, 54)',\n    'rgb(179, 39, 0)',\n    'rgb(195, 16, 77)',\n    'rgb(147, 180, 84)',\n    'rgb(150, 156, 117)',\n    'rgb(221, 221, 221)',\n    // saturation\n    'rgb(168, 239, 168)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(169, 5, 76)',\n    'rgb(228, 11, 11)',\n    'rgb(255, 0, 0)',\n    'rgb(165, 171, 81)',\n    'rgb(150, 157, 112)',\n    'rgb(221, 221, 221)',\n    // color\n    'rgb(223, 207, 191)',\n    'rgb(255, 255, 255)',\n    'rgb(255, 255, 255)',\n    'rgb(125, 32, 54)',\n    'rgb(179, 39, 0)',\n    'rgb(195, 16, 77)',\n    'rgb(144, 182, 81)',\n    'rgb(150, 156, 120)',\n    'rgb(221, 221, 221)',\n    // luminosity\n    'rgb(124, 156, 124)',\n    'rgb(166, 166, 166)',\n    'rgb(210, 210, 210)',\n    'rgb(183, 4, 81)',\n    'rgb(254, 0, 0)',\n    'rgb(207, 0, 0)',\n    'rgb(171, 177, 87)',\n    'rgb(148, 154, 112)',\n    'rgb(221, 221, 221)'\n  ];\n\n  const fixture = document.querySelector('#fixture');\n  const testGroup = document.querySelector('.test-group');\n  [\n    'multiply',\n    'screen',\n    'overlay',\n    'darken',\n    'lighten',\n    'color-dodge',\n    'color-burn',\n    'hard-light',\n    'soft-light',\n    'difference',\n    'exclusion',\n    'hue',\n    'saturation',\n    'color',\n    'luminosity'\n  ].forEach(blendMode => {\n    const nodes = testGroup.cloneNode(true);\n    const group = testGroup.cloneNode();\n\n    const heading = document.createElement('h2');\n    heading.textContent = blendMode;\n    fixture.appendChild(heading);\n\n    Array.from(nodes.children).forEach((node, index) => {\n      const id = node.id;\n      const target = node.querySelector('#' + id + '-target');\n      const result = node.querySelector('#' + id + '-result');\n      const blendModeIndex = blendMode + (index + 1);\n\n      node.id = blendModeIndex;\n      target.id = blendModeIndex + '-target';\n      result.id = blendModeIndex + '-result';\n\n      target.textContent = blendModeIndex;\n      result.textContent = blendModeIndex + ' result';\n\n      target.style.mixBlendMode = blendMode;\n      group.appendChild(node);\n    });\n\n    fixture.appendChild(group);\n  });\n  const testElms = Array.from(document.querySelectorAll('.test-group > div'));\n  testElms.forEach(testElm => {\n    const id = testElm.id;\n    const target = testElm.querySelector('#' + id + '-target');\n    const result = testElm.querySelector('#' + id + '-result');\n    include.push(target);\n    resultElms.push(result);\n  });\n\n  before(done => {\n    axe.run(\n      { include: include },\n      { runOnly: ['color-contrast'] },\n      (err, res) => {\n        assert.isNull(err);\n\n        // don't care where the result goes as we just want to\n        // extract the background color for each one\n        const results = []\n          .concat(res.passes)\n          .concat(res.violations)\n          .concat(res.incomplete);\n        results.forEach(result => {\n          result.nodes.forEach(node => {\n            const bgColor = node.any[0].data.bgColor;\n            const id = node.target[0].substring(\n              0,\n              node.target[0].lastIndexOf('-')\n            );\n            const resultNode = document.querySelector(id + '-result');\n            resultNode.style.backgroundColor = bgColor;\n          });\n        });\n\n        done();\n      }\n    );\n  });\n\n  resultElms.forEach((elm, index) => {\n    it('produces the correct blended color for ' + elm.id, () => {\n      const style = window.getComputedStyle(elm);\n      assert.equal(style.getPropertyValue('background-color'), expected[index]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/code-highlighting.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <link rel=\"stylesheet\" href=\"./prism.min.css\" />\n    <link rel=\"stylesheet\" href=\"./prism-okaidia.min.css\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style></style>\n  </head>\n\n  <body>\n    <div id=\"fixture\">\n      <pre>\n        <code class=\"language-html\">&lt;!DOCTYPE html&gt;\n          &lt;html lang=\"en\"&gt;\n            &lt;head&gt;\n              &lt;title&gt;Test Page&lt;/title&gt;\n              &lt;link\n                rel=\"stylesheet\"\n                type=\"text/css\"\n                href=\"/node_modules/mocha/mocha.css\"\n              /&gt;\n              &lt;script src=\"/node_modules/mocha/mocha.js\"&gt;&lt;/script&gt;\n              &lt;script src=\"/node_modules/chai/chai.js\"&gt;&lt;/script&gt;\n              &lt;script src=\"/axe.js\"&gt;&lt;/script&gt;\n              &lt;script&gt;\n                mocha.setup({\n                  timeout: 10000,\n                  ui: 'bdd'\n                });\n                var assert = chai.assert;\n              &lt;/script&gt;\n              &lt;script src=\"/test/integration/no-ui-reporter.js\"&gt;&lt;/script&gt;\n              &lt;style&gt;&lt;/style&gt;\n            &lt;/head&gt;\n\n            &lt;body&gt;\n              &lt;pre&gt;\n                &lt;code&gt;\n\n                &lt;/code&gt;\n              &lt;/pre&gt;\n              &lt;script src=\"/test/testutils.js\"&gt;&lt;/script&gt;\n              &lt;script src=\"code-highlighting.js\"&gt;&lt;/script&gt;\n              &lt;script src=\"/test/integration/adapter.js\"&gt;&lt;/script&gt;\n            &lt;/body&gt;\n          &lt;/html&gt;</code>\n      </pre>\n    </div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"code-highlighting.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/code-highlighting.js",
    "content": "describe('color-contrast code highlighting test', function () {\n  'use strict';\n\n  var results;\n  function run(done) {\n    axe.run(\n      '#fixture',\n      { runOnly: { type: 'rule', values: ['color-contrast'] } },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  }\n\n  before(function (done) {\n    // wait for window load event (or if the window has already loaded) so the\n    // prism styles have loaded before running the tests (in Chrome the load\n    // even was already fired before Mocha starts the test suite)\n    if (document.readyState === 'complete') {\n      run(done);\n    } else {\n      window.addEventListener('load', function () {\n        run(done);\n      });\n    }\n  });\n\n  describe('violations', function () {\n    it('should find issues', function () {\n      assert.lengthOf(results.violations, 1);\n      assert.lengthOf(results.violations[0].nodes, 32);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find passes', function () {\n      assert.lengthOf(results.passes, 1);\n      assert.lengthOf(results.passes[0].nodes, 28);\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 0 incomplete', function () {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/memory.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 2000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style>\n      * {\n        box-sizing: border-box;\n      }\n      html,\n      body {\n        padding: 0;\n        margin: 0;\n        height: 100%;\n        background: #fff;\n        color: #000;\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <div style=\"overflow: hidden; width: 200px; height: 200px\">\n        <!-- browsers each have their own CSS size limit so this number (which should be greater than the limit) will be capped to that -->\n        <div style=\"width: 1e9px; height: 1e9px\">Hello World</div>\n      </div>\n    </div>\n    <script src=\"memory.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/memory.js",
    "content": "/*\n  Since we can't easily test for memory issue we'll assume that if this test doesn't time out then the memory issue isn't a problem\n*/\ndescribe('color-contrast memory test', () => {\n  describe('violations', () => {\n    it('should find none', done => {\n      axe.run(\n        '#fixture',\n        { runOnly: { type: 'rule', values: ['color-contrast'] } },\n        (err, results) => {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 0);\n          done();\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/prototype-lib-1.7.3.js",
    "content": "/* eslint-disable */\n/*  Prototype JavaScript framework, version 1.7.3\n *  (c) 2005-2010 Sam Stephenson\n *\n *  Prototype is freely distributable under the terms of an MIT-style license.\n *  For details, see the Prototype web site: http://www.prototypejs.org/\n *\n *--------------------------------------------------------------------------*/\n\nvar Prototype = {\n  Version: '1.7.3',\n\n  Browser: (function () {\n    var ua = navigator.userAgent;\n    var isOpera =\n      Object.prototype.toString.call(window.opera) == '[object Opera]';\n    return {\n      IE: !!window.attachEvent && !isOpera,\n      Opera: isOpera,\n      WebKit: ua.indexOf('AppleWebKit/') > -1,\n      Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,\n      MobileSafari: /Apple.*Mobile/.test(ua)\n    };\n  })(),\n\n  BrowserFeatures: {\n    XPath: !!document.evaluate,\n\n    SelectorsAPI: !!document.querySelector,\n\n    ElementExtensions: (function () {\n      var constructor = window.Element || window.HTMLElement;\n      return !!(constructor && constructor.prototype);\n    })(),\n    SpecificElementExtensions: (function () {\n      if (typeof window.HTMLDivElement !== 'undefined') return true;\n\n      var div = document.createElement('div'),\n        form = document.createElement('form'),\n        isSupported = false;\n\n      if (div['__proto__'] && div['__proto__'] !== form['__proto__']) {\n        isSupported = true;\n      }\n\n      div = form = null;\n\n      return isSupported;\n    })()\n  },\n\n  ScriptFragment: '<script[^>]*>([\\\\S\\\\s]*?)</script\\\\s*>',\n  JSONFilter: /^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,\n\n  emptyFunction: function () {},\n\n  K: function (x) {\n    return x;\n  }\n};\n\nif (Prototype.Browser.MobileSafari)\n  Prototype.BrowserFeatures.SpecificElementExtensions = false;\n/* Based on Alex Arnell's inheritance implementation. */\n\nvar Class = (function () {\n  var IS_DONTENUM_BUGGY = (function () {\n    for (var p in { toString: 1 }) {\n      if (p === 'toString') return false;\n    }\n    return true;\n  })();\n\n  function subclass() {}\n  function create() {\n    var parent = null,\n      properties = $A(arguments);\n    if (Object.isFunction(properties[0])) parent = properties.shift();\n\n    function klass() {\n      this.initialize.apply(this, arguments);\n    }\n\n    Object.extend(klass, Class.Methods);\n    klass.superclass = parent;\n    klass.subclasses = [];\n\n    if (parent) {\n      subclass.prototype = parent.prototype;\n      klass.prototype = new subclass();\n      parent.subclasses.push(klass);\n    }\n\n    for (var i = 0, length = properties.length; i < length; i++)\n      klass.addMethods(properties[i]);\n\n    if (!klass.prototype.initialize)\n      klass.prototype.initialize = Prototype.emptyFunction;\n\n    klass.prototype.constructor = klass;\n    return klass;\n  }\n\n  function addMethods(source) {\n    var ancestor = this.superclass && this.superclass.prototype,\n      properties = Object.keys(source);\n\n    if (IS_DONTENUM_BUGGY) {\n      if (source.toString != Object.prototype.toString)\n        properties.push('toString');\n      if (source.valueOf != Object.prototype.valueOf)\n        properties.push('valueOf');\n    }\n\n    for (var i = 0, length = properties.length; i < length; i++) {\n      var property = properties[i],\n        value = source[property];\n      if (\n        ancestor &&\n        Object.isFunction(value) &&\n        value.argumentNames()[0] == '$super'\n      ) {\n        var method = value;\n        value = (function (m) {\n          return function () {\n            return ancestor[m].apply(this, arguments);\n          };\n        })(property).wrap(method);\n\n        value.valueOf = (function (method) {\n          return function () {\n            return method.valueOf.call(method);\n          };\n        })(method);\n\n        value.toString = (function (method) {\n          return function () {\n            return method.toString.call(method);\n          };\n        })(method);\n      }\n      this.prototype[property] = value;\n    }\n\n    return this;\n  }\n\n  return {\n    create: create,\n    Methods: {\n      addMethods: addMethods\n    }\n  };\n})();\n(function () {\n  var _toString = Object.prototype.toString,\n    _hasOwnProperty = Object.prototype.hasOwnProperty,\n    NULL_TYPE = 'Null',\n    UNDEFINED_TYPE = 'Undefined',\n    BOOLEAN_TYPE = 'Boolean',\n    NUMBER_TYPE = 'Number',\n    STRING_TYPE = 'String',\n    OBJECT_TYPE = 'Object',\n    FUNCTION_CLASS = '[object Function]',\n    BOOLEAN_CLASS = '[object Boolean]',\n    NUMBER_CLASS = '[object Number]',\n    STRING_CLASS = '[object String]',\n    ARRAY_CLASS = '[object Array]',\n    DATE_CLASS = '[object Date]',\n    NATIVE_JSON_STRINGIFY_SUPPORT =\n      window.JSON &&\n      typeof JSON.stringify === 'function' &&\n      JSON.stringify(0) === '0' &&\n      typeof JSON.stringify(Prototype.K) === 'undefined';\n\n  var DONT_ENUMS = [\n    'toString',\n    'toLocaleString',\n    'valueOf',\n    'hasOwnProperty',\n    'isPrototypeOf',\n    'propertyIsEnumerable',\n    'constructor'\n  ];\n\n  var IS_DONTENUM_BUGGY = (function () {\n    for (var p in { toString: 1 }) {\n      if (p === 'toString') return false;\n    }\n    return true;\n  })();\n\n  function Type(o) {\n    switch (o) {\n      case null:\n        return NULL_TYPE;\n      case void 0:\n        return UNDEFINED_TYPE;\n    }\n    var type = typeof o;\n    switch (type) {\n      case 'boolean':\n        return BOOLEAN_TYPE;\n      case 'number':\n        return NUMBER_TYPE;\n      case 'string':\n        return STRING_TYPE;\n    }\n    return OBJECT_TYPE;\n  }\n\n  function extend(destination, source) {\n    for (var property in source) destination[property] = source[property];\n    return destination;\n  }\n\n  function inspect(object) {\n    try {\n      if (isUndefined(object)) return 'undefined';\n      if (object === null) return 'null';\n      return object.inspect ? object.inspect() : String(object);\n    } catch (e) {\n      if (e instanceof RangeError) return '...';\n      throw e;\n    }\n  }\n\n  function toJSON(value) {\n    return Str('', { '': value }, []);\n  }\n\n  function Str(key, holder, stack) {\n    var value = holder[key];\n    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {\n      value = value.toJSON(key);\n    }\n\n    var _class = _toString.call(value);\n\n    switch (_class) {\n      case NUMBER_CLASS:\n      case BOOLEAN_CLASS:\n      case STRING_CLASS:\n        value = value.valueOf();\n    }\n\n    switch (value) {\n      case null:\n        return 'null';\n      case true:\n        return 'true';\n      case false:\n        return 'false';\n    }\n\n    var type = typeof value;\n    switch (type) {\n      case 'string':\n        return value.inspect(true);\n      case 'number':\n        return isFinite(value) ? String(value) : 'null';\n      case 'object':\n        for (var i = 0, length = stack.length; i < length; i++) {\n          if (stack[i] === value) {\n            throw new TypeError(\n              \"Cyclic reference to '\" + value + \"' in object\"\n            );\n          }\n        }\n        stack.push(value);\n\n        var partial = [];\n        if (_class === ARRAY_CLASS) {\n          for (var i = 0, length = value.length; i < length; i++) {\n            var str = Str(i, value, stack);\n            partial.push(typeof str === 'undefined' ? 'null' : str);\n          }\n          partial = '[' + partial.join(',') + ']';\n        } else {\n          var keys = Object.keys(value);\n          for (var i = 0, length = keys.length; i < length; i++) {\n            var key = keys[i],\n              str = Str(key, value, stack);\n            if (typeof str !== 'undefined') {\n              partial.push(key.inspect(true) + ':' + str);\n            }\n          }\n          partial = '{' + partial.join(',') + '}';\n        }\n        stack.pop();\n        return partial;\n    }\n  }\n\n  function stringify(object) {\n    return JSON.stringify(object);\n  }\n\n  function toQueryString(object) {\n    return $H(object).toQueryString();\n  }\n\n  function toHTML(object) {\n    return object && object.toHTML ? object.toHTML() : String.interpret(object);\n  }\n\n  function keys(object) {\n    if (Type(object) !== OBJECT_TYPE) {\n      throw new TypeError();\n    }\n    var results = [];\n    for (var property in object) {\n      if (_hasOwnProperty.call(object, property)) results.push(property);\n    }\n\n    if (IS_DONTENUM_BUGGY) {\n      for (var i = 0; (property = DONT_ENUMS[i]); i++) {\n        if (_hasOwnProperty.call(object, property)) results.push(property);\n      }\n    }\n\n    return results;\n  }\n\n  function values(object) {\n    var results = [];\n    for (var property in object) results.push(object[property]);\n    return results;\n  }\n\n  function clone(object) {\n    return extend({}, object);\n  }\n\n  function isElement(object) {\n    return !!(object && object.nodeType == 1);\n  }\n\n  function isArray(object) {\n    return _toString.call(object) === ARRAY_CLASS;\n  }\n\n  var hasNativeIsArray =\n    typeof Array.isArray == 'function' &&\n    Array.isArray([]) &&\n    !Array.isArray({});\n\n  if (hasNativeIsArray) {\n    isArray = Array.isArray;\n  }\n\n  function isHash(object) {\n    return object instanceof Hash;\n  }\n\n  function isFunction(object) {\n    return _toString.call(object) === FUNCTION_CLASS;\n  }\n\n  function isString(object) {\n    return _toString.call(object) === STRING_CLASS;\n  }\n\n  function isNumber(object) {\n    return _toString.call(object) === NUMBER_CLASS;\n  }\n\n  function isDate(object) {\n    return _toString.call(object) === DATE_CLASS;\n  }\n\n  function isUndefined(object) {\n    return typeof object === 'undefined';\n  }\n\n  extend(Object, {\n    extend: extend,\n    inspect: inspect,\n    toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,\n    toQueryString: toQueryString,\n    toHTML: toHTML,\n    keys: Object.keys || keys,\n    values: values,\n    clone: clone,\n    isElement: isElement,\n    isArray: isArray,\n    isHash: isHash,\n    isFunction: isFunction,\n    isString: isString,\n    isNumber: isNumber,\n    isDate: isDate,\n    isUndefined: isUndefined\n  });\n})();\nObject.extend(\n  Function.prototype,\n  (function () {\n    var slice = Array.prototype.slice;\n\n    function update(array, args) {\n      var arrayLength = array.length,\n        length = args.length;\n      while (length--) array[arrayLength + length] = args[length];\n      return array;\n    }\n\n    function merge(array, args) {\n      array = slice.call(array, 0);\n      return update(array, args);\n    }\n\n    function argumentNames() {\n      var names = this.toString()\n        .match(/^[\\s\\(]*function[^(]*\\(([^)]*)\\)/)[1]\n        .replace(/\\/\\/.*?[\\r\\n]|\\/\\*(?:.|[\\r\\n])*?\\*\\//g, '')\n        .replace(/\\s+/g, '')\n        .split(',');\n      return names.length == 1 && !names[0] ? [] : names;\n    }\n\n    function bind(context) {\n      if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;\n\n      if (!Object.isFunction(this))\n        throw new TypeError('The object is not callable.');\n\n      var nop = function () {};\n      var __method = this,\n        args = slice.call(arguments, 1);\n\n      var bound = function () {\n        var a = merge(args, arguments);\n        var c = this instanceof bound ? this : context;\n        return __method.apply(c, a);\n      };\n\n      nop.prototype = this.prototype;\n      bound.prototype = new nop();\n\n      return bound;\n    }\n\n    function bindAsEventListener(context) {\n      var __method = this,\n        args = slice.call(arguments, 1);\n      return function (event) {\n        var a = update([event || window.event], args);\n        return __method.apply(context, a);\n      };\n    }\n\n    function curry() {\n      if (!arguments.length) return this;\n      var __method = this,\n        args = slice.call(arguments, 0);\n      return function () {\n        var a = merge(args, arguments);\n        return __method.apply(this, a);\n      };\n    }\n\n    function delay(timeout) {\n      var __method = this,\n        args = slice.call(arguments, 1);\n      timeout = timeout * 1000;\n      return window.setTimeout(function () {\n        return __method.apply(__method, args);\n      }, timeout);\n    }\n\n    function defer() {\n      var args = update([0.01], arguments);\n      return this.delay.apply(this, args);\n    }\n\n    function wrap(wrapper) {\n      var __method = this;\n      return function () {\n        var a = update([__method.bind(this)], arguments);\n        return wrapper.apply(this, a);\n      };\n    }\n\n    function methodize() {\n      if (this._methodized) return this._methodized;\n      var __method = this;\n      return (this._methodized = function () {\n        var a = update([this], arguments);\n        return __method.apply(null, a);\n      });\n    }\n\n    var extensions = {\n      argumentNames: argumentNames,\n      bindAsEventListener: bindAsEventListener,\n      curry: curry,\n      delay: delay,\n      defer: defer,\n      wrap: wrap,\n      methodize: methodize\n    };\n\n    if (!Function.prototype.bind) extensions.bind = bind;\n\n    return extensions;\n  })()\n);\n\n(function (proto) {\n  function toISOString() {\n    return (\n      this.getUTCFullYear() +\n      '-' +\n      (this.getUTCMonth() + 1).toPaddedString(2) +\n      '-' +\n      this.getUTCDate().toPaddedString(2) +\n      'T' +\n      this.getUTCHours().toPaddedString(2) +\n      ':' +\n      this.getUTCMinutes().toPaddedString(2) +\n      ':' +\n      this.getUTCSeconds().toPaddedString(2) +\n      'Z'\n    );\n  }\n\n  function toJSON() {\n    return this.toISOString();\n  }\n\n  if (!proto.toISOString) proto.toISOString = toISOString;\n  if (!proto.toJSON) proto.toJSON = toJSON;\n})(Date.prototype);\n\nRegExp.prototype.match = RegExp.prototype.test;\n\nRegExp.escape = function (str) {\n  return String(str).replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\nvar PeriodicalExecuter = Class.create({\n  initialize: function (callback, frequency) {\n    this.callback = callback;\n    this.frequency = frequency;\n    this.currentlyExecuting = false;\n\n    this.registerCallback();\n  },\n\n  registerCallback: function () {\n    this.timer = setInterval(\n      this.onTimerEvent.bind(this),\n      this.frequency * 1000\n    );\n  },\n\n  execute: function () {\n    this.callback(this);\n  },\n\n  stop: function () {\n    if (!this.timer) return;\n    clearInterval(this.timer);\n    this.timer = null;\n  },\n\n  onTimerEvent: function () {\n    if (!this.currentlyExecuting) {\n      try {\n        this.currentlyExecuting = true;\n        this.execute();\n        this.currentlyExecuting = false;\n      } catch (e) {\n        this.currentlyExecuting = false;\n        throw e;\n      }\n    }\n  }\n});\nObject.extend(String, {\n  interpret: function (value) {\n    return value == null ? '' : String(value);\n  },\n  specialChar: {\n    '\\b': '\\\\b',\n    '\\t': '\\\\t',\n    '\\n': '\\\\n',\n    '\\f': '\\\\f',\n    '\\r': '\\\\r',\n    '\\\\': '\\\\\\\\'\n  }\n});\n\nObject.extend(\n  String.prototype,\n  (function () {\n    var NATIVE_JSON_PARSE_SUPPORT =\n      window.JSON &&\n      typeof JSON.parse === 'function' &&\n      JSON.parse('{\"test\": true}').test;\n\n    function prepareReplacement(replacement) {\n      if (Object.isFunction(replacement)) return replacement;\n      var template = new Template(replacement);\n      return function (match) {\n        return template.evaluate(match);\n      };\n    }\n\n    function isNonEmptyRegExp(regexp) {\n      return regexp.source && regexp.source !== '(?:)';\n    }\n\n    function gsub(pattern, replacement) {\n      var result = '',\n        source = this,\n        match;\n      replacement = prepareReplacement(replacement);\n\n      if (Object.isString(pattern)) pattern = RegExp.escape(pattern);\n\n      if (!(pattern.length || isNonEmptyRegExp(pattern))) {\n        replacement = replacement('');\n        return replacement + source.split('').join(replacement) + replacement;\n      }\n\n      while (source.length > 0) {\n        match = source.match(pattern);\n        if (match && match[0].length > 0) {\n          result += source.slice(0, match.index);\n          result += String.interpret(replacement(match));\n          source = source.slice(match.index + match[0].length);\n        } else {\n          ((result += source), (source = ''));\n        }\n      }\n      return result;\n    }\n\n    function sub(pattern, replacement, count) {\n      replacement = prepareReplacement(replacement);\n      count = Object.isUndefined(count) ? 1 : count;\n\n      return this.gsub(pattern, function (match) {\n        if (--count < 0) return match[0];\n        return replacement(match);\n      });\n    }\n\n    function scan(pattern, iterator) {\n      this.gsub(pattern, iterator);\n      return String(this);\n    }\n\n    function truncate(length, truncation) {\n      length = length || 30;\n      truncation = Object.isUndefined(truncation) ? '...' : truncation;\n      return this.length > length\n        ? this.slice(0, length - truncation.length) + truncation\n        : String(this);\n    }\n\n    function strip() {\n      return this.replace(/^\\s+/, '').replace(/\\s+$/, '');\n    }\n\n    function stripTags() {\n      return this.replace(\n        /<\\w+(\\s+(\"[^\"]*\"|'[^']*'|[^>])+)?(\\/)?>|<\\/\\w+>/gi,\n        ''\n      );\n    }\n\n    function stripScripts() {\n      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\n    }\n\n    function extractScripts() {\n      var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),\n        matchOne = new RegExp(Prototype.ScriptFragment, 'im');\n      return (this.match(matchAll) || []).map(function (scriptTag) {\n        return (scriptTag.match(matchOne) || ['', ''])[1];\n      });\n    }\n\n    function evalScripts() {\n      return this.extractScripts().map(function (script) {\n        return eval(script);\n      });\n    }\n\n    function escapeHTML() {\n      return this.replace(/&/g, '&amp;')\n        .replace(/</g, '&lt;')\n        .replace(/>/g, '&gt;');\n    }\n\n    function unescapeHTML() {\n      return this.stripTags()\n        .replace(/&lt;/g, '<')\n        .replace(/&gt;/g, '>')\n        .replace(/&amp;/g, '&');\n    }\n\n    function toQueryParams(separator) {\n      var match = this.strip().match(/([^?#]*)(#.*)?$/);\n      if (!match) return {};\n\n      return match[1].split(separator || '&').inject({}, function (hash, pair) {\n        if ((pair = pair.split('='))[0]) {\n          var key = decodeURIComponent(pair.shift()),\n            value = pair.length > 1 ? pair.join('=') : pair[0];\n\n          if (value != undefined) {\n            value = value.gsub('+', ' ');\n            value = decodeURIComponent(value);\n          }\n\n          if (key in hash) {\n            if (!Object.isArray(hash[key])) hash[key] = [hash[key]];\n            hash[key].push(value);\n          } else hash[key] = value;\n        }\n        return hash;\n      });\n    }\n\n    function toArray() {\n      return this.split('');\n    }\n\n    function succ() {\n      return (\n        this.slice(0, this.length - 1) +\n        String.fromCharCode(this.charCodeAt(this.length - 1) + 1)\n      );\n    }\n\n    function times(count) {\n      return count < 1 ? '' : new Array(count + 1).join(this);\n    }\n\n    function camelize() {\n      return this.replace(/-+(.)?/g, function (match, chr) {\n        return chr ? chr.toUpperCase() : '';\n      });\n    }\n\n    function capitalize() {\n      return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();\n    }\n\n    function underscore() {\n      return this.replace(/::/g, '/')\n        .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n        .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n        .replace(/-/g, '_')\n        .toLowerCase();\n    }\n\n    function dasherize() {\n      return this.replace(/_/g, '-');\n    }\n\n    function inspect(useDoubleQuotes) {\n      var escapedString = this.replace(/[\\x00-\\x1f\\\\]/g, function (character) {\n        if (character in String.specialChar) {\n          return String.specialChar[character];\n        }\n        return '\\\\u00' + character.charCodeAt().toPaddedString(2, 16);\n      });\n      if (useDoubleQuotes)\n        return '\"' + escapedString.replace(/\"/g, '\\\\\"') + '\"';\n      return \"'\" + escapedString.replace(/'/g, \"\\\\'\") + \"'\";\n    }\n\n    function unfilterJSON(filter) {\n      return this.replace(filter || Prototype.JSONFilter, '$1');\n    }\n\n    function isJSON() {\n      var str = this;\n      if (str.blank()) return false;\n      str = str.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');\n      str = str.replace(\n        /\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\n        ']'\n      );\n      str = str.replace(/(?:^|:|,)(?:\\s*\\[)+/g, '');\n      return /^[\\],:{}\\s]*$/.test(str);\n    }\n\n    function evalJSON(sanitize) {\n      var json = this.unfilterJSON(),\n        cx =\n          /[\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff\\u0000]/g;\n      if (cx.test(json)) {\n        json = json.replace(cx, function (a) {\n          return '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n        });\n      }\n      try {\n        if (!sanitize || json.isJSON()) return eval('(' + json + ')');\n      } catch (e) {}\n      throw new SyntaxError('Badly formed JSON string: ' + this.inspect());\n    }\n\n    function parseJSON() {\n      var json = this.unfilterJSON();\n      return JSON.parse(json);\n    }\n\n    function include(pattern) {\n      return this.indexOf(pattern) > -1;\n    }\n\n    function startsWith(pattern, position) {\n      position = Object.isNumber(position) ? position : 0;\n      return this.lastIndexOf(pattern, position) === position;\n    }\n\n    function endsWith(pattern, position) {\n      pattern = String(pattern);\n      position = Object.isNumber(position) ? position : this.length;\n      if (position < 0) position = 0;\n      if (position > this.length) position = this.length;\n      var d = position - pattern.length;\n      return d >= 0 && this.indexOf(pattern, d) === d;\n    }\n\n    function empty() {\n      return this == '';\n    }\n\n    function blank() {\n      return /^\\s*$/.test(this);\n    }\n\n    function interpolate(object, pattern) {\n      return new Template(this, pattern).evaluate(object);\n    }\n\n    return {\n      gsub: gsub,\n      sub: sub,\n      scan: scan,\n      truncate: truncate,\n      strip: String.prototype.trim || strip,\n      stripTags: stripTags,\n      stripScripts: stripScripts,\n      extractScripts: extractScripts,\n      evalScripts: evalScripts,\n      escapeHTML: escapeHTML,\n      unescapeHTML: unescapeHTML,\n      toQueryParams: toQueryParams,\n      parseQuery: toQueryParams,\n      toArray: toArray,\n      succ: succ,\n      times: times,\n      camelize: camelize,\n      capitalize: capitalize,\n      underscore: underscore,\n      dasherize: dasherize,\n      inspect: inspect,\n      unfilterJSON: unfilterJSON,\n      isJSON: isJSON,\n      evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,\n      include: include,\n      startsWith: String.prototype.startsWith || startsWith,\n      endsWith: String.prototype.endsWith || endsWith,\n      empty: empty,\n      blank: blank,\n      interpolate: interpolate\n    };\n  })()\n);\n\nvar Template = Class.create({\n  initialize: function (template, pattern) {\n    this.template = template.toString();\n    this.pattern = pattern || Template.Pattern;\n  },\n\n  evaluate: function (object) {\n    if (object && Object.isFunction(object.toTemplateReplacements))\n      object = object.toTemplateReplacements();\n\n    return this.template.gsub(this.pattern, function (match) {\n      if (object == null) return match[1] + '';\n\n      var before = match[1] || '';\n      if (before == '\\\\') return match[2];\n\n      var ctx = object,\n        expr = match[3],\n        pattern = /^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/;\n\n      match = pattern.exec(expr);\n      if (match == null) return before;\n\n      while (match != null) {\n        var comp = match[1].startsWith('[')\n          ? match[2].replace(/\\\\\\\\]/g, ']')\n          : match[1];\n        ctx = ctx[comp];\n        if (null == ctx || '' == match[3]) break;\n        expr = expr.substring(\n          '[' == match[3] ? match[1].length : match[0].length\n        );\n        match = pattern.exec(expr);\n      }\n\n      return before + String.interpret(ctx);\n    });\n  }\n});\nTemplate.Pattern = /(^|.|\\r|\\n)(#\\{(.*?)\\})/;\n\nvar $break = {};\n\nvar Enumerable = (function () {\n  function each(iterator, context) {\n    try {\n      this._each(iterator, context);\n    } catch (e) {\n      if (e != $break) throw e;\n    }\n    return this;\n  }\n\n  function eachSlice(number, iterator, context) {\n    var index = -number,\n      slices = [],\n      array = this.toArray();\n    if (number < 1) return array;\n    while ((index += number) < array.length)\n      slices.push(array.slice(index, index + number));\n    return slices.collect(iterator, context);\n  }\n\n  function all(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = true;\n    this.each(function (value, index) {\n      result = result && !!iterator.call(context, value, index, this);\n      if (!result) throw $break;\n    }, this);\n    return result;\n  }\n\n  function any(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = false;\n    this.each(function (value, index) {\n      if ((result = !!iterator.call(context, value, index, this))) throw $break;\n    }, this);\n    return result;\n  }\n\n  function collect(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n    this.each(function (value, index) {\n      results.push(iterator.call(context, value, index, this));\n    }, this);\n    return results;\n  }\n\n  function detect(iterator, context) {\n    var result;\n    this.each(function (value, index) {\n      if (iterator.call(context, value, index, this)) {\n        result = value;\n        throw $break;\n      }\n    }, this);\n    return result;\n  }\n\n  function findAll(iterator, context) {\n    var results = [];\n    this.each(function (value, index) {\n      if (iterator.call(context, value, index, this)) results.push(value);\n    }, this);\n    return results;\n  }\n\n  function grep(filter, iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n\n    if (Object.isString(filter)) filter = new RegExp(RegExp.escape(filter));\n\n    this.each(function (value, index) {\n      if (filter.match(value))\n        results.push(iterator.call(context, value, index, this));\n    }, this);\n    return results;\n  }\n\n  function include(object) {\n    if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1)\n      return true;\n\n    var found = false;\n    this.each(function (value) {\n      if (value == object) {\n        found = true;\n        throw $break;\n      }\n    });\n    return found;\n  }\n\n  function inGroupsOf(number, fillWith) {\n    fillWith = Object.isUndefined(fillWith) ? null : fillWith;\n    return this.eachSlice(number, function (slice) {\n      while (slice.length < number) slice.push(fillWith);\n      return slice;\n    });\n  }\n\n  function inject(memo, iterator, context) {\n    this.each(function (value, index) {\n      memo = iterator.call(context, memo, value, index, this);\n    }, this);\n    return memo;\n  }\n\n  function invoke(method) {\n    var args = $A(arguments).slice(1);\n    return this.map(function (value) {\n      return value[method].apply(value, args);\n    });\n  }\n\n  function max(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function (value, index) {\n      value = iterator.call(context, value, index, this);\n      if (result == null || value >= result) result = value;\n    }, this);\n    return result;\n  }\n\n  function min(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function (value, index) {\n      value = iterator.call(context, value, index, this);\n      if (result == null || value < result) result = value;\n    }, this);\n    return result;\n  }\n\n  function partition(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var trues = [],\n      falses = [];\n    this.each(function (value, index) {\n      (iterator.call(context, value, index, this) ? trues : falses).push(value);\n    }, this);\n    return [trues, falses];\n  }\n\n  function pluck(property) {\n    var results = [];\n    this.each(function (value) {\n      results.push(value[property]);\n    });\n    return results;\n  }\n\n  function reject(iterator, context) {\n    var results = [];\n    this.each(function (value, index) {\n      if (!iterator.call(context, value, index, this)) results.push(value);\n    }, this);\n    return results;\n  }\n\n  function sortBy(iterator, context) {\n    return this.map(function (value, index) {\n      return {\n        value: value,\n        criteria: iterator.call(context, value, index, this)\n      };\n    }, this)\n      .sort(function (left, right) {\n        var a = left.criteria,\n          b = right.criteria;\n        return a < b ? -1 : a > b ? 1 : 0;\n      })\n      .pluck('value');\n  }\n\n  function toArray() {\n    return this.map();\n  }\n\n  function zip() {\n    var iterator = Prototype.K,\n      args = $A(arguments);\n    if (Object.isFunction(args.last())) iterator = args.pop();\n\n    var collections = [this].concat(args).map($A);\n    return this.map(function (value, index) {\n      return iterator(collections.pluck(index));\n    });\n  }\n\n  function size() {\n    return this.toArray().length;\n  }\n\n  function inspect() {\n    return '#<Enumerable:' + this.toArray().inspect() + '>';\n  }\n\n  return {\n    each: each,\n    eachSlice: eachSlice,\n    all: all,\n    every: all,\n    any: any,\n    some: any,\n    collect: collect,\n    map: collect,\n    detect: detect,\n    findAll: findAll,\n    select: findAll,\n    filter: findAll,\n    grep: grep,\n    include: include,\n    member: include,\n    inGroupsOf: inGroupsOf,\n    inject: inject,\n    invoke: invoke,\n    max: max,\n    min: min,\n    partition: partition,\n    pluck: pluck,\n    reject: reject,\n    sortBy: sortBy,\n    toArray: toArray,\n    entries: toArray,\n    zip: zip,\n    size: size,\n    inspect: inspect,\n    find: detect\n  };\n})();\n\nfunction $A(iterable) {\n  if (!iterable) return [];\n  if ('toArray' in Object(iterable)) return iterable.toArray();\n  var length = iterable.length || 0,\n    results = new Array(length);\n  while (length--) results[length] = iterable[length];\n  return results;\n}\n\nfunction $w(string) {\n  if (!Object.isString(string)) return [];\n  string = string.strip();\n  return string ? string.split(/\\s+/) : [];\n}\n\nArray.from = $A;\n\n(function () {\n  var arrayProto = Array.prototype,\n    slice = arrayProto.slice,\n    _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available\n\n  function each(iterator, context) {\n    for (var i = 0, length = this.length >>> 0; i < length; i++) {\n      if (i in this) iterator.call(context, this[i], i, this);\n    }\n  }\n  if (!_each) _each = each;\n\n  function clear() {\n    this.length = 0;\n    return this;\n  }\n\n  function first() {\n    return this[0];\n  }\n\n  function last() {\n    return this[this.length - 1];\n  }\n\n  function compact() {\n    return this.select(function (value) {\n      return value != null;\n    });\n  }\n\n  function flatten() {\n    return this.inject([], function (array, value) {\n      if (Object.isArray(value)) return array.concat(value.flatten());\n      array.push(value);\n      return array;\n    });\n  }\n\n  function without() {\n    var values = slice.call(arguments, 0);\n    return this.select(function (value) {\n      return !values.include(value);\n    });\n  }\n\n  function reverse(inline) {\n    return (inline === false ? this.toArray() : this)._reverse();\n  }\n\n  function uniq(sorted) {\n    return this.inject([], function (array, value, index) {\n      if (\n        0 == index ||\n        (sorted ? array.last() != value : !array.include(value))\n      )\n        array.push(value);\n      return array;\n    });\n  }\n\n  function intersect(array) {\n    return this.uniq().findAll(function (item) {\n      return array.indexOf(item) !== -1;\n    });\n  }\n\n  function clone() {\n    return slice.call(this, 0);\n  }\n\n  function size() {\n    return this.length;\n  }\n\n  function inspect() {\n    return '[' + this.map(Object.inspect).join(', ') + ']';\n  }\n\n  function indexOf(item, i) {\n    if (this == null) throw new TypeError();\n\n    var array = Object(this),\n      length = array.length >>> 0;\n    if (length === 0) return -1;\n\n    i = Number(i);\n    if (isNaN(i)) {\n      i = 0;\n    } else if (i !== 0 && isFinite(i)) {\n      i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));\n    }\n\n    if (i > length) return -1;\n\n    var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0);\n    for (; k < length; k++) if (k in array && array[k] === item) return k;\n    return -1;\n  }\n\n  function lastIndexOf(item, i) {\n    if (this == null) throw new TypeError();\n\n    var array = Object(this),\n      length = array.length >>> 0;\n    if (length === 0) return -1;\n\n    if (!Object.isUndefined(i)) {\n      i = Number(i);\n      if (isNaN(i)) {\n        i = 0;\n      } else if (i !== 0 && isFinite(i)) {\n        i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));\n      }\n    } else {\n      i = length;\n    }\n\n    var k = i >= 0 ? Math.min(i, length - 1) : length - Math.abs(i);\n\n    for (; k >= 0; k--) if (k in array && array[k] === item) return k;\n    return -1;\n  }\n\n  function concat(_) {\n    var array = [],\n      items = slice.call(arguments, 0),\n      item,\n      n = 0;\n    items.unshift(this);\n    for (var i = 0, length = items.length; i < length; i++) {\n      item = items[i];\n      if (Object.isArray(item) && !('callee' in item)) {\n        for (var j = 0, arrayLength = item.length; j < arrayLength; j++) {\n          if (j in item) array[n] = item[j];\n          n++;\n        }\n      } else {\n        array[n++] = item;\n      }\n    }\n    array.length = n;\n    return array;\n  }\n\n  function wrapNative(method) {\n    return function () {\n      if (arguments.length === 0) {\n        return method.call(this, Prototype.K);\n      } else if (arguments[0] === undefined) {\n        var args = slice.call(arguments, 1);\n        args.unshift(Prototype.K);\n        return method.apply(this, args);\n      } else {\n        return method.apply(this, arguments);\n      }\n    };\n  }\n\n  function map(iterator) {\n    if (this == null) throw new TypeError();\n    iterator = iterator || Prototype.K;\n\n    var object = Object(this);\n    var results = [],\n      context = arguments[1],\n      n = 0;\n\n    for (var i = 0, length = object.length >>> 0; i < length; i++) {\n      if (i in object) {\n        results[n] = iterator.call(context, object[i], i, object);\n      }\n      n++;\n    }\n    results.length = n;\n    return results;\n  }\n\n  if (arrayProto.map) {\n    map = wrapNative(Array.prototype.map);\n  }\n\n  function filter(iterator) {\n    if (this == null || !Object.isFunction(iterator)) throw new TypeError();\n\n    var object = Object(this);\n    var results = [],\n      context = arguments[1],\n      value;\n\n    for (var i = 0, length = object.length >>> 0; i < length; i++) {\n      if (i in object) {\n        value = object[i];\n        if (iterator.call(context, value, i, object)) {\n          results.push(value);\n        }\n      }\n    }\n    return results;\n  }\n\n  if (arrayProto.filter) {\n    filter = Array.prototype.filter;\n  }\n\n  function some(iterator) {\n    if (this == null) throw new TypeError();\n    iterator = iterator || Prototype.K;\n    var context = arguments[1];\n\n    var object = Object(this);\n    for (var i = 0, length = object.length >>> 0; i < length; i++) {\n      if (i in object && iterator.call(context, object[i], i, object)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  if (arrayProto.some) {\n    some = wrapNative(Array.prototype.some);\n  }\n\n  function every(iterator) {\n    if (this == null) throw new TypeError();\n    iterator = iterator || Prototype.K;\n    var context = arguments[1];\n\n    var object = Object(this);\n    for (var i = 0, length = object.length >>> 0; i < length; i++) {\n      if (i in object && !iterator.call(context, object[i], i, object)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  if (arrayProto.every) {\n    every = wrapNative(Array.prototype.every);\n  }\n\n  Object.extend(arrayProto, Enumerable);\n\n  if (arrayProto.entries === Enumerable.entries) {\n    delete arrayProto.entries;\n  }\n\n  if (!arrayProto._reverse) arrayProto._reverse = arrayProto.reverse;\n\n  Object.extend(arrayProto, {\n    _each: _each,\n\n    map: map,\n    collect: map,\n    select: filter,\n    filter: filter,\n    findAll: filter,\n    some: some,\n    any: some,\n    every: every,\n    all: every,\n\n    clear: clear,\n    first: first,\n    last: last,\n    compact: compact,\n    flatten: flatten,\n    without: without,\n    reverse: reverse,\n    uniq: uniq,\n    intersect: intersect,\n    clone: clone,\n    toArray: clone,\n    size: size,\n    inspect: inspect\n  });\n\n  var CONCAT_ARGUMENTS_BUGGY = (function () {\n    return [].concat(arguments)[0][0] !== 1;\n  })(1, 2);\n\n  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;\n\n  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;\n  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;\n})();\nfunction $H(object) {\n  return new Hash(object);\n}\n\nvar Hash = Class.create(\n  Enumerable,\n  (function () {\n    function initialize(object) {\n      this._object = Object.isHash(object)\n        ? object.toObject()\n        : Object.clone(object);\n    }\n\n    function _each(iterator, context) {\n      var i = 0;\n      for (var key in this._object) {\n        var value = this._object[key],\n          pair = [key, value];\n        pair.key = key;\n        pair.value = value;\n        iterator.call(context, pair, i);\n        i++;\n      }\n    }\n\n    function set(key, value) {\n      return (this._object[key] = value);\n    }\n\n    function get(key) {\n      if (this._object[key] !== Object.prototype[key]) return this._object[key];\n    }\n\n    function unset(key) {\n      var value = this._object[key];\n      delete this._object[key];\n      return value;\n    }\n\n    function toObject() {\n      return Object.clone(this._object);\n    }\n\n    function keys() {\n      return this.pluck('key');\n    }\n\n    function values() {\n      return this.pluck('value');\n    }\n\n    function index(value) {\n      var match = this.detect(function (pair) {\n        return pair.value === value;\n      });\n      return match && match.key;\n    }\n\n    function merge(object) {\n      return this.clone().update(object);\n    }\n\n    function update(object) {\n      return new Hash(object).inject(this, function (result, pair) {\n        result.set(pair.key, pair.value);\n        return result;\n      });\n    }\n\n    function toQueryPair(key, value) {\n      if (Object.isUndefined(value)) return key;\n\n      value = String.interpret(value);\n\n      value = value.gsub(/(\\r)?\\n/, '\\r\\n');\n      value = encodeURIComponent(value);\n      value = value.gsub(/%20/, '+');\n      return key + '=' + value;\n    }\n\n    function toQueryString() {\n      return this.inject([], function (results, pair) {\n        var key = encodeURIComponent(pair.key),\n          values = pair.value;\n\n        if (values && typeof values == 'object') {\n          if (Object.isArray(values)) {\n            var queryValues = [];\n            for (var i = 0, len = values.length, value; i < len; i++) {\n              value = values[i];\n              queryValues.push(toQueryPair(key, value));\n            }\n            return results.concat(queryValues);\n          }\n        } else results.push(toQueryPair(key, values));\n        return results;\n      }).join('&');\n    }\n\n    function inspect() {\n      return (\n        '#<Hash:{' +\n        this.map(function (pair) {\n          return pair.map(Object.inspect).join(': ');\n        }).join(', ') +\n        '}>'\n      );\n    }\n\n    function clone() {\n      return new Hash(this);\n    }\n\n    return {\n      initialize: initialize,\n      _each: _each,\n      set: set,\n      get: get,\n      unset: unset,\n      toObject: toObject,\n      toTemplateReplacements: toObject,\n      keys: keys,\n      values: values,\n      index: index,\n      merge: merge,\n      update: update,\n      toQueryString: toQueryString,\n      inspect: inspect,\n      toJSON: toObject,\n      clone: clone\n    };\n  })()\n);\n\nHash.from = $H;\nObject.extend(\n  Number.prototype,\n  (function () {\n    function toColorPart() {\n      return this.toPaddedString(2, 16);\n    }\n\n    function succ() {\n      return this + 1;\n    }\n\n    function times(iterator, context) {\n      $R(0, this, true).each(iterator, context);\n      return this;\n    }\n\n    function toPaddedString(length, radix) {\n      var string = this.toString(radix || 10);\n      return '0'.times(length - string.length) + string;\n    }\n\n    function abs() {\n      return Math.abs(this);\n    }\n\n    function round() {\n      return Math.round(this);\n    }\n\n    function ceil() {\n      return Math.ceil(this);\n    }\n\n    function floor() {\n      return Math.floor(this);\n    }\n\n    return {\n      toColorPart: toColorPart,\n      succ: succ,\n      times: times,\n      toPaddedString: toPaddedString,\n      abs: abs,\n      round: round,\n      ceil: ceil,\n      floor: floor\n    };\n  })()\n);\n\nfunction $R(start, end, exclusive) {\n  return new ObjectRange(start, end, exclusive);\n}\n\nvar ObjectRange = Class.create(\n  Enumerable,\n  (function () {\n    function initialize(start, end, exclusive) {\n      this.start = start;\n      this.end = end;\n      this.exclusive = exclusive;\n    }\n\n    function _each(iterator, context) {\n      var value = this.start,\n        i;\n      for (i = 0; this.include(value); i++) {\n        iterator.call(context, value, i);\n        value = value.succ();\n      }\n    }\n\n    function include(value) {\n      if (value < this.start) return false;\n      if (this.exclusive) return value < this.end;\n      return value <= this.end;\n    }\n\n    return {\n      initialize: initialize,\n      _each: _each,\n      include: include\n    };\n  })()\n);\n\nvar Abstract = {};\n\nvar Try = {\n  these: function () {\n    var returnValue;\n\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      var lambda = arguments[i];\n      try {\n        returnValue = lambda();\n        break;\n      } catch (e) {}\n    }\n\n    return returnValue;\n  }\n};\n\nvar Ajax = {\n  getTransport: function () {\n    return (\n      Try.these(\n        function () {\n          return new XMLHttpRequest();\n        },\n        function () {\n          return new ActiveXObject('Msxml2.XMLHTTP');\n        },\n        function () {\n          return new ActiveXObject('Microsoft.XMLHTTP');\n        }\n      ) || false\n    );\n  },\n\n  activeRequestCount: 0\n};\n\nAjax.Responders = {\n  responders: [],\n\n  _each: function (iterator, context) {\n    this.responders._each(iterator, context);\n  },\n\n  register: function (responder) {\n    if (!this.include(responder)) this.responders.push(responder);\n  },\n\n  unregister: function (responder) {\n    this.responders = this.responders.without(responder);\n  },\n\n  dispatch: function (callback, request, transport, json) {\n    this.each(function (responder) {\n      if (Object.isFunction(responder[callback])) {\n        try {\n          responder[callback].apply(responder, [request, transport, json]);\n        } catch (e) {}\n      }\n    });\n  }\n};\n\nObject.extend(Ajax.Responders, Enumerable);\n\nAjax.Responders.register({\n  onCreate: function () {\n    Ajax.activeRequestCount++;\n  },\n  onComplete: function () {\n    Ajax.activeRequestCount--;\n  }\n});\nAjax.Base = Class.create({\n  initialize: function (options) {\n    this.options = {\n      method: 'post',\n      asynchronous: true,\n      contentType: 'application/x-www-form-urlencoded',\n      encoding: 'UTF-8',\n      parameters: '',\n      evalJSON: true,\n      evalJS: true\n    };\n    Object.extend(this.options, options || {});\n\n    this.options.method = this.options.method.toLowerCase();\n\n    if (Object.isHash(this.options.parameters))\n      this.options.parameters = this.options.parameters.toObject();\n  }\n});\nAjax.Request = Class.create(Ajax.Base, {\n  _complete: false,\n\n  initialize: function ($super, url, options) {\n    $super(options);\n    this.transport = Ajax.getTransport();\n    this.request(url);\n  },\n\n  request: function (url) {\n    this.url = url;\n    this.method = this.options.method;\n    var params = Object.isString(this.options.parameters)\n      ? this.options.parameters\n      : Object.toQueryString(this.options.parameters);\n\n    if (!['get', 'post'].include(this.method)) {\n      params += (params ? '&' : '') + '_method=' + this.method;\n      this.method = 'post';\n    }\n\n    if (params && this.method === 'get') {\n      this.url += (this.url.include('?') ? '&' : '?') + params;\n    }\n\n    this.parameters = params.toQueryParams();\n\n    try {\n      var response = new Ajax.Response(this);\n      if (this.options.onCreate) this.options.onCreate(response);\n      Ajax.Responders.dispatch('onCreate', this, response);\n\n      this.transport.open(\n        this.method.toUpperCase(),\n        this.url,\n        this.options.asynchronous\n      );\n\n      if (this.options.asynchronous)\n        this.respondToReadyState.bind(this).defer(1);\n\n      this.transport.onreadystatechange = this.onStateChange.bind(this);\n      this.setRequestHeaders();\n\n      this.body =\n        this.method == 'post' ? this.options.postBody || params : null;\n      this.transport.send(this.body);\n\n      /* Force Firefox to handle ready state 4 for synchronous requests */\n      if (!this.options.asynchronous && this.transport.overrideMimeType)\n        this.onStateChange();\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  onStateChange: function () {\n    var readyState = this.transport.readyState;\n    if (readyState > 1 && !(readyState == 4 && this._complete))\n      this.respondToReadyState(this.transport.readyState);\n  },\n\n  setRequestHeaders: function () {\n    var headers = {\n      'X-Requested-With': 'XMLHttpRequest',\n      'X-Prototype-Version': Prototype.Version,\n      Accept: 'text/javascript, text/html, application/xml, text/xml, */*'\n    };\n\n    if (this.method == 'post') {\n      headers['Content-type'] =\n        this.options.contentType +\n        (this.options.encoding ? '; charset=' + this.options.encoding : '');\n\n      /* Force \"Connection: close\" for older Mozilla browsers to work\n       * around a bug where XMLHttpRequest sends an incorrect\n       * Content-length header. See Mozilla Bugzilla #246651.\n       */\n      if (\n        this.transport.overrideMimeType &&\n        (navigator.userAgent.match(/Gecko\\/(\\d{4})/) || [0, 2005])[1] < 2005\n      )\n        headers['Connection'] = 'close';\n    }\n\n    if (typeof this.options.requestHeaders == 'object') {\n      var extras = this.options.requestHeaders;\n\n      if (Object.isFunction(extras.push))\n        for (var i = 0, length = extras.length; i < length; i += 2)\n          headers[extras[i]] = extras[i + 1];\n      else\n        $H(extras).each(function (pair) {\n          headers[pair.key] = pair.value;\n        });\n    }\n\n    for (var name in headers)\n      if (headers[name] != null)\n        this.transport.setRequestHeader(name, headers[name]);\n  },\n\n  success: function () {\n    var status = this.getStatus();\n    return !status || (status >= 200 && status < 300) || status == 304;\n  },\n\n  getStatus: function () {\n    try {\n      if (this.transport.status === 1223) return 204;\n      return this.transport.status || 0;\n    } catch (e) {\n      return 0;\n    }\n  },\n\n  respondToReadyState: function (readyState) {\n    var state = Ajax.Request.Events[readyState],\n      response = new Ajax.Response(this);\n\n    if (state == 'Complete') {\n      try {\n        this._complete = true;\n        (\n          this.options['on' + response.status] ||\n          this.options['on' + (this.success() ? 'Success' : 'Failure')] ||\n          Prototype.emptyFunction\n        )(response, response.headerJSON);\n      } catch (e) {\n        this.dispatchException(e);\n      }\n\n      var contentType = response.getHeader('Content-type');\n      if (\n        this.options.evalJS == 'force' ||\n        (this.options.evalJS &&\n          this.isSameOrigin() &&\n          contentType &&\n          contentType.match(\n            /^\\s*(text|application)\\/(x-)?(java|ecma)script(;.*)?\\s*$/i\n          ))\n      )\n        this.evalResponse();\n    }\n\n    try {\n      (this.options['on' + state] || Prototype.emptyFunction)(\n        response,\n        response.headerJSON\n      );\n      Ajax.Responders.dispatch(\n        'on' + state,\n        this,\n        response,\n        response.headerJSON\n      );\n    } catch (e) {\n      this.dispatchException(e);\n    }\n\n    if (state == 'Complete') {\n      this.transport.onreadystatechange = Prototype.emptyFunction;\n    }\n  },\n\n  isSameOrigin: function () {\n    var m = this.url.match(/^\\s*https?:\\/\\/[^\\/]*/);\n    return (\n      !m ||\n      m[0] ==\n        '#{protocol}//#{domain}#{port}'.interpolate({\n          protocol: location.protocol,\n          domain: document.domain,\n          port: location.port ? ':' + location.port : ''\n        })\n    );\n  },\n\n  getHeader: function (name) {\n    try {\n      return this.transport.getResponseHeader(name) || null;\n    } catch (e) {\n      return null;\n    }\n  },\n\n  evalResponse: function () {\n    try {\n      return eval((this.transport.responseText || '').unfilterJSON());\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  dispatchException: function (exception) {\n    (this.options.onException || Prototype.emptyFunction)(this, exception);\n    Ajax.Responders.dispatch('onException', this, exception);\n  }\n});\n\nAjax.Request.Events = [\n  'Uninitialized',\n  'Loading',\n  'Loaded',\n  'Interactive',\n  'Complete'\n];\n\nAjax.Response = Class.create({\n  initialize: function (request) {\n    this.request = request;\n    var transport = (this.transport = request.transport),\n      readyState = (this.readyState = transport.readyState);\n\n    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {\n      this.status = this.getStatus();\n      this.statusText = this.getStatusText();\n      this.responseText = String.interpret(transport.responseText);\n      this.headerJSON = this._getHeaderJSON();\n    }\n\n    if (readyState == 4) {\n      var xml = transport.responseXML;\n      this.responseXML = Object.isUndefined(xml) ? null : xml;\n      this.responseJSON = this._getResponseJSON();\n    }\n  },\n\n  status: 0,\n\n  statusText: '',\n\n  getStatus: Ajax.Request.prototype.getStatus,\n\n  getStatusText: function () {\n    try {\n      return this.transport.statusText || '';\n    } catch (e) {\n      return '';\n    }\n  },\n\n  getHeader: Ajax.Request.prototype.getHeader,\n\n  getAllHeaders: function () {\n    try {\n      return this.getAllResponseHeaders();\n    } catch (e) {\n      return null;\n    }\n  },\n\n  getResponseHeader: function (name) {\n    return this.transport.getResponseHeader(name);\n  },\n\n  getAllResponseHeaders: function () {\n    return this.transport.getAllResponseHeaders();\n  },\n\n  _getHeaderJSON: function () {\n    var json = this.getHeader('X-JSON');\n    if (!json) return null;\n\n    try {\n      json = decodeURIComponent(escape(json));\n    } catch (e) {}\n\n    try {\n      return json.evalJSON(\n        this.request.options.sanitizeJSON || !this.request.isSameOrigin()\n      );\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  },\n\n  _getResponseJSON: function () {\n    var options = this.request.options;\n    if (\n      !options.evalJSON ||\n      (options.evalJSON != 'force' &&\n        !(this.getHeader('Content-type') || '').include('application/json')) ||\n      this.responseText.blank()\n    )\n      return null;\n    try {\n      return this.responseText.evalJSON(\n        options.sanitizeJSON || !this.request.isSameOrigin()\n      );\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  }\n});\n\nAjax.Updater = Class.create(Ajax.Request, {\n  initialize: function ($super, container, url, options) {\n    this.container = {\n      success: container.success || container,\n      failure: container.failure || (container.success ? null : container)\n    };\n\n    options = Object.clone(options);\n    var onComplete = options.onComplete;\n    options.onComplete = function (response, json) {\n      this.updateContent(response.responseText);\n      if (Object.isFunction(onComplete)) onComplete(response, json);\n    }.bind(this);\n\n    $super(url, options);\n  },\n\n  updateContent: function (responseText) {\n    var receiver = this.container[this.success() ? 'success' : 'failure'],\n      options = this.options;\n\n    if (!options.evalScripts) responseText = responseText.stripScripts();\n\n    if ((receiver = $(receiver))) {\n      if (options.insertion) {\n        if (Object.isString(options.insertion)) {\n          var insertion = {};\n          insertion[options.insertion] = responseText;\n          receiver.insert(insertion);\n        } else options.insertion(receiver, responseText);\n      } else receiver.update(responseText);\n    }\n  }\n});\n\nAjax.PeriodicalUpdater = Class.create(Ajax.Base, {\n  initialize: function ($super, container, url, options) {\n    $super(options);\n    this.onComplete = this.options.onComplete;\n\n    this.frequency = this.options.frequency || 2;\n    this.decay = this.options.decay || 1;\n\n    this.updater = {};\n    this.container = container;\n    this.url = url;\n\n    this.start();\n  },\n\n  start: function () {\n    this.options.onComplete = this.updateComplete.bind(this);\n    this.onTimerEvent();\n  },\n\n  stop: function () {\n    this.updater.options.onComplete = undefined;\n    clearTimeout(this.timer);\n    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\n  },\n\n  updateComplete: function (response) {\n    if (this.options.decay) {\n      this.decay =\n        response.responseText == this.lastText\n          ? this.decay * this.options.decay\n          : 1;\n\n      this.lastText = response.responseText;\n    }\n    this.timer = this.onTimerEvent\n      .bind(this)\n      .delay(this.decay * this.frequency);\n  },\n\n  onTimerEvent: function () {\n    this.updater = new Ajax.Updater(this.container, this.url, this.options);\n  }\n});\n\n(function (GLOBAL) {\n  var UNDEFINED;\n  var SLICE = Array.prototype.slice;\n\n  var DIV = document.createElement('div');\n\n  function $(element) {\n    if (arguments.length > 1) {\n      for (var i = 0, elements = [], length = arguments.length; i < length; i++)\n        elements.push($(arguments[i]));\n      return elements;\n    }\n\n    if (Object.isString(element)) element = document.getElementById(element);\n    return Element.extend(element);\n  }\n\n  GLOBAL.$ = $;\n\n  if (!GLOBAL.Node) GLOBAL.Node = {};\n\n  if (!GLOBAL.Node.ELEMENT_NODE) {\n    Object.extend(GLOBAL.Node, {\n      ELEMENT_NODE: 1,\n      ATTRIBUTE_NODE: 2,\n      TEXT_NODE: 3,\n      CDATA_SECTION_NODE: 4,\n      ENTITY_REFERENCE_NODE: 5,\n      ENTITY_NODE: 6,\n      PROCESSING_INSTRUCTION_NODE: 7,\n      COMMENT_NODE: 8,\n      DOCUMENT_NODE: 9,\n      DOCUMENT_TYPE_NODE: 10,\n      DOCUMENT_FRAGMENT_NODE: 11,\n      NOTATION_NODE: 12\n    });\n  }\n\n  var ELEMENT_CACHE = {};\n\n  function shouldUseCreationCache(tagName, attributes) {\n    if (tagName === 'select') return false;\n    if ('type' in attributes) return false;\n    return true;\n  }\n\n  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function () {\n    try {\n      var el = document.createElement('<input name=\"x\">');\n      return el.tagName.toLowerCase() === 'input' && el.name === 'x';\n    } catch (err) {\n      return false;\n    }\n  })();\n\n  var oldElement = GLOBAL.Element;\n  function Element(tagName, attributes) {\n    attributes = attributes || {};\n    tagName = tagName.toLowerCase();\n\n    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {\n      tagName = '<' + tagName + ' name=\"' + attributes.name + '\">';\n      delete attributes.name;\n      return Element.writeAttribute(\n        document.createElement(tagName),\n        attributes\n      );\n    }\n\n    if (!ELEMENT_CACHE[tagName])\n      ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName));\n\n    var node = shouldUseCreationCache(tagName, attributes)\n      ? ELEMENT_CACHE[tagName].cloneNode(false)\n      : document.createElement(tagName);\n\n    return Element.writeAttribute(node, attributes);\n  }\n\n  GLOBAL.Element = Element;\n\n  Object.extend(GLOBAL.Element, oldElement || {});\n  if (oldElement) GLOBAL.Element.prototype = oldElement.prototype;\n\n  Element.Methods = { ByTag: {}, Simulated: {} };\n\n  var methods = {};\n\n  var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' };\n  function inspect(element) {\n    element = $(element);\n    var result = '<' + element.tagName.toLowerCase();\n\n    var attribute, value;\n    for (var property in INSPECT_ATTRIBUTES) {\n      attribute = INSPECT_ATTRIBUTES[property];\n      value = (element[property] || '').toString();\n      if (value) result += ' ' + attribute + '=' + value.inspect(true);\n    }\n\n    return result + '>';\n  }\n\n  methods.inspect = inspect;\n\n  function visible(element) {\n    return $(element).getStyle('display') !== 'none';\n  }\n\n  function toggle(element, bool) {\n    element = $(element);\n    if (typeof bool !== 'boolean') bool = !Element.visible(element);\n    Element[bool ? 'show' : 'hide'](element);\n\n    return element;\n  }\n\n  function hide(element) {\n    element = $(element);\n    element.style.display = 'none';\n    return element;\n  }\n\n  function show(element) {\n    element = $(element);\n    element.style.display = '';\n    return element;\n  }\n\n  Object.extend(methods, {\n    visible: visible,\n    toggle: toggle,\n    hide: hide,\n    show: show\n  });\n\n  function remove(element) {\n    element = $(element);\n    element.parentNode.removeChild(element);\n    return element;\n  }\n\n  var SELECT_ELEMENT_INNERHTML_BUGGY = (function () {\n    var el = document.createElement('select'),\n      isBuggy = true;\n    el.innerHTML = '<option value=\"test\">test</option>';\n    if (el.options && el.options[0]) {\n      isBuggy = el.options[0].nodeName.toUpperCase() !== 'OPTION';\n    }\n    el = null;\n    return isBuggy;\n  })();\n\n  var TABLE_ELEMENT_INNERHTML_BUGGY = (function () {\n    try {\n      var el = document.createElement('table');\n      if (el && el.tBodies) {\n        el.innerHTML = '<tbody><tr><td>test</td></tr></tbody>';\n        var isBuggy = typeof el.tBodies[0] == 'undefined';\n        el = null;\n        return isBuggy;\n      }\n    } catch (e) {\n      return true;\n    }\n  })();\n\n  var LINK_ELEMENT_INNERHTML_BUGGY = (function () {\n    try {\n      var el = document.createElement('div');\n      el.innerHTML = '<link />';\n      var isBuggy = el.childNodes.length === 0;\n      el = null;\n      return isBuggy;\n    } catch (e) {\n      return true;\n    }\n  })();\n\n  var ANY_INNERHTML_BUGGY =\n    SELECT_ELEMENT_INNERHTML_BUGGY ||\n    TABLE_ELEMENT_INNERHTML_BUGGY ||\n    LINK_ELEMENT_INNERHTML_BUGGY;\n\n  var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {\n    var s = document.createElement('script'),\n      isBuggy = false;\n    try {\n      s.appendChild(document.createTextNode(''));\n      isBuggy = !s.firstChild || (s.firstChild && s.firstChild.nodeType !== 3);\n    } catch (e) {\n      isBuggy = true;\n    }\n    s = null;\n    return isBuggy;\n  })();\n\n  function update(element, content) {\n    element = $(element);\n\n    var descendants = element.getElementsByTagName('*'),\n      i = descendants.length;\n    while (i--) purgeElement(descendants[i]);\n\n    if (content && content.toElement) content = content.toElement();\n\n    if (Object.isElement(content)) return element.update().insert(content);\n\n    content = Object.toHTML(content);\n    var tagName = element.tagName.toUpperCase();\n\n    if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {\n      element.text = content;\n      return element;\n    }\n\n    if (ANY_INNERHTML_BUGGY) {\n      if (tagName in INSERTION_TRANSLATIONS.tags) {\n        while (element.firstChild) element.removeChild(element.firstChild);\n\n        var nodes = getContentFromAnonymousElement(\n          tagName,\n          content.stripScripts()\n        );\n        for (var i = 0, node; (node = nodes[i]); i++) element.appendChild(node);\n      } else if (\n        LINK_ELEMENT_INNERHTML_BUGGY &&\n        Object.isString(content) &&\n        content.indexOf('<link') > -1\n      ) {\n        while (element.firstChild) element.removeChild(element.firstChild);\n\n        var nodes = getContentFromAnonymousElement(\n          tagName,\n          content.stripScripts(),\n          true\n        );\n\n        for (var i = 0, node; (node = nodes[i]); i++) element.appendChild(node);\n      } else {\n        element.innerHTML = content.stripScripts();\n      }\n    } else {\n      element.innerHTML = content.stripScripts();\n    }\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  }\n\n  function replace(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) {\n      content = content.toElement();\n    } else if (!Object.isElement(content)) {\n      content = Object.toHTML(content);\n      var range = element.ownerDocument.createRange();\n      range.selectNode(element);\n      content.evalScripts.bind(content).defer();\n      content = range.createContextualFragment(content.stripScripts());\n    }\n\n    element.parentNode.replaceChild(content, element);\n    return element;\n  }\n\n  var INSERTION_TRANSLATIONS = {\n    before: function (element, node) {\n      element.parentNode.insertBefore(node, element);\n    },\n    top: function (element, node) {\n      element.insertBefore(node, element.firstChild);\n    },\n    bottom: function (element, node) {\n      element.appendChild(node);\n    },\n    after: function (element, node) {\n      element.parentNode.insertBefore(node, element.nextSibling);\n    },\n\n    tags: {\n      TABLE: ['<table>', '</table>', 1],\n      TBODY: ['<table><tbody>', '</tbody></table>', 2],\n      TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],\n      TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],\n      SELECT: ['<select>', '</select>', 1]\n    }\n  };\n\n  var tags = INSERTION_TRANSLATIONS.tags;\n\n  Object.extend(tags, {\n    THEAD: tags.TBODY,\n    TFOOT: tags.TBODY,\n    TH: tags.TD\n  });\n\n  function replace_IE(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      element.parentNode.replaceChild(content, element);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var parent = element.parentNode,\n      tagName = parent.tagName.toUpperCase();\n\n    if (tagName in INSERTION_TRANSLATIONS.tags) {\n      var nextSibling = Element.next(element);\n      var fragments = getContentFromAnonymousElement(\n        tagName,\n        content.stripScripts()\n      );\n\n      parent.removeChild(element);\n\n      var iterator;\n      if (nextSibling)\n        iterator = function (node) {\n          parent.insertBefore(node, nextSibling);\n        };\n      else\n        iterator = function (node) {\n          parent.appendChild(node);\n        };\n\n      fragments.each(iterator);\n    } else {\n      element.outerHTML = content.stripScripts();\n    }\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  }\n\n  if ('outerHTML' in document.documentElement) replace = replace_IE;\n\n  function isContent(content) {\n    if (Object.isUndefined(content) || content === null) return false;\n\n    if (Object.isString(content) || Object.isNumber(content)) return true;\n    if (Object.isElement(content)) return true;\n    if (content.toElement || content.toHTML) return true;\n\n    return false;\n  }\n\n  function insertContentAt(element, content, position) {\n    position = position.toLowerCase();\n    var method = INSERTION_TRANSLATIONS[position];\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      method(element, content);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var tagName = (\n      position === 'before' || position === 'after'\n        ? element.parentNode\n        : element\n    ).tagName.toUpperCase();\n\n    var childNodes = getContentFromAnonymousElement(\n      tagName,\n      content.stripScripts()\n    );\n\n    if (position === 'top' || position === 'after') childNodes.reverse();\n\n    for (var i = 0, node; (node = childNodes[i]); i++) method(element, node);\n\n    content.evalScripts.bind(content).defer();\n  }\n\n  function insert(element, insertions) {\n    element = $(element);\n\n    if (isContent(insertions)) insertions = { bottom: insertions };\n\n    for (var position in insertions)\n      insertContentAt(element, insertions[position], position);\n\n    return element;\n  }\n\n  function wrap(element, wrapper, attributes) {\n    element = $(element);\n\n    if (Object.isElement(wrapper)) {\n      $(wrapper).writeAttribute(attributes || {});\n    } else if (Object.isString(wrapper)) {\n      wrapper = new Element(wrapper, attributes);\n    } else {\n      wrapper = new Element('div', wrapper);\n    }\n\n    if (element.parentNode) element.parentNode.replaceChild(wrapper, element);\n\n    wrapper.appendChild(element);\n\n    return wrapper;\n  }\n\n  function cleanWhitespace(element) {\n    element = $(element);\n    var node = element.firstChild;\n\n    while (node) {\n      var nextNode = node.nextSibling;\n      if (node.nodeType === Node.TEXT_NODE && !/\\S/.test(node.nodeValue))\n        element.removeChild(node);\n      node = nextNode;\n    }\n    return element;\n  }\n\n  function empty(element) {\n    return $(element).innerHTML.blank();\n  }\n\n  function getContentFromAnonymousElement(tagName, html, force) {\n    var t = INSERTION_TRANSLATIONS.tags[tagName],\n      div = DIV;\n\n    var workaround = !!t;\n    if (!workaround && force) {\n      workaround = true;\n      t = ['', '', 0];\n    }\n\n    if (workaround) {\n      div.innerHTML = '&#160;' + t[0] + html + t[1];\n      div.removeChild(div.firstChild);\n      for (var i = t[2]; i--; ) div = div.firstChild;\n    } else {\n      div.innerHTML = html;\n    }\n\n    return $A(div.childNodes);\n  }\n\n  function clone(element, deep) {\n    if (!(element = $(element))) return;\n    var clone = element.cloneNode(deep);\n    if (!HAS_UNIQUE_ID_PROPERTY) {\n      clone._prototypeUID = UNDEFINED;\n      if (deep) {\n        var descendants = Element.select(clone, '*'),\n          i = descendants.length;\n        while (i--) descendants[i]._prototypeUID = UNDEFINED;\n      }\n    }\n    return Element.extend(clone);\n  }\n\n  function purgeElement(element) {\n    var uid = getUniqueElementID(element);\n    if (uid) {\n      Element.stopObserving(element);\n      if (!HAS_UNIQUE_ID_PROPERTY) element._prototypeUID = UNDEFINED;\n      delete Element.Storage[uid];\n    }\n  }\n\n  function purgeCollection(elements) {\n    var i = elements.length;\n    while (i--) purgeElement(elements[i]);\n  }\n\n  function purgeCollection_IE(elements) {\n    var i = elements.length,\n      element,\n      uid;\n    while (i--) {\n      element = elements[i];\n      uid = getUniqueElementID(element);\n      delete Element.Storage[uid];\n      delete Event.cache[uid];\n    }\n  }\n\n  if (HAS_UNIQUE_ID_PROPERTY) {\n    purgeCollection = purgeCollection_IE;\n  }\n\n  function purge(element) {\n    if (!(element = $(element))) return;\n    purgeElement(element);\n\n    var descendants = element.getElementsByTagName('*'),\n      i = descendants.length;\n\n    while (i--) purgeElement(descendants[i]);\n\n    return null;\n  }\n\n  Object.extend(methods, {\n    remove: remove,\n    update: update,\n    replace: replace,\n    insert: insert,\n    wrap: wrap,\n    cleanWhitespace: cleanWhitespace,\n    empty: empty,\n    clone: clone,\n    purge: purge\n  });\n\n  function recursivelyCollect(element, property, maximumLength) {\n    element = $(element);\n    maximumLength = maximumLength || -1;\n    var elements = [];\n\n    while ((element = element[property])) {\n      if (element.nodeType === Node.ELEMENT_NODE)\n        elements.push(Element.extend(element));\n\n      if (elements.length === maximumLength) break;\n    }\n\n    return elements;\n  }\n\n  function ancestors(element) {\n    return recursivelyCollect(element, 'parentNode');\n  }\n\n  function descendants(element) {\n    return Element.select(element, '*');\n  }\n\n  function firstDescendant(element) {\n    element = $(element).firstChild;\n    while (element && element.nodeType !== Node.ELEMENT_NODE)\n      element = element.nextSibling;\n\n    return $(element);\n  }\n\n  function immediateDescendants(element) {\n    var results = [],\n      child = $(element).firstChild;\n\n    while (child) {\n      if (child.nodeType === Node.ELEMENT_NODE)\n        results.push(Element.extend(child));\n\n      child = child.nextSibling;\n    }\n\n    return results;\n  }\n\n  function previousSiblings(element) {\n    return recursivelyCollect(element, 'previousSibling');\n  }\n\n  function nextSiblings(element) {\n    return recursivelyCollect(element, 'nextSibling');\n  }\n\n  function siblings(element) {\n    element = $(element);\n    var previous = previousSiblings(element),\n      next = nextSiblings(element);\n    return previous.reverse().concat(next);\n  }\n\n  function match(element, selector) {\n    element = $(element);\n\n    if (Object.isString(selector))\n      return Prototype.Selector.match(element, selector);\n\n    return selector.match(element);\n  }\n\n  function _recursivelyFind(element, property, expression, index) {\n    ((element = $(element)),\n      (expression = expression || 0),\n      (index = index || 0));\n    if (Object.isNumber(expression)) {\n      ((index = expression), (expression = null));\n    }\n\n    while ((element = element[property])) {\n      if (element.nodeType !== 1) continue;\n      if (expression && !Prototype.Selector.match(element, expression))\n        continue;\n      if (--index >= 0) continue;\n\n      return Element.extend(element);\n    }\n  }\n\n  function up(element, expression, index) {\n    element = $(element);\n\n    if (arguments.length === 1) return $(element.parentNode);\n    return _recursivelyFind(element, 'parentNode', expression, index);\n  }\n\n  function down(element, expression, index) {\n    if (arguments.length === 1) return firstDescendant(element);\n    ((element = $(element)),\n      (expression = expression || 0),\n      (index = index || 0));\n\n    if (Object.isNumber(expression)) ((index = expression), (expression = '*'));\n\n    var node = Prototype.Selector.select(expression, element)[index];\n    return Element.extend(node);\n  }\n\n  function previous(element, expression, index) {\n    return _recursivelyFind(element, 'previousSibling', expression, index);\n  }\n\n  function next(element, expression, index) {\n    return _recursivelyFind(element, 'nextSibling', expression, index);\n  }\n\n  function select(element) {\n    element = $(element);\n    var expressions = SLICE.call(arguments, 1).join(', ');\n    return Prototype.Selector.select(expressions, element);\n  }\n\n  function adjacent(element) {\n    element = $(element);\n    var expressions = SLICE.call(arguments, 1).join(', ');\n    var siblings = Element.siblings(element),\n      results = [];\n    for (var i = 0, sibling; (sibling = siblings[i]); i++) {\n      if (Prototype.Selector.match(sibling, expressions)) results.push(sibling);\n    }\n\n    return results;\n  }\n\n  function descendantOf_DOM(element, ancestor) {\n    ((element = $(element)), (ancestor = $(ancestor)));\n    if (!element || !ancestor) return false;\n    while ((element = element.parentNode))\n      if (element === ancestor) return true;\n    return false;\n  }\n\n  function descendantOf_contains(element, ancestor) {\n    ((element = $(element)), (ancestor = $(ancestor)));\n    if (!element || !ancestor) return false;\n    if (!ancestor.contains) return descendantOf_DOM(element, ancestor);\n    return ancestor.contains(element) && ancestor !== element;\n  }\n\n  function descendantOf_compareDocumentPosition(element, ancestor) {\n    ((element = $(element)), (ancestor = $(ancestor)));\n    if (!element || !ancestor) return false;\n    return (element.compareDocumentPosition(ancestor) & 8) === 8;\n  }\n\n  var descendantOf;\n  if (DIV.compareDocumentPosition) {\n    descendantOf = descendantOf_compareDocumentPosition;\n  } else if (DIV.contains) {\n    descendantOf = descendantOf_contains;\n  } else {\n    descendantOf = descendantOf_DOM;\n  }\n\n  Object.extend(methods, {\n    recursivelyCollect: recursivelyCollect,\n    ancestors: ancestors,\n    descendants: descendants,\n    firstDescendant: firstDescendant,\n    immediateDescendants: immediateDescendants,\n    previousSiblings: previousSiblings,\n    nextSiblings: nextSiblings,\n    siblings: siblings,\n    match: match,\n    up: up,\n    down: down,\n    previous: previous,\n    next: next,\n    select: select,\n    adjacent: adjacent,\n    descendantOf: descendantOf,\n\n    getElementsBySelector: select,\n\n    childElements: immediateDescendants\n  });\n\n  var idCounter = 1;\n  function identify(element) {\n    element = $(element);\n    var id = Element.readAttribute(element, 'id');\n    if (id) return id;\n\n    do {\n      id = 'anonymous_element_' + idCounter++;\n    } while ($(id));\n\n    Element.writeAttribute(element, 'id', id);\n    return id;\n  }\n\n  function readAttribute(element, name) {\n    return $(element).getAttribute(name);\n  }\n\n  function readAttribute_IE(element, name) {\n    element = $(element);\n\n    var table = ATTRIBUTE_TRANSLATIONS.read;\n    if (table.values[name]) return table.values[name](element, name);\n\n    if (table.names[name]) name = table.names[name];\n\n    if (name.include(':')) {\n      if (!element.attributes || !element.attributes[name]) return null;\n      return element.attributes[name].value;\n    }\n\n    return element.getAttribute(name);\n  }\n\n  function readAttribute_Opera(element, name) {\n    if (name === 'title') return element.title;\n    return element.getAttribute(name);\n  }\n\n  var PROBLEMATIC_ATTRIBUTE_READING = (function () {\n    DIV.setAttribute('onclick', []);\n    var value = DIV.getAttribute('onclick');\n    var isFunction = Object.isArray(value);\n    DIV.removeAttribute('onclick');\n    return isFunction;\n  })();\n\n  if (PROBLEMATIC_ATTRIBUTE_READING) {\n    readAttribute = readAttribute_IE;\n  } else if (Prototype.Browser.Opera) {\n    readAttribute = readAttribute_Opera;\n  }\n\n  function writeAttribute(element, name, value) {\n    element = $(element);\n    var attributes = {},\n      table = ATTRIBUTE_TRANSLATIONS.write;\n\n    if (typeof name === 'object') {\n      attributes = name;\n    } else {\n      attributes[name] = Object.isUndefined(value) ? true : value;\n    }\n\n    for (var attr in attributes) {\n      name = table.names[attr] || attr;\n      value = attributes[attr];\n      if (table.values[attr]) {\n        value = table.values[attr](element, value);\n        if (Object.isUndefined(value)) continue;\n      }\n      if (value === false || value === null) element.removeAttribute(name);\n      else if (value === true) element.setAttribute(name, name);\n      else element.setAttribute(name, value);\n    }\n\n    return element;\n  }\n\n  var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () {\n    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) {\n      return false;\n    }\n    var checkbox = document.createElement('<input type=\"checkbox\">');\n    checkbox.checked = true;\n    var node = checkbox.getAttributeNode('checked');\n    return !node || !node.specified;\n  })();\n\n  function hasAttribute(element, attribute) {\n    attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute;\n    var node = $(element).getAttributeNode(attribute);\n    return !!(node && node.specified);\n  }\n\n  function hasAttribute_IE(element, attribute) {\n    if (attribute === 'checked') {\n      return element.checked;\n    }\n    return hasAttribute(element, attribute);\n  }\n\n  GLOBAL.Element.Methods.Simulated.hasAttribute =\n    PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ? hasAttribute_IE : hasAttribute;\n\n  function classNames(element) {\n    return new Element.ClassNames(element);\n  }\n\n  var regExpCache = {};\n  function getRegExpForClassName(className) {\n    if (regExpCache[className]) return regExpCache[className];\n\n    var re = new RegExp('(^|\\\\s+)' + className + '(\\\\s+|$)');\n    regExpCache[className] = re;\n    return re;\n  }\n\n  function hasClassName(element, className) {\n    if (!(element = $(element))) return;\n\n    var elementClassName = element.className;\n\n    if (elementClassName.length === 0) return false;\n    if (elementClassName === className) return true;\n\n    return getRegExpForClassName(className).test(elementClassName);\n  }\n\n  function addClassName(element, className) {\n    if (!(element = $(element))) return;\n\n    if (!hasClassName(element, className))\n      element.className += (element.className ? ' ' : '') + className;\n\n    return element;\n  }\n\n  function removeClassName(element, className) {\n    if (!(element = $(element))) return;\n\n    element.className = element.className\n      .replace(getRegExpForClassName(className), ' ')\n      .strip();\n\n    return element;\n  }\n\n  function toggleClassName(element, className, bool) {\n    if (!(element = $(element))) return;\n\n    if (Object.isUndefined(bool)) bool = !hasClassName(element, className);\n\n    var method = Element[bool ? 'addClassName' : 'removeClassName'];\n    return method(element, className);\n  }\n\n  var ATTRIBUTE_TRANSLATIONS = {};\n\n  var classProp = 'className',\n    forProp = 'for';\n\n  DIV.setAttribute(classProp, 'x');\n  if (DIV.className !== 'x') {\n    DIV.setAttribute('class', 'x');\n    if (DIV.className === 'x') classProp = 'class';\n  }\n\n  var LABEL = document.createElement('label');\n  LABEL.setAttribute(forProp, 'x');\n  if (LABEL.htmlFor !== 'x') {\n    LABEL.setAttribute('htmlFor', 'x');\n    if (LABEL.htmlFor === 'x') forProp = 'htmlFor';\n  }\n  LABEL = null;\n\n  function _getAttr(element, attribute) {\n    return element.getAttribute(attribute);\n  }\n\n  function _getAttr2(element, attribute) {\n    return element.getAttribute(attribute, 2);\n  }\n\n  function _getAttrNode(element, attribute) {\n    var node = element.getAttributeNode(attribute);\n    return node ? node.value : '';\n  }\n\n  function _getFlag(element, attribute) {\n    return $(element).hasAttribute(attribute) ? attribute : null;\n  }\n\n  DIV.onclick = Prototype.emptyFunction;\n  var onclickValue = DIV.getAttribute('onclick');\n\n  var _getEv;\n\n  if (String(onclickValue).indexOf('{') > -1) {\n    _getEv = function (element, attribute) {\n      var value = element.getAttribute(attribute);\n      if (!value) return null;\n      value = value.toString();\n      value = value.split('{')[1];\n      value = value.split('}')[0];\n      return value.strip();\n    };\n  } else if (onclickValue === '') {\n    _getEv = function (element, attribute) {\n      var value = element.getAttribute(attribute);\n      if (!value) return null;\n      return value.strip();\n    };\n  }\n\n  ATTRIBUTE_TRANSLATIONS.read = {\n    names: {\n      class: classProp,\n      className: classProp,\n      for: forProp,\n      htmlFor: forProp\n    },\n\n    values: {\n      style: function (element) {\n        return element.style.cssText.toLowerCase();\n      },\n      title: function (element) {\n        return element.title;\n      }\n    }\n  };\n\n  ATTRIBUTE_TRANSLATIONS.write = {\n    names: {\n      className: 'class',\n      htmlFor: 'for',\n      cellpadding: 'cellPadding',\n      cellspacing: 'cellSpacing'\n    },\n\n    values: {\n      checked: function (element, value) {\n        value = !!value;\n        element.checked = value;\n        return value ? 'checked' : null;\n      },\n\n      style: function (element, value) {\n        element.style.cssText = value ? value : '';\n      }\n    }\n  };\n\n  ATTRIBUTE_TRANSLATIONS.has = { names: {} };\n\n  Object.extend(\n    ATTRIBUTE_TRANSLATIONS.write.names,\n    ATTRIBUTE_TRANSLATIONS.read.names\n  );\n\n  var CAMEL_CASED_ATTRIBUTE_NAMES = $w(\n    'colSpan rowSpan vAlign dateTime ' +\n      'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'\n  );\n\n  for (var i = 0, attr; (attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]); i++) {\n    ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr;\n    ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr;\n  }\n\n  Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, {\n    href: _getAttr2,\n    src: _getAttr2,\n    type: _getAttr,\n    action: _getAttrNode,\n    disabled: _getFlag,\n    checked: _getFlag,\n    readonly: _getFlag,\n    multiple: _getFlag,\n    onload: _getEv,\n    onunload: _getEv,\n    onclick: _getEv,\n    ondblclick: _getEv,\n    onmousedown: _getEv,\n    onmouseup: _getEv,\n    onmouseover: _getEv,\n    onmousemove: _getEv,\n    onmouseout: _getEv,\n    onfocus: _getEv,\n    onblur: _getEv,\n    onkeypress: _getEv,\n    onkeydown: _getEv,\n    onkeyup: _getEv,\n    onsubmit: _getEv,\n    onreset: _getEv,\n    onselect: _getEv,\n    onchange: _getEv\n  });\n\n  Object.extend(methods, {\n    identify: identify,\n    readAttribute: readAttribute,\n    writeAttribute: writeAttribute,\n    classNames: classNames,\n    hasClassName: hasClassName,\n    addClassName: addClassName,\n    removeClassName: removeClassName,\n    toggleClassName: toggleClassName\n  });\n\n  function normalizeStyleName(style) {\n    if (style === 'float' || style === 'styleFloat') return 'cssFloat';\n    return style.camelize();\n  }\n\n  function normalizeStyleName_IE(style) {\n    if (style === 'float' || style === 'cssFloat') return 'styleFloat';\n    return style.camelize();\n  }\n\n  function setStyle(element, styles) {\n    element = $(element);\n    var elementStyle = element.style,\n      match;\n\n    if (Object.isString(styles)) {\n      elementStyle.cssText += ';' + styles;\n      if (styles.include('opacity')) {\n        var opacity = styles.match(/opacity:\\s*(\\d?\\.?\\d*)/)[1];\n        Element.setOpacity(element, opacity);\n      }\n      return element;\n    }\n\n    for (var property in styles) {\n      if (property === 'opacity') {\n        Element.setOpacity(element, styles[property]);\n      } else {\n        var value = styles[property];\n        if (property === 'float' || property === 'cssFloat') {\n          property = Object.isUndefined(elementStyle.styleFloat)\n            ? 'cssFloat'\n            : 'styleFloat';\n        }\n        elementStyle[property] = value;\n      }\n    }\n\n    return element;\n  }\n\n  function getStyle(element, style) {\n    element = $(element);\n    style = normalizeStyleName(style);\n\n    var value = element.style[style];\n    if (!value || value === 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n\n    if (style === 'opacity') return value ? parseFloat(value) : 1.0;\n    return value === 'auto' ? null : value;\n  }\n\n  function getStyle_Opera(element, style) {\n    switch (style) {\n      case 'height':\n      case 'width':\n        if (!Element.visible(element)) return null;\n\n        var dim = parseInt(getStyle(element, style), 10);\n\n        if (dim !== element['offset' + style.capitalize()]) return dim + 'px';\n\n        return Element.measure(element, style);\n\n      default:\n        return getStyle(element, style);\n    }\n  }\n\n  function getStyle_IE(element, style) {\n    element = $(element);\n    style = normalizeStyleName_IE(style);\n\n    var value = element.style[style];\n    if (!value && element.currentStyle) {\n      value = element.currentStyle[style];\n    }\n\n    if (style === 'opacity') {\n      if (!STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity_IE(element);\n      else return value ? parseFloat(value) : 1.0;\n    }\n\n    if (value === 'auto') {\n      if ((style === 'width' || style === 'height') && Element.visible(element))\n        return Element.measure(element, style) + 'px';\n      return null;\n    }\n\n    return value;\n  }\n\n  function stripAlphaFromFilter_IE(filter) {\n    return (filter || '').replace(/alpha\\([^\\)]*\\)/gi, '');\n  }\n\n  function hasLayout_IE(element) {\n    if (!element.currentStyle || !element.currentStyle.hasLayout)\n      element.style.zoom = 1;\n    return element;\n  }\n\n  var STANDARD_CSS_OPACITY_SUPPORTED = (function () {\n    DIV.style.cssText = 'opacity:.55';\n    return /^0.55/.test(DIV.style.opacity);\n  })();\n\n  function setOpacity(element, value) {\n    element = $(element);\n    if (value == 1 || value === '') value = '';\n    else if (value < 0.00001) value = 0;\n    element.style.opacity = value;\n    return element;\n  }\n\n  function setOpacity_IE(element, value) {\n    if (STANDARD_CSS_OPACITY_SUPPORTED) return setOpacity(element, value);\n\n    element = hasLayout_IE($(element));\n    var filter = Element.getStyle(element, 'filter'),\n      style = element.style;\n\n    if (value == 1 || value === '') {\n      filter = stripAlphaFromFilter_IE(filter);\n      if (filter) style.filter = filter;\n      else style.removeAttribute('filter');\n      return element;\n    }\n\n    if (value < 0.00001) value = 0;\n\n    style.filter =\n      stripAlphaFromFilter_IE(filter) + ' alpha(opacity=' + value * 100 + ')';\n\n    return element;\n  }\n\n  function getOpacity(element) {\n    return Element.getStyle(element, 'opacity');\n  }\n\n  function getOpacity_IE(element) {\n    if (STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity(element);\n\n    var filter = Element.getStyle(element, 'filter');\n    if (filter.length === 0) return 1.0;\n    var match = (filter || '').match(/alpha\\(opacity=(.*)\\)/i);\n    if (match && match[1]) return parseFloat(match[1]) / 100;\n    return 1.0;\n  }\n\n  Object.extend(methods, {\n    setStyle: setStyle,\n    getStyle: getStyle,\n    setOpacity: setOpacity,\n    getOpacity: getOpacity\n  });\n\n  if ('styleFloat' in DIV.style) {\n    methods.getStyle = getStyle_IE;\n    methods.setOpacity = setOpacity_IE;\n    methods.getOpacity = getOpacity_IE;\n  }\n\n  var UID = 0;\n\n  GLOBAL.Element.Storage = { UID: 1 };\n\n  function getUniqueElementID(element) {\n    if (element === window) return 0;\n\n    if (typeof element._prototypeUID === 'undefined')\n      element._prototypeUID = Element.Storage.UID++;\n    return element._prototypeUID;\n  }\n\n  function getUniqueElementID_IE(element) {\n    if (element === window) return 0;\n    if (element == document) return 1;\n    return element.uniqueID;\n  }\n\n  var HAS_UNIQUE_ID_PROPERTY = 'uniqueID' in DIV;\n  if (HAS_UNIQUE_ID_PROPERTY) getUniqueElementID = getUniqueElementID_IE;\n\n  function getStorage(element) {\n    if (!(element = $(element))) return;\n\n    var uid = getUniqueElementID(element);\n\n    if (!Element.Storage[uid]) Element.Storage[uid] = $H();\n\n    return Element.Storage[uid];\n  }\n\n  function store(element, key, value) {\n    if (!(element = $(element))) return;\n    var storage = getStorage(element);\n    if (arguments.length === 2) {\n      storage.update(key);\n    } else {\n      storage.set(key, value);\n    }\n    return element;\n  }\n\n  function retrieve(element, key, defaultValue) {\n    if (!(element = $(element))) return;\n    var storage = getStorage(element),\n      value = storage.get(key);\n\n    if (Object.isUndefined(value)) {\n      storage.set(key, defaultValue);\n      value = defaultValue;\n    }\n\n    return value;\n  }\n\n  Object.extend(methods, {\n    getStorage: getStorage,\n    store: store,\n    retrieve: retrieve\n  });\n\n  var Methods = {},\n    ByTag = Element.Methods.ByTag,\n    F = Prototype.BrowserFeatures;\n\n  if (!F.ElementExtensions && '__proto__' in DIV) {\n    GLOBAL.HTMLElement = {};\n    GLOBAL.HTMLElement.prototype = DIV['__proto__'];\n    F.ElementExtensions = true;\n  }\n\n  function checkElementPrototypeDeficiency(tagName) {\n    if (typeof window.Element === 'undefined') return false;\n    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false;\n    var proto = window.Element.prototype;\n    if (proto) {\n      var id = '_' + (Math.random() + '').slice(2),\n        el = document.createElement(tagName);\n      proto[id] = 'x';\n      var isBuggy = el[id] !== 'x';\n      delete proto[id];\n      el = null;\n      return isBuggy;\n    }\n\n    return false;\n  }\n\n  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY =\n    checkElementPrototypeDeficiency('object');\n\n  function extendElementWith(element, methods) {\n    for (var property in methods) {\n      var value = methods[property];\n      if (Object.isFunction(value) && !(property in element))\n        element[property] = value.methodize();\n    }\n  }\n\n  var EXTENDED = {};\n  function elementIsExtended(element) {\n    var uid = getUniqueElementID(element);\n    return uid in EXTENDED;\n  }\n\n  function extend(element) {\n    if (!element || elementIsExtended(element)) return element;\n    if (element.nodeType !== Node.ELEMENT_NODE || element == window)\n      return element;\n\n    var methods = Object.clone(Methods),\n      tagName = element.tagName.toUpperCase();\n\n    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);\n\n    extendElementWith(element, methods);\n    EXTENDED[getUniqueElementID(element)] = true;\n    return element;\n  }\n\n  function extend_IE8(element) {\n    if (!element || elementIsExtended(element)) return element;\n\n    var t = element.tagName;\n    if (t && /^(?:object|applet|embed)$/i.test(t)) {\n      extendElementWith(element, Element.Methods);\n      extendElementWith(element, Element.Methods.Simulated);\n      extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);\n    }\n\n    return element;\n  }\n\n  if (F.SpecificElementExtensions) {\n    extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K;\n  }\n\n  function addMethodsToTagName(tagName, methods) {\n    tagName = tagName.toUpperCase();\n    if (!ByTag[tagName]) ByTag[tagName] = {};\n    Object.extend(ByTag[tagName], methods);\n  }\n\n  function mergeMethods(destination, methods, onlyIfAbsent) {\n    if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false;\n    for (var property in methods) {\n      var value = methods[property];\n      if (!Object.isFunction(value)) continue;\n      if (!onlyIfAbsent || !(property in destination))\n        destination[property] = value.methodize();\n    }\n  }\n\n  function findDOMClass(tagName) {\n    var klass;\n    var trans = {\n      OPTGROUP: 'OptGroup',\n      TEXTAREA: 'TextArea',\n      P: 'Paragraph',\n      FIELDSET: 'FieldSet',\n      UL: 'UList',\n      OL: 'OList',\n      DL: 'DList',\n      DIR: 'Directory',\n      H1: 'Heading',\n      H2: 'Heading',\n      H3: 'Heading',\n      H4: 'Heading',\n      H5: 'Heading',\n      H6: 'Heading',\n      Q: 'Quote',\n      INS: 'Mod',\n      DEL: 'Mod',\n      A: 'Anchor',\n      IMG: 'Image',\n      CAPTION: 'TableCaption',\n      COL: 'TableCol',\n      COLGROUP: 'TableCol',\n      THEAD: 'TableSection',\n      TFOOT: 'TableSection',\n      TBODY: 'TableSection',\n      TR: 'TableRow',\n      TH: 'TableCell',\n      TD: 'TableCell',\n      FRAMESET: 'FrameSet',\n      IFRAME: 'IFrame'\n    };\n    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName.capitalize() + 'Element';\n    if (window[klass]) return window[klass];\n\n    var element = document.createElement(tagName),\n      proto = element['__proto__'] || element.constructor.prototype;\n\n    element = null;\n    return proto;\n  }\n\n  function addMethods(methods) {\n    if (arguments.length === 0) addFormMethods();\n\n    if (arguments.length === 2) {\n      var tagName = methods;\n      methods = arguments[1];\n    }\n\n    if (!tagName) {\n      Object.extend(Element.Methods, methods || {});\n    } else {\n      if (Object.isArray(tagName)) {\n        for (var i = 0, tag; (tag = tagName[i]); i++)\n          addMethodsToTagName(tag, methods);\n      } else {\n        addMethodsToTagName(tagName, methods);\n      }\n    }\n\n    var ELEMENT_PROTOTYPE = window.HTMLElement\n      ? HTMLElement.prototype\n      : Element.prototype;\n\n    if (F.ElementExtensions) {\n      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods);\n      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true);\n    }\n\n    if (F.SpecificElementExtensions) {\n      for (var tag in Element.Methods.ByTag) {\n        var klass = findDOMClass(tag);\n        if (Object.isUndefined(klass)) continue;\n        mergeMethods(klass.prototype, ByTag[tag]);\n      }\n    }\n\n    Object.extend(Element, Element.Methods);\n    Object.extend(Element, Element.Methods.Simulated);\n    delete Element.ByTag;\n    delete Element.Simulated;\n\n    Element.extend.refresh();\n\n    ELEMENT_CACHE = {};\n  }\n\n  Object.extend(GLOBAL.Element, {\n    extend: extend,\n    addMethods: addMethods\n  });\n\n  if (extend === Prototype.K) {\n    GLOBAL.Element.extend.refresh = Prototype.emptyFunction;\n  } else {\n    GLOBAL.Element.extend.refresh = function () {\n      if (Prototype.BrowserFeatures.ElementExtensions) return;\n      Object.extend(Methods, Element.Methods);\n      Object.extend(Methods, Element.Methods.Simulated);\n\n      EXTENDED = {};\n    };\n  }\n\n  function addFormMethods() {\n    Object.extend(Form, Form.Methods);\n    Object.extend(Form.Element, Form.Element.Methods);\n    Object.extend(Element.Methods.ByTag, {\n      FORM: Object.clone(Form.Methods),\n      INPUT: Object.clone(Form.Element.Methods),\n      SELECT: Object.clone(Form.Element.Methods),\n      TEXTAREA: Object.clone(Form.Element.Methods),\n      BUTTON: Object.clone(Form.Element.Methods)\n    });\n  }\n\n  Element.addMethods(methods);\n\n  function destroyCache_IE() {\n    DIV = null;\n    ELEMENT_CACHE = null;\n  }\n\n  if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE);\n})(this);\n(function () {\n  function toDecimal(pctString) {\n    var match = pctString.match(/^(\\d+)%?$/i);\n    if (!match) return null;\n    return Number(match[1]) / 100;\n  }\n\n  function getRawStyle(element, style) {\n    element = $(element);\n\n    var value = element.style[style];\n    if (!value || value === 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n\n    if (style === 'opacity') return value ? parseFloat(value) : 1.0;\n    return value === 'auto' ? null : value;\n  }\n\n  function getRawStyle_IE(element, style) {\n    var value = element.style[style];\n    if (!value && element.currentStyle) {\n      value = element.currentStyle[style];\n    }\n    return value;\n  }\n\n  function getContentWidth(element, context) {\n    var boxWidth = element.offsetWidth;\n\n    var bl = getPixelValue(element, 'borderLeftWidth', context) || 0;\n    var br = getPixelValue(element, 'borderRightWidth', context) || 0;\n    var pl = getPixelValue(element, 'paddingLeft', context) || 0;\n    var pr = getPixelValue(element, 'paddingRight', context) || 0;\n\n    return boxWidth - bl - br - pl - pr;\n  }\n\n  if (\n    !Object.isUndefined(document.documentElement.currentStyle) &&\n    !Prototype.Browser.Opera\n  ) {\n    getRawStyle = getRawStyle_IE;\n  }\n\n  function getPixelValue(value, property, context) {\n    var element = null;\n    if (Object.isElement(value)) {\n      element = value;\n      value = getRawStyle(element, property);\n    }\n\n    if (value === null || Object.isUndefined(value)) {\n      return null;\n    }\n\n    if (/^(?:-)?\\d+(\\.\\d+)?(px)?$/i.test(value)) {\n      return window.parseFloat(value);\n    }\n\n    var isPercentage = value.include('%'),\n      isViewport = context === document.viewport;\n\n    if (\n      /\\d/.test(value) &&\n      element &&\n      element.runtimeStyle &&\n      !(isPercentage && isViewport)\n    ) {\n      var style = element.style.left,\n        rStyle = element.runtimeStyle.left;\n      element.runtimeStyle.left = element.currentStyle.left;\n      element.style.left = value || 0;\n      value = element.style.pixelLeft;\n      element.style.left = style;\n      element.runtimeStyle.left = rStyle;\n\n      return value;\n    }\n\n    if (element && isPercentage) {\n      context = context || element.parentNode;\n      var decimal = toDecimal(value),\n        whole = null;\n\n      var isHorizontal =\n        property.include('left') ||\n        property.include('right') ||\n        property.include('width');\n\n      var isVertical =\n        property.include('top') ||\n        property.include('bottom') ||\n        property.include('height');\n\n      if (context === document.viewport) {\n        if (isHorizontal) {\n          whole = document.viewport.getWidth();\n        } else if (isVertical) {\n          whole = document.viewport.getHeight();\n        }\n      } else {\n        if (isHorizontal) {\n          whole = $(context).measure('width');\n        } else if (isVertical) {\n          whole = $(context).measure('height');\n        }\n      }\n\n      return whole === null ? 0 : whole * decimal;\n    }\n\n    return 0;\n  }\n\n  function toCSSPixels(number) {\n    if (Object.isString(number) && number.endsWith('px')) return number;\n    return number + 'px';\n  }\n\n  function isDisplayed(element) {\n    while (element && element.parentNode) {\n      var display = element.getStyle('display');\n      if (display === 'none') {\n        return false;\n      }\n      element = $(element.parentNode);\n    }\n    return true;\n  }\n\n  var hasLayout = Prototype.K;\n  if ('currentStyle' in document.documentElement) {\n    hasLayout = function (element) {\n      if (!element.currentStyle.hasLayout) {\n        element.style.zoom = 1;\n      }\n      return element;\n    };\n  }\n\n  function cssNameFor(key) {\n    if (key.include('border')) key = key + '-width';\n    return key.camelize();\n  }\n\n  Element.Layout = Class.create(Hash, {\n    initialize: function ($super, element, preCompute) {\n      $super();\n      this.element = $(element);\n\n      Element.Layout.PROPERTIES.each(function (property) {\n        this._set(property, null);\n      }, this);\n\n      if (preCompute) {\n        this._preComputing = true;\n        this._begin();\n        Element.Layout.PROPERTIES.each(this._compute, this);\n        this._end();\n        this._preComputing = false;\n      }\n    },\n\n    _set: function (property, value) {\n      return Hash.prototype.set.call(this, property, value);\n    },\n\n    set: function (property, value) {\n      throw 'Properties of Element.Layout are read-only.';\n    },\n\n    get: function ($super, property) {\n      var value = $super(property);\n      return value === null ? this._compute(property) : value;\n    },\n\n    _begin: function () {\n      if (this._isPrepared()) return;\n\n      var element = this.element;\n      if (isDisplayed(element)) {\n        this._setPrepared(true);\n        return;\n      }\n\n      var originalStyles = {\n        position: element.style.position || '',\n        width: element.style.width || '',\n        visibility: element.style.visibility || '',\n        display: element.style.display || ''\n      };\n\n      element.store('prototype_original_styles', originalStyles);\n\n      var position = getRawStyle(element, 'position'),\n        width = element.offsetWidth;\n\n      if (width === 0 || width === null) {\n        element.style.display = 'block';\n        width = element.offsetWidth;\n      }\n\n      var context =\n        position === 'fixed' ? document.viewport : element.parentNode;\n\n      var tempStyles = {\n        visibility: 'hidden',\n        display: 'block'\n      };\n\n      if (position !== 'fixed') tempStyles.position = 'absolute';\n\n      element.setStyle(tempStyles);\n\n      var positionedWidth = element.offsetWidth,\n        newWidth;\n      if (width && positionedWidth === width) {\n        newWidth = getContentWidth(element, context);\n      } else if (position === 'absolute' || position === 'fixed') {\n        newWidth = getContentWidth(element, context);\n      } else {\n        var parent = element.parentNode,\n          pLayout = $(parent).getLayout();\n\n        newWidth =\n          pLayout.get('width') -\n          this.get('margin-left') -\n          this.get('border-left') -\n          this.get('padding-left') -\n          this.get('padding-right') -\n          this.get('border-right') -\n          this.get('margin-right');\n      }\n\n      element.setStyle({ width: newWidth + 'px' });\n\n      this._setPrepared(true);\n    },\n\n    _end: function () {\n      var element = this.element;\n      var originalStyles = element.retrieve('prototype_original_styles');\n      element.store('prototype_original_styles', null);\n      element.setStyle(originalStyles);\n      this._setPrepared(false);\n    },\n\n    _compute: function (property) {\n      var COMPUTATIONS = Element.Layout.COMPUTATIONS;\n      if (!(property in COMPUTATIONS)) {\n        throw 'Property not found.';\n      }\n\n      return this._set(\n        property,\n        COMPUTATIONS[property].call(this, this.element)\n      );\n    },\n\n    _isPrepared: function () {\n      return this.element.retrieve('prototype_element_layout_prepared', false);\n    },\n\n    _setPrepared: function (bool) {\n      return this.element.store('prototype_element_layout_prepared', bool);\n    },\n\n    toObject: function () {\n      var args = $A(arguments);\n      var keys =\n        args.length === 0\n          ? Element.Layout.PROPERTIES\n          : args.join(' ').split(' ');\n      var obj = {};\n      keys.each(function (key) {\n        if (!Element.Layout.PROPERTIES.include(key)) return;\n        var value = this.get(key);\n        if (value != null) obj[key] = value;\n      }, this);\n      return obj;\n    },\n\n    toHash: function () {\n      var obj = this.toObject.apply(this, arguments);\n      return new Hash(obj);\n    },\n\n    toCSS: function () {\n      var args = $A(arguments);\n      var keys =\n        args.length === 0\n          ? Element.Layout.PROPERTIES\n          : args.join(' ').split(' ');\n      var css = {};\n\n      keys.each(function (key) {\n        if (!Element.Layout.PROPERTIES.include(key)) return;\n        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;\n\n        var value = this.get(key);\n        if (value != null) css[cssNameFor(key)] = value + 'px';\n      }, this);\n      return css;\n    },\n\n    inspect: function () {\n      return '#<Element.Layout>';\n    }\n  });\n\n  Object.extend(Element.Layout, {\n    PROPERTIES: $w(\n      'height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'\n    ),\n\n    COMPOSITE_PROPERTIES: $w(\n      'padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'\n    ),\n\n    COMPUTATIONS: {\n      height: function (element) {\n        if (!this._preComputing) this._begin();\n\n        var bHeight = this.get('border-box-height');\n        if (bHeight <= 0) {\n          if (!this._preComputing) this._end();\n          return 0;\n        }\n\n        var bTop = this.get('border-top'),\n          bBottom = this.get('border-bottom');\n\n        var pTop = this.get('padding-top'),\n          pBottom = this.get('padding-bottom');\n\n        if (!this._preComputing) this._end();\n\n        return bHeight - bTop - bBottom - pTop - pBottom;\n      },\n\n      width: function (element) {\n        if (!this._preComputing) this._begin();\n\n        var bWidth = this.get('border-box-width');\n        if (bWidth <= 0) {\n          if (!this._preComputing) this._end();\n          return 0;\n        }\n\n        var bLeft = this.get('border-left'),\n          bRight = this.get('border-right');\n\n        var pLeft = this.get('padding-left'),\n          pRight = this.get('padding-right');\n\n        if (!this._preComputing) this._end();\n        return bWidth - bLeft - bRight - pLeft - pRight;\n      },\n\n      'padding-box-height': function (element) {\n        var height = this.get('height'),\n          pTop = this.get('padding-top'),\n          pBottom = this.get('padding-bottom');\n\n        return height + pTop + pBottom;\n      },\n\n      'padding-box-width': function (element) {\n        var width = this.get('width'),\n          pLeft = this.get('padding-left'),\n          pRight = this.get('padding-right');\n\n        return width + pLeft + pRight;\n      },\n\n      'border-box-height': function (element) {\n        if (!this._preComputing) this._begin();\n        var height = element.offsetHeight;\n        if (!this._preComputing) this._end();\n        return height;\n      },\n\n      'border-box-width': function (element) {\n        if (!this._preComputing) this._begin();\n        var width = element.offsetWidth;\n        if (!this._preComputing) this._end();\n        return width;\n      },\n\n      'margin-box-height': function (element) {\n        var bHeight = this.get('border-box-height'),\n          mTop = this.get('margin-top'),\n          mBottom = this.get('margin-bottom');\n\n        if (bHeight <= 0) return 0;\n\n        return bHeight + mTop + mBottom;\n      },\n\n      'margin-box-width': function (element) {\n        var bWidth = this.get('border-box-width'),\n          mLeft = this.get('margin-left'),\n          mRight = this.get('margin-right');\n\n        if (bWidth <= 0) return 0;\n\n        return bWidth + mLeft + mRight;\n      },\n\n      top: function (element) {\n        var offset = element.positionedOffset();\n        return offset.top;\n      },\n\n      bottom: function (element) {\n        var offset = element.positionedOffset(),\n          parent = element.getOffsetParent(),\n          pHeight = parent.measure('height');\n\n        var mHeight = this.get('border-box-height');\n\n        return pHeight - mHeight - offset.top;\n      },\n\n      left: function (element) {\n        var offset = element.positionedOffset();\n        return offset.left;\n      },\n\n      right: function (element) {\n        var offset = element.positionedOffset(),\n          parent = element.getOffsetParent(),\n          pWidth = parent.measure('width');\n\n        var mWidth = this.get('border-box-width');\n\n        return pWidth - mWidth - offset.left;\n      },\n\n      'padding-top': function (element) {\n        return getPixelValue(element, 'paddingTop');\n      },\n\n      'padding-bottom': function (element) {\n        return getPixelValue(element, 'paddingBottom');\n      },\n\n      'padding-left': function (element) {\n        return getPixelValue(element, 'paddingLeft');\n      },\n\n      'padding-right': function (element) {\n        return getPixelValue(element, 'paddingRight');\n      },\n\n      'border-top': function (element) {\n        return getPixelValue(element, 'borderTopWidth');\n      },\n\n      'border-bottom': function (element) {\n        return getPixelValue(element, 'borderBottomWidth');\n      },\n\n      'border-left': function (element) {\n        return getPixelValue(element, 'borderLeftWidth');\n      },\n\n      'border-right': function (element) {\n        return getPixelValue(element, 'borderRightWidth');\n      },\n\n      'margin-top': function (element) {\n        return getPixelValue(element, 'marginTop');\n      },\n\n      'margin-bottom': function (element) {\n        return getPixelValue(element, 'marginBottom');\n      },\n\n      'margin-left': function (element) {\n        return getPixelValue(element, 'marginLeft');\n      },\n\n      'margin-right': function (element) {\n        return getPixelValue(element, 'marginRight');\n      }\n    }\n  });\n\n  if ('getBoundingClientRect' in document.documentElement) {\n    Object.extend(Element.Layout.COMPUTATIONS, {\n      right: function (element) {\n        var parent = hasLayout(element.getOffsetParent());\n        var rect = element.getBoundingClientRect(),\n          pRect = parent.getBoundingClientRect();\n\n        return (pRect.right - rect.right).round();\n      },\n\n      bottom: function (element) {\n        var parent = hasLayout(element.getOffsetParent());\n        var rect = element.getBoundingClientRect(),\n          pRect = parent.getBoundingClientRect();\n\n        return (pRect.bottom - rect.bottom).round();\n      }\n    });\n  }\n\n  Element.Offset = Class.create({\n    initialize: function (left, top) {\n      this.left = left.round();\n      this.top = top.round();\n\n      this[0] = this.left;\n      this[1] = this.top;\n    },\n\n    relativeTo: function (offset) {\n      return new Element.Offset(this.left - offset.left, this.top - offset.top);\n    },\n\n    inspect: function () {\n      return '#<Element.Offset left: #{left} top: #{top}>'.interpolate(this);\n    },\n\n    toString: function () {\n      return '[#{left}, #{top}]'.interpolate(this);\n    },\n\n    toArray: function () {\n      return [this.left, this.top];\n    }\n  });\n\n  function getLayout(element, preCompute) {\n    return new Element.Layout(element, preCompute);\n  }\n\n  function measure(element, property) {\n    return $(element).getLayout().get(property);\n  }\n\n  function getHeight(element) {\n    return Element.getDimensions(element).height;\n  }\n\n  function getWidth(element) {\n    return Element.getDimensions(element).width;\n  }\n\n  function getDimensions(element) {\n    element = $(element);\n    var display = Element.getStyle(element, 'display');\n\n    if (display && display !== 'none') {\n      return { width: element.offsetWidth, height: element.offsetHeight };\n    }\n\n    var style = element.style;\n    var originalStyles = {\n      visibility: style.visibility,\n      position: style.position,\n      display: style.display\n    };\n\n    var newStyles = {\n      visibility: 'hidden',\n      display: 'block'\n    };\n\n    if (originalStyles.position !== 'fixed') newStyles.position = 'absolute';\n\n    Element.setStyle(element, newStyles);\n\n    var dimensions = {\n      width: element.offsetWidth,\n      height: element.offsetHeight\n    };\n\n    Element.setStyle(element, originalStyles);\n\n    return dimensions;\n  }\n\n  function getOffsetParent(element) {\n    element = $(element);\n\n    function selfOrBody(element) {\n      return isHtml(element) ? $(document.body) : $(element);\n    }\n\n    if (\n      isDocument(element) ||\n      isDetached(element) ||\n      isBody(element) ||\n      isHtml(element)\n    )\n      return $(document.body);\n\n    var isInline = Element.getStyle(element, 'display') === 'inline';\n    if (!isInline && element.offsetParent)\n      return selfOrBody(element.offsetParent);\n\n    while ((element = element.parentNode) && element !== document.body) {\n      if (Element.getStyle(element, 'position') !== 'static') {\n        return selfOrBody(element);\n      }\n    }\n\n    return $(document.body);\n  }\n\n  function cumulativeOffset(element) {\n    element = $(element);\n    var valueT = 0,\n      valueL = 0;\n    if (element.parentNode) {\n      do {\n        valueT += element.offsetTop || 0;\n        valueL += element.offsetLeft || 0;\n        element = element.offsetParent;\n      } while (element);\n    }\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function positionedOffset(element) {\n    element = $(element);\n\n    var layout = element.getLayout();\n\n    var valueT = 0,\n      valueL = 0;\n    do {\n      valueT += element.offsetTop || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (isBody(element)) break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n\n    valueL -= layout.get('margin-left');\n    valueT -= layout.get('margin-top');\n\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function cumulativeScrollOffset(element) {\n    var valueT = 0,\n      valueL = 0;\n    do {\n      if (element === document.body) {\n        var bodyScrollNode =\n          document.documentElement || document.body.parentNode || document.body;\n        valueT += !Object.isUndefined(window.pageYOffset)\n          ? window.pageYOffset\n          : bodyScrollNode.scrollTop || 0;\n        valueL += !Object.isUndefined(window.pageXOffset)\n          ? window.pageXOffset\n          : bodyScrollNode.scrollLeft || 0;\n        break;\n      } else {\n        valueT += element.scrollTop || 0;\n        valueL += element.scrollLeft || 0;\n        element = element.parentNode;\n      }\n    } while (element);\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function viewportOffset(forElement) {\n    var valueT = 0,\n      valueL = 0,\n      docBody = document.body;\n\n    forElement = $(forElement);\n    var element = forElement;\n    do {\n      valueT += element.offsetTop || 0;\n      valueL += element.offsetLeft || 0;\n      if (\n        element.offsetParent == docBody &&\n        Element.getStyle(element, 'position') == 'absolute'\n      )\n        break;\n    } while ((element = element.offsetParent));\n\n    element = forElement;\n    do {\n      if (element != docBody) {\n        valueT -= element.scrollTop || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while ((element = element.parentNode));\n    return new Element.Offset(valueL, valueT);\n  }\n\n  function absolutize(element) {\n    element = $(element);\n\n    if (Element.getStyle(element, 'position') === 'absolute') {\n      return element;\n    }\n\n    var offsetParent = getOffsetParent(element);\n    var eOffset = element.viewportOffset(),\n      pOffset = offsetParent.viewportOffset();\n\n    var offset = eOffset.relativeTo(pOffset);\n    var layout = element.getLayout();\n\n    element.store('prototype_absolutize_original_styles', {\n      position: element.getStyle('position'),\n      left: element.getStyle('left'),\n      top: element.getStyle('top'),\n      width: element.getStyle('width'),\n      height: element.getStyle('height')\n    });\n\n    element.setStyle({\n      position: 'absolute',\n      top: offset.top + 'px',\n      left: offset.left + 'px',\n      width: layout.get('width') + 'px',\n      height: layout.get('height') + 'px'\n    });\n\n    return element;\n  }\n\n  function relativize(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') === 'relative') {\n      return element;\n    }\n\n    var originalStyles = element.retrieve(\n      'prototype_absolutize_original_styles'\n    );\n\n    if (originalStyles) element.setStyle(originalStyles);\n    return element;\n  }\n\n  function scrollTo(element) {\n    element = $(element);\n    var pos = Element.cumulativeOffset(element);\n    window.scrollTo(pos.left, pos.top);\n    return element;\n  }\n\n  function makePositioned(element) {\n    element = $(element);\n    var position = Element.getStyle(element, 'position'),\n      styles = {};\n    if (position === 'static' || !position) {\n      styles.position = 'relative';\n      if (Prototype.Browser.Opera) {\n        styles.top = 0;\n        styles.left = 0;\n      }\n      Element.setStyle(element, styles);\n      Element.store(element, 'prototype_made_positioned', true);\n    }\n    return element;\n  }\n\n  function undoPositioned(element) {\n    element = $(element);\n    var storage = Element.getStorage(element),\n      madePositioned = storage.get('prototype_made_positioned');\n\n    if (madePositioned) {\n      storage.unset('prototype_made_positioned');\n      Element.setStyle(element, {\n        position: '',\n        top: '',\n        bottom: '',\n        left: '',\n        right: ''\n      });\n    }\n    return element;\n  }\n\n  function makeClipping(element) {\n    element = $(element);\n\n    var storage = Element.getStorage(element),\n      madeClipping = storage.get('prototype_made_clipping');\n\n    if (Object.isUndefined(madeClipping)) {\n      var overflow = Element.getStyle(element, 'overflow');\n      storage.set('prototype_made_clipping', overflow);\n      if (overflow !== 'hidden') element.style.overflow = 'hidden';\n    }\n\n    return element;\n  }\n\n  function undoClipping(element) {\n    element = $(element);\n    var storage = Element.getStorage(element),\n      overflow = storage.get('prototype_made_clipping');\n\n    if (!Object.isUndefined(overflow)) {\n      storage.unset('prototype_made_clipping');\n      element.style.overflow = overflow || '';\n    }\n\n    return element;\n  }\n\n  function clonePosition(element, source, options) {\n    options = Object.extend(\n      {\n        setLeft: true,\n        setTop: true,\n        setWidth: true,\n        setHeight: true,\n        offsetTop: 0,\n        offsetLeft: 0\n      },\n      options || {}\n    );\n\n    var docEl = document.documentElement;\n\n    source = $(source);\n    element = $(element);\n    var p,\n      delta,\n      layout,\n      styles = {};\n\n    if (options.setLeft || options.setTop) {\n      p = Element.viewportOffset(source);\n      delta = [0, 0];\n      if (Element.getStyle(element, 'position') === 'absolute') {\n        var parent = Element.getOffsetParent(element);\n        if (parent !== document.body) delta = Element.viewportOffset(parent);\n      }\n    }\n\n    function pageScrollXY() {\n      var x = 0,\n        y = 0;\n      if (Object.isNumber(window.pageXOffset)) {\n        x = window.pageXOffset;\n        y = window.pageYOffset;\n      } else if (\n        document.body &&\n        (document.body.scrollLeft || document.body.scrollTop)\n      ) {\n        x = document.body.scrollLeft;\n        y = document.body.scrollTop;\n      } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) {\n        x = docEl.scrollLeft;\n        y = docEl.scrollTop;\n      }\n      return { x: x, y: y };\n    }\n\n    var pageXY = pageScrollXY();\n\n    if (options.setWidth || options.setHeight) {\n      layout = Element.getLayout(source);\n    }\n\n    if (options.setLeft)\n      styles.left = p[0] + pageXY.x - delta[0] + options.offsetLeft + 'px';\n    if (options.setTop)\n      styles.top = p[1] + pageXY.y - delta[1] + options.offsetTop + 'px';\n\n    var currentLayout = element.getLayout();\n\n    if (options.setWidth) {\n      styles.width = layout.get('width') + 'px';\n    }\n    if (options.setHeight) {\n      styles.height = layout.get('height') + 'px';\n    }\n\n    return Element.setStyle(element, styles);\n  }\n\n  if (Prototype.Browser.IE) {\n    getOffsetParent = getOffsetParent.wrap(function (proceed, element) {\n      element = $(element);\n\n      if (\n        isDocument(element) ||\n        isDetached(element) ||\n        isBody(element) ||\n        isHtml(element)\n      )\n        return $(document.body);\n\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    });\n\n    positionedOffset = positionedOffset.wrap(function (proceed, element) {\n      element = $(element);\n      if (!element.parentNode) return new Element.Offset(0, 0);\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n\n      var offsetParent = element.getOffsetParent();\n      if (offsetParent && offsetParent.getStyle('position') === 'fixed')\n        hasLayout(offsetParent);\n\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    });\n  } else if (Prototype.Browser.Webkit) {\n    cumulativeOffset = function (element) {\n      element = $(element);\n      var valueT = 0,\n        valueL = 0;\n      do {\n        valueT += element.offsetTop || 0;\n        valueL += element.offsetLeft || 0;\n        if (element.offsetParent == document.body) {\n          if (Element.getStyle(element, 'position') == 'absolute') break;\n        }\n\n        element = element.offsetParent;\n      } while (element);\n\n      return new Element.Offset(valueL, valueT);\n    };\n  }\n\n  Element.addMethods({\n    getLayout: getLayout,\n    measure: measure,\n    getWidth: getWidth,\n    getHeight: getHeight,\n    getDimensions: getDimensions,\n    getOffsetParent: getOffsetParent,\n    cumulativeOffset: cumulativeOffset,\n    positionedOffset: positionedOffset,\n    cumulativeScrollOffset: cumulativeScrollOffset,\n    viewportOffset: viewportOffset,\n    absolutize: absolutize,\n    relativize: relativize,\n    scrollTo: scrollTo,\n    makePositioned: makePositioned,\n    undoPositioned: undoPositioned,\n    makeClipping: makeClipping,\n    undoClipping: undoClipping,\n    clonePosition: clonePosition\n  });\n\n  function isBody(element) {\n    return element.nodeName.toUpperCase() === 'BODY';\n  }\n\n  function isHtml(element) {\n    return element.nodeName.toUpperCase() === 'HTML';\n  }\n\n  function isDocument(element) {\n    return element.nodeType === Node.DOCUMENT_NODE;\n  }\n\n  function isDetached(element) {\n    return (\n      element !== document.body && !Element.descendantOf(element, document.body)\n    );\n  }\n\n  if ('getBoundingClientRect' in document.documentElement) {\n    Element.addMethods({\n      viewportOffset: function (element) {\n        element = $(element);\n        if (isDetached(element)) return new Element.Offset(0, 0);\n\n        var rect = element.getBoundingClientRect(),\n          docEl = document.documentElement;\n        return new Element.Offset(\n          rect.left - docEl.clientLeft,\n          rect.top - docEl.clientTop\n        );\n      }\n    });\n  }\n})();\n\n(function () {\n  var IS_OLD_OPERA =\n    Prototype.Browser.Opera && window.parseFloat(window.opera.version()) < 9.5;\n  var ROOT = null;\n  function getRootElement() {\n    if (ROOT) return ROOT;\n    ROOT = IS_OLD_OPERA ? document.body : document.documentElement;\n    return ROOT;\n  }\n\n  function getDimensions() {\n    return { width: this.getWidth(), height: this.getHeight() };\n  }\n\n  function getWidth() {\n    return getRootElement().clientWidth;\n  }\n\n  function getHeight() {\n    return getRootElement().clientHeight;\n  }\n\n  function getScrollOffsets() {\n    var x =\n      window.pageXOffset ||\n      document.documentElement.scrollLeft ||\n      document.body.scrollLeft;\n    var y =\n      window.pageYOffset ||\n      document.documentElement.scrollTop ||\n      document.body.scrollTop;\n\n    return new Element.Offset(x, y);\n  }\n\n  document.viewport = {\n    getDimensions: getDimensions,\n    getWidth: getWidth,\n    getHeight: getHeight,\n    getScrollOffsets: getScrollOffsets\n  };\n})();\nwindow.$$ = function () {\n  var expression = $A(arguments).join(', ');\n  return Prototype.Selector.select(expression, document);\n};\n\nPrototype.Selector = (function () {\n  function select() {\n    throw new Error('Method \"Prototype.Selector.select\" must be defined.');\n  }\n\n  function match() {\n    throw new Error('Method \"Prototype.Selector.match\" must be defined.');\n  }\n\n  function find(elements, expression, index) {\n    index = index || 0;\n    var match = Prototype.Selector.match,\n      length = elements.length,\n      matchIndex = 0,\n      i;\n\n    for (i = 0; i < length; i++) {\n      if (match(elements[i], expression) && index == matchIndex++) {\n        return Element.extend(elements[i]);\n      }\n    }\n  }\n\n  function extendElements(elements) {\n    for (var i = 0, length = elements.length; i < length; i++) {\n      Element.extend(elements[i]);\n    }\n    return elements;\n  }\n\n  var K = Prototype.K;\n\n  return {\n    select: select,\n    match: match,\n    find: find,\n    extendElements: Element.extend === K ? K : extendElements,\n    extendElement: Element.extend\n  };\n})();\nPrototype._original_property = window.Sizzle;\n\n(function () {\n  function fakeDefine(fn) {\n    Prototype._actual_sizzle = fn();\n  }\n  fakeDefine.amd = true;\n\n  if (typeof define !== 'undefined' && define.amd) {\n    Prototype._original_define = define;\n    Prototype._actual_sizzle = null;\n    window.define = fakeDefine;\n  }\n})();\n\n/*!\n * Sizzle CSS Selector Engine v1.10.18\n * http://sizzlejs.com/\n *\n * Copyright 2013 jQuery Foundation, Inc. and other contributors\n * Released under the MIT license\n * http://jquery.org/license\n *\n * Date: 2014-02-05\n */\n(function (window) {\n  var i,\n    support,\n    Expr,\n    getText,\n    isXML,\n    compile,\n    select,\n    outermostContext,\n    sortInput,\n    hasDuplicate,\n    setDocument,\n    document,\n    docElem,\n    documentIsHTML,\n    rbuggyQSA,\n    rbuggyMatches,\n    matches,\n    contains,\n    expando = 'sizzle' + -new Date(),\n    preferredDoc = window.document,\n    dirruns = 0,\n    done = 0,\n    classCache = createCache(),\n    tokenCache = createCache(),\n    compilerCache = createCache(),\n    sortOrder = function (a, b) {\n      if (a === b) {\n        hasDuplicate = true;\n      }\n      return 0;\n    },\n    strundefined = typeof undefined,\n    MAX_NEGATIVE = 1 << 31,\n    hasOwn = {}.hasOwnProperty,\n    arr = [],\n    pop = arr.pop,\n    push_native = arr.push,\n    push = arr.push,\n    slice = arr.slice,\n    indexOf =\n      arr.indexOf ||\n      function (elem) {\n        var i = 0,\n          len = this.length;\n        for (; i < len; i++) {\n          if (this[i] === elem) {\n            return i;\n          }\n        }\n        return -1;\n      },\n    booleans =\n      'checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped',\n    whitespace = '[\\\\x20\\\\t\\\\r\\\\n\\\\f]',\n    characterEncoding = '(?:\\\\\\\\.|[\\\\w-]|[^\\\\x00-\\\\xa0])+',\n    identifier = characterEncoding.replace('w', 'w#'),\n    attributes =\n      '\\\\[' +\n      whitespace +\n      '*(' +\n      characterEncoding +\n      ')' +\n      whitespace +\n      '*(?:([*^$|!~]?=)' +\n      whitespace +\n      '*(?:([\\'\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|(' +\n      identifier +\n      ')|)|)' +\n      whitespace +\n      '*\\\\]',\n    pseudos =\n      ':(' +\n      characterEncoding +\n      ')(?:\\\\((([\\'\"])((?:\\\\\\\\.|[^\\\\\\\\])*?)\\\\3|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|' +\n      attributes.replace(3, 8) +\n      ')*)|.*)\\\\)|)',\n    rtrim = new RegExp(\n      '^' + whitespace + '+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)' + whitespace + '+$',\n      'g'\n    ),\n    rcomma = new RegExp('^' + whitespace + '*,' + whitespace + '*'),\n    rcombinators = new RegExp(\n      '^' + whitespace + '*([>+~]|' + whitespace + ')' + whitespace + '*'\n    ),\n    rattributeQuotes = new RegExp(\n      '=' + whitespace + '*([^\\\\]\\'\"]*?)' + whitespace + '*\\\\]',\n      'g'\n    ),\n    rpseudo = new RegExp(pseudos),\n    ridentifier = new RegExp('^' + identifier + '$'),\n    matchExpr = {\n      ID: new RegExp('^#(' + characterEncoding + ')'),\n      CLASS: new RegExp('^\\\\.(' + characterEncoding + ')'),\n      TAG: new RegExp('^(' + characterEncoding.replace('w', 'w*') + ')'),\n      ATTR: new RegExp('^' + attributes),\n      PSEUDO: new RegExp('^' + pseudos),\n      CHILD: new RegExp(\n        '^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(' +\n          whitespace +\n          '*(even|odd|(([+-]|)(\\\\d*)n|)' +\n          whitespace +\n          '*(?:([+-]|)' +\n          whitespace +\n          '*(\\\\d+)|))' +\n          whitespace +\n          '*\\\\)|)',\n        'i'\n      ),\n      bool: new RegExp('^(?:' + booleans + ')$', 'i'),\n      needsContext: new RegExp(\n        '^' +\n          whitespace +\n          '*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(' +\n          whitespace +\n          '*((?:-\\\\d)?\\\\d*)' +\n          whitespace +\n          '*\\\\)|)(?=[^-]|$)',\n        'i'\n      )\n    },\n    rinputs = /^(?:input|select|textarea|button)$/i,\n    rheader = /^h\\d$/i,\n    rnative = /^[^{]+\\{\\s*\\[native \\w/,\n    rquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n    rsibling = /[+~]/,\n    rescape = /'|\\\\/g,\n    runescape = new RegExp(\n      '\\\\\\\\([\\\\da-f]{1,6}' + whitespace + '?|(' + whitespace + ')|.)',\n      'ig'\n    ),\n    funescape = function (_, escaped, escapedWhitespace) {\n      var high = '0x' + escaped - 0x10000;\n      return high !== high || escapedWhitespace\n        ? escaped\n        : high < 0\n          ? String.fromCharCode(high + 0x10000)\n          : String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);\n    };\n\n  try {\n    push.apply(\n      (arr = slice.call(preferredDoc.childNodes)),\n      preferredDoc.childNodes\n    );\n    arr[preferredDoc.childNodes.length].nodeType;\n  } catch (e) {\n    push = {\n      apply: arr.length\n        ? function (target, els) {\n            push_native.apply(target, slice.call(els));\n          }\n        : function (target, els) {\n            var j = target.length,\n              i = 0;\n            while ((target[j++] = els[i++])) {}\n            target.length = j - 1;\n          }\n    };\n  }\n\n  function Sizzle(selector, context, results, seed) {\n    var match, elem, m, nodeType, i, groups, old, nid, newContext, newSelector;\n\n    if (\n      (context ? context.ownerDocument || context : preferredDoc) !== document\n    ) {\n      setDocument(context);\n    }\n\n    context = context || document;\n    results = results || [];\n\n    if (!selector || typeof selector !== 'string') {\n      return results;\n    }\n\n    if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {\n      return [];\n    }\n\n    if (documentIsHTML && !seed) {\n      if ((match = rquickExpr.exec(selector))) {\n        if ((m = match[1])) {\n          if (nodeType === 9) {\n            elem = context.getElementById(m);\n            if (elem && elem.parentNode) {\n              if (elem.id === m) {\n                results.push(elem);\n                return results;\n              }\n            } else {\n              return results;\n            }\n          } else {\n            if (\n              context.ownerDocument &&\n              (elem = context.ownerDocument.getElementById(m)) &&\n              contains(context, elem) &&\n              elem.id === m\n            ) {\n              results.push(elem);\n              return results;\n            }\n          }\n        } else if (match[2]) {\n          push.apply(results, context.getElementsByTagName(selector));\n          return results;\n        } else if (\n          (m = match[3]) &&\n          support.getElementsByClassName &&\n          context.getElementsByClassName\n        ) {\n          push.apply(results, context.getElementsByClassName(m));\n          return results;\n        }\n      }\n\n      if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {\n        nid = old = expando;\n        newContext = context;\n        newSelector = nodeType === 9 && selector;\n\n        if (nodeType === 1 && context.nodeName.toLowerCase() !== 'object') {\n          groups = tokenize(selector);\n\n          if ((old = context.getAttribute('id'))) {\n            nid = old.replace(rescape, '\\\\$&');\n          } else {\n            context.setAttribute('id', nid);\n          }\n          nid = \"[id='\" + nid + \"'] \";\n\n          i = groups.length;\n          while (i--) {\n            groups[i] = nid + toSelector(groups[i]);\n          }\n          newContext =\n            (rsibling.test(selector) && testContext(context.parentNode)) ||\n            context;\n          newSelector = groups.join(',');\n        }\n\n        if (newSelector) {\n          try {\n            push.apply(results, newContext.querySelectorAll(newSelector));\n            return results;\n          } catch (qsaError) {\n          } finally {\n            if (!old) {\n              context.removeAttribute('id');\n            }\n          }\n        }\n      }\n    }\n\n    return select(selector.replace(rtrim, '$1'), context, results, seed);\n  }\n\n  /**\n   * Create key-value caches of limited size\n   * @returns {Function(string, Object)} Returns the Object data after storing it on itself with\n   *  property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n   *  deleting the oldest entry\n   */\n  function createCache() {\n    var keys = [];\n\n    function cache(key, value) {\n      if (keys.push(key + ' ') > Expr.cacheLength) {\n        delete cache[keys.shift()];\n      }\n      return (cache[key + ' '] = value);\n    }\n    return cache;\n  }\n\n  /**\n   * Mark a function for special use by Sizzle\n   * @param {Function} fn The function to mark\n   */\n  function markFunction(fn) {\n    fn[expando] = true;\n    return fn;\n  }\n\n  /**\n   * Support testing using an element\n   * @param {Function} fn Passed the created div and expects a boolean result\n   */\n  function assert(fn) {\n    var div = document.createElement('div');\n\n    try {\n      return !!fn(div);\n    } catch (e) {\n      return false;\n    } finally {\n      if (div.parentNode) {\n        div.parentNode.removeChild(div);\n      }\n      div = null;\n    }\n  }\n\n  /**\n   * Adds the same handler for all of the specified attrs\n   * @param {String} attrs Pipe-separated list of attributes\n   * @param {Function} handler The method that will be applied\n   */\n  function addHandle(attrs, handler) {\n    var arr = attrs.split('|'),\n      i = attrs.length;\n\n    while (i--) {\n      Expr.attrHandle[arr[i]] = handler;\n    }\n  }\n\n  /**\n   * Checks document order of two siblings\n   * @param {Element} a\n   * @param {Element} b\n   * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n   */\n  function siblingCheck(a, b) {\n    var cur = b && a,\n      diff =\n        cur &&\n        a.nodeType === 1 &&\n        b.nodeType === 1 &&\n        (~b.sourceIndex || MAX_NEGATIVE) - (~a.sourceIndex || MAX_NEGATIVE);\n\n    if (diff) {\n      return diff;\n    }\n\n    if (cur) {\n      while ((cur = cur.nextSibling)) {\n        if (cur === b) {\n          return -1;\n        }\n      }\n    }\n\n    return a ? 1 : -1;\n  }\n\n  /**\n   * Returns a function to use in pseudos for input types\n   * @param {String} type\n   */\n  function createInputPseudo(type) {\n    return function (elem) {\n      var name = elem.nodeName.toLowerCase();\n      return name === 'input' && elem.type === type;\n    };\n  }\n\n  /**\n   * Returns a function to use in pseudos for buttons\n   * @param {String} type\n   */\n  function createButtonPseudo(type) {\n    return function (elem) {\n      var name = elem.nodeName.toLowerCase();\n      return (name === 'input' || name === 'button') && elem.type === type;\n    };\n  }\n\n  /**\n   * Returns a function to use in pseudos for positionals\n   * @param {Function} fn\n   */\n  function createPositionalPseudo(fn) {\n    return markFunction(function (argument) {\n      argument = +argument;\n      return markFunction(function (seed, matches) {\n        var j,\n          matchIndexes = fn([], seed.length, argument),\n          i = matchIndexes.length;\n\n        while (i--) {\n          if (seed[(j = matchIndexes[i])]) {\n            seed[j] = !(matches[j] = seed[j]);\n          }\n        }\n      });\n    });\n  }\n\n  /**\n   * Checks a node for validity as a Sizzle context\n   * @param {Element|Object=} context\n   * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n   */\n  function testContext(context) {\n    return (\n      context && typeof context.getElementsByTagName !== strundefined && context\n    );\n  }\n\n  support = Sizzle.support = {};\n\n  /**\n   * Detects XML nodes\n   * @param {Element|Object} elem An element or a document\n   * @returns {Boolean} True iff elem is a non-HTML XML node\n   */\n  isXML = Sizzle.isXML = function (elem) {\n    var documentElement = elem && (elem.ownerDocument || elem).documentElement;\n    return documentElement ? documentElement.nodeName !== 'HTML' : false;\n  };\n\n  /**\n   * Sets document-related variables once based on the current document\n   * @param {Element|Object} [doc] An element or document object to use to set the document\n   * @returns {Object} Returns the current document\n   */\n  setDocument = Sizzle.setDocument = function (node) {\n    var hasCompare,\n      doc = node ? node.ownerDocument || node : preferredDoc,\n      parent = doc.defaultView;\n\n    if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {\n      return document;\n    }\n\n    document = doc;\n    docElem = doc.documentElement;\n\n    documentIsHTML = !isXML(doc);\n\n    if (parent && parent !== parent.top) {\n      if (parent.addEventListener) {\n        parent.addEventListener(\n          'unload',\n          function () {\n            setDocument();\n          },\n          false\n        );\n      } else if (parent.attachEvent) {\n        parent.attachEvent('onunload', function () {\n          setDocument();\n        });\n      }\n    }\n\n    /* Attributes\n  ---------------------------------------------------------------------- */\n\n    support.attributes = assert(function (div) {\n      div.className = 'i';\n      return !div.getAttribute('className');\n    });\n\n    /* getElement(s)By*\n  ---------------------------------------------------------------------- */\n\n    support.getElementsByTagName = assert(function (div) {\n      div.appendChild(doc.createComment(''));\n      return !div.getElementsByTagName('*').length;\n    });\n\n    support.getElementsByClassName =\n      rnative.test(doc.getElementsByClassName) &&\n      assert(function (div) {\n        div.innerHTML = \"<div class='a'></div><div class='a i'></div>\";\n\n        div.firstChild.className = 'i';\n        return div.getElementsByClassName('i').length === 2;\n      });\n\n    support.getById = assert(function (div) {\n      docElem.appendChild(div).id = expando;\n      return !doc.getElementsByName || !doc.getElementsByName(expando).length;\n    });\n\n    if (support.getById) {\n      Expr.find['ID'] = function (id, context) {\n        if (typeof context.getElementById !== strundefined && documentIsHTML) {\n          var m = context.getElementById(id);\n          return m && m.parentNode ? [m] : [];\n        }\n      };\n      Expr.filter['ID'] = function (id) {\n        var attrId = id.replace(runescape, funescape);\n        return function (elem) {\n          return elem.getAttribute('id') === attrId;\n        };\n      };\n    } else {\n      delete Expr.find['ID'];\n\n      Expr.filter['ID'] = function (id) {\n        var attrId = id.replace(runescape, funescape);\n        return function (elem) {\n          var node =\n            typeof elem.getAttributeNode !== strundefined &&\n            elem.getAttributeNode('id');\n          return node && node.value === attrId;\n        };\n      };\n    }\n\n    Expr.find['TAG'] = support.getElementsByTagName\n      ? function (tag, context) {\n          if (typeof context.getElementsByTagName !== strundefined) {\n            return context.getElementsByTagName(tag);\n          }\n        }\n      : function (tag, context) {\n          var elem,\n            tmp = [],\n            i = 0,\n            results = context.getElementsByTagName(tag);\n\n          if (tag === '*') {\n            while ((elem = results[i++])) {\n              if (elem.nodeType === 1) {\n                tmp.push(elem);\n              }\n            }\n\n            return tmp;\n          }\n          return results;\n        };\n\n    Expr.find['CLASS'] =\n      support.getElementsByClassName &&\n      function (className, context) {\n        if (\n          typeof context.getElementsByClassName !== strundefined &&\n          documentIsHTML\n        ) {\n          return context.getElementsByClassName(className);\n        }\n      };\n\n    /* QSA/matchesSelector\n  ---------------------------------------------------------------------- */\n\n    rbuggyMatches = [];\n\n    rbuggyQSA = [];\n\n    if ((support.qsa = rnative.test(doc.querySelectorAll))) {\n      assert(function (div) {\n        div.innerHTML = \"<select t=''><option selected=''></option></select>\";\n\n        if (div.querySelectorAll(\"[t^='']\").length) {\n          rbuggyQSA.push('[*^$]=' + whitespace + '*(?:\\'\\'|\"\")');\n        }\n\n        if (!div.querySelectorAll('[selected]').length) {\n          rbuggyQSA.push('\\\\[' + whitespace + '*(?:value|' + booleans + ')');\n        }\n\n        if (!div.querySelectorAll(':checked').length) {\n          rbuggyQSA.push(':checked');\n        }\n      });\n\n      assert(function (div) {\n        var input = doc.createElement('input');\n        input.setAttribute('type', 'hidden');\n        div.appendChild(input).setAttribute('name', 'D');\n\n        if (div.querySelectorAll('[name=d]').length) {\n          rbuggyQSA.push('name' + whitespace + '*[*^$|!~]?=');\n        }\n\n        if (!div.querySelectorAll(':enabled').length) {\n          rbuggyQSA.push(':enabled', ':disabled');\n        }\n\n        div.querySelectorAll('*,:x');\n        rbuggyQSA.push(',.*:');\n      });\n    }\n\n    if (\n      (support.matchesSelector = rnative.test(\n        (matches =\n          docElem.webkitMatchesSelector ||\n          docElem.mozMatchesSelector ||\n          docElem.oMatchesSelector ||\n          docElem.msMatchesSelector)\n      ))\n    ) {\n      assert(function (div) {\n        support.disconnectedMatch = matches.call(div, 'div');\n\n        matches.call(div, \"[s!='']:x\");\n        rbuggyMatches.push('!=', pseudos);\n      });\n    }\n\n    rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join('|'));\n    rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join('|'));\n\n    /* Contains\n  ---------------------------------------------------------------------- */\n    hasCompare = rnative.test(docElem.compareDocumentPosition);\n\n    contains =\n      hasCompare || rnative.test(docElem.contains)\n        ? function (a, b) {\n            var adown = a.nodeType === 9 ? a.documentElement : a,\n              bup = b && b.parentNode;\n            return (\n              a === bup ||\n              !!(\n                bup &&\n                bup.nodeType === 1 &&\n                (adown.contains\n                  ? adown.contains(bup)\n                  : a.compareDocumentPosition &&\n                    a.compareDocumentPosition(bup) & 16)\n              )\n            );\n          }\n        : function (a, b) {\n            if (b) {\n              while ((b = b.parentNode)) {\n                if (b === a) {\n                  return true;\n                }\n              }\n            }\n            return false;\n          };\n\n    /* Sorting\n  ---------------------------------------------------------------------- */\n\n    sortOrder = hasCompare\n      ? function (a, b) {\n          if (a === b) {\n            hasDuplicate = true;\n            return 0;\n          }\n\n          var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n          if (compare) {\n            return compare;\n          }\n\n          compare =\n            (a.ownerDocument || a) === (b.ownerDocument || b)\n              ? a.compareDocumentPosition(b)\n              : 1;\n\n          if (\n            compare & 1 ||\n            (!support.sortDetached && b.compareDocumentPosition(a) === compare)\n          ) {\n            if (\n              a === doc ||\n              (a.ownerDocument === preferredDoc && contains(preferredDoc, a))\n            ) {\n              return -1;\n            }\n            if (\n              b === doc ||\n              (b.ownerDocument === preferredDoc && contains(preferredDoc, b))\n            ) {\n              return 1;\n            }\n\n            return sortInput\n              ? indexOf.call(sortInput, a) - indexOf.call(sortInput, b)\n              : 0;\n          }\n\n          return compare & 4 ? -1 : 1;\n        }\n      : function (a, b) {\n          if (a === b) {\n            hasDuplicate = true;\n            return 0;\n          }\n\n          var cur,\n            i = 0,\n            aup = a.parentNode,\n            bup = b.parentNode,\n            ap = [a],\n            bp = [b];\n\n          if (!aup || !bup) {\n            return a === doc\n              ? -1\n              : b === doc\n                ? 1\n                : aup\n                  ? -1\n                  : bup\n                    ? 1\n                    : sortInput\n                      ? indexOf.call(sortInput, a) - indexOf.call(sortInput, b)\n                      : 0;\n          } else if (aup === bup) {\n            return siblingCheck(a, b);\n          }\n\n          cur = a;\n          while ((cur = cur.parentNode)) {\n            ap.unshift(cur);\n          }\n          cur = b;\n          while ((cur = cur.parentNode)) {\n            bp.unshift(cur);\n          }\n\n          while (ap[i] === bp[i]) {\n            i++;\n          }\n\n          return i\n            ? siblingCheck(ap[i], bp[i])\n            : ap[i] === preferredDoc\n              ? -1\n              : bp[i] === preferredDoc\n                ? 1\n                : 0;\n        };\n\n    return doc;\n  };\n\n  Sizzle.matches = function (expr, elements) {\n    return Sizzle(expr, null, null, elements);\n  };\n\n  Sizzle.matchesSelector = function (elem, expr) {\n    if ((elem.ownerDocument || elem) !== document) {\n      setDocument(elem);\n    }\n\n    expr = expr.replace(rattributeQuotes, \"='$1']\");\n\n    if (\n      support.matchesSelector &&\n      documentIsHTML &&\n      (!rbuggyMatches || !rbuggyMatches.test(expr)) &&\n      (!rbuggyQSA || !rbuggyQSA.test(expr))\n    ) {\n      try {\n        var ret = matches.call(elem, expr);\n\n        if (\n          ret ||\n          support.disconnectedMatch ||\n          (elem.document && elem.document.nodeType !== 11)\n        ) {\n          return ret;\n        }\n      } catch (e) {}\n    }\n\n    return Sizzle(expr, document, null, [elem]).length > 0;\n  };\n\n  Sizzle.contains = function (context, elem) {\n    if ((context.ownerDocument || context) !== document) {\n      setDocument(context);\n    }\n    return contains(context, elem);\n  };\n\n  Sizzle.attr = function (elem, name) {\n    if ((elem.ownerDocument || elem) !== document) {\n      setDocument(elem);\n    }\n\n    var fn = Expr.attrHandle[name.toLowerCase()],\n      val =\n        fn && hasOwn.call(Expr.attrHandle, name.toLowerCase())\n          ? fn(elem, name, !documentIsHTML)\n          : undefined;\n\n    return val !== undefined\n      ? val\n      : support.attributes || !documentIsHTML\n        ? elem.getAttribute(name)\n        : (val = elem.getAttributeNode(name)) && val.specified\n          ? val.value\n          : null;\n  };\n\n  Sizzle.error = function (msg) {\n    throw new Error('Syntax error, unrecognized expression: ' + msg);\n  };\n\n  /**\n   * Document sorting and removing duplicates\n   * @param {ArrayLike} results\n   */\n  Sizzle.uniqueSort = function (results) {\n    var elem,\n      duplicates = [],\n      j = 0,\n      i = 0;\n\n    hasDuplicate = !support.detectDuplicates;\n    sortInput = !support.sortStable && results.slice(0);\n    results.sort(sortOrder);\n\n    if (hasDuplicate) {\n      while ((elem = results[i++])) {\n        if (elem === results[i]) {\n          j = duplicates.push(i);\n        }\n      }\n      while (j--) {\n        results.splice(duplicates[j], 1);\n      }\n    }\n\n    sortInput = null;\n\n    return results;\n  };\n\n  /**\n   * Utility function for retrieving the text value of an array of DOM nodes\n   * @param {Array|Element} elem\n   */\n  getText = Sizzle.getText = function (elem) {\n    var node,\n      ret = '',\n      i = 0,\n      nodeType = elem.nodeType;\n\n    if (!nodeType) {\n      while ((node = elem[i++])) {\n        ret += getText(node);\n      }\n    } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {\n      if (typeof elem.textContent === 'string') {\n        return elem.textContent;\n      } else {\n        for (elem = elem.firstChild; elem; elem = elem.nextSibling) {\n          ret += getText(elem);\n        }\n      }\n    } else if (nodeType === 3 || nodeType === 4) {\n      return elem.nodeValue;\n    }\n\n    return ret;\n  };\n\n  Expr = Sizzle.selectors = {\n    cacheLength: 50,\n\n    createPseudo: markFunction,\n\n    match: matchExpr,\n\n    attrHandle: {},\n\n    find: {},\n\n    relative: {\n      '>': { dir: 'parentNode', first: true },\n      ' ': { dir: 'parentNode' },\n      '+': { dir: 'previousSibling', first: true },\n      '~': { dir: 'previousSibling' }\n    },\n\n    preFilter: {\n      ATTR: function (match) {\n        match[1] = match[1].replace(runescape, funescape);\n\n        match[3] = (match[4] || match[5] || '').replace(runescape, funescape);\n\n        if (match[2] === '~=') {\n          match[3] = ' ' + match[3] + ' ';\n        }\n\n        return match.slice(0, 4);\n      },\n\n      CHILD: function (match) {\n        /* matches from matchExpr[\"CHILD\"]\n        1 type (only|nth|...)\n        2 what (child|of-type)\n        3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n        4 xn-component of xn+y argument ([+-]?\\d*n|)\n        5 sign of xn-component\n        6 x of xn-component\n        7 sign of y-component\n        8 y of y-component\n      */\n        match[1] = match[1].toLowerCase();\n\n        if (match[1].slice(0, 3) === 'nth') {\n          if (!match[3]) {\n            Sizzle.error(match[0]);\n          }\n\n          match[4] = +(match[4]\n            ? match[5] + (match[6] || 1)\n            : 2 * (match[3] === 'even' || match[3] === 'odd'));\n          match[5] = +(match[7] + match[8] || match[3] === 'odd');\n        } else if (match[3]) {\n          Sizzle.error(match[0]);\n        }\n\n        return match;\n      },\n\n      PSEUDO: function (match) {\n        var excess,\n          unquoted = !match[5] && match[2];\n\n        if (matchExpr['CHILD'].test(match[0])) {\n          return null;\n        }\n\n        if (match[3] && match[4] !== undefined) {\n          match[2] = match[4];\n        } else if (\n          unquoted &&\n          rpseudo.test(unquoted) &&\n          (excess = tokenize(unquoted, true)) &&\n          (excess =\n            unquoted.indexOf(')', unquoted.length - excess) - unquoted.length)\n        ) {\n          match[0] = match[0].slice(0, excess);\n          match[2] = unquoted.slice(0, excess);\n        }\n\n        return match.slice(0, 3);\n      }\n    },\n\n    filter: {\n      TAG: function (nodeNameSelector) {\n        var nodeName = nodeNameSelector\n          .replace(runescape, funescape)\n          .toLowerCase();\n        return nodeNameSelector === '*'\n          ? function () {\n              return true;\n            }\n          : function (elem) {\n              return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n            };\n      },\n\n      CLASS: function (className) {\n        var pattern = classCache[className + ' '];\n\n        return (\n          pattern ||\n          ((pattern = new RegExp(\n            '(^|' + whitespace + ')' + className + '(' + whitespace + '|$)'\n          )) &&\n            classCache(className, function (elem) {\n              return pattern.test(\n                (typeof elem.className === 'string' && elem.className) ||\n                  (typeof elem.getAttribute !== strundefined &&\n                    elem.getAttribute('class')) ||\n                  ''\n              );\n            }))\n        );\n      },\n\n      ATTR: function (name, operator, check) {\n        return function (elem) {\n          var result = Sizzle.attr(elem, name);\n\n          if (result == null) {\n            return operator === '!=';\n          }\n          if (!operator) {\n            return true;\n          }\n\n          result += '';\n\n          return operator === '='\n            ? result === check\n            : operator === '!='\n              ? result !== check\n              : operator === '^='\n                ? check && result.indexOf(check) === 0\n                : operator === '*='\n                  ? check && result.indexOf(check) > -1\n                  : operator === '$='\n                    ? check && result.slice(-check.length) === check\n                    : operator === '~='\n                      ? (' ' + result + ' ').indexOf(check) > -1\n                      : operator === '|='\n                        ? result === check ||\n                          result.slice(0, check.length + 1) === check + '-'\n                        : false;\n        };\n      },\n\n      CHILD: function (type, what, argument, first, last) {\n        var simple = type.slice(0, 3) !== 'nth',\n          forward = type.slice(-4) !== 'last',\n          ofType = what === 'of-type';\n\n        return first === 1 && last === 0\n          ? function (elem) {\n              return !!elem.parentNode;\n            }\n          : function (elem, context, xml) {\n              var cache,\n                outerCache,\n                node,\n                diff,\n                nodeIndex,\n                start,\n                dir = simple !== forward ? 'nextSibling' : 'previousSibling',\n                parent = elem.parentNode,\n                name = ofType && elem.nodeName.toLowerCase(),\n                useCache = !xml && !ofType;\n\n              if (parent) {\n                if (simple) {\n                  while (dir) {\n                    node = elem;\n                    while ((node = node[dir])) {\n                      if (\n                        ofType\n                          ? node.nodeName.toLowerCase() === name\n                          : node.nodeType === 1\n                      ) {\n                        return false;\n                      }\n                    }\n                    start = dir = type === 'only' && !start && 'nextSibling';\n                  }\n                  return true;\n                }\n\n                start = [forward ? parent.firstChild : parent.lastChild];\n\n                if (forward && useCache) {\n                  outerCache = parent[expando] || (parent[expando] = {});\n                  cache = outerCache[type] || [];\n                  nodeIndex = cache[0] === dirruns && cache[1];\n                  diff = cache[0] === dirruns && cache[2];\n                  node = nodeIndex && parent.childNodes[nodeIndex];\n\n                  while (\n                    (node =\n                      (++nodeIndex && node && node[dir]) ||\n                      (diff = nodeIndex = 0) ||\n                      start.pop())\n                  ) {\n                    if (node.nodeType === 1 && ++diff && node === elem) {\n                      outerCache[type] = [dirruns, nodeIndex, diff];\n                      break;\n                    }\n                  }\n                } else if (\n                  useCache &&\n                  (cache = (elem[expando] || (elem[expando] = {}))[type]) &&\n                  cache[0] === dirruns\n                ) {\n                  diff = cache[1];\n                } else {\n                  while (\n                    (node =\n                      (++nodeIndex && node && node[dir]) ||\n                      (diff = nodeIndex = 0) ||\n                      start.pop())\n                  ) {\n                    if (\n                      (ofType\n                        ? node.nodeName.toLowerCase() === name\n                        : node.nodeType === 1) &&\n                      ++diff\n                    ) {\n                      if (useCache) {\n                        (node[expando] || (node[expando] = {}))[type] = [\n                          dirruns,\n                          diff\n                        ];\n                      }\n\n                      if (node === elem) {\n                        break;\n                      }\n                    }\n                  }\n                }\n\n                diff -= last;\n                return (\n                  diff === first || (diff % first === 0 && diff / first >= 0)\n                );\n              }\n            };\n      },\n\n      PSEUDO: function (pseudo, argument) {\n        var args,\n          fn =\n            Expr.pseudos[pseudo] ||\n            Expr.setFilters[pseudo.toLowerCase()] ||\n            Sizzle.error('unsupported pseudo: ' + pseudo);\n\n        if (fn[expando]) {\n          return fn(argument);\n        }\n\n        if (fn.length > 1) {\n          args = [pseudo, pseudo, '', argument];\n          return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase())\n            ? markFunction(function (seed, matches) {\n                var idx,\n                  matched = fn(seed, argument),\n                  i = matched.length;\n                while (i--) {\n                  idx = indexOf.call(seed, matched[i]);\n                  seed[idx] = !(matches[idx] = matched[i]);\n                }\n              })\n            : function (elem) {\n                return fn(elem, 0, args);\n              };\n        }\n\n        return fn;\n      }\n    },\n\n    pseudos: {\n      not: markFunction(function (selector) {\n        var input = [],\n          results = [],\n          matcher = compile(selector.replace(rtrim, '$1'));\n\n        return matcher[expando]\n          ? markFunction(function (seed, matches, context, xml) {\n              var elem,\n                unmatched = matcher(seed, null, xml, []),\n                i = seed.length;\n\n              while (i--) {\n                if ((elem = unmatched[i])) {\n                  seed[i] = !(matches[i] = elem);\n                }\n              }\n            })\n          : function (elem, context, xml) {\n              input[0] = elem;\n              matcher(input, null, xml, results);\n              return !results.pop();\n            };\n      }),\n\n      has: markFunction(function (selector) {\n        return function (elem) {\n          return Sizzle(selector, elem).length > 0;\n        };\n      }),\n\n      contains: markFunction(function (text) {\n        return function (elem) {\n          return (\n            (elem.textContent || elem.innerText || getText(elem)).indexOf(\n              text\n            ) > -1\n          );\n        };\n      }),\n\n      lang: markFunction(function (lang) {\n        if (!ridentifier.test(lang || '')) {\n          Sizzle.error('unsupported lang: ' + lang);\n        }\n        lang = lang.replace(runescape, funescape).toLowerCase();\n        return function (elem) {\n          var elemLang;\n          do {\n            if (\n              (elemLang = documentIsHTML\n                ? elem.lang\n                : elem.getAttribute('xml:lang') || elem.getAttribute('lang'))\n            ) {\n              elemLang = elemLang.toLowerCase();\n              return elemLang === lang || elemLang.indexOf(lang + '-') === 0;\n            }\n          } while ((elem = elem.parentNode) && elem.nodeType === 1);\n          return false;\n        };\n      }),\n\n      target: function (elem) {\n        var hash = window.location && window.location.hash;\n        return hash && hash.slice(1) === elem.id;\n      },\n\n      root: function (elem) {\n        return elem === docElem;\n      },\n\n      focus: function (elem) {\n        return (\n          elem === document.activeElement &&\n          (!document.hasFocus || document.hasFocus()) &&\n          !!(elem.type || elem.href || ~elem.tabIndex)\n        );\n      },\n\n      enabled: function (elem) {\n        return elem.disabled === false;\n      },\n\n      disabled: function (elem) {\n        return elem.disabled === true;\n      },\n\n      checked: function (elem) {\n        var nodeName = elem.nodeName.toLowerCase();\n        return (\n          (nodeName === 'input' && !!elem.checked) ||\n          (nodeName === 'option' && !!elem.selected)\n        );\n      },\n\n      selected: function (elem) {\n        if (elem.parentNode) {\n          elem.parentNode.selectedIndex;\n        }\n\n        return elem.selected === true;\n      },\n\n      empty: function (elem) {\n        for (elem = elem.firstChild; elem; elem = elem.nextSibling) {\n          if (elem.nodeType < 6) {\n            return false;\n          }\n        }\n        return true;\n      },\n\n      parent: function (elem) {\n        return !Expr.pseudos['empty'](elem);\n      },\n\n      header: function (elem) {\n        return rheader.test(elem.nodeName);\n      },\n\n      input: function (elem) {\n        return rinputs.test(elem.nodeName);\n      },\n\n      button: function (elem) {\n        var name = elem.nodeName.toLowerCase();\n        return (\n          (name === 'input' && elem.type === 'button') || name === 'button'\n        );\n      },\n\n      text: function (elem) {\n        var attr;\n        return (\n          elem.nodeName.toLowerCase() === 'input' &&\n          elem.type === 'text' &&\n          ((attr = elem.getAttribute('type')) == null ||\n            attr.toLowerCase() === 'text')\n        );\n      },\n\n      first: createPositionalPseudo(function () {\n        return [0];\n      }),\n\n      last: createPositionalPseudo(function (matchIndexes, length) {\n        return [length - 1];\n      }),\n\n      eq: createPositionalPseudo(function (matchIndexes, length, argument) {\n        return [argument < 0 ? argument + length : argument];\n      }),\n\n      even: createPositionalPseudo(function (matchIndexes, length) {\n        var i = 0;\n        for (; i < length; i += 2) {\n          matchIndexes.push(i);\n        }\n        return matchIndexes;\n      }),\n\n      odd: createPositionalPseudo(function (matchIndexes, length) {\n        var i = 1;\n        for (; i < length; i += 2) {\n          matchIndexes.push(i);\n        }\n        return matchIndexes;\n      }),\n\n      lt: createPositionalPseudo(function (matchIndexes, length, argument) {\n        var i = argument < 0 ? argument + length : argument;\n        for (; --i >= 0; ) {\n          matchIndexes.push(i);\n        }\n        return matchIndexes;\n      }),\n\n      gt: createPositionalPseudo(function (matchIndexes, length, argument) {\n        var i = argument < 0 ? argument + length : argument;\n        for (; ++i < length; ) {\n          matchIndexes.push(i);\n        }\n        return matchIndexes;\n      })\n    }\n  };\n\n  Expr.pseudos['nth'] = Expr.pseudos['eq'];\n\n  for (i in {\n    radio: true,\n    checkbox: true,\n    file: true,\n    password: true,\n    image: true\n  }) {\n    Expr.pseudos[i] = createInputPseudo(i);\n  }\n  for (i in { submit: true, reset: true }) {\n    Expr.pseudos[i] = createButtonPseudo(i);\n  }\n\n  function setFilters() {}\n  setFilters.prototype = Expr.filters = Expr.pseudos;\n  Expr.setFilters = new setFilters();\n\n  function tokenize(selector, parseOnly) {\n    var matched,\n      match,\n      tokens,\n      type,\n      soFar,\n      groups,\n      preFilters,\n      cached = tokenCache[selector + ' '];\n\n    if (cached) {\n      return parseOnly ? 0 : cached.slice(0);\n    }\n\n    soFar = selector;\n    groups = [];\n    preFilters = Expr.preFilter;\n\n    while (soFar) {\n      if (!matched || (match = rcomma.exec(soFar))) {\n        if (match) {\n          soFar = soFar.slice(match[0].length) || soFar;\n        }\n        groups.push((tokens = []));\n      }\n\n      matched = false;\n\n      if ((match = rcombinators.exec(soFar))) {\n        matched = match.shift();\n        tokens.push({\n          value: matched,\n          type: match[0].replace(rtrim, ' ')\n        });\n        soFar = soFar.slice(matched.length);\n      }\n\n      for (type in Expr.filter) {\n        if (\n          (match = matchExpr[type].exec(soFar)) &&\n          (!preFilters[type] || (match = preFilters[type](match)))\n        ) {\n          matched = match.shift();\n          tokens.push({\n            value: matched,\n            type: type,\n            matches: match\n          });\n          soFar = soFar.slice(matched.length);\n        }\n      }\n\n      if (!matched) {\n        break;\n      }\n    }\n\n    return parseOnly\n      ? soFar.length\n      : soFar\n        ? Sizzle.error(selector)\n        : tokenCache(selector, groups).slice(0);\n  }\n\n  function toSelector(tokens) {\n    var i = 0,\n      len = tokens.length,\n      selector = '';\n    for (; i < len; i++) {\n      selector += tokens[i].value;\n    }\n    return selector;\n  }\n\n  function addCombinator(matcher, combinator, base) {\n    var dir = combinator.dir,\n      checkNonElements = base && dir === 'parentNode',\n      doneName = done++;\n\n    return combinator.first\n      ? function (elem, context, xml) {\n          while ((elem = elem[dir])) {\n            if (elem.nodeType === 1 || checkNonElements) {\n              return matcher(elem, context, xml);\n            }\n          }\n        }\n      : function (elem, context, xml) {\n          var oldCache,\n            outerCache,\n            newCache = [dirruns, doneName];\n\n          if (xml) {\n            while ((elem = elem[dir])) {\n              if (elem.nodeType === 1 || checkNonElements) {\n                if (matcher(elem, context, xml)) {\n                  return true;\n                }\n              }\n            }\n          } else {\n            while ((elem = elem[dir])) {\n              if (elem.nodeType === 1 || checkNonElements) {\n                outerCache = elem[expando] || (elem[expando] = {});\n                if (\n                  (oldCache = outerCache[dir]) &&\n                  oldCache[0] === dirruns &&\n                  oldCache[1] === doneName\n                ) {\n                  return (newCache[2] = oldCache[2]);\n                } else {\n                  outerCache[dir] = newCache;\n\n                  if ((newCache[2] = matcher(elem, context, xml))) {\n                    return true;\n                  }\n                }\n              }\n            }\n          }\n        };\n  }\n\n  function elementMatcher(matchers) {\n    return matchers.length > 1\n      ? function (elem, context, xml) {\n          var i = matchers.length;\n          while (i--) {\n            if (!matchers[i](elem, context, xml)) {\n              return false;\n            }\n          }\n          return true;\n        }\n      : matchers[0];\n  }\n\n  function multipleContexts(selector, contexts, results) {\n    var i = 0,\n      len = contexts.length;\n    for (; i < len; i++) {\n      Sizzle(selector, contexts[i], results);\n    }\n    return results;\n  }\n\n  function condense(unmatched, map, filter, context, xml) {\n    var elem,\n      newUnmatched = [],\n      i = 0,\n      len = unmatched.length,\n      mapped = map != null;\n\n    for (; i < len; i++) {\n      if ((elem = unmatched[i])) {\n        if (!filter || filter(elem, context, xml)) {\n          newUnmatched.push(elem);\n          if (mapped) {\n            map.push(i);\n          }\n        }\n      }\n    }\n\n    return newUnmatched;\n  }\n\n  function setMatcher(\n    preFilter,\n    selector,\n    matcher,\n    postFilter,\n    postFinder,\n    postSelector\n  ) {\n    if (postFilter && !postFilter[expando]) {\n      postFilter = setMatcher(postFilter);\n    }\n    if (postFinder && !postFinder[expando]) {\n      postFinder = setMatcher(postFinder, postSelector);\n    }\n    return markFunction(function (seed, results, context, xml) {\n      var temp,\n        i,\n        elem,\n        preMap = [],\n        postMap = [],\n        preexisting = results.length,\n        elems =\n          seed ||\n          multipleContexts(\n            selector || '*',\n            context.nodeType ? [context] : context,\n            []\n          ),\n        matcherIn =\n          preFilter && (seed || !selector)\n            ? condense(elems, preMap, preFilter, context, xml)\n            : elems,\n        matcherOut = matcher\n          ? postFinder || (seed ? preFilter : preexisting || postFilter)\n            ? []\n            : results\n          : matcherIn;\n\n      if (matcher) {\n        matcher(matcherIn, matcherOut, context, xml);\n      }\n\n      if (postFilter) {\n        temp = condense(matcherOut, postMap);\n        postFilter(temp, [], context, xml);\n\n        i = temp.length;\n        while (i--) {\n          if ((elem = temp[i])) {\n            matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);\n          }\n        }\n      }\n\n      if (seed) {\n        if (postFinder || preFilter) {\n          if (postFinder) {\n            temp = [];\n            i = matcherOut.length;\n            while (i--) {\n              if ((elem = matcherOut[i])) {\n                temp.push((matcherIn[i] = elem));\n              }\n            }\n            postFinder(null, (matcherOut = []), temp, xml);\n          }\n\n          i = matcherOut.length;\n          while (i--) {\n            if (\n              (elem = matcherOut[i]) &&\n              (temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1\n            ) {\n              seed[temp] = !(results[temp] = elem);\n            }\n          }\n        }\n      } else {\n        matcherOut = condense(\n          matcherOut === results\n            ? matcherOut.splice(preexisting, matcherOut.length)\n            : matcherOut\n        );\n        if (postFinder) {\n          postFinder(null, results, matcherOut, xml);\n        } else {\n          push.apply(results, matcherOut);\n        }\n      }\n    });\n  }\n\n  function matcherFromTokens(tokens) {\n    var checkContext,\n      matcher,\n      j,\n      len = tokens.length,\n      leadingRelative = Expr.relative[tokens[0].type],\n      implicitRelative = leadingRelative || Expr.relative[' '],\n      i = leadingRelative ? 1 : 0,\n      matchContext = addCombinator(\n        function (elem) {\n          return elem === checkContext;\n        },\n        implicitRelative,\n        true\n      ),\n      matchAnyContext = addCombinator(\n        function (elem) {\n          return indexOf.call(checkContext, elem) > -1;\n        },\n        implicitRelative,\n        true\n      ),\n      matchers = [\n        function (elem, context, xml) {\n          return (\n            (!leadingRelative && (xml || context !== outermostContext)) ||\n            ((checkContext = context).nodeType\n              ? matchContext(elem, context, xml)\n              : matchAnyContext(elem, context, xml))\n          );\n        }\n      ];\n\n    for (; i < len; i++) {\n      if ((matcher = Expr.relative[tokens[i].type])) {\n        matchers = [addCombinator(elementMatcher(matchers), matcher)];\n      } else {\n        matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);\n\n        if (matcher[expando]) {\n          j = ++i;\n          for (; j < len; j++) {\n            if (Expr.relative[tokens[j].type]) {\n              break;\n            }\n          }\n          return setMatcher(\n            i > 1 && elementMatcher(matchers),\n            i > 1 &&\n              toSelector(\n                tokens\n                  .slice(0, i - 1)\n                  .concat({ value: tokens[i - 2].type === ' ' ? '*' : '' })\n              ).replace(rtrim, '$1'),\n            matcher,\n            i < j && matcherFromTokens(tokens.slice(i, j)),\n            j < len && matcherFromTokens((tokens = tokens.slice(j))),\n            j < len && toSelector(tokens)\n          );\n        }\n        matchers.push(matcher);\n      }\n    }\n\n    return elementMatcher(matchers);\n  }\n\n  function matcherFromGroupMatchers(elementMatchers, setMatchers) {\n    var bySet = setMatchers.length > 0,\n      byElement = elementMatchers.length > 0,\n      superMatcher = function (seed, context, xml, results, outermost) {\n        var elem,\n          j,\n          matcher,\n          matchedCount = 0,\n          i = '0',\n          unmatched = seed && [],\n          setMatched = [],\n          contextBackup = outermostContext,\n          elems = seed || (byElement && Expr.find['TAG']('*', outermost)),\n          dirrunsUnique = (dirruns +=\n            contextBackup == null ? 1 : Math.random() || 0.1),\n          len = elems.length;\n\n        if (outermost) {\n          outermostContext = context !== document && context;\n        }\n\n        for (; i !== len && (elem = elems[i]) != null; i++) {\n          if (byElement && elem) {\n            j = 0;\n            while ((matcher = elementMatchers[j++])) {\n              if (matcher(elem, context, xml)) {\n                results.push(elem);\n                break;\n              }\n            }\n            if (outermost) {\n              dirruns = dirrunsUnique;\n            }\n          }\n\n          if (bySet) {\n            if ((elem = !matcher && elem)) {\n              matchedCount--;\n            }\n\n            if (seed) {\n              unmatched.push(elem);\n            }\n          }\n        }\n\n        matchedCount += i;\n        if (bySet && i !== matchedCount) {\n          j = 0;\n          while ((matcher = setMatchers[j++])) {\n            matcher(unmatched, setMatched, context, xml);\n          }\n\n          if (seed) {\n            if (matchedCount > 0) {\n              while (i--) {\n                if (!(unmatched[i] || setMatched[i])) {\n                  setMatched[i] = pop.call(results);\n                }\n              }\n            }\n\n            setMatched = condense(setMatched);\n          }\n\n          push.apply(results, setMatched);\n\n          if (\n            outermost &&\n            !seed &&\n            setMatched.length > 0 &&\n            matchedCount + setMatchers.length > 1\n          ) {\n            Sizzle.uniqueSort(results);\n          }\n        }\n\n        if (outermost) {\n          dirruns = dirrunsUnique;\n          outermostContext = contextBackup;\n        }\n\n        return unmatched;\n      };\n\n    return bySet ? markFunction(superMatcher) : superMatcher;\n  }\n\n  compile = Sizzle.compile = function (\n    selector,\n    match /* Internal Use Only */\n  ) {\n    var i,\n      setMatchers = [],\n      elementMatchers = [],\n      cached = compilerCache[selector + ' '];\n\n    if (!cached) {\n      if (!match) {\n        match = tokenize(selector);\n      }\n      i = match.length;\n      while (i--) {\n        cached = matcherFromTokens(match[i]);\n        if (cached[expando]) {\n          setMatchers.push(cached);\n        } else {\n          elementMatchers.push(cached);\n        }\n      }\n\n      cached = compilerCache(\n        selector,\n        matcherFromGroupMatchers(elementMatchers, setMatchers)\n      );\n\n      cached.selector = selector;\n    }\n    return cached;\n  };\n\n  /**\n   * A low-level selection function that works with Sizzle's compiled\n   *  selector functions\n   * @param {String|Function} selector A selector or a pre-compiled\n   *  selector function built with Sizzle.compile\n   * @param {Element} context\n   * @param {Array} [results]\n   * @param {Array} [seed] A set of elements to match against\n   */\n  select = Sizzle.select = function (selector, context, results, seed) {\n    var i,\n      tokens,\n      token,\n      type,\n      find,\n      compiled = typeof selector === 'function' && selector,\n      match = !seed && tokenize((selector = compiled.selector || selector));\n\n    results = results || [];\n\n    if (match.length === 1) {\n      tokens = match[0] = match[0].slice(0);\n      if (\n        tokens.length > 2 &&\n        (token = tokens[0]).type === 'ID' &&\n        support.getById &&\n        context.nodeType === 9 &&\n        documentIsHTML &&\n        Expr.relative[tokens[1].type]\n      ) {\n        context = (Expr.find['ID'](\n          token.matches[0].replace(runescape, funescape),\n          context\n        ) || [])[0];\n        if (!context) {\n          return results;\n        } else if (compiled) {\n          context = context.parentNode;\n        }\n\n        selector = selector.slice(tokens.shift().value.length);\n      }\n\n      i = matchExpr['needsContext'].test(selector) ? 0 : tokens.length;\n      while (i--) {\n        token = tokens[i];\n\n        if (Expr.relative[(type = token.type)]) {\n          break;\n        }\n        if ((find = Expr.find[type])) {\n          if (\n            (seed = find(\n              token.matches[0].replace(runescape, funescape),\n              (rsibling.test(tokens[0].type) &&\n                testContext(context.parentNode)) ||\n                context\n            ))\n          ) {\n            tokens.splice(i, 1);\n            selector = seed.length && toSelector(tokens);\n            if (!selector) {\n              push.apply(results, seed);\n              return results;\n            }\n\n            break;\n          }\n        }\n      }\n    }\n\n    (compiled || compile(selector, match))(\n      seed,\n      context,\n      !documentIsHTML,\n      results,\n      (rsibling.test(selector) && testContext(context.parentNode)) || context\n    );\n    return results;\n  };\n\n  support.sortStable = expando.split('').sort(sortOrder).join('') === expando;\n\n  support.detectDuplicates = !!hasDuplicate;\n\n  setDocument();\n\n  support.sortDetached = assert(function (div1) {\n    return div1.compareDocumentPosition(document.createElement('div')) & 1;\n  });\n\n  if (\n    !assert(function (div) {\n      div.innerHTML = \"<a href='#'></a>\";\n      return div.firstChild.getAttribute('href') === '#';\n    })\n  ) {\n    addHandle('type|href|height|width', function (elem, name, isXML) {\n      if (!isXML) {\n        return elem.getAttribute(name, name.toLowerCase() === 'type' ? 1 : 2);\n      }\n    });\n  }\n\n  if (\n    !support.attributes ||\n    !assert(function (div) {\n      div.innerHTML = '<input/>';\n      div.firstChild.setAttribute('value', '');\n      return div.firstChild.getAttribute('value') === '';\n    })\n  ) {\n    addHandle('value', function (elem, name, isXML) {\n      if (!isXML && elem.nodeName.toLowerCase() === 'input') {\n        return elem.defaultValue;\n      }\n    });\n  }\n\n  if (\n    !assert(function (div) {\n      return div.getAttribute('disabled') == null;\n    })\n  ) {\n    addHandle(booleans, function (elem, name, isXML) {\n      var val;\n      if (!isXML) {\n        return elem[name] === true\n          ? name.toLowerCase()\n          : (val = elem.getAttributeNode(name)) && val.specified\n            ? val.value\n            : null;\n      }\n    });\n  }\n\n  if (typeof define === 'function' && define.amd) {\n    define(function () {\n      return Sizzle;\n    });\n  } else if (typeof module !== 'undefined' && module.exports) {\n    module.exports = Sizzle;\n  } else {\n    window.Sizzle = Sizzle;\n  }\n})(window);\n\n(function () {\n  if (typeof Sizzle !== 'undefined') {\n    return;\n  }\n\n  if (typeof define !== 'undefined' && define.amd) {\n    window.Sizzle = Prototype._actual_sizzle;\n    window.define = Prototype._original_define;\n    delete Prototype._actual_sizzle;\n    delete Prototype._original_define;\n  } else if (typeof module !== 'undefined' && module.exports) {\n    window.Sizzle = module.exports;\n    module.exports = {};\n  }\n})();\n\n(function (engine) {\n  var extendElements = Prototype.Selector.extendElements;\n\n  function select(selector, scope) {\n    return extendElements(engine(selector, scope || document));\n  }\n\n  function match(element, selector) {\n    return engine.matches(selector, [element]).length == 1;\n  }\n\n  Prototype.Selector.engine = engine;\n  Prototype.Selector.select = select;\n  Prototype.Selector.match = match;\n})(Sizzle);\n\nwindow.Sizzle = Prototype._original_property;\ndelete Prototype._original_property;\n\nvar Form = {\n  reset: function (form) {\n    form = $(form);\n    form.reset();\n    return form;\n  },\n\n  serializeElements: function (elements, options) {\n    if (typeof options != 'object') options = { hash: !!options };\n    else if (Object.isUndefined(options.hash)) options.hash = true;\n    var key,\n      value,\n      submitted = false,\n      submit = options.submit,\n      accumulator,\n      initial;\n\n    if (options.hash) {\n      initial = {};\n      accumulator = function (result, key, value) {\n        if (key in result) {\n          if (!Object.isArray(result[key])) result[key] = [result[key]];\n          result[key] = result[key].concat(value);\n        } else result[key] = value;\n        return result;\n      };\n    } else {\n      initial = '';\n      accumulator = function (result, key, values) {\n        if (!Object.isArray(values)) {\n          values = [values];\n        }\n        if (!values.length) {\n          return result;\n        }\n        var encodedKey = encodeURIComponent(key).gsub(/%20/, '+');\n        return (\n          result +\n          (result ? '&' : '') +\n          values\n            .map(function (value) {\n              value = value.gsub(/(\\r)?\\n/, '\\r\\n');\n              value = encodeURIComponent(value);\n              value = value.gsub(/%20/, '+');\n              return encodedKey + '=' + value;\n            })\n            .join('&')\n        );\n      };\n    }\n\n    return elements.inject(initial, function (result, element) {\n      if (!element.disabled && element.name) {\n        key = element.name;\n        value = $(element).getValue();\n        if (\n          value != null &&\n          element.type != 'file' &&\n          (element.type != 'submit' ||\n            (!submitted &&\n              submit !== false &&\n              (!submit || key == submit) &&\n              (submitted = true)))\n        ) {\n          result = accumulator(result, key, value);\n        }\n      }\n      return result;\n    });\n  }\n};\n\nForm.Methods = {\n  serialize: function (form, options) {\n    return Form.serializeElements(Form.getElements(form), options);\n  },\n\n  getElements: function (form) {\n    var elements = $(form).getElementsByTagName('*');\n    var element,\n      results = [],\n      serializers = Form.Element.Serializers;\n\n    for (var i = 0; (element = elements[i]); i++) {\n      if (serializers[element.tagName.toLowerCase()])\n        results.push(Element.extend(element));\n    }\n    return results;\n  },\n\n  getInputs: function (form, typeName, name) {\n    form = $(form);\n    var inputs = form.getElementsByTagName('input');\n\n    if (!typeName && !name) return $A(inputs).map(Element.extend);\n\n    for (\n      var i = 0, matchingInputs = [], length = inputs.length;\n      i < length;\n      i++\n    ) {\n      var input = inputs[i];\n      if ((typeName && input.type != typeName) || (name && input.name != name))\n        continue;\n      matchingInputs.push(Element.extend(input));\n    }\n\n    return matchingInputs;\n  },\n\n  disable: function (form) {\n    form = $(form);\n    Form.getElements(form).invoke('disable');\n    return form;\n  },\n\n  enable: function (form) {\n    form = $(form);\n    Form.getElements(form).invoke('enable');\n    return form;\n  },\n\n  findFirstElement: function (form) {\n    var elements = $(form)\n      .getElements()\n      .findAll(function (element) {\n        return 'hidden' != element.type && !element.disabled;\n      });\n    var firstByIndex = elements\n      .findAll(function (element) {\n        return element.hasAttribute('tabIndex') && element.tabIndex >= 0;\n      })\n      .sortBy(function (element) {\n        return element.tabIndex;\n      })\n      .first();\n\n    return firstByIndex\n      ? firstByIndex\n      : elements.find(function (element) {\n          return /^(?:input|select|textarea)$/i.test(element.tagName);\n        });\n  },\n\n  focusFirstElement: function (form) {\n    form = $(form);\n    var element = form.findFirstElement();\n    if (element) element.activate();\n    return form;\n  },\n\n  request: function (form, options) {\n    ((form = $(form)), (options = Object.clone(options || {})));\n\n    var params = options.parameters,\n      action = form.readAttribute('action') || '';\n    if (action.blank()) action = window.location.href;\n    options.parameters = form.serialize(true);\n\n    if (params) {\n      if (Object.isString(params)) params = params.toQueryParams();\n      Object.extend(options.parameters, params);\n    }\n\n    if (form.hasAttribute('method') && !options.method)\n      options.method = form.method;\n\n    return new Ajax.Request(action, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element = {\n  focus: function (element) {\n    $(element).focus();\n    return element;\n  },\n\n  select: function (element) {\n    $(element).select();\n    return element;\n  }\n};\n\nForm.Element.Methods = {\n  serialize: function (element) {\n    element = $(element);\n    if (!element.disabled && element.name) {\n      var value = element.getValue();\n      if (value != undefined) {\n        var pair = {};\n        pair[element.name] = value;\n        return Object.toQueryString(pair);\n      }\n    }\n    return '';\n  },\n\n  getValue: function (element) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    return Form.Element.Serializers[method](element);\n  },\n\n  setValue: function (element, value) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    Form.Element.Serializers[method](element, value);\n    return element;\n  },\n\n  clear: function (element) {\n    $(element).value = '';\n    return element;\n  },\n\n  present: function (element) {\n    return $(element).value != '';\n  },\n\n  activate: function (element) {\n    element = $(element);\n    try {\n      element.focus();\n      if (\n        element.select &&\n        (element.tagName.toLowerCase() != 'input' ||\n          !/^(?:button|reset|submit)$/i.test(element.type))\n      )\n        element.select();\n    } catch (e) {}\n    return element;\n  },\n\n  disable: function (element) {\n    element = $(element);\n    element.disabled = true;\n    return element;\n  },\n\n  enable: function (element) {\n    element = $(element);\n    element.disabled = false;\n    return element;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Field = Form.Element;\n\nvar $F = Form.Element.Methods.getValue;\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element.Serializers = (function () {\n  function input(element, value) {\n    switch (element.type.toLowerCase()) {\n      case 'checkbox':\n      case 'radio':\n        return inputSelector(element, value);\n      default:\n        return valueSelector(element, value);\n    }\n  }\n\n  function inputSelector(element, value) {\n    if (Object.isUndefined(value))\n      return element.checked ? element.value : null;\n    else element.checked = !!value;\n  }\n\n  function valueSelector(element, value) {\n    if (Object.isUndefined(value)) return element.value;\n    else element.value = value;\n  }\n\n  function select(element, value) {\n    if (Object.isUndefined(value))\n      return (element.type === 'select-one' ? selectOne : selectMany)(element);\n\n    var opt,\n      currentValue,\n      single = !Object.isArray(value);\n    for (var i = 0, length = element.length; i < length; i++) {\n      opt = element.options[i];\n      currentValue = this.optionValue(opt);\n      if (single) {\n        if (currentValue == value) {\n          opt.selected = true;\n          return;\n        }\n      } else opt.selected = value.include(currentValue);\n    }\n  }\n\n  function selectOne(element) {\n    var index = element.selectedIndex;\n    return index >= 0 ? optionValue(element.options[index]) : null;\n  }\n\n  function selectMany(element) {\n    var values,\n      length = element.length;\n    if (!length) return null;\n\n    for (var i = 0, values = []; i < length; i++) {\n      var opt = element.options[i];\n      if (opt.selected) values.push(optionValue(opt));\n    }\n    return values;\n  }\n\n  function optionValue(opt) {\n    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;\n  }\n\n  return {\n    input: input,\n    inputSelector: inputSelector,\n    textarea: valueSelector,\n    select: select,\n    selectOne: selectOne,\n    selectMany: selectMany,\n    optionValue: optionValue,\n    button: valueSelector\n  };\n})();\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.TimedObserver = Class.create(PeriodicalExecuter, {\n  initialize: function ($super, element, frequency, callback) {\n    $super(callback, frequency);\n    this.element = $(element);\n    this.lastValue = this.getValue();\n  },\n\n  execute: function () {\n    var value = this.getValue();\n    if (\n      Object.isString(this.lastValue) && Object.isString(value)\n        ? this.lastValue != value\n        : String(this.lastValue) != String(value)\n    ) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  }\n});\n\nForm.Element.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function () {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function () {\n    return Form.serialize(this.element);\n  }\n});\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.EventObserver = Class.create({\n  initialize: function (element, callback) {\n    this.element = $(element);\n    this.callback = callback;\n\n    this.lastValue = this.getValue();\n    if (this.element.tagName.toLowerCase() == 'form')\n      this.registerFormCallbacks();\n    else this.registerCallback(this.element);\n  },\n\n  onElementEvent: function () {\n    var value = this.getValue();\n    if (this.lastValue != value) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  },\n\n  registerFormCallbacks: function () {\n    Form.getElements(this.element).each(this.registerCallback, this);\n  },\n\n  registerCallback: function (element) {\n    if (element.type) {\n      switch (element.type.toLowerCase()) {\n        case 'checkbox':\n        case 'radio':\n          Event.observe(element, 'click', this.onElementEvent.bind(this));\n          break;\n        default:\n          Event.observe(element, 'change', this.onElementEvent.bind(this));\n          break;\n      }\n    }\n  }\n});\n\nForm.Element.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function () {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function () {\n    return Form.serialize(this.element);\n  }\n});\n(function (GLOBAL) {\n  var DIV = document.createElement('div');\n  var docEl = document.documentElement;\n  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED =\n    'onmouseenter' in docEl && 'onmouseleave' in docEl;\n\n  var Event = {\n    KEY_BACKSPACE: 8,\n    KEY_TAB: 9,\n    KEY_RETURN: 13,\n    KEY_ESC: 27,\n    KEY_LEFT: 37,\n    KEY_UP: 38,\n    KEY_RIGHT: 39,\n    KEY_DOWN: 40,\n    KEY_DELETE: 46,\n    KEY_HOME: 36,\n    KEY_END: 35,\n    KEY_PAGEUP: 33,\n    KEY_PAGEDOWN: 34,\n    KEY_INSERT: 45\n  };\n\n  var isIELegacyEvent = function (event) {\n    return false;\n  };\n\n  if (window.attachEvent) {\n    if (window.addEventListener) {\n      isIELegacyEvent = function (event) {\n        return !(event instanceof window.Event);\n      };\n    } else {\n      isIELegacyEvent = function (event) {\n        return true;\n      };\n    }\n  }\n\n  var _isButton;\n\n  function _isButtonForDOMEvents(event, code) {\n    return event.which ? event.which === code + 1 : event.button === code;\n  }\n\n  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };\n  function _isButtonForLegacyEvents(event, code) {\n    return event.button === legacyButtonMap[code];\n  }\n\n  function _isButtonForWebKit(event, code) {\n    switch (code) {\n      case 0:\n        return event.which == 1 && !event.metaKey;\n      case 1:\n        return event.which == 2 || (event.which == 1 && event.metaKey);\n      case 2:\n        return event.which == 3;\n      default:\n        return false;\n    }\n  }\n\n  if (window.attachEvent) {\n    if (!window.addEventListener) {\n      _isButton = _isButtonForLegacyEvents;\n    } else {\n      _isButton = function (event, code) {\n        return isIELegacyEvent(event)\n          ? _isButtonForLegacyEvents(event, code)\n          : _isButtonForDOMEvents(event, code);\n      };\n    }\n  } else if (Prototype.Browser.WebKit) {\n    _isButton = _isButtonForWebKit;\n  } else {\n    _isButton = _isButtonForDOMEvents;\n  }\n\n  function isLeftClick(event) {\n    return _isButton(event, 0);\n  }\n\n  function isMiddleClick(event) {\n    return _isButton(event, 1);\n  }\n\n  function isRightClick(event) {\n    return _isButton(event, 2);\n  }\n\n  function element(event) {\n    return Element.extend(_element(event));\n  }\n\n  function _element(event) {\n    event = Event.extend(event);\n\n    var node = event.target,\n      type = event.type,\n      currentTarget = event.currentTarget;\n\n    if (currentTarget && currentTarget.tagName) {\n      if (\n        type === 'load' ||\n        type === 'error' ||\n        (type === 'click' &&\n          currentTarget.tagName.toLowerCase() === 'input' &&\n          currentTarget.type === 'radio')\n      )\n        node = currentTarget;\n    }\n\n    return node.nodeType == Node.TEXT_NODE ? node.parentNode : node;\n  }\n\n  function findElement(event, expression) {\n    var element = _element(event),\n      selector = Prototype.Selector;\n    if (!expression) return Element.extend(element);\n    while (element) {\n      if (Object.isElement(element) && selector.match(element, expression))\n        return Element.extend(element);\n      element = element.parentNode;\n    }\n  }\n\n  function pointer(event) {\n    return { x: pointerX(event), y: pointerY(event) };\n  }\n\n  function pointerX(event) {\n    var docElement = document.documentElement,\n      body = document.body || { scrollLeft: 0 };\n\n    return (\n      event.pageX ||\n      event.clientX +\n        (docElement.scrollLeft || body.scrollLeft) -\n        (docElement.clientLeft || 0)\n    );\n  }\n\n  function pointerY(event) {\n    var docElement = document.documentElement,\n      body = document.body || { scrollTop: 0 };\n\n    return (\n      event.pageY ||\n      event.clientY +\n        (docElement.scrollTop || body.scrollTop) -\n        (docElement.clientTop || 0)\n    );\n  }\n\n  function stop(event) {\n    Event.extend(event);\n    event.preventDefault();\n    event.stopPropagation();\n\n    event.stopped = true;\n  }\n\n  Event.Methods = {\n    isLeftClick: isLeftClick,\n    isMiddleClick: isMiddleClick,\n    isRightClick: isRightClick,\n\n    element: element,\n    findElement: findElement,\n\n    pointer: pointer,\n    pointerX: pointerX,\n    pointerY: pointerY,\n\n    stop: stop\n  };\n\n  var methods = Object.keys(Event.Methods).inject({}, function (m, name) {\n    m[name] = Event.Methods[name].methodize();\n    return m;\n  });\n\n  if (window.attachEvent) {\n    function _relatedTarget(event) {\n      var element;\n      switch (event.type) {\n        case 'mouseover':\n        case 'mouseenter':\n          element = event.fromElement;\n          break;\n        case 'mouseout':\n        case 'mouseleave':\n          element = event.toElement;\n          break;\n        default:\n          return null;\n      }\n      return Element.extend(element);\n    }\n\n    var additionalMethods = {\n      stopPropagation: function () {\n        this.cancelBubble = true;\n      },\n      preventDefault: function () {\n        this.returnValue = false;\n      },\n      inspect: function () {\n        return '[object Event]';\n      }\n    };\n\n    Event.extend = function (event, element) {\n      if (!event) return false;\n\n      if (!isIELegacyEvent(event)) return event;\n\n      if (event._extendedByPrototype) return event;\n      event._extendedByPrototype = Prototype.emptyFunction;\n\n      var pointer = Event.pointer(event);\n\n      Object.extend(event, {\n        target: event.srcElement || element,\n        relatedTarget: _relatedTarget(event),\n        pageX: pointer.x,\n        pageY: pointer.y\n      });\n\n      Object.extend(event, methods);\n      Object.extend(event, additionalMethods);\n\n      return event;\n    };\n  } else {\n    Event.extend = Prototype.K;\n  }\n\n  if (window.addEventListener) {\n    Event.prototype =\n      window.Event.prototype || document.createEvent('HTMLEvents').__proto__;\n    Object.extend(Event.prototype, methods);\n  }\n\n  var EVENT_TRANSLATIONS = {\n    mouseenter: 'mouseover',\n    mouseleave: 'mouseout'\n  };\n\n  function getDOMEventName(eventName) {\n    return EVENT_TRANSLATIONS[eventName] || eventName;\n  }\n\n  if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) getDOMEventName = Prototype.K;\n\n  function getUniqueElementID(element) {\n    if (element === window) return 0;\n\n    if (typeof element._prototypeUID === 'undefined')\n      element._prototypeUID = Element.Storage.UID++;\n    return element._prototypeUID;\n  }\n\n  function getUniqueElementID_IE(element) {\n    if (element === window) return 0;\n    if (element == document) return 1;\n    return element.uniqueID;\n  }\n\n  if ('uniqueID' in DIV) getUniqueElementID = getUniqueElementID_IE;\n\n  function isCustomEvent(eventName) {\n    return eventName.include(':');\n  }\n\n  Event._isCustomEvent = isCustomEvent;\n\n  function getOrCreateRegistryFor(element, uid) {\n    var CACHE = GLOBAL.Event.cache;\n    if (Object.isUndefined(uid)) uid = getUniqueElementID(element);\n    if (!CACHE[uid]) CACHE[uid] = { element: element };\n    return CACHE[uid];\n  }\n\n  function destroyRegistryForElement(element, uid) {\n    if (Object.isUndefined(uid)) uid = getUniqueElementID(element);\n    delete GLOBAL.Event.cache[uid];\n  }\n\n  function register(element, eventName, handler) {\n    var registry = getOrCreateRegistryFor(element);\n    if (!registry[eventName]) registry[eventName] = [];\n    var entries = registry[eventName];\n\n    var i = entries.length;\n    while (i--) if (entries[i].handler === handler) return null;\n\n    var uid = getUniqueElementID(element);\n    var responder = GLOBAL.Event._createResponder(uid, eventName, handler);\n    var entry = {\n      responder: responder,\n      handler: handler\n    };\n\n    entries.push(entry);\n    return entry;\n  }\n\n  function unregister(element, eventName, handler) {\n    var registry = getOrCreateRegistryFor(element);\n    var entries = registry[eventName] || [];\n\n    var i = entries.length,\n      entry;\n    while (i--) {\n      if (entries[i].handler === handler) {\n        entry = entries[i];\n        break;\n      }\n    }\n\n    if (entry) {\n      var index = entries.indexOf(entry);\n      entries.splice(index, 1);\n    }\n\n    if (entries.length === 0) {\n      delete registry[eventName];\n      if (Object.keys(registry).length === 1 && 'element' in registry)\n        destroyRegistryForElement(element);\n    }\n\n    return entry;\n  }\n\n  function observe(element, eventName, handler) {\n    element = $(element);\n    var entry = register(element, eventName, handler);\n\n    if (entry === null) return element;\n\n    var responder = entry.responder;\n    if (isCustomEvent(eventName))\n      observeCustomEvent(element, eventName, responder);\n    else observeStandardEvent(element, eventName, responder);\n\n    return element;\n  }\n\n  function observeStandardEvent(element, eventName, responder) {\n    var actualEventName = getDOMEventName(eventName);\n    if (element.addEventListener) {\n      element.addEventListener(actualEventName, responder, false);\n    } else {\n      element.attachEvent('on' + actualEventName, responder);\n    }\n  }\n\n  function observeCustomEvent(element, eventName, responder) {\n    if (element.addEventListener) {\n      element.addEventListener('dataavailable', responder, false);\n    } else {\n      element.attachEvent('ondataavailable', responder);\n      element.attachEvent('onlosecapture', responder);\n    }\n  }\n\n  function stopObserving(element, eventName, handler) {\n    element = $(element);\n    var handlerGiven = !Object.isUndefined(handler),\n      eventNameGiven = !Object.isUndefined(eventName);\n\n    if (!eventNameGiven && !handlerGiven) {\n      stopObservingElement(element);\n      return element;\n    }\n\n    if (!handlerGiven) {\n      stopObservingEventName(element, eventName);\n      return element;\n    }\n\n    var entry = unregister(element, eventName, handler);\n\n    if (!entry) return element;\n    removeEvent(element, eventName, entry.responder);\n    return element;\n  }\n\n  function stopObservingStandardEvent(element, eventName, responder) {\n    var actualEventName = getDOMEventName(eventName);\n    if (element.removeEventListener) {\n      element.removeEventListener(actualEventName, responder, false);\n    } else {\n      element.detachEvent('on' + actualEventName, responder);\n    }\n  }\n\n  function stopObservingCustomEvent(element, eventName, responder) {\n    if (element.removeEventListener) {\n      element.removeEventListener('dataavailable', responder, false);\n    } else {\n      element.detachEvent('ondataavailable', responder);\n      element.detachEvent('onlosecapture', responder);\n    }\n  }\n\n  function stopObservingElement(element) {\n    var uid = getUniqueElementID(element),\n      registry = GLOBAL.Event.cache[uid];\n    if (!registry) return;\n\n    destroyRegistryForElement(element, uid);\n\n    var entries, i;\n    for (var eventName in registry) {\n      if (eventName === 'element') continue;\n\n      entries = registry[eventName];\n      i = entries.length;\n      while (i--) removeEvent(element, eventName, entries[i].responder);\n    }\n  }\n\n  function stopObservingEventName(element, eventName) {\n    var registry = getOrCreateRegistryFor(element);\n    var entries = registry[eventName];\n    if (entries) {\n      delete registry[eventName];\n    }\n\n    entries = entries || [];\n\n    var i = entries.length;\n    while (i--) removeEvent(element, eventName, entries[i].responder);\n\n    for (var name in registry) {\n      if (name === 'element') continue;\n      return; // There is another registered event\n    }\n\n    destroyRegistryForElement(element);\n  }\n\n  function removeEvent(element, eventName, handler) {\n    if (isCustomEvent(eventName))\n      stopObservingCustomEvent(element, eventName, handler);\n    else stopObservingStandardEvent(element, eventName, handler);\n  }\n\n  function getFireTarget(element) {\n    if (element !== document) return element;\n    if (document.createEvent && !element.dispatchEvent)\n      return document.documentElement;\n    return element;\n  }\n\n  function fire(element, eventName, memo, bubble) {\n    element = getFireTarget($(element));\n    if (Object.isUndefined(bubble)) bubble = true;\n    memo = memo || {};\n\n    var event = fireEvent(element, eventName, memo, bubble);\n    return Event.extend(event);\n  }\n\n  function fireEvent_DOM(element, eventName, memo, bubble) {\n    var event = document.createEvent('HTMLEvents');\n    event.initEvent('dataavailable', bubble, true);\n\n    event.eventName = eventName;\n    event.memo = memo;\n\n    element.dispatchEvent(event);\n    return event;\n  }\n\n  function fireEvent_IE(element, eventName, memo, bubble) {\n    var event = document.createEventObject();\n    event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';\n\n    event.eventName = eventName;\n    event.memo = memo;\n\n    element.fireEvent(event.eventType, event);\n    return event;\n  }\n\n  var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE;\n\n  Event.Handler = Class.create({\n    initialize: function (element, eventName, selector, callback) {\n      this.element = $(element);\n      this.eventName = eventName;\n      this.selector = selector;\n      this.callback = callback;\n      this.handler = this.handleEvent.bind(this);\n    },\n\n    start: function () {\n      Event.observe(this.element, this.eventName, this.handler);\n      return this;\n    },\n\n    stop: function () {\n      Event.stopObserving(this.element, this.eventName, this.handler);\n      return this;\n    },\n\n    handleEvent: function (event) {\n      var element = Event.findElement(event, this.selector);\n      if (element) this.callback.call(this.element, event, element);\n    }\n  });\n\n  function on(element, eventName, selector, callback) {\n    element = $(element);\n    if (Object.isFunction(selector) && Object.isUndefined(callback)) {\n      ((callback = selector), (selector = null));\n    }\n\n    return new Event.Handler(element, eventName, selector, callback).start();\n  }\n\n  Object.extend(Event, Event.Methods);\n\n  Object.extend(Event, {\n    fire: fire,\n    observe: observe,\n    stopObserving: stopObserving,\n    on: on\n  });\n\n  Element.addMethods({\n    fire: fire,\n\n    observe: observe,\n\n    stopObserving: stopObserving,\n\n    on: on\n  });\n\n  Object.extend(document, {\n    fire: fire.methodize(),\n\n    observe: observe.methodize(),\n\n    stopObserving: stopObserving.methodize(),\n\n    on: on.methodize(),\n\n    loaded: false\n  });\n\n  if (GLOBAL.Event) Object.extend(window.Event, Event);\n  else GLOBAL.Event = Event;\n\n  GLOBAL.Event.cache = {};\n\n  function destroyCache_IE() {\n    GLOBAL.Event.cache = null;\n  }\n\n  if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE);\n\n  DIV = null;\n  docEl = null;\n})(this);\n\n(function (GLOBAL) {\n  /* Code for creating leak-free event responders is based on work by\n   John-David Dalton. */\n\n  var docEl = document.documentElement;\n  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED =\n    'onmouseenter' in docEl && 'onmouseleave' in docEl;\n\n  function isSimulatedMouseEnterLeaveEvent(eventName) {\n    return (\n      !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&\n      (eventName === 'mouseenter' || eventName === 'mouseleave')\n    );\n  }\n\n  function createResponder(uid, eventName, handler) {\n    if (Event._isCustomEvent(eventName))\n      return createResponderForCustomEvent(uid, eventName, handler);\n    if (isSimulatedMouseEnterLeaveEvent(eventName))\n      return createMouseEnterLeaveResponder(uid, eventName, handler);\n\n    return function (event) {\n      if (!Event.cache) return;\n\n      var element = Event.cache[uid].element;\n      Event.extend(event, element);\n      handler.call(element, event);\n    };\n  }\n\n  function createResponderForCustomEvent(uid, eventName, handler) {\n    return function (event) {\n      var cache = Event.cache[uid];\n      var element = cache && cache.element;\n\n      if (Object.isUndefined(event.eventName)) return false;\n\n      if (event.eventName !== eventName) return false;\n\n      Event.extend(event, element);\n      handler.call(element, event);\n    };\n  }\n\n  function createMouseEnterLeaveResponder(uid, eventName, handler) {\n    return function (event) {\n      var element = Event.cache[uid].element;\n\n      Event.extend(event, element);\n      var parent = event.relatedTarget;\n\n      while (parent && parent !== element) {\n        try {\n          parent = parent.parentNode;\n        } catch (e) {\n          parent = element;\n        }\n      }\n\n      if (parent === element) return;\n      handler.call(element, event);\n    };\n  }\n\n  GLOBAL.Event._createResponder = createResponder;\n  docEl = null;\n})(this);\n\n(function (GLOBAL) {\n  /* Support for the DOMContentLoaded event is based on work by Dan Webb,\n     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */\n\n  var TIMER;\n\n  function fireContentLoadedEvent() {\n    if (document.loaded) return;\n    if (TIMER) window.clearTimeout(TIMER);\n    document.loaded = true;\n    document.fire('dom:loaded');\n  }\n\n  function checkReadyState() {\n    if (document.readyState === 'complete') {\n      document.detachEvent('onreadystatechange', checkReadyState);\n      fireContentLoadedEvent();\n    }\n  }\n\n  function pollDoScroll() {\n    try {\n      document.documentElement.doScroll('left');\n    } catch (e) {\n      TIMER = pollDoScroll.defer();\n      return;\n    }\n\n    fireContentLoadedEvent();\n  }\n\n  if (document.readyState === 'complete') {\n    fireContentLoadedEvent();\n    return;\n  }\n\n  if (document.addEventListener) {\n    document.addEventListener(\n      'DOMContentLoaded',\n      fireContentLoadedEvent,\n      false\n    );\n  } else {\n    document.attachEvent('onreadystatechange', checkReadyState);\n    if (window == top) TIMER = pollDoScroll.defer();\n  }\n\n  Event.observe(window, 'load', fireContentLoadedEvent);\n})(this);\n\nElement.addMethods();\n/*------------------------------- DEPRECATED -------------------------------*/\n\nHash.toQueryString = Object.toQueryString;\n\nvar Toggle = { display: Element.toggle };\n\nElement.addMethods({\n  childOf: Element.Methods.descendantOf\n});\n\nvar Insertion = {\n  Before: function (element, content) {\n    return Element.insert(element, { before: content });\n  },\n\n  Top: function (element, content) {\n    return Element.insert(element, { top: content });\n  },\n\n  Bottom: function (element, content) {\n    return Element.insert(element, { bottom: content });\n  },\n\n  After: function (element, content) {\n    return Element.insert(element, { after: content });\n  }\n};\n\nvar $continue = new Error(\n  '\"throw $continue\" is deprecated, use \"return\" instead'\n);\n\nvar Position = {\n  includeScrollOffsets: false,\n\n  prepare: function () {\n    this.deltaX =\n      window.pageXOffset ||\n      document.documentElement.scrollLeft ||\n      document.body.scrollLeft ||\n      0;\n    this.deltaY =\n      window.pageYOffset ||\n      document.documentElement.scrollTop ||\n      document.body.scrollTop ||\n      0;\n  },\n\n  within: function (element, x, y) {\n    if (this.includeScrollOffsets)\n      return this.withinIncludingScrolloffsets(element, x, y);\n    this.xcomp = x;\n    this.ycomp = y;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (\n      y >= this.offset[1] &&\n      y < this.offset[1] + element.offsetHeight &&\n      x >= this.offset[0] &&\n      x < this.offset[0] + element.offsetWidth\n    );\n  },\n\n  withinIncludingScrolloffsets: function (element, x, y) {\n    var offsetcache = Element.cumulativeScrollOffset(element);\n\n    this.xcomp = x + offsetcache[0] - this.deltaX;\n    this.ycomp = y + offsetcache[1] - this.deltaY;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (\n      this.ycomp >= this.offset[1] &&\n      this.ycomp < this.offset[1] + element.offsetHeight &&\n      this.xcomp >= this.offset[0] &&\n      this.xcomp < this.offset[0] + element.offsetWidth\n    );\n  },\n\n  overlap: function (mode, element) {\n    if (!mode) return 0;\n    if (mode == 'vertical')\n      return (\n        (this.offset[1] + element.offsetHeight - this.ycomp) /\n        element.offsetHeight\n      );\n    if (mode == 'horizontal')\n      return (\n        (this.offset[0] + element.offsetWidth - this.xcomp) /\n        element.offsetWidth\n      );\n  },\n\n  cumulativeOffset: Element.Methods.cumulativeOffset,\n\n  positionedOffset: Element.Methods.positionedOffset,\n\n  absolutize: function (element) {\n    Position.prepare();\n    return Element.absolutize(element);\n  },\n\n  relativize: function (element) {\n    Position.prepare();\n    return Element.relativize(element);\n  },\n\n  realOffset: Element.Methods.cumulativeScrollOffset,\n\n  offsetParent: Element.Methods.getOffsetParent,\n\n  page: Element.Methods.viewportOffset,\n\n  clone: function (source, target, options) {\n    options = options || {};\n    return Element.clonePosition(target, source, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nif (!document.getElementsByClassName)\n  document.getElementsByClassName = (function (instanceMethods) {\n    function iter(name) {\n      return name.blank()\n        ? null\n        : \"[contains(concat(' ', @class, ' '), ' \" + name + \" ')]\";\n    }\n\n    instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath\n      ? function (element, className) {\n          className = className.toString().strip();\n          var cond = /\\s/.test(className)\n            ? $w(className).map(iter).join('')\n            : iter(className);\n          return cond\n            ? document._getElementsByXPath('.//*' + cond, element)\n            : [];\n        }\n      : function (element, className) {\n          className = className.toString().strip();\n          var elements = [],\n            classNames = /\\s/.test(className) ? $w(className) : null;\n          if (!classNames && !className) return elements;\n\n          var nodes = $(element).getElementsByTagName('*');\n          className = ' ' + className + ' ';\n\n          for (var i = 0, child, cn; (child = nodes[i]); i++) {\n            if (\n              child.className &&\n              (cn = ' ' + child.className + ' ') &&\n              (cn.include(className) ||\n                (classNames &&\n                  classNames.all(function (name) {\n                    return (\n                      !name.toString().blank() && cn.include(' ' + name + ' ')\n                    );\n                  })))\n            )\n              elements.push(Element.extend(child));\n          }\n          return elements;\n        };\n\n    return function (className, parentElement) {\n      return $(parentElement || document.body).getElementsByClassName(\n        className\n      );\n    };\n  })(Element.Methods);\n\n/*--------------------------------------------------------------------------*/\n\nElement.ClassNames = Class.create();\nElement.ClassNames.prototype = {\n  initialize: function (element) {\n    this.element = $(element);\n  },\n\n  _each: function (iterator, context) {\n    this.element.className\n      .split(/\\s+/)\n      .select(function (name) {\n        return name.length > 0;\n      })\n      ._each(iterator, context);\n  },\n\n  set: function (className) {\n    this.element.className = className;\n  },\n\n  add: function (classNameToAdd) {\n    if (this.include(classNameToAdd)) return;\n    this.set($A(this).concat(classNameToAdd).join(' '));\n  },\n\n  remove: function (classNameToRemove) {\n    if (!this.include(classNameToRemove)) return;\n    this.set($A(this).without(classNameToRemove).join(' '));\n  },\n\n  toString: function () {\n    return $A(this).join(' ');\n  }\n};\n\nObject.extend(Element.ClassNames.prototype, Enumerable);\n\n/*--------------------------------------------------------------------------*/\n\n(function () {\n  window.Selector = Class.create({\n    initialize: function (expression) {\n      this.expression = expression.strip();\n    },\n\n    findElements: function (rootElement) {\n      return Prototype.Selector.select(this.expression, rootElement);\n    },\n\n    match: function (element) {\n      return Prototype.Selector.match(element, this.expression);\n    },\n\n    toString: function () {\n      return this.expression;\n    },\n\n    inspect: function () {\n      return '#<Selector: ' + this.expression + '>';\n    }\n  });\n\n  Object.extend(Selector, {\n    matchElements: function (elements, expression) {\n      var match = Prototype.Selector.match,\n        results = [];\n\n      for (var i = 0, length = elements.length; i < length; i++) {\n        var element = elements[i];\n        if (match(element, expression)) {\n          results.push(Element.extend(element));\n        }\n      }\n      return results;\n    },\n\n    findElement: function (elements, expression, index) {\n      index = index || 0;\n      var matchIndex = 0,\n        element;\n      for (var i = 0, length = elements.length; i < length; i++) {\n        element = elements[i];\n        if (\n          Prototype.Selector.match(element, expression) &&\n          index === matchIndex++\n        ) {\n          return Element.extend(element);\n        }\n      }\n    },\n\n    findChildElements: function (element, expressions) {\n      var selector = expressions.toArray().join(', ');\n      return Prototype.Selector.select(selector, element || document);\n    }\n  });\n})();\n"
  },
  {
    "path": "test/integration/full/contrast/prototype.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <!-- prototype.js must be loaded before axe is loaded on the page -->\n    <script src=\"prototype-lib-1.7.3.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style></style>\n  </head>\n\n  <body>\n    <div id=\"fixture\" style=\"background-color: darkred; color: black\">Text</div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"prototype.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/prototype.js",
    "content": "describe('color-contrast prototype.js test', () => {\n  let results;\n\n  before(done => {\n    axe.testUtils.awaitNestedLoad(async () => {\n      const options = {\n        runOnly: ['color-contrast'],\n        elementRef: true\n      };\n      results = await axe.run('#fixture', options);\n      done();\n    });\n  });\n\n  describe('incomplete', () => {\n    it('should find none', () => {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n\n  describe('violations', () => {\n    it('should find one', () => {\n      assert.lengthOf(results.violations, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/shadow-dom.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style></style>\n  </head>\n\n  <body>\n    <div style=\"background-color: darkred\" id=\"redHerring\">Text</div>\n    <div id=\"fixture\" style=\"background-color: black\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"shadow-dom.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/shadow-dom.js",
    "content": "describe('color-contrast shadow dom test', function () {\n  'use strict';\n\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  before(function () {\n    var fixture = document.querySelector('#fixture');\n    if (shadowSupported) {\n      var shadow = fixture.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<button style=\"background-color:red;color:white;\">Go!</button>' +\n        '<span style=\"color:#ccc;\">Text</span>' +\n        '<div><label style=\"color:#ccc;\">Text<input type=\"text\"></label></div>' +\n        '<div style=\"background-color:black; height:20px;\">' +\n        '<div style=\"color:#666; position:absolute;\">Text</div>' +\n        '</div>';\n    }\n  });\n\n  describe('violations', function () {\n    (shadowSupported ? it : xit)(\n      'should find issues in shadow tree',\n      function (done) {\n        axe.run(\n          '#fixture',\n          { runOnly: { type: 'rule', values: ['color-contrast'] } },\n          function (err, results) {\n            assert.isNull(err);\n            assert.lengthOf(results.violations, 1);\n            assert.lengthOf(results.violations[0].nodes, 2);\n            assert.equal(\n              results.violations[0].nodes[1].any[0].data.bgColor,\n              '#000000'\n            );\n            assert.lengthOf(results.incomplete, 0);\n            done();\n          }\n        );\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast/sticky-header.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style>\n      * {\n        box-sizing: border-box;\n      }\n      html,\n      body {\n        padding: 0;\n        margin: 0;\n        height: 100%;\n        background: #fff;\n        color: #000;\n      }\n\n      header {\n        top: 0;\n      }\n\n      footer {\n        bottom: 0;\n      }\n\n      header,\n      footer {\n        padding: 20px;\n        position: fixed;\n        left: 0;\n        right: 0;\n        height: 80px;\n        background: #000;\n        color: #fff;\n        z-index: 2;\n      }\n\n      main {\n        position: relative;\n        z-index: 1;\n        left: 1px;\n        padding: 80px 20px;\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <header>\n        <h1>Hi</h1>\n      </header>\n      <main>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n        <p>Some content</p>\n      </main>\n      <footer>Footer</footer>\n    </div>\n    <script src=\"sticky-header.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast/sticky-header.js",
    "content": "describe('color-contrast sticky header test', function () {\n  'use strict';\n\n  describe('violations', function () {\n    it('should find none', function (done) {\n      axe.run(\n        '#fixture',\n        { runOnly: { type: 'rule', values: ['color-contrast'] } },\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.violations, 0);\n          done();\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/contrast-enhanced/simple.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Test Page</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <style></style>\n  </head>\n\n  <body>\n    <div id=\"fixture\">\n      <div style=\"background: #ffffff; color: #000000\">\n        Pass (Regular size text, 21:1)\n      </div>\n      <div style=\"background: #ffffff; color: #556666\">\n        Fail (Regular size text, 6:1)\n      </div>\n      <div style=\"background: #ffffff; color: #556000\">\n        Fail (Regular size text, 6.9:1)\n      </div>\n      <div style=\"background: #ffffff; color: #555e00\">\n        Pass (Regular size text, 7:1)\n      </div>\n\n      <div style=\"background: #ffffff; color: #118488; font-size: 18pt\">\n        Fail (Large text, 4.487:1)\n      </div>\n      <div style=\"background: #ffffff; color: #048488; font-size: 18pt\">\n        Pass (Large text, 4.5:1)\n      </div>\n      <div style=\"background: #ffffff; color: #048488; font-size: 17.9pt\">\n        Fail (Large text, but not quite large enough, 4.5:1)\n      </div>\n\n      <div\n        style=\"\n          background: #ffffff;\n          color: #118488;\n          font-size: 14pt;\n          font-weight: bold;\n        \"\n      >\n        Fail (Bold text, 4.487:1)\n      </div>\n      <div\n        style=\"\n          background: #ffffff;\n          color: #048488;\n          font-size: 14pt;\n          font-weight: bold;\n        \"\n      >\n        Pass (Bold text, 4.5:1)\n      </div>\n      <div\n        style=\"\n          background: #ffffff;\n          color: #048488;\n          font-size: 13.9pt;\n          font-weight: bold;\n        \"\n      >\n        Fail (Bold text, but not quite large enough, 4.5:1)\n      </div>\n      <div\n        style=\"\n          background: #ffffff;\n          color: #048488;\n          font-size: 14pt;\n          font-weight: 699;\n        \"\n      >\n        Fail (Bold text, but not quite bold enough, 4.5:1)\n      </div>\n    </div>\n\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"simple.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/contrast-enhanced/simple.js",
    "content": "describe('color-contrast shadow dom test', function () {\n  'use strict';\n\n  describe('violations', function () {\n    it('should find issues in simple tree', function (done) {\n      axe.run(\n        '#fixture',\n        { runOnly: { type: 'rule', values: ['color-contrast-enhanced'] } },\n        function (err, results) {\n          assert.isNull(err);\n          assert.lengthOf(results.passes, 1);\n          assert.lengthOf(results.passes[0].nodes, 4);\n          assert.lengthOf(results.incomplete, 0);\n          assert.lengthOf(results.violations, 1);\n          assert.lengthOf(results.violations[0].nodes, 7);\n          assert.equal(\n            results.violations[0].nodes[0].any[0].data.fgColor,\n            '#556666'\n          );\n          assert.equal(\n            results.violations[0].nodes[1].any[0].data.fgColor,\n            '#556000'\n          );\n          assert.equal(\n            results.violations[0].nodes[2].any[0].data.fgColor,\n            '#118488'\n          );\n          assert.equal(\n            results.violations[0].nodes[3].any[0].data.fgColor,\n            '#048488'\n          );\n          assert.equal(\n            results.violations[0].nodes[4].any[0].data.fgColor,\n            '#118488'\n          );\n          assert.equal(\n            results.violations[0].nodes[5].any[0].data.fgColor,\n            '#048488'\n          );\n          assert.equal(\n            results.violations[0].nodes[6].any[0].data.fgColor,\n            '#048488'\n          );\n          assert.lengthOf(results.incomplete, 0);\n          done();\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/incomplete.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>css orientation lock test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <div class=\"someDiv\">some div content</div>\n    <div id=\"shadow-fixture\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"incomplete.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/incomplete.js",
    "content": "describe('css-orientation-lock incomplete test', function () {\n  'use strict';\n\n  it('returns INCOMPLETE if preload is set to FALSE', function (done) {\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['css-orientation-lock']\n        },\n        preload: false\n      },\n      function (err, res) {\n        assert.isNull(err);\n        assert.isDefined(res);\n\n        assert.hasAnyKeys(res, ['incomplete', 'passes']);\n        assert.lengthOf(res.incomplete, 1);\n        done();\n      }\n    );\n  });\n\n  it('returns INCOMPLETE as page has no styles (not even mocha styles)', function (done) {\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['css-orientation-lock']\n        }\n      },\n      function (err, res) {\n        assert.isNull(err);\n        assert.isDefined(res);\n\n        assert.property(res, 'incomplete');\n        assert.lengthOf(res.incomplete, 1);\n        done();\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/passes.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>css orientation lock test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <div class=\"someDiv\">some div content</div>\n    <div id=\"shadow-fixture\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"passes.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/passes.js",
    "content": "describe('css-orientation-lock passes test', function () {\n  'use strict';\n\n  var shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  var styleSheets = [\n    {\n      href: 'https://stackpath.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'\n    },\n    {\n      text: '@media screen and (min-width: 10px) and (max-width: 3000px) {\thtml { width: 100vh; } }'\n    }\n  ];\n\n  before(function (done) {\n    axe.testUtils\n      .addStyleSheets(styleSheets)\n      .then(function () {\n        done();\n      })\n      .catch(function (error) {\n        done(new Error('Could not load stylesheets for testing. ' + error));\n      });\n  });\n\n  it('returns PASSES when page has STYLE with MEDIA rules (not orientation)', function (done) {\n    // the sheets included in the html, have styles for transform and rotate, hence the violation\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['css-orientation-lock']\n        }\n      },\n      function (err, res) {\n        assert.isNull(err);\n        assert.isDefined(res);\n\n        // check for violation\n        assert.property(res, 'passes');\n        assert.lengthOf(res.passes, 1);\n        var checkedNode = res.passes[0].nodes[0];\n        assert.isTrue(/html/i.test(checkedNode.html));\n\n        done();\n      }\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns PASSES whilst also accommodating shadowDOM styles with MEDIA rules (not orientation)',\n    function (done) {\n      // here although media styles are pumped into shadow dom\n      // they are not orientation locks, so returns as passes\n      var fixture = document.getElementById('shadow-fixture');\n      var shadow = fixture.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<style> @media screen and (min-width: 10px) and (max-width: 2000px) { .shadowDiv { transform: rotate(90deg); } } </style>' +\n        '<div class=\"green\">green</div>' +\n        '<div class=\"shadowDiv\">red</div>';\n\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['css-orientation-lock']\n          }\n        },\n        function (err, res) {\n          assert.isNull(err);\n          assert.isDefined(res);\n\n          // check for violation\n          assert.property(res, 'passes');\n          assert.lengthOf(res.passes, 1);\n\n          var checkedNode = res.passes[0].nodes[0];\n          assert.isTrue(/html/i.test(checkedNode.html));\n\n          var checkResult = checkedNode.all[0];\n          assert.lengthOf(checkResult.relatedNodes, 0);\n\n          done();\n        }\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/violations.css",
    "content": "@media screen and (min-width: 20px) and (max-width: 2300px) and (orientation: portrait) {\n  .thatDiv {\n    transform: rotate(90deg);\n  }\n  .rotateDiv {\n    rotate: 90deg;\n  }\n}\n\n@media screen and (min-width: 10px) and (max-width: 3000px) and (orientation: landscape) {\n  html {\n    transform: rotateZ(0, 0, 1, 1.5708rad);\n  }\n  .someDiv {\n    transform: matrix3d(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);\n  }\n  .rotateMatrix {\n    rotate: 0 0 1 1.5708rad;\n  }\n}\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/violations.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>css orientation lock test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <div class=\"someDiv\">some div content</div>\n    <div class=\"thatDiv\">that div content</div>\n    <div class=\"rotateDiv\">that div content</div>\n    <div class=\"rotateMatrix\">that div content</div>\n    <div id=\"shadow-fixture\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"violations.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/css-orientation-lock/violations.js",
    "content": "describe('css-orientation-lock violations test', () => {\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n\n  const styleSheets = [\n    {\n      href: 'https://stackpath.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'\n    },\n    {\n      href: 'violations.css'\n    }\n  ];\n\n  before(done => {\n    axe.testUtils\n      .addStyleSheets(styleSheets)\n      .then(() => {\n        done();\n      })\n      .catch(error => {\n        done(new Error('Could not load stylesheets for testing. ' + error));\n      });\n  });\n\n  function assertViolatedSelectors(relatedNodes, violatedSelectors) {\n    relatedNodes.forEach(node => {\n      const target = node.target[0];\n      const className = Array.isArray(target) ? target.reverse()[0] : target;\n      assert.isTrue(violatedSelectors.indexOf(className) !== -1);\n    });\n  }\n\n  it('returns VIOLATIONS if preload is set to TRUE', done => {\n    // the sheets included in the html, have styles for transform and rotate, hence the violation\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['css-orientation-lock']\n        }\n      },\n      (err, res) => {\n        try {\n          assert.isNull(err);\n          assert.isDefined(res);\n\n          // check for violation\n          assert.property(res, 'violations');\n          assert.lengthOf(res.violations, 1);\n\n          // assert the node\n          const checkedNode = res.violations[0].nodes[0];\n          assert.isTrue(/html/i.test(checkedNode.html));\n\n          // assert the relatedNodes\n          const checkResult = checkedNode.all[0];\n          assert.lengthOf(checkResult.relatedNodes, 4);\n          assertViolatedSelectors(checkResult.relatedNodes, [\n            '.someDiv',\n            '.thatDiv',\n            '.rotateDiv',\n            '.rotateMatrix'\n          ]);\n\n          done();\n        } catch (e) {\n          done(e);\n        }\n      }\n    );\n  });\n\n  (shadowSupported ? it : xit)(\n    'returns VIOLATIONS whilst also accommodating shadowDOM styles',\n    done => {\n      const fixture = document.getElementById('shadow-fixture');\n      const shadow = fixture.attachShadow({ mode: 'open' });\n      shadow.innerHTML =\n        '<style> @media screen and (min-width: 10px) and (max-width: 2000px) and (orientation: portrait) { .shadowDiv { transform: rotate3d(0,0,1,90deg); } } .green { background-color: green; } </style>' +\n        '<div class=\"green\">green</div>' +\n        '<div class=\"shadowDiv\">red</div>';\n\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['css-orientation-lock']\n          }\n        },\n        (err, res) => {\n          try {\n            assert.isNull(err);\n            assert.isDefined(res);\n\n            // check for violation\n            assert.property(res, 'violations');\n            assert.lengthOf(res.violations, 1);\n\n            // assert the node\n            const checkedNode = res.violations[0].nodes[0];\n            assert.isTrue(/html/i.test(checkedNode.html));\n\n            // assert the relatedNodes\n            const checkResult = checkedNode.all[0];\n            assert.lengthOf(checkResult.relatedNodes, 5);\n            assertViolatedSelectors(checkResult.relatedNodes, [\n              '.someDiv',\n              '.thatDiv',\n              '.rotateDiv',\n              '.rotateMatrix',\n              '.shadowDiv'\n            ]);\n\n            done();\n          } catch (e) {\n            done(e);\n          }\n        }\n      );\n    }\n  );\n});\n"
  },
  {
    "path": "test/integration/full/definition-list/dl-role.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>dl aria test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"list\" id=\"target\">\n      <dl role=\"listitem\">\n        <dd role=\"presentation\">\n          <a href=\"#somethin\">Stuff</a>\n        </dd>\n      </dl>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"dl-role.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/definition-list/dl-role.js",
    "content": "describe('definition-list overriden with ARIA role', function () {\n  'use strict';\n  it('should find no matches', function (done) {\n    axe.run(\n      { runOnly: { type: 'rule', values: ['definition-list'] } },\n      function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0);\n        assert.lengthOf(results.passes, 0);\n        done();\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/definition-list/dlitem-role.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>dlitem aria test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"list\" id=\"target\">\n      <div role=\"listitem\">\n        <dd role=\"presentation\">\n          <a href=\"#somethin\">Stuff</a>\n        </dd>\n        <dt role=\"presentation\">Thing</dt>\n      </div>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"dlitem-role.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/definition-list/dlitem-role.js",
    "content": "describe('dlitem overriden with ARIA role', function () {\n  'use strict';\n  it('should find no matches', function (done) {\n    axe.run(\n      { runOnly: { type: 'rule', values: ['dlitem'] } },\n      function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0);\n        assert.lengthOf(results.passes, 0);\n        done();\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/dialog/dialog.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>dialog test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"target\">\n      <button id=\"root-button\"></button>\n      <div style=\"background-color: #333; color: #000\">\n        <span id=\"root-color\">Contrast failure</span>\n      </div>\n      <dialog>\n        <button id=\"dialog-button\"></button>\n        <div style=\"background-color: #333; color: #000\">\n          <span id=\"dialog-color\">Contrast failure</span>\n        </div>\n      </dialog>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"dialog.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/dialog/dialog.js",
    "content": "describe('dialog tests', () => {\n  const dialog = document.querySelector('dialog');\n  const target = document.querySelector('#target');\n\n  async function getViolations() {\n    const results = await axe.run(target);\n    const buttonName = results.violations.find(\n      ({ id }) => id === 'button-name'\n    );\n    const colorContrast = results.violations.find(\n      ({ id }) => id === 'color-contrast'\n    );\n\n    return { buttonName, colorContrast };\n  }\n\n  afterEach(function () {\n    dialog.close();\n  });\n\n  it('should not find violations inside a closed dialog', async () => {\n    const { buttonName, colorContrast } = await getViolations();\n\n    assert.lengthOf(buttonName.nodes, 1);\n    assert.deepEqual(buttonName.nodes[0].target, ['#root-button']);\n    assert.lengthOf(colorContrast.nodes, 1);\n    assert.deepEqual(colorContrast.nodes[0].target, ['#root-color']);\n  });\n\n  it('should not find violations outside a modal dialog', async () => {\n    dialog.showModal();\n    const { buttonName, colorContrast } = await getViolations();\n\n    assert.lengthOf(buttonName.nodes, 1);\n    assert.deepEqual(buttonName.nodes[0].target, ['#dialog-button']);\n    assert.lengthOf(colorContrast.nodes, 1);\n    assert.deepEqual(colorContrast.nodes[0].target, ['#dialog-color']);\n  });\n\n  it('should find violations inside and outside an open dialog', async () => {\n    dialog.show();\n    const { buttonName, colorContrast } = await getViolations();\n\n    assert.lengthOf(buttonName.nodes, 2);\n    assert.deepEqual(buttonName.nodes[0].target, ['#root-button']);\n    assert.deepEqual(buttonName.nodes[1].target, ['#dialog-button']);\n    assert.lengthOf(colorContrast.nodes, 2);\n    assert.deepEqual(colorContrast.nodes[0].target, ['#root-color']);\n    assert.deepEqual(colorContrast.nodes[1].target, ['#dialog-color']);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/document-title/document-title-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"fail1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"document-title-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/document-title/document-title-fail.js",
    "content": "describe('document-title test failure', function () {\n  'use strict';\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['document-title'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n    it('should find first level iframe', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/document-title/document-title-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>document-title test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"document-title-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/document-title/document-title-pass.js",
    "content": "describe('document-title test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['document-title'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/document-title/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"violation1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2-a.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/document-title/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"pass2\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/document-title/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/error-occurred/error-frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>error-occurred in frame test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"frame\" src=\"frames/error.html\" title=\"error-frame\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"error-ruleset.js\"></script>\n    <script src=\"error-frame.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/error-occurred/error-frame.js",
    "content": "describe('error-occurred test', () => {\n  const { runPartialRecursive } = axe.testUtils;\n  let results;\n\n  describe('axe.run()', () => {\n    before(done => {\n      axe.testUtils.awaitNestedLoad(() => {\n        axe.run(\n          {\n            runOnly: ['matches-error', 'evaluate-error', 'after-error']\n          },\n          function (err, r) {\n            assert.isNull(err);\n            results = r;\n            done();\n          }\n        );\n      });\n    });\n\n    it('should find 0 violations', () => {\n      assert.lengthOf(results.violations, 0);\n    });\n\n    it('should find  0 passes', () => {\n      assert.lengthOf(results.passes, 0);\n    });\n\n    describe('incomplete', () => {\n      it('should find matches-error', () => {\n        const matchesError = results.incomplete.find(\n          result => result.id === 'matches-error'\n        );\n        window.assertIsErrorOccurred(matchesError, {\n          message: 'matches error',\n          target: ['#frame', '#target']\n        });\n      });\n\n      it('should find evaluate-error', () => {\n        const evaluateError = results.incomplete.find(\n          result => result.id === 'evaluate-error'\n        );\n        window.assertIsErrorOccurred(evaluateError, {\n          message: 'evaluate error',\n          target: ['#frame', '#target']\n        });\n      });\n\n      it('should find after-error', () => {\n        const afterError = results.incomplete.find(\n          result => result.id === 'after-error'\n        );\n        window.assertIsErrorOccurred(afterError, {\n          message: 'after error',\n          target: ['#frame', '#target']\n        });\n      });\n    });\n  });\n\n  describe('axe.runPartial() + axe.finishRun()', () => {\n    before(() => {\n      return new Promise(resolve => {\n        axe.testUtils.awaitNestedLoad(async () => {\n          const runOptions = {\n            runOnly: ['matches-error', 'evaluate-error', 'after-error']\n          };\n          const partialResults = await Promise.all(\n            runPartialRecursive(document, runOptions)\n          );\n          results = await axe.finishRun(partialResults, runOptions);\n          resolve();\n        });\n      });\n    });\n\n    it('should find 0 violations', () => {\n      assert.lengthOf(results.violations, 0);\n    });\n\n    it('should find  0 passes', () => {\n      assert.lengthOf(results.passes, 0);\n    });\n\n    describe('incomplete', () => {\n      it('should find matches-error', () => {\n        const matchesError = results.incomplete.find(\n          result => result.id === 'matches-error'\n        );\n        window.assertIsErrorOccurred(matchesError, {\n          message: 'matches error',\n          target: ['#frame', '#target']\n        });\n      });\n\n      it('should find evaluate-error', () => {\n        const evaluateError = results.incomplete.find(\n          result => result.id === 'evaluate-error'\n        );\n        window.assertIsErrorOccurred(evaluateError, {\n          message: 'evaluate error',\n          target: ['#frame', '#target']\n        });\n      });\n\n      it('should find after-error', () => {\n        const afterError = results.incomplete.find(\n          result => result.id === 'after-error'\n        );\n        window.assertIsErrorOccurred(afterError, {\n          message: 'after error',\n          target: ['#frame', '#target']\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/error-occurred/error-occurred.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>error-occurred test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"target\"></div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"error-ruleset.js\"></script>\n    <script src=\"error-occurred.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/error-occurred/error-occurred.js",
    "content": "describe('error-occurred test', () => {\n  const { runPartialRecursive } = axe.testUtils;\n  let results;\n\n  describe('axe.run()', () => {\n    before(done => {\n      axe.testUtils.awaitNestedLoad(() => {\n        axe.run(\n          {\n            runOnly: ['matches-error', 'evaluate-error', 'after-error']\n          },\n          function (err, r) {\n            assert.isNull(err);\n            results = r;\n            done();\n          }\n        );\n      });\n    });\n\n    it('should find 0 violations', () => {\n      assert.lengthOf(results.violations, 0);\n    });\n\n    it('should find  0 passes', () => {\n      assert.lengthOf(results.passes, 0);\n    });\n\n    describe('incomplete', () => {\n      it('should find matches-error', () => {\n        const matchesError = results.incomplete.find(\n          result => result.id === 'matches-error'\n        );\n        window.assertIsErrorOccurred(matchesError, {\n          message: 'matches error',\n          target: ['#target']\n        });\n      });\n\n      it('should find evaluate-error', () => {\n        const evaluateError = results.incomplete.find(\n          result => result.id === 'evaluate-error'\n        );\n        window.assertIsErrorOccurred(evaluateError, {\n          message: 'evaluate error',\n          target: ['#target']\n        });\n      });\n\n      it('should find after-error', () => {\n        const afterError = results.incomplete.find(\n          result => result.id === 'after-error'\n        );\n        window.assertIsErrorOccurred(afterError, {\n          message: 'after error',\n          target: ['#target']\n        });\n      });\n    });\n  });\n\n  describe('axe.runPartial() + axe.finishRun()', () => {\n    before(() => {\n      return new Promise(resolve => {\n        axe.testUtils.awaitNestedLoad(async () => {\n          const runOptions = {\n            runOnly: ['matches-error', 'evaluate-error', 'after-error']\n          };\n          const partialResults = await Promise.all(\n            runPartialRecursive(document, runOptions)\n          );\n          results = await axe.finishRun(partialResults, runOptions);\n          resolve();\n        });\n      });\n    });\n\n    it('should find 0 violations', () => {\n      assert.lengthOf(results.violations, 0);\n    });\n\n    it('should find  0 passes', () => {\n      assert.lengthOf(results.passes, 0);\n    });\n\n    describe('incomplete', () => {\n      it('should find matches-error', () => {\n        const matchesError = results.incomplete.find(\n          result => result.id === 'matches-error'\n        );\n        window.assertIsErrorOccurred(matchesError, {\n          message: 'matches error',\n          target: ['#target']\n        });\n      });\n\n      it('should find evaluate-error', () => {\n        const evaluateError = results.incomplete.find(\n          result => result.id === 'evaluate-error'\n        );\n        window.assertIsErrorOccurred(evaluateError, {\n          message: 'evaluate error',\n          target: ['#target']\n        });\n      });\n\n      it('should find after-error', () => {\n        const afterError = results.incomplete.find(\n          result => result.id === 'after-error'\n        );\n        window.assertIsErrorOccurred(afterError, {\n          message: 'after error',\n          target: ['#target']\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/error-occurred/error-ruleset.js",
    "content": "window.assertIsErrorOccurred = function (result, { message, target }) {\n  assert.isDefined(result);\n  assert.isDefined(result.error);\n  assert.include(result.error.message, message);\n  assert.isDefined(result.error.method);\n  // errorNode is not included as it can be unsafe to serialize\n  assert.isUndefined(result.error.errorNode);\n  assert.isUndefined(result.errorNode);\n\n  assert.lengthOf(result.nodes, 1);\n  const node = result.nodes[0];\n  assert.lengthOf(node.any, 0);\n  assert.lengthOf(node.all, 0);\n  assert.lengthOf(node.none, 1);\n  assert.equal(node.none[0].id, 'error-occurred');\n  assert.include(node.none[0].message, 'Axe encountered an error');\n  assert.deepEqual(node.none[0].data, result.error);\n  assert.deepEqual(node.target, target);\n  assert.isDefined(node.html);\n};\n\naxe.configure({\n  rules: [\n    {\n      id: 'matches-error',\n      selector: '#target',\n      matches: () => {\n        throw new Error('matches error');\n      },\n      any: ['exists']\n    },\n    {\n      id: 'evaluate-error',\n      selector: '#target',\n      any: ['check-evaluate-error']\n    },\n    {\n      id: 'after-error',\n      selector: '#target',\n      any: ['check-after-error']\n    }\n  ],\n  checks: [\n    {\n      id: 'check-evaluate-error',\n      evaluate: () => {\n        throw new Error('evaluate error');\n      },\n      after: () => {\n        throw new Error('I should not be seen');\n      }\n    },\n    {\n      id: 'check-after-error',\n      evaluate: () => true,\n      after: () => {\n        throw new Error('after error');\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "test/integration/full/error-occurred/frames/error.html",
    "content": "<div id=\"target\"></div>\n\n<script src=\"/axe.js\"></script>\n<script src=\"../error-ruleset.js\"></script>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>frame-tested test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"frame\" src=\"frames/nested-no-axe.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"frame-tested-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-fail.js",
    "content": "describe('frame-tested-fail test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: { type: 'rule', values: ['frame-tested'] },\n          checks: {\n            'frame-tested': { options: { isViolation: true } }\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n    it('should find the failing iframe', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        '#frame',\n        '#fail'\n      ]);\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 2', function () {\n      assert.lengthOf(results.passes, 1);\n      assert.lengthOf(results.passes[0].nodes, 2);\n\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#frame']);\n      assert.deepEqual(results.passes[0].nodes[1].target, ['#frame', '#pass']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-incomplete.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>frame-tested test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"incomplete\" src=\"frames/no-axe.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"frame-tested-incomplete.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-incomplete.js",
    "content": "describe('frame-tested-incomplete test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['frame-tested'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete[0].nodes, 1);\n    });\n    it('should find first iframe', function () {\n      assert.deepEqual(results.incomplete[0].nodes[0].target, ['#incomplete']);\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>frame-tested test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"pass\" src=\"frames/with-axe.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"frame-tested-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frame-tested-pass.js",
    "content": "describe('frame-tested-pass test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['frame-tested'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n    it('should find first iframe', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass']);\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('incomplete', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.incomplete, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/frame-tested/frames/nested-no-axe.html",
    "content": "<!doctype html>\n<html id=\"pass1\">\n  <head>\n    <title>Frame with axe-core</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"pass\" src=\"with-axe.html\"></iframe>\n    <iframe id=\"fail\" src=\"no-axe.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frames/no-axe.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Frame without axe-core</title>\n    <meta charset=\"utf8\" />\n  </head>\n  <body>\n    <h1>Frame without axe-core</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-tested/frames/with-axe.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Frame with axe-core</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1>Frame with axe-core</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-wait-time/frame-wait-time.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>frame-wait-time test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/node_modules/sinon/pkg/sinon.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <main>\n      <iframe\n        id=\"frame\"\n        title=\"frame-wait-time test frame\"\n        src=\"frames/frame.html\"\n      ></iframe>\n    </main>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"frame-wait-time.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/frame-wait-time/frame-wait-time.js",
    "content": "/* global sinon */\n\n// TODO: remove when tests are fixed\ndescribe('frame-wait-time optin', function () {\n  it('works', function () {\n    assert.isTrue(true);\n  });\n});\n\ndescribe.skip('frame-wait-time option', function () {\n  'use strict';\n  var spy;\n  var respondable = axe.utils.respondable;\n\n  before(function (done) {\n    // Fix Function#name on browsers that do not support it (IE):\n    // @see https://stackoverflow.com/a/17056530\n    if (!function f() {}.name) {\n      Object.defineProperty(Function.prototype, 'name', {\n        get: function () {\n          var name = (this.toString().match(/^function\\s*([^\\s(]+)/) || [])[1];\n          // For better performance only parse once, and then cache the\n          // result through a new accessor for repeated access.\n          Object.defineProperty(this, 'name', { value: name });\n          return name;\n        }\n      });\n    }\n\n    axe.testUtils.awaitNestedLoad(function () {\n      done();\n    });\n  });\n\n  beforeEach(function () {\n    // prevent test from running axe inside the iframe multiple times\n    axe.utils.respondable = function (a, b, c, d, callback) {\n      setTimeout(function () {\n        callback();\n      }, 50);\n    };\n    spy = sinon.spy(window, 'setTimeout');\n  });\n\n  afterEach(function () {\n    axe.utils.respondable = respondable;\n    spy.restore();\n  });\n\n  function getTimeoutCall() {\n    var calls = spy.getCalls();\n    var timeoutCall;\n    for (var i = 0; i < calls.length; i++) {\n      var fn = calls[i].args[0];\n      if (fn.name === 'collectResultFramesTimeout') {\n        timeoutCall = calls[i];\n        break;\n      }\n    }\n\n    return timeoutCall;\n  }\n\n  describe('when set', function () {\n    it('should modify the default frame timeout', function (done) {\n      var opts = {\n        frameWaitTime: 1,\n        runOnly: {\n          type: 'rule',\n          values: ['html-has-lang']\n        }\n      };\n      axe.run('#frame', opts, function () {\n        var timeoutCall = getTimeoutCall();\n        assert.exists(timeoutCall, 'FrameTimeout not called');\n        assert.equal(timeoutCall.args[1], 1);\n        done();\n      });\n    });\n  });\n\n  describe('when not set', function () {\n    it('should use the default frame timeout', function (done) {\n      axe.run('#frame', function () {\n        var timeoutCall = getTimeoutCall();\n        assert.exists(timeoutCall, 'FrameTimeout not called');\n        assert.equal(timeoutCall.args[1], 60000);\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/frame-wait-time/frames/frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>frame-wait-time test frame</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1 style=\"opacity: 0.1\">So Dim</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/get-selector/get-selector.js",
    "content": "describe('axe.utils.getSelector', function () {\n  'use strict';\n  before(function () {\n    axe.setup();\n  });\n  it('should work on namespaced elements', function () {\n    var fixture = document.querySelector('#fixture');\n    var node = fixture.firstElementChild;\n    var sel = axe.utils.getSelector(node);\n    var result = document.querySelectorAll(sel);\n    assert.lengthOf(result, 1);\n    assert.equal(result[0], node);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/get-selector/get-selector.xhtml",
    "content": "<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <title>axe.utils.getSelector test</title>\n    <meta charset=\"utf-8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <m:math xmlns:m=\"http://www.w3.org/1998/Math/MathML\"></m:math>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"get-selector.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/frames/heading.html",
    "content": "<!doctype html>\n<html id=\"level1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h3>Heading 3</h3>\n    <iframe src=\"nested-heading.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/frames/nested-heading.html",
    "content": "<!doctype html>\n<html id=\"level1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h4>Heading 4</h4>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/no-heading-page.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe src=\"frames/heading.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"no-heading-page.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/no-heading-page.js",
    "content": "describe('heading-order-partial-context-with-iframe test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { exclude: ['#mocha'] },\n        { runOnly: ['heading-order'] },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  it('should find 2 passes', function () {\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.passes[0].nodes, 2);\n  });\n\n  it('should find 0 violations', function () {\n    assert.lengthOf(results.violations, 0);\n  });\n\n  it('should find 0 incompletes', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context-with-iframe-excluded.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <header>\n      <h1>Foo</h1>\n    </header>\n    <main>\n      <h2>bar</h2>\n    </main>\n    <iframe src=\"frames/heading.html\"></iframe>\n    <footer>\n      <h4>Baz</h4>\n    </footer>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"partial-context-with-iframe-excluded.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context-with-iframe-excluded.js",
    "content": "describe('heading-order-partial-context-with-iframe test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { include: [['header'], ['footer']] },\n        { runOnly: ['heading-order'] },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  it('should find 1 passes', function () {\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.passes[0].nodes, 1);\n  });\n\n  it('should find 1 violation', function () {\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.violations[0].nodes, 1);\n  });\n\n  it('should find 0 incompletes', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context-with-iframe.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <header>\n      <h1>Foo</h1>\n    </header>\n    <main>\n      <h2>bar</h2>\n    </main>\n    <iframe src=\"frames/heading.html\"></iframe>\n    <footer>\n      <h5>Baz</h5>\n    </footer>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"partial-context-with-iframe.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context-with-iframe.js",
    "content": "describe('heading-order-partial-context-with-iframe test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { include: [['header'], ['footer'], ['iframe']] },\n        { runOnly: ['heading-order'] },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  it('should find 4 passes', function () {\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.passes[0].nodes, 4);\n  });\n\n  it('should find 0 violations', function () {\n    assert.lengthOf(results.violations, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <header>\n      <h1>Foo</h1>\n    </header>\n    <main>\n      <h2>bar</h2>\n    </main>\n    <footer>\n      <h3>Baz</h3>\n    </footer>\n    <div id=\"mocha\"></div>\n    <script src=\"partial-context.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/heading-order/partial-context.js",
    "content": "describe('heading-order-partial-context test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.run(\n      { include: [['header'], ['footer']] },\n      { runOnly: ['heading-order'] },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  });\n\n  it('should find 2 passes', function () {\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.passes[0].nodes, 2);\n  });\n\n  it('should find 0 violations', function () {\n    assert.lengthOf(results.violations, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/html-has-lang/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"violation1\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2-a.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"pass2\" lang=\"Elven\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"violation2\" lang=\"\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.html",
    "content": "<!doctype html>\n<html id=\"fail1\" xml:lang=\"fr\">\n  <head>\n    <title>html-has-lang test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n\n      // TODO: remove global helpers\n      helpers = axe._thisWillBeDeletedDoNotUse.helpers;\n    </script>\n  </head>\n\n  <body>\n    <!--\n\t\t\tNote:\n\t\t\tThis rule does not include `iframe` uses matches \"window-is-top.js\"\n\t\t-->\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"html-has-lang-fail-xml-lang.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.js",
    "content": "/**\n * Note:\n * This rule does not include `iframe` uses matches \"window-is-top.js\"\n */\ndescribe('html-has-lang fail test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['html-has-lang'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1 violations', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find #fail1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0 passes', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-fail.html",
    "content": "<!doctype html>\n<html id=\"fail1\">\n  <head>\n    <title>html-has-lang test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n\n      // TODO: remove global helpers\n      helpers = axe._thisWillBeDeletedDoNotUse.helpers;\n    </script>\n  </head>\n\n  <body>\n    <!--\n\t\t\tNote:\n\t\t\tThis rule does not include `iframe` uses matches \"window-is-top.js\"\n\t\t-->\n\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"html-has-lang-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-fail.js",
    "content": "/**\n * Note:\n * This rule does not include `iframe` uses matches \"window-is-top.js\"\n */\ndescribe('html-has-lang fail test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['html-has-lang'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1 violations', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find #fail1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0 passes', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-pass-xhtml.js",
    "content": "/**\n * Note:\n * This rule does not include `iframe` uses matches \"window-is-top.js\"\n */\ndescribe('html-has-lang pass test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['html-has-lang'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0 violations', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-pass-xhtml.xhtml",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html\n  lang=\"klingon\"\n  xml:lang=\"klingon\"\n  id=\"pass1\"\n  xmlns=\"http://www.w3.org/1999/xhtml\"\n>\n  <head>\n    <title>html-has-lang test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <!--\n\t\t\tNote:\n\t\t\tThis rule excludes `iframe`, uses matches \"window-is-top.js\"\n\t\t-->\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"html-has-lang-pass-xhtml.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-pass.html",
    "content": "<!doctype html>\n<html lang=\"klingon\" id=\"pass1\">\n  <head>\n    <title>html-has-lang test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <!--\n\t\t\tNote:\n\t\t\tThis rule excludes `iframe`, uses matches \"window-is-top.js\"\n\t\t-->\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"html-has-lang-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-has-lang/html-has-lang-pass.js",
    "content": "/**\n * Note:\n * This rule does not include `iframe` uses matches \"window-is-top.js\"\n */\ndescribe('html-has-lang pass test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['html-has-lang'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0 violations', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"violation1\" lang=\"@(#$*\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame4\" src=\"level2-b.html\"></iframe>\n    <iframe id=\"frame5\" src=\"level2-c.html\"></iframe>\n    <iframe id=\"frame6\" src=\"level2-d.html\"></iframe>\n    <iframe id=\"frame7\" src=\"level2-e.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"pass1\" lang=\"en\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2-b.html",
    "content": "<!doctype html>\n<html id=\"pass2b\" xml:lang=\"en\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2-c.html",
    "content": "<!doctype html>\n<html id=\"violation2c\" xml:lang=\"unknownLang\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2-d.html",
    "content": "<!doctype html>\n<html id=\"inapplicable2d\" lang=\"\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Empty lang\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2-e.html",
    "content": "<!doctype html>\n<html id=\"inapplicable2e\" xml:lang=\"\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Empty xml:lang\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"violation2\" lang=\"english\">\n  <head>\n    <title>No lang test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/html-lang-valid.html",
    "content": "<!doctype html>\n<html id=\"ignored1\">\n  <head>\n    <title>html-lang-valid test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"html-lang-valid.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/html-lang-valid/html-lang-valid.js",
    "content": "describe('html-lang-valid test', function () {\n  'use strict';\n\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['html-lang-valid'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.violations[0].nodes, 3);\n    });\n\n    it('should find first level iframe', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        '#frame1',\n        '#violation1'\n      ]);\n    });\n\n    it('should find second level iframe', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#frame2',\n        '#violation2'\n      ]);\n    });\n\n    it('should find #violation2c', function () {\n      assert.deepEqual(results.violations[0].nodes[2].target, [\n        '#frame1',\n        '#frame5',\n        '#violation2c'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 2', function () {\n      assert.lengthOf(results.passes[0].nodes, 2);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, [\n        '#frame1',\n        '#frame3',\n        '#pass1'\n      ]);\n    });\n\n    it('should find #pass2b', function () {\n      assert.deepEqual(results.passes[0].nodes[1].target, [\n        '#frame1',\n        '#frame4',\n        '#pass2b'\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/identical-links-same-purpose/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"level1\" lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n\n  <body>\n    <!-- pass  -->\n    <a id=\"pass-inside-frame\" href=\"/home/#/foo\">Pass 1</a>\n\n    <!-- incomplete -->\n    <map name=\"infographic-incomplete\">\n      <area\n        id=\"incomplete-inside-frame\"\n        shape=\"poly\"\n        coords=\"130,147,130,228,6,219,59,107\"\n        href=\"https://developer.mozilla.org/docs/Web/CSS\"\n        target=\"_blank\"\n        title=\"HTML\"\n      />\n    </map>\n    <img\n      usemap=\"#infographic-incomplete\"\n      src=\"https://interactive-examples.mdn.mozilla.net/media/examples/mdn-info2.png\"\n      alt=\"MDN infographic\"\n    />\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/identical-links-same-purpose/page.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>identical-links-same-purpose test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <div id=\"frame-container\">\n      <!-- pass  -->\n      <a id=\"pass-outside-frame\" href=\"/home/#/foo\">Pass 1</a>\n      <iframe id=\"myframe\" src=\"frames/level1.html\"></iframe>\n\n      <!-- incomplete -->\n      <map name=\"infographic-incomplete\">\n        <area\n          id=\"incomplete-outside-frame\"\n          shape=\"poly\"\n          coords=\"130,147,200,107,254,219,130,228\"\n          href=\"https://developer.mozilla.org/docs/Web/HTML\"\n          title=\"HTML\"\n        />\n      </map>\n      <img\n        usemap=\"#infographic-incomplete\"\n        src=\"https://interactive-examples.mdn.mozilla.net/media/examples/mdn-info2.png\"\n        alt=\"MDN infographic\"\n      />\n    </div>\n\n    <!-- removing mocha div as I do not want to filter out these anchors for assertion -->\n    <!-- <div id=\"mocha\"></div> -->\n\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/identical-links-same-purpose/page.js",
    "content": "describe('identical-links-same-purpose test', function () {\n  'use strict';\n\n  var config = {\n    runOnly: {\n      type: 'rule',\n      values: ['identical-links-same-purpose']\n    }\n  };\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n    axe._tree = undefined;\n  });\n\n  it('should find no violations given a selector array', function (done) {\n    axe.run(config, function (err, results) {\n      assert.isNull(err);\n\n      /**\n       * assert `passes`\n       */\n      assert.lengthOf(results.passes, 1, 'passes');\n      assert.lengthOf(results.passes[0].nodes, 1);\n      assert.deepEqual(results.passes[0].nodes[0].target, [\n        '#pass-outside-frame'\n      ]);\n      assert.deepEqual(\n        results.passes[0].nodes[0].all[0].relatedNodes[0].target,\n        ['#myframe', '#pass-inside-frame']\n      );\n\n      /**\n       * assert `incomplete`\n       */\n      assert.lengthOf(results.incomplete, 1, 'incomplete');\n      assert.lengthOf(results.incomplete[0].nodes, 1);\n      assert.deepEqual(results.incomplete[0].nodes[0].target, [\n        '#myframe',\n        '#incomplete-inside-frame'\n      ]);\n      assert.deepEqual(\n        results.incomplete[0].nodes[0].all[0].relatedNodes[0].target,\n        ['#incomplete-outside-frame']\n      );\n\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/incomplete/color-contrast.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Contrast Incomplete test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\" style=\"width: 100%; position: relative\">\n      <div\n        id=\"parent\"\n        style=\"\n          height: 50px;\n          width: 50px;\n          background-color: white;\n          position: relative;\n          z-index: 5;\n        \"\n      >\n        <div\n          id=\"shifted\"\n          style=\"\n            position: absolute;\n            top: -10px;\n            height: 50px;\n            width: 50px;\n            z-index: 15;\n          \"\n        >\n          <img src=\"some-img.png\" width=\"50\" height=\"50\" />\n        </div>\n        <div\n          id=\"target\"\n          style=\"\n            position: relative;\n            top: 1px;\n            height: 20px;\n            width: 50px;\n            z-index: 25;\n            background: rgba(0, 125, 0, 0.5);\n          \"\n        >\n          Text\n        </div>\n      </div>\n      <div style=\"background-image: linear-gradient(red, orange); color: #fff\">\n        Text over gradient\n      </div>\n\n      <style type=\"text/css\">\n        #pseudoOnButton::before {\n          background-color: #000;\n          content: '';\n          position: absolute;\n          width: 100%;\n          height: 100%;\n          z-index: -1;\n        }\n      </style>\n      <button\n        id=\"pseudoOnButton\"\n        style=\"\n          position: relative;\n          background-color: transparent;\n          color: #ffffaa;\n        \"\n      >\n        Button with ::before\n      </button>\n    </div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"color-contrast.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/incomplete/color-contrast.js",
    "content": "describe('contrast cantTell test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        '#fixture',\n        {\n          runOnly: { type: 'rule', values: ['color-contrast'] }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('passes/violations', function () {\n    it('should find 0 passes', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n    it('should find 0 violations', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('incomplete data', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.incomplete, 1);\n    });\n\n    describe('indicating specific reasons', function () {\n      it('works for image nodes', function () {\n        var resultNodes = results.incomplete[0].nodes;\n        resultNodes[0].any.forEach(function (check) {\n          assert.match(check.message, /image node/);\n        });\n      });\n\n      it('works for background gradients', function () {\n        var resultNodes = results.incomplete[0].nodes;\n        resultNodes[1].any.forEach(function (check) {\n          assert.match(check.message, /background gradient/);\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/incomplete/th-has-data-cells.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>th-has-data-cells incomplete test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <table id=\"table1\">\n        <tr>\n          <th>hi</th>\n        </tr>\n        <tr>\n          <th>hi</th>\n        </tr>\n      </table>\n\n      <table id=\"table3\">\n        <tr>\n          <td>axe</td>\n          <td role=\"columnheader\">AXE</td>\n        </tr>\n      </table>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"th-has-data-cells.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/incomplete/th-has-data-cells.js",
    "content": "describe('th-has-data-cells cantTell test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        '#fixture',\n        {\n          runOnly: { type: 'rule', values: ['th-has-data-cells'] }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('passes/violations', function () {\n    it('should find 0 passes', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n    it('should find 0 violations', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('incomplete data', function () {\n    it('should be incomplete for missing or empty data cells', function () {\n      var resultNodes = results.incomplete[0].nodes;\n      assert.lengthOf(resultNodes, 2);\n      resultNodes[0].any.forEach(function (check) {\n        assert.match(check.message, 'Table data cells are missing or empty');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/is-xhtml/is-xhtml.js",
    "content": "describe('axe.utils.isXHTML', function () {\n  'use strict';\n\n  it('should return true on any document that is XHTML', function () {\n    assert.isTrue(axe.utils.isXHTML(document));\n  });\n});\n"
  },
  {
    "path": "test/integration/full/is-xhtml/is-xhtml.xhtml",
    "content": "<html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <title>axe.utils.isXHTML test</title>\n    <meta charset=\"utf-8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\"></div>\n    <div id=\"mocha\"></div>\n    <script src=\"is-xhtml.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/isolated-env/frames/focusable.html",
    "content": "<!doctype html>\n<html id=\"focusable\">\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <button>Click</button>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/isolated-env/frames/isolated.html",
    "content": "<!doctype html>\n<html>\n  <body>\n    <script>\n      window\n        .fetch('/axe.js')\n        .then(function (response) {\n          return response.text();\n        })\n        .then(function (axeSource) {\n          // add axe to the \"this\" object instead of\n          // window so we can ensure window and document\n          // do not exist for after and reporter\n          // functions\n          window.eval(`\n            var win = window;\n            (function() {\n              var window = undefined;\n              var document = undefined;\n\n              ${axeSource}\n\n              win.axeConfigure = this.axe.configure;\n              win.axeFinishRun = this.axe.finishRun;\n            }).call({\n              document: document,\n              getComputedStyle: function () {}\n            });\n          `);\n          window.parent.postMessage('axe-loaded');\n        });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/isolated-env/isolated-env.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"en\">\n  <head>\n    <title>all rules test</title>\n    <meta charset=\"utf8\" />\n    <meta http-equiv=\"refresh\" content=\"foo\" />\n    <meta name=\"viewport\" content=\"maximum-scale=2\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <a href=\"#fail1-tgt\" style=\"position: absolute; margin: -10000px\"\n        >bad link 1</a\n      >\n      <banner></banner>\n      <main>\n        <div accesskey=\"B\"></div>\n        <map>\n          <area href=\"#\" id=\"pass1\" alt=\"monkeys\" />\n        </map>\n        <div aria-label=\"foo\">Foo</div>\n        <div role=\"contentinfo\"></div>\n        <div role=\"link\">Home</div>\n        <div role=\"dialog\" aria-label=\"Cookies\"></div>\n        <p aria-hidden=\"true\">Some text</p>\n        <div role=\"spinbutton\" aria-label=\"foo\"></div>\n        <div role=\"meter\" title=\"foo\"></div>\n        <div role=\"progressbar\" title=\"foo\"></div>\n        <div role=\"list\">\n          <div role=\"listitem\">Item 1</div>\n        </div>\n        <button aria-roledescription=\"my button\">button</button>\n        <div role=\"text\">Some text<span> and some more text</span></div>\n        <div role=\"checkbox\">Newspaper</div>\n        <div role=\"tooltip\">Copy this content</div>\n        <div role=\"tree\">\n          <div role=\"treeitem\">Item</div>\n        </div>\n        <div role=\"tab\">Tab Name</div>\n        <audio id=\"caption\"><track kind=\"captions\" /></audio>\n        <input autocomplete=\"username\" />\n        <p id=\"fail1\" style=\"line-height: 1.5 !important\">Banana error</p>\n        <p><blink>text</blink></p>\n        <button id=\"text\">Name</button>\n        <dl>\n          <dt>foo</dt>\n          <dd>bar</dd>\n        </dl>\n        <div id=\"foo\"></div>\n        <h1>Ok</h1>\n        <h2>Ok</h2>\n        <table>\n          <tr>\n            <th scope=\"col\">Ok</th>\n          </tr>\n        </table>\n\n        <img src=\"img.jpg\" alt=\"\" aria-braillelabel=\"my image\" />\n        <video><track kind=\"captions\" /></video>\n        <svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          role=\"img\"\n          width=\"100\"\n          height=\"100\"\n        >\n          <title>I am a circle</title>\n          <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n        </svg>\n        <div lang=\"en\">English</div>\n        <object\n          title=\"This object has text\"\n          data=\"data:text/html,Object%20content\"\n        ></object>\n        <li role=\"presentation\" aria-label=\"My Heading\">Hello</li>\n        <div role=\"img\" aria-label=\"blah\"></div>\n        <div style=\"overflow-y: scroll; height: 5px\">\n          <input type=\"text\" />\n        </div>\n        <select aria-label=\"foo\"></select>\n        <img ismap src=\"image.jpg\" />\n        <p tabindex=\"-1\">Paragraph.</p>\n        <input type=\"button\" />\n        <input type=\"image\" src=\"img.jpg\" />\n        <marquee>This content is inside a marquee.</marquee>\n        <div role=\"navigation\">\n          <div role=\"banner\"></div>\n          <div role=\"complementary\"></div>\n        </div>\n        <span id=\"fail1\" class=\"fail1\"></span>\n        <button id=\"fail1\"></button>\n        <span id=\"pass1\"></span>\n        <button id=\"pass2\"></button>\n        <details>\n          <summary>Hello world</summary>\n          <p>Some text</p>\n        </details>\n        <div aria-labelledby=\"fail1 pass1 pass2\"></div>\n        <audio\n          id=\"incomplete1\"\n          src=\"/test/assets/moon-speech.mp3\"\n          autoplay=\"true\"\n        ></audio>\n      </main>\n      <footer></footer>\n\n      <script src=\"/test/testutils.js\"></script>\n      <script src=\"isolated-env.js\"></script>\n\n      <iframe\n        id=\"isolated-frame\"\n        title=\"foo\"\n        src=\"frames/isolated.html\"\n      ></iframe>\n      <iframe\n        id=\"focusable-iframe\"\n        title=\"bar\"\n        src=\"frames/focusable.html\"\n        tabindex=\"-1\"\n      ></iframe>\n      <p>Paragraph with a <a href=\"#\">link</a>.</p>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/isolated-env/isolated-env.js",
    "content": "/* global chai */\nvar messages = [];\nwindow.addEventListener('message', function (msg) {\n  messages.push(msg.data);\n});\n\ndescribe('isolated-env test', function () {\n  'use strict';\n  var fixture = document.querySelector('#fixture');\n  var origPartialResults;\n  var partialResults;\n  var win;\n\n  // just a nicer assertion error rather than just doing\n  // done(err)\n  function doesNotThrow(err, done) {\n    if (err instanceof chai.AssertionError) {\n      return done(err);\n    }\n\n    var error = new chai.AssertionError(\n      \"expected [Function] to not throw an error but '\" +\n        err.toString() +\n        \"' was thrown\"\n    );\n    done(error);\n  }\n\n  function setEmptyReporter() {\n    win.axeConfigure({\n      reporter: function (results, options, callback) {\n        if (typeof options === 'function') {\n          callback = options;\n          options = {};\n        }\n        callback(results);\n      }\n    });\n  }\n\n  before(function (done) {\n    var nestedLoadPromise = new Promise(function (resolve, reject) {\n      axe.testUtils.awaitNestedLoad(resolve, reject);\n    });\n\n    var isloadedPromise = new Promise(function (resolve, reject) {\n      if (messages.includes('axe-loaded')) {\n        resolve();\n      } else {\n        window.addEventListener('message', function (msg) {\n          if (msg.data === 'axe-loaded') {\n            resolve();\n          }\n        });\n      }\n\n      setTimeout(function () {\n        reject(new Error('axe-loaded message not called'));\n      }, 5000);\n    });\n\n    Promise.all([nestedLoadPromise, isloadedPromise])\n      .then(function () {\n        win = fixture.querySelector('#isolated-frame').contentWindow;\n        var focusableFrame = fixture.querySelector('#focusable-iframe');\n\n        // trigger frame-focusable-content rule\n        var iframePromise = focusableFrame.contentWindow.axe.runPartial({\n          include: [],\n          exclude: [],\n          initiator: false,\n          focusable: false,\n          size: { width: 10, height: 10 }\n        });\n\n        Promise.all([axe.runPartial(), iframePromise])\n          .then(function (r) {\n            origPartialResults = r;\n            done();\n          })\n          .catch(done);\n      })\n      .catch(done);\n  });\n\n  beforeEach(function () {\n    // calling axe.finishRun mutates the partial results\n    // object and prevents calling finishRun again with\n    // the same object\n    partialResults = axe.utils.clone(origPartialResults);\n\n    if (win.axeConfigure) {\n      win.axeConfigure({ reporter: 'v1' });\n    }\n  });\n\n  it('successfully isolates axe object in iframe', function () {\n    assert.isUndefined(win.axe);\n    assert.isDefined(win.axeFinishRun);\n    assert.isDefined(win.axeConfigure);\n  });\n\n  it('after methods do not error by calling window or DOM methods', function (done) {\n    setEmptyReporter();\n\n    win\n      .axeFinishRun(partialResults)\n      .then(function (results) {\n        assert.isDefined(results);\n        done();\n      })\n      .catch(function (err) {\n        doesNotThrow(err, done);\n      });\n  });\n\n  it('runs all rules and after methods', function (done) {\n    win\n      .axeFinishRun(partialResults)\n      .then(function (results) {\n        assert.lengthOf(results.inapplicable, 0);\n        done();\n      })\n      .catch(function (err) {\n        doesNotThrow(err, done);\n      });\n  });\n\n  describe('reporters', function () {\n    var reporters = axe._thisWillBeDeletedDoNotUse.public.reporters;\n    Object.keys(reporters).forEach(function (reporterName) {\n      it(\n        reporterName +\n          ' reporter does not error by calling window or DOM methods',\n        function (done) {\n          win.axeConfigure({\n            reporter: reporterName\n          });\n\n          win\n            .axeFinishRun(partialResults)\n            .then(function (results) {\n              assert.isDefined(results);\n              done();\n            })\n            .catch(function (err) {\n              doesNotThrow(err, done);\n            });\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/jquery/run-include-exclude.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>jQuery include test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <div id=\"target\">\n        <ol role=\"dinosaur\">\n          <div>\n            <a href=\"#somethin\">Stuff</a>\n          </div>\n        </ol>\n        <ul role=\"mary-poppins\">\n          <span>Different stuff</span>\n        </ul>\n      </div>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/node_modules/jquery/dist/jquery.js\"></script>\n    <script src=\"run-include-exclude.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/jquery/run-include-exclude.js",
    "content": "/*global $ */\n\ndescribe('jQuery object with axe.run', function () {\n  'use strict';\n\n  var config = { runOnly: { type: 'rule', values: ['aria-roles'] } };\n\n  describe('include', function () {\n    it('should find violations', function (done) {\n      var target = $('#target')[0];\n      axe.run({ include: [target] }, config, function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      });\n    });\n  });\n\n  describe('exclude', function () {\n    it('should find no violations', function (done) {\n      var target = $('#target')[0];\n      axe.run({ exclude: [target] }, config, function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/jquery/run-object.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>jQuery object test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <div role=\"list\" id=\"target\">\n        <ol role=\"listitem\">\n          <div>\n            <a href=\"#somethin\">Stuff</a>\n          </div>\n        </ol>\n        <ul role=\"listitem\">\n          <span>Different stuff</span>\n        </ul>\n      </div>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/node_modules/jquery/dist/jquery.js\"></script>\n    <script src=\"run-object.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/jquery/run-object.js",
    "content": "/*global $ */\n\ndescribe('jQuery object as axe.run context', function () {\n  'use strict';\n  var config = { runOnly: { type: 'rule', values: ['aria-roles'] } };\n  it('should find no violations', function (done) {\n    var fixture = $('#fixture');\n    axe.run(fixture, config, function (err, results) {\n      assert.isNull(err);\n      assert.lengthOf(results.violations, 0, 'violations');\n      assert.lengthOf(results.passes, 1, 'passes');\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/frames/level1-fail.html",
    "content": "<!doctype html>\n<html id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should fail, too</p>\n    <div role=\"complementary\">\n      <header>\n        <p>This banner landmark is in a complementary landmark</p>\n      </header>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass, too</p>\n\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <header>\n      <p>This banner content is not within another landmark</p>\n    </header>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass</p>\n    <p></p>\n    <main>\n      <header>\n        <p>This header is in a main landmark and should be ignored</p>\n      </header>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/landmark-banner-is-top-level-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation1\">\n  <head>\n    <title>landmark-banner-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <div role=\"banner\">\n        <p>This is going to fail</p>\n      </div>\n    </div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-banner-is-top-level-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/landmark-banner-is-top-level-fail.js",
    "content": "describe('landmark-banner-is-top-level test fail', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-banner-is-top-level'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find 2 nodes', function () {\n      assert.lengthOf(results.violations[0].nodes, 2);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/landmark-banner-is-top-level-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>landmark-banner-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <div role=\"banner\">\n      <p>This banner content is not within another landmark</p>\n    </div>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-banner-is-top-level-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-banner-is-top-level/landmark-banner-is-top-level-pass.js",
    "content": "describe('landmark-banner-is-top-level test pass', function () {\n  'use strict';\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-banner-is-top-level'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.passes[0].nodes, 2);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/frames/level1-fail.html",
    "content": "<!doctype html>\n<html id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should fail, too</p>\n    <nav>\n      <div role=\"complementary\">\n        <p>This complementary landmark is in a main landmark</p>\n      </div>\n    </nav>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass, too</p>\n\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <header>\n      <p>This banner content is not within another landmark</p>\n    </header>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass</p>\n    <p></p>\n    <aside>\n      <p>This aside is top level and should be ignored</p>\n    </aside>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/landmark-complementary-is-top-level-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation1\">\n  <head>\n    <title>landmark-complementary-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <div role=\"complementary\">\n        <p>This is going to fail</p>\n      </div>\n    </div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-complementary-is-top-level-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/landmark-complementary-is-top-level-fail.js",
    "content": "describe('landmark-complementary-is-top-level test fail', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-complementary-is-top-level']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find 2 nodes', function () {\n      assert.lengthOf(results.violations[0].nodes, 2);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/landmark-complementary-is-top-level-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>landmark-complementary-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <div role=\"banner\">\n      <p>This banner content is not within another landmark</p>\n    </div>\n    <aside>\n      <p>This aside has an implicit role complementary</p>\n    </aside>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n    <div role=\"main\">\n      <div role=\"complementary\">\n        <p>This div has role complementary></p>\n      </div>\n    </div>\n\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-complementary-is-top-level-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-complementary-is-top-level/landmark-complementary-is-top-level-pass.js",
    "content": "describe('landmark-complementary-is-top-level test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-complementary-is-top-level']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 5', function () {\n      assert.lengthOf(results.passes[0].nodes, 5);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/frames/level1-fail.html",
    "content": "<!doctype html>\n<html id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should fail, too</p>\n    <div role=\"complementary\">\n      <footer>\n        <p>This footer is in a complementary landmark</p>\n      </footer>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass, too</p>\n\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <footer>\n      <p>This footer is not within another landmark</p>\n    </footer>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass</p>\n    <p></p>\n    <main>\n      <footer>\n        <p>This footer is in a main landmark should be ignored</p>\n      </footer>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/landmark-contentinfo-is-top-level-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation1\">\n  <head>\n    <title>landmark-contentinfo-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <div role=\"contentinfo\">\n        <p>This is going to fail</p>\n      </div>\n    </div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-contentinfo-is-top-level-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/landmark-contentinfo-is-top-level-fail.js",
    "content": "describe('landmark-contentinfo-is-top-level test fail', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-contentinfo-is-top-level']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find 2 nodes', function () {\n      assert.lengthOf(results.violations[0].nodes, 2);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/landmark-contentinfo-is-top-level-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>landmark-contentinfo-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <div role=\"contentinfo\">\n      <p>This contentinfo is not within another landmark</p>\n    </div>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-contentinfo-is-top-level-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-contentinfo-is-top-level/landmark-contentinfo-is-top-level-pass.js",
    "content": "describe('landmark-contentinfo-is-top-level test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-contentinfo-is-top-level']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 2', function () {\n      assert.lengthOf(results.passes[0].nodes, 2);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/frames/level1-fail.html",
    "content": "<!doctype html>\n<html id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should fail, too</p>\n    <div role=\"complementary\">\n      <div role=\"main\">\n        <p>This main landmark is in a complementary landmark</p>\n      </div>\n    </div>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2-a.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe should pass, too</p>\n\n    <div role=\"banner\">\n      <p>This div has role banner</p>\n    </div>\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <div role=\"main\">\n      <p>This main content is not within another landmark</p>\n    </div>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"violation4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe is also a violation</p>\n    <div role=\"navigation\">\n      <main>\n        <p>This main landmark is in a navigation landmark</p>\n      </main>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/frames/level2.html",
    "content": "<!doctype html>\n<html id=\"violation3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>This iframe is another violation</p>\n    <p></p>\n    <div role=\"search\">\n      <main>\n        <p>This main landmark is in a search landmark</p>\n      </main>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/landmark-main-is-top-level-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation1\">\n  <head>\n    <title>landmark-main-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"navigation\">\n      <div role=\"main\">\n        <p>This is going to fail</p>\n      </div>\n    </div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-main-is-top-level-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/landmark-main-is-top-level-fail.js",
    "content": "describe('landmark-main-is-top-level test fail', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-main-is-top-level'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find 4 nodes', function () {\n      assert.lengthOf(results.violations[0].nodes, 4);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/landmark-main-is-top-level-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>landmark-main-is-top-level test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"banner\">\n      <p>This div has role banner</p>\n    </div>\n    <div role=\"navigation\">\n      <p>This div has role navigation</p>\n    </div>\n    <main>\n      <p>This main content is not within another landmark</p>\n    </main>\n    <div role=\"complementary\">\n      <p>This div has role complementary</p>\n    </div>\n    <div role=\"search\">\n      <p>This div has role search</p>\n    </div>\n    <div role=\"form\">\n      <p>This div has role form</p>\n      <p></p>\n    </div>\n\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-main-is-top-level-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-main-is-top-level/landmark-main-is-top-level-pass.js",
    "content": "describe('landmark-main-is-top-level test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-main-is-top-level'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 2', function () {\n      assert.lengthOf(results.passes[0].nodes, 2);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <header id=\"fail2\">Header 1</header>\n    <header>Header 2</header>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <header id=\"pass2\">Top level header</header>\n    <article>\n      <header>Header in article</header>\n    </article>\n    <aside>\n      <header>Header in aside</header>\n    </aside>\n    <main>\n      <header>Header in main landmark</header>\n    </main>\n    <nav>\n      <header>Header in nav</header>\n    </nav>\n    <section>\n      <header>Header in section</header>\n    </section>\n    <div role=\"article\">\n      <header>Header in role=article</header>\n    </div>\n    <div role=\"complementary\">\n      <header>Header in role=complementary</header>\n    </div>\n    <div role=\"main\">\n      <header>Header in role=main landmark</header>\n    </div>\n    <div role=\"navigation\">\n      <header>Header in role=navigation</header>\n    </div>\n    <div role=\"region\">\n      <header>Header in role=region</header>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main>\n      <div role=\"banner\" id=\"fail3\">\n        Div 1 with role banner in main landmark\n      </div>\n      <div role=\"banner\">Div 2 with role banner in main landmark</div>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/landmark-no-duplicate-banner-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"banner\" id=\"fail1\">Div 1 with role of \"banner\"</div>\n    <div role=\"banner\">Div 2 with role of \"banner\"</div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-banner-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/landmark-no-duplicate-banner-fail.js",
    "content": "describe('landmark-no-duplicate-banner test failure', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-no-duplicate-banner'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.violations[0].nodes, 3);\n    });\n\n    it('should find #fail1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n\n    it('should find #frame1, #fail2', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#fail2'\n      ]);\n    });\n\n    it('should find #frame1, #frame2, #fail3', function () {\n      assert.deepEqual(results.violations[0].nodes[2].target, [\n        '#frame1',\n        '#frame2',\n        '#fail3'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/landmark-no-duplicate-banner-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No banner landmarks</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-banner-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-banner/landmark-no-duplicate-banner-pass.js",
    "content": "describe('landmark-no-duplicate-banner test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-no-duplicate-banner'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #frame1, #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, [\n        '#frame1',\n        '#pass2'\n      ]);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <footer id=\"fail2\">Footer 1</footer>\n    <footer>Footer 2</footer>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <footer id=\"pass2\">Top level footer</footer>\n    <article>\n      <footer>Footer in article</footer>\n    </article>\n    <aside>\n      <footer>Footer in aside</footer>\n    </aside>\n    <main>\n      <footer>Footer in main landmark</footer>\n    </main>\n    <nav>\n      <footer>Footer in nav</footer>\n    </nav>\n    <section>\n      <footer>Footer in section</footer>\n    </section>\n    <div role=\"article\">\n      <footer>Footer in role=article</footer>\n    </div>\n    <div role=\"complementary\">\n      <footer>Footer in role=complementary</footer>\n    </div>\n    <div role=\"main\">\n      <footer>Footer in role=main landmark</footer>\n    </div>\n    <div role=\"navigation\">\n      <footer>Footer in role=navigation</footer>\n    </div>\n    <div role=\"region\">\n      <footer>Footer in role=region</footer>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main>\n      <div role=\"contentinfo\" id=\"fail3\">\n        Div 1 with role contentinfo in main landmark\n      </div>\n      <div role=\"contentinfo\">Div 2 with role contentinfo in main landmark</div>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/landmark-no-duplicate-contentinfo-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"contentinfo\" id=\"fail1\">Div 1 with role of \"contentinfo\"</div>\n    <div role=\"contentinfo\">Div 2 with role of \"contentinfo\"</div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-contentinfo-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/landmark-no-duplicate-contentinfo-fail.js",
    "content": "describe('landmark-no-more-than-one-contentinfo test failure', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-no-duplicate-contentinfo']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.violations[0].nodes, 3);\n    });\n\n    it('should find #fail1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n\n    it('should find #frame1, #fail2', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#fail2'\n      ]);\n    });\n\n    it('should find #frame1, #frame2, #fail3', function () {\n      assert.deepEqual(results.violations[0].nodes[2].target, [\n        '#frame1',\n        '#frame2',\n        '#fail3'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/landmark-no-duplicate-contentinfo-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No contentinfo landmarks</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-contentinfo-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-contentinfo/landmark-no-duplicate-contentinfo-pass.js",
    "content": "describe('landmark-no-more-than-one-contentinfo test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['landmark-no-duplicate-contentinfo']\n          }\n        },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #frame1, #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, [\n        '#frame1',\n        '#pass2'\n      ]);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main id=\"fail2\">Main 1</main>\n    <main>Main 2</main>\n    <iframe id=\"frame2\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main id=\"pass2\">Top level main</main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div role=\"main\" id=\"fail3\">Div 1 with role main in main landmark</div>\n    <div role=\"main\">Div 2 with role main in main landmark</div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/landmark-no-duplicate-main-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"main\" id=\"fail1\">Div 1 with role of \"main\"</div>\n    <div role=\"main\">Div 2 with role of \"main\"</div>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-main-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/landmark-no-duplicate-main-fail.js",
    "content": "describe('landmark-no-duplicate-main test failure', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-no-duplicate-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.violations[0].nodes, 3);\n    });\n\n    it('should find #fail1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n\n    it('should find #frame1, #fail2', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#fail2'\n      ]);\n    });\n\n    it('should find #frame1, #frame2, #fail3', function () {\n      assert.deepEqual(results.violations[0].nodes[2].target, [\n        '#frame1',\n        '#frame2',\n        '#fail3'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/landmark-no-duplicate-main-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No main landmarks</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-no-duplicate-main-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-no-duplicate-main/landmark-no-duplicate-main-pass.js",
    "content": "describe('landmark-no-duplicate-main test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-no-duplicate-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #frame1, #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, [\n        '#frame1',\n        '#pass2'\n      ]);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No main content here either</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No main content here either</p>\n    <iframe id=\"frame2\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/frames/level2-a.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main>\n      <p>Main landmark created with main tag</p>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No main content in this iframe</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"fail1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No main content here</p>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-one-main-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-fail.js",
    "content": "describe('landmark-one-main test failure', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-one-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations[0].nodes, 2);\n    });\n\n    it('should find #frame1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n\n    it('should find #frame1, #violation2', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#violation2'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass1.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No main content</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-one-main-pass1.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass1.js",
    "content": "describe('landmark-one-main test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-one-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 4', function () {\n      assert.lengthOf(results.passes[0].nodes, 4);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n\n    it('should find #frame1, #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[1].target, [\n        '#frame1',\n        '#pass2'\n      ]);\n    });\n\n    it('should find #frame1, #frame2, #pass3', function () {\n      assert.deepEqual(results.passes[0].nodes[2].target, [\n        '#frame1',\n        '#frame2',\n        '#pass3'\n      ]);\n    });\n\n    it('should find #frame1, #frame3, #pass4', function () {\n      assert.deepEqual(results.passes[0].nodes[3].target, [\n        '#frame1',\n        '#frame3',\n        '#pass4'\n      ]);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass2.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main role=\"main\">\n      <p>Main content</p>\n    </main>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-one-main-pass2.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass2.js",
    "content": "describe('landmark-one-main test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-one-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass3.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <style type=\"text/css\">\n      .sr-only {\n        border: 0;\n        clip: rect(0 0 0 0);\n        clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        height: 1px;\n        margin: -1px;\n        overflow: hidden;\n        padding: 0;\n        position: absolute;\n        width: 1px;\n        white-space: nowrap;\n      }\n    </style>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main role=\"main\" class=\"sr-only\">\n      <p>Main content</p>\n    </main>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-one-main-pass3.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass3.js",
    "content": "describe('landmark-one-main test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-one-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass4.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main aria-hidden=\"true\">\n      <h1>Level one heading!</h1>\n    </main>\n    <div role=\"dialog\">Modal open</div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"landmark-one-main-pass4.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/landmark-one-main/landmark-one-main-pass4.js",
    "content": "describe('landmark-one-main test pass', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['landmark-one-main'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass4', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass4']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/list/li-role.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>li role test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <div role=\"list\" id=\"target\">\n        <li>Thing</li>\n      </div>\n\n      <li role=\"presentation\">Just a presentation thing</li>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"li-role.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/list/li-role.js",
    "content": "describe('li overriden with ARIA role', function () {\n  'use strict';\n  it('should find no matching violations and one pass', function (done) {\n    axe.run(\n      '#fixture',\n      { runOnly: { type: 'rule', values: ['listitem'] } },\n      function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 1, 'passes');\n        assert.lengthOf(results.passes[0].nodes, 1, 'ARIA container');\n        done();\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/list/list-role.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>list role test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"fixture\">\n      <div role=\"list\" id=\"target\">\n        <ol role=\"listitem\">\n          <div>\n            <a href=\"#somethin\">Stuff</a>\n          </div>\n        </ol>\n        <ul role=\"listitem\">\n          <span>Different stuff</span>\n        </ul>\n      </div>\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"list-role.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/list/list-role.js",
    "content": "describe('list overriden with ARIA role', function () {\n  'use strict';\n  it('should find no matches', function (done) {\n    axe.run(\n      '#fixture',\n      { runOnly: { type: 'rule', values: ['list'] } },\n      function (err, results) {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        done();\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-fail.js",
    "content": "describe('meta-refresh fail', function () {\n  'use strict';\n\n  it('should be a violation', function (done) {\n    axe.run({ runOnly: 'meta-refresh' }, function (err, results) {\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        assert.lengthOf(results.inapplicable, 0, 'inapplicable');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-fail1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh fail 1</title>\n    <meta http-equiv=\"refresh\" content=\"10 https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-inapplicable.js",
    "content": "describe('meta-refresh inapplicable', function () {\n  'use strict';\n\n  it('should be inapplicable', function (done) {\n    axe.run({ runOnly: 'meta-refresh' }, function (err, results) {\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        assert.lengthOf(results.inapplicable, 1, 'inapplicable');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-inapplicable1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh inapplicable 1</title>\n    <!-- no content attribute -->\n    <meta http-equiv=\"refresh\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-inapplicable2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh inapplicable 2</title>\n    <!-- no http-equiv=\"refresh\" attribute -->\n    <meta http-equiv=\"something-else\" content=\"5\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-pass.js",
    "content": "describe('meta-refresh pass', function () {\n  'use strict';\n\n  it('should pass', function (done) {\n    axe.run({ runOnly: 'meta-refresh' }, function (err, results) {\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 1, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-pass1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh pass 1</title>\n    <!-- \"-10\" is not valid, this will be ignored -->\n    <meta http-equiv=\"refresh\" content=\"-10 https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh/meta-refresh-pass2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh pass 1</title>\n    <!-- \"90000\" is greater than the minimum 20 hours (72000 seconds) -->\n    <meta http-equiv=\"refresh\" content=\"90000; https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-fail.js",
    "content": "describe('meta-refresh-no-exceptions fail', function () {\n  'use strict';\n\n  it('should be a violation', function (done) {\n    axe.run({ runOnly: 'meta-refresh-no-exceptions' }, function (err, results) {\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 1, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        assert.lengthOf(results.inapplicable, 0, 'inapplicable');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-fail1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh-no-exceptions fail 1</title>\n    <!-- Greater than 20 hour is still not allowed at Level AAA -->\n    <meta http-equiv=\"refresh\" content=\"90000; https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-inapplicable.js",
    "content": "describe('meta-refresh-no-exceptions inapplicable', function () {\n  'use strict';\n\n  it('should be inapplicable', function (done) {\n    axe.run({ runOnly: 'meta-refresh-no-exceptions' }, function (err, results) {\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 0, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        assert.lengthOf(results.inapplicable, 1, 'inapplicable');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-inapplicable1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh-no-exceptions inapplicable 1</title>\n    <!-- no content attribute -->\n    <meta http-equiv=\"refresh\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-inapplicable2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh-no-exceptions inapplicable 2</title>\n    <!-- no http-equiv=\"refresh\" attribute -->\n    <meta http-equiv=\"something-else\" content=\"5\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass.js",
    "content": "describe('meta-refresh-no-exceptions pass', function () {\n  'use strict';\n\n  it('should pass', function (done) {\n    axe.run({ runOnly: 'meta-refresh-no-exceptions' }, function (err, results) {\n      console.log(results);\n      try {\n        assert.isNull(err);\n        assert.lengthOf(results.violations, 0, 'violations');\n        assert.lengthOf(results.passes, 1, 'passes');\n        assert.lengthOf(results.incomplete, 0, 'passes');\n        done();\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh-no-exceptions pass 1</title>\n    <!-- invalid, but will be caught by meta-refresh -->\n    <meta http-equiv=\"refresh\" content=\"10 https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/meta-refresh-no-exceptions/meta-refresh-pass2.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>Meta-refresh-no-exceptions pass 2</title>\n    <!-- \"-10\" is not valid, this will be ignored -->\n    <meta http-equiv=\"refresh\" content=\"-10 https://deque.com/\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"meta-refresh-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/no-autoplay-audio/no-autoplay-audio.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <div id=\"fixture\">\n      <!-- Passed  -->\n      <!-- pass 1 -->\n      <audio\n        id=\"pass1\"\n        src=\"/test/assets/moon-speech.mp3\"\n        autoplay=\"true\"\n        controls\n      ></audio>\n      <!-- pass 2 -->\n      <video id=\"pass2\" autoplay=\"true\" controls>\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n      <!-- pass 3  -->\n      <audio\n        id=\"pass3\"\n        src=\"/test/assets/moon-speech.mp3#t=25\"\n        autoplay=\"true\"\n      ></audio>\n      <!-- pass 4 -->\n      <video id=\"pass4\" autoplay=\"true\">\n        <source src=\"/test/assets/video.webm#t=8,10\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=8,10\" type=\"video/mp4\" />\n      </video>\n      <!-- pass 5  -->\n      <audio\n        id=\"pass5\"\n        src=\"/test/assets/moon-speech.mp3\"\n        autoplay\n        controls\n      ></audio>\n      <!-- pass 6 -->\n      <video id=\"pass6\" autoplay=\"true\" loop controls>\n        <source src=\"/test/assets/video.webm#t=8,10\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=8,10\" type=\"video/mp4\" />\n      </video>\n      <!-- pass 7  -->\n      <audio\n        id=\"pass7\"\n        src=\"/test/assets/moon-speech.mp3\"\n        autoplay\n        loop\n        controls\n      ></audio>\n\n      <!-- Incomplete  -->\n      <!-- incomplete 1 -->\n      <audio\n        id=\"incomplete1\"\n        src=\"/test/assets/moon-speech.mp3\"\n        autoplay=\"true\"\n      ></audio>\n      <!-- incomplete 2 -->\n      <video id=\"incomplete2\" autoplay=\"true\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n      <!-- incomplete 3 -->\n      <video id=\"incomplete3\" autoplay=\"true\" loop=\"true\">\n        <source src=\"/test/assets/video.webm#t=8,10\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4#t=8,10\" type=\"video/mp4\" />\n      </video>\n      <!-- incomplete 4  -->\n      <audio\n        id=\"incomplete4\"\n        src=\"/test/assets/moon-speech.mp3#t=1,3\"\n        autoplay=\"true\"\n        loop=\"true\"\n      ></audio>\n      <!-- inapplicable 5 -->\n      <audio\n        id=\"incomplete5\"\n        src=\"/test/assets/moon-speech.mp3\"\n        preload=\"none\"\n        autoplay\n        loop\n      ></audio>\n      <!-- inapplicable 6 -->\n      <video id=\"incomplete6\" preload=\"none\" autoplay loop>\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n\n      <!-- Inapplicable  -->\n      <!-- inapplicable 1 -->\n      <video id=\"inapplicable1\" autoplay=\"true\" muted=\"true\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n      <!-- inapplicable 2 -->\n      <audio\n        id=\"inapplicable2\"\n        src=\"/test/assets/moon-speech.mp3\"\n        controls\n      ></audio>\n      <!-- inapplicable 3 -->\n      <video id=\"inapplicable3\" autoplay=\"true\" paused=\"true\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n      <!-- inapplicable 4 -->\n      <audio\n        id=\"inapplicable4\"\n        src=\"/test/assets/moon-speech.mp3\"\n        preload=\"none\"\n      ></audio>\n      <!-- inapplicable 5 -->\n      <video id=\"inapplicable5\" preload=\"none\">\n        <source src=\"/test/assets/video.webm\" type=\"video/webm\" />\n        <source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />\n      </video>\n    </div>\n\n    <div id=\"mocha\"></div>\n\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"no-autoplay-audio.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/no-autoplay-audio/no-autoplay-audio.js",
    "content": "describe('landmark-no-duplicate-main test failure', function () {\n  'use strict';\n\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['no-autoplay-audio'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 7', function () {\n      assert.isDefined(results.passes);\n\n      var passNodes = results.passes[0].nodes;\n      assert.lengthOf(passNodes, 7);\n      assert.deepEqual(passNodes[0].target, ['#pass1']);\n      assert.deepEqual(passNodes[1].target, ['#pass2']);\n      assert.deepEqual(passNodes[2].target, ['#pass3']);\n      assert.deepEqual(passNodes[3].target, ['#pass4']);\n      assert.deepEqual(passNodes[4].target, ['#pass5']);\n      assert.deepEqual(passNodes[5].target, ['#pass6']);\n      assert.deepEqual(passNodes[6].target, ['#pass7']);\n    });\n  });\n\n  it('should find 0 violations', function () {\n    assert.lengthOf(results.violations, 0);\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  describe('incomplete', function () {\n    it('should find 6', function () {\n      assert.isDefined(results.incomplete);\n\n      var incompleteNodes = results.incomplete[0].nodes;\n      assert.lengthOf(incompleteNodes, 6);\n      assert.deepEqual(incompleteNodes[0].target, ['#incomplete1']);\n      assert.deepEqual(incompleteNodes[1].target, ['#incomplete2']);\n      assert.deepEqual(incompleteNodes[2].target, ['#incomplete3']);\n      assert.deepEqual(incompleteNodes[3].target, ['#incomplete4']);\n      assert.deepEqual(incompleteNodes[4].target, ['#incomplete5']);\n      assert.deepEqual(incompleteNodes[5].target, ['#incomplete6']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/options-parameter/frames/frame.html",
    "content": "<!doctype html>\n<html id=\"level1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <span id=\"target\"> This is the target </span>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'div#target',\n            selector: '#target',\n            any: ['has-target']\n          }\n        ],\n        checks: [\n          {\n            id: 'has-target',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ]\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/options-parameter/options-parameter.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"main\">\n  <head>\n    <title>frame exclude test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n\n      axe._load({\n        rules: [\n          {\n            id: 'div#target',\n            selector: '#target',\n            any: ['has-target']\n          }\n        ],\n        checks: [\n          {\n            id: 'has-target',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ]\n      });\n    </script>\n  </head>\n  <body>\n    <div id=\"frame-container\">\n      <iframe id=\"myframe\" src=\"frames/frame.html\"></iframe>\n    </div>\n    <div id=\"target\">This is the target in the parent frame.</div>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"options-parameter.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/options-parameter/options-parameter.js",
    "content": "describe('Options parameter', function () {\n  'use strict';\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  function $id(id) {\n    return document.getElementById(id);\n  }\n\n  describe('iframes', function () {\n    it('should include iframes if `iframes` is true', function (done) {\n      var config = { iframes: true };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.isTrue(\n            results.passes[0].nodes.some(function (node) {\n              if (node.target.length !== 2) {\n                return false;\n              }\n              return node.target[0] === '#myframe';\n            }),\n            \"couldn't find iframe result\"\n          );\n          assert.lengthOf(\n            results.passes[0].nodes,\n            2,\n            'results from main and iframe'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should exclude iframes if `iframes` is false', function (done) {\n      var config = { iframes: false };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.isFalse(\n            results.passes[0].nodes.some(function (node) {\n              if (node.target.length !== 2) {\n                return false;\n              }\n              return node.target[0] === '#myframe';\n            }),\n            'unexpectedly found iframe result'\n          );\n          assert.lengthOf(\n            results.passes[0].nodes,\n            1,\n            'results from main frame only'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should include iframes by default', function (done) {\n      var config = {};\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.isTrue(\n            results.passes[0].nodes.some(function (node) {\n              if (node.target.length !== 2) {\n                return false;\n              }\n              return node.target[0] === '#myframe';\n            }),\n            \"couldn't find iframe result\"\n          );\n          assert.lengthOf(\n            results.passes[0].nodes,\n            2,\n            'results from main and iframe'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n  });\n\n  describe('elementRef', function () {\n    it('should not return an elementRef by default', function (done) {\n      var config = {};\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(results.passes[0].nodes, 2, '');\n          assert.isFalse(\n            results.passes[0].nodes.some(function (node) {\n              return 'element' in node;\n            }),\n            'unexpectedly foud element ref'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should not return an elementRef if `elementRef` is false', function (done) {\n      var config = { elementRef: false };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(\n            results.passes[0].nodes,\n            2,\n            'results from main frame and iframe'\n          );\n          assert.isFalse(\n            results.passes[0].nodes.some(function (node) {\n              return 'element' in node;\n            }),\n            'unexpectedly found element ref'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should return element refs for the top frame only if `elementRef` is true', function (done) {\n      var config = { elementRef: true };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(results.passes[0].nodes, 2, '');\n          assert.isTrue(\n            results.passes[0].nodes.every(function (node) {\n              if ('element' in node) {\n                return node.element === $id('target');\n              }\n              return 'target' in node && node.target.length > 1;\n            }),\n            'every result node should either be in an iframe or have an element ref'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n  });\n\n  describe('no selectors', function () {\n    it('should return a selector by default', function (done) {\n      var config = {};\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(results.passes[0].nodes, 2, '');\n          assert.isTrue(\n            results.passes[0].nodes.every(function (node) {\n              return 'target' in node;\n            }),\n            'every result node should have a target'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should return a selector if `selectors` is true', function (done) {\n      var config = { selectors: true };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(results.passes[0].nodes, 2, '');\n          assert.isTrue(\n            results.passes[0].nodes.every(function (node) {\n              return 'target' in node;\n            }),\n            'every result node should have a target'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n\n    it('should return no selector in top frame if `selectors` is false', function (done) {\n      var config = { selectors: false };\n      axe.run(document, config, function (err, results) {\n        try {\n          assert.lengthOf(results.violations, 0, 'violations');\n          assert.lengthOf(results.passes, 1, 'passes');\n          assert.lengthOf(results.passes[0].nodes, 2, '');\n          assert.isFalse(\n            results.passes[0].nodes.some(function (node) {\n              return 'target' in node && node.target.length === 1;\n            }),\n            'only iframe result nodes should have a target'\n          );\n          done();\n        } catch (e) {\n          done(e);\n        }\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/frames/level1-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 here either</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 here either</p>\n    <iframe id=\"frame2\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame3\" src=\"level2.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/frames/level2-a.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1>This page has an h1</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/frames/level2.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>No h1 content in this iframe</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"fail1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No h1 here</p>\n    <h1 role=\"alert\">Hello!</h1>\n    <h1 aria-level=\"2\">Goodbye</h1>\n    <h1 role=\"alert\" aria-level=\"1\">Error</h1>\n    <iframe id=\"frame1\" src=\"frames/level1-fail.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-fail.js",
    "content": "describe('page-has-heading-one test failure', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations[0].nodes, 2);\n    });\n\n    it('should find #frame1', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);\n    });\n\n    it('should find #frame1, #violation2', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, [\n        '#frame1',\n        '#violation2'\n      ]);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass1.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <p>No h1 content</p>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div id=\"additional\"></div>\n    <script>\n      // only way to get a 2nd html element on the page is to create it with createElement\n      // if you try to do it through the html parser it won't work\n      var root = document.createElement('html');\n      document.getElementById('additional').append(root);\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass1.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass1.js",
    "content": "describe('page-has-heading-one test pass 1', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 4', function () {\n      assert.lengthOf(results.passes[0].nodes, 4);\n    });\n\n    it('should find #pass1', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);\n    });\n\n    it('should find #frame1, #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[1].target, [\n        '#frame1',\n        '#pass2'\n      ]);\n    });\n\n    it('should find #frame1, #frame2, #pass3', function () {\n      assert.deepEqual(results.passes[0].nodes[2].target, [\n        '#frame1',\n        '#frame2',\n        '#pass3'\n      ]);\n    });\n\n    it('should find #frame1, #frame3, #pass4', function () {\n      assert.deepEqual(results.passes[0].nodes[3].target, [\n        '#frame1',\n        '#frame3',\n        '#pass4'\n      ]);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass10.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass10\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main aria-hidden=\"true\">\n      <h1>Level one heading!</h1>\n    </main>\n    <div role=\"dialog\">Modal open</div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass10.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass10.js",
    "content": "describe('page-has-heading-one test pass 2', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass10', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass10']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass2.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div role=\"heading\" aria-level=\"1\">Level one heading!</div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass2.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass2.js",
    "content": "describe('page-has-heading-one test pass 2', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass2', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass2']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass3.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass3\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <style type=\"text/css\">\n      .sr-only {\n        border: 0;\n        clip: rect(0 0 0 0);\n        clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        height: 1px;\n        margin: -1px;\n        overflow: hidden;\n        padding: 0;\n        position: absolute;\n        width: 1px;\n        white-space: nowrap;\n      }\n    </style>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h1 class=\"sr-only\">Level one heading!</h1>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass3.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass3.js",
    "content": "describe('page-has-heading-one test pass 3', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass3', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass3']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass4.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass4\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h2 aria-level=\"1\">Level one heading!</h2>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass4.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass4.js",
    "content": "describe('page-has-heading-one test pass 4', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass4', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass4']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass5.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass5\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h3 aria-level=\"1\">Level one heading!</h3>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass5.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass5.js",
    "content": "describe('page-has-heading-one test pass 5', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass5', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass5']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass6.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass6\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h4 aria-level=\"1\">Level one heading!</h4>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass6.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass6.js",
    "content": "describe('page-has-heading-one test pass 6', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass6', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass6']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass7.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass7\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h5 aria-level=\"1\">Level one heading!</h5>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass7.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass7.js",
    "content": "describe('page-has-heading-one test pass 7', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass7', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass7']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass8.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass8\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h6 aria-level=\"1\">Level one heading!</h6>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass8.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass8.js",
    "content": "describe('page-has-heading-one test pass 8', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass8', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass8']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass9.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass9\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h1 aria-level=\"1\">Level one heading!</h1>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-has-heading-one-pass9.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/page-has-heading-one/page-has-heading-one-pass9.js",
    "content": "describe('page-has-heading-one test pass 9', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<h2>page-has-heading-one test</h2>';\n      }\n\n      axe.run(\n        { runOnly: { type: 'rule', values: ['page-has-heading-one'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find #pass9', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['#pass9']);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/patch/patch.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>patch test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"patch.mjs\" type=\"module\"></script>\n    <script src=\"/test/integration/adapter.js\" type=\"module\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/patch/patch.mjs",
    "content": "// Solves for situations where global code is mocked, like old Jest docs\n// recommending to `null` out `window.CSS` for JSDOM's benefit\n// https://github.com/thymikee/jest-preset-angular/commit/ac30648347ab41e0cbce741f66ae2a06b766fe13#diff-f2981abe444e6cc2b341b0d7cadb3932d2f1fbb6601aebeaf70f8bb387439d35\n\nconst originalWindowCSS = window.CSS;\n\nfunction resetWindowCSSMock() {\n  Object.defineProperty(window, 'CSS', { value: originalWindowCSS });\n}\n\nfunction mockWindowCSS() {\n  Object.defineProperty(window, 'CSS', { value: null });\n}\n\ndescribe('patch', function () {\n  'use strict';\n\n  beforeEach(mockWindowCSS);\n  afterEach(resetWindowCSSMock);\n\n  it('can mock window.CSS to `null` on its own', function () {\n    assert.isNull(window.CSS);\n  });\n\n  it('resets css window mock', function () {\n    resetWindowCSSMock();\n    assert.equal(window.CSS, originalWindowCSS);\n  });\n\n  it('imports axe.js and works while patched and mocked', async function () {\n    assert.isNull(window.CSS);\n    try {\n      await import('/axe.js');\n    } catch (error) {\n      // Should not hit this assertion\n      assert.notOk(error);\n    }\n  });\n\n  it('imports axe.min.js and works while patched and mocked', async function () {\n    assert.isNull(window.CSS);\n    try {\n      await import('/axe.min.js');\n    } catch (error) {\n      // Should not hit this assertion\n      assert.notOk(error);\n    }\n  });\n});\n"
  },
  {
    "path": "test/integration/full/plugin/plugin.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Plugin Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <h1 class=\"my-heading\">Hello World</h1>\n    <main id=\"mocha\"></main>\n    <script src=\"plugin.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/plugin/plugin.js",
    "content": "describe('plugin test', function () {\n  it('should register the plugin', function () {\n    axe.registerPlugin({\n      id: 'doStuff',\n      run: function (id, action, options, callback) {\n        var frames;\n        var q = axe.utils.queue();\n        var that = this;\n        frames = axe.utils.toArray(document.querySelectorAll('iframe, frame'));\n\n        frames.forEach(function (frame) {\n          q.defer(function (done) {\n            axe.utils.sendCommandToFrame(\n              frame,\n              {\n                options: options,\n                command: 'run-doStuff',\n                parameter: id,\n                action: action\n              },\n              function () {\n                done();\n              }\n            );\n          });\n        });\n\n        if (!options.context.length) {\n          q.defer(function (done) {\n            that._registry[id][action].call(\n              that._registry[id],\n              document,\n              options,\n              done\n            );\n          });\n        }\n        q.then(callback);\n      },\n      commands: [\n        {\n          id: 'run-doStuff',\n          callback: function (data, callback) {\n            return axe.plugins.doStuff.run(\n              data.parameter,\n              data.action,\n              data.options,\n              callback\n            );\n          }\n        }\n      ]\n    });\n\n    assert.isOk(axe.plugins.doStuff);\n  });\n\n  it('should add plugin instance', function () {\n    var highlight = {\n      id: 'highlight',\n      highlighter: function (node) {\n        node.style.backgroundColor = 'yellow';\n        this._node = node;\n      },\n      run: function (contextNode, options, done) {\n        var that = this;\n        Array.prototype.slice\n          .call(contextNode.querySelectorAll(options.selector))\n          .forEach(function (node) {\n            that.highlighter(node, options);\n          });\n        done();\n      },\n      cleanup: function (done) {\n        this._node.style.backgroundColor = '';\n        done();\n      }\n    };\n\n    axe.plugins.doStuff.add(highlight);\n    assert.equal(axe.plugins.doStuff._registry.highlight, highlight);\n  });\n\n  it('should run the plugin', function (done) {\n    var h1 = document.querySelector('.my-heading');\n\n    axe.plugins.doStuff.run(\n      'highlight',\n      'run',\n      {\n        selector: '.my-heading',\n        context: []\n      },\n      function () {\n        assert.equal(h1.style.backgroundColor, 'yellow');\n        done();\n      }\n    );\n  });\n\n  it('should cleanup the plugin', function () {\n    var h1 = document.querySelector('.my-heading');\n\n    axe.cleanup();\n    assert.equal(h1.style.backgroundColor, '');\n  });\n});\n"
  },
  {
    "path": "test/integration/full/preload/import-non-existing-cross-origin.css",
    "content": "/* loads a non existing cross origin stylesheet */\n\n@import 'https://non.existing/style.css';\n"
  },
  {
    "path": "test/integration/full/preload/preload.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <!-- \n\t\tNote:\n\t\tVarious styles -> `link`, `style` etc., are appended dynamically to this page. \n\t\tTo assert the right count of sheets, removing `mocha` styles \n\t\t-->\n    <script>\n      mocha.setup({\n        timeout: 50000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div id=\"run-now-target\">Sample DIV which acts as target for run now</div>\n    <div id=\"run-later-target\">\n      Sample DIV which acts as target for run later\n    </div>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"preload.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/preload/preload.js",
    "content": "describe('axe.utils.preload integration test', () => {\n  const styleSheets = {\n    crossOriginLinkHref: {\n      id: 'crossOriginLinkHref',\n      href: 'https://unpkg.com/gutenberg-css@0.4.0/dist/gutenberg.css'\n    },\n    crossOriginDoesNotExist: {\n      id: 'styleTag',\n      text: '@import \"https://i-do-not-exist.css\"'\n    },\n    crossOriginLinkHrefMediaPrint: {\n      id: 'crossOriginLinkHrefMediaPrint',\n      href: 'https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css',\n      mediaPrint: true\n    },\n    styleTag: {\n      id: 'styleTag',\n      text: '.inline-css-test{font-size:inherit;}'\n    }\n  };\n  let stylesForPage;\n\n  function attachStylesheets(options, callback) {\n    axe.testUtils\n      .addStyleSheets(options.styles, document)\n      .then(() => {\n        callback();\n      })\n      .catch(error => {\n        callback(new Error('Could not load stylesheets for testing. ' + error));\n      });\n  }\n\n  function detachStylesheets(done) {\n    if (!stylesForPage) {\n      return done();\n    }\n    axe.testUtils\n      .removeStyleSheets(stylesForPage)\n      .then(() => {\n        done();\n        stylesForPage = undefined;\n      })\n      .catch(err => {\n        done(err);\n        stylesForPage = undefined;\n      });\n  }\n\n  function getPreload(timeout) {\n    axe._tree = axe.utils.getFlattenedTree(document);\n    // options.treeRoot = treeRoot;\n    return axe.utils.preload({\n      preload: {\n        assets: ['cssom'],\n        timeout: timeout\n      }\n    });\n  }\n\n  afterEach(done => {\n    axe._tree = undefined;\n    detachStylesheets(done);\n  });\n\n  it('returns preloaded assets defined via <style> tag', done => {\n    stylesForPage = [styleSheets.styleTag];\n    attachStylesheets({ styles: stylesForPage }, err => {\n      if (err) {\n        done(err);\n      }\n      getPreload()\n        .then(preloadedAssets => {\n          assert.property(preloadedAssets, 'cssom');\n          assert.lengthOf(preloadedAssets.cssom, 1);\n          const sheetData = preloadedAssets.cssom[0].sheet;\n          axe.testUtils.assertStylesheet(\n            sheetData,\n            '.inline-css-test',\n            stylesForPage[0].text\n          );\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  it('returns NO preloaded CSSOM assets when requested stylesheets are of media=print', done => {\n    stylesForPage = [styleSheets.crossOriginLinkHrefMediaPrint];\n    attachStylesheets({ styles: stylesForPage }, err => {\n      if (err) {\n        done(err);\n      }\n      getPreload()\n        .then(preloadedAssets => {\n          assert.property(preloadedAssets, 'cssom');\n          assert.lengthOf(preloadedAssets.cssom, 0);\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  it.skip('returns NO preloaded CSSOM assets when requested stylesheet does not exist`', done => {\n    stylesForPage = [styleSheets.crossOriginDoesNotExist];\n    attachStylesheets({ styles: stylesForPage }, err => {\n      if (err) {\n        done(err);\n      }\n      getPreload()\n        .then(() => {\n          done(new Error('Not expecting to complete the promise'));\n        })\n        .catch(e => {\n          assert.isNotNull(e);\n          assert.isTrue(!e.message.includes('Preload assets timed out'));\n          done();\n        })\n        .catch(done);\n    });\n  });\n\n  it.skip('rejects preload function when timed out before fetching assets', done => {\n    stylesForPage = [styleSheets.crossOriginLinkHref];\n\n    const origPreloadCssom = axe.utils.preloadCssom;\n    axe.utils.preloadCssom = () => {\n      return new Promise(res => {\n        setTimeout(() => {\n          res(true);\n        }, 2000);\n      });\n    };\n\n    attachStylesheets({ styles: stylesForPage }, err => {\n      if (err) {\n        done(err);\n      }\n      getPreload(1)\n        .then(() => {\n          done(new Error('Not expecting to complete the promise'));\n        })\n        .catch(e => {\n          assert.isNotNull(e);\n          assert.isTrue(e.message.includes('Preload assets timed out'));\n          axe.utils.preloadCssom = origPreloadCssom;\n          done();\n        })\n        .catch(e => {\n          axe.utils.preloadCssom = origPreloadCssom;\n          done(e);\n        });\n    });\n  });\n\n  describe('verify preloaded assets via axe.run against custom rules', () => {\n    function customCheckEvalFn(node, options, virtualNode, context) {\n      // populate the data here which is asserted in tests\n      this.data(context);\n      return true;\n    }\n\n    beforeEach(done => {\n      /**\n       * Load custom rule & check\n       * -> one check is preload dependent\n       * -> another check is not preload dependent\n       */\n      axe._load({\n        rules: [\n          {\n            // this rule is not preload dependent and can run immediately\n            id: 'run-now-rule',\n            selector: 'div#run-now-target',\n            any: ['check-context-exists']\n          },\n          {\n            // this rule requires preload and will run after preload assets are ready\n            id: 'run-later-rule',\n            selector: 'div#run-later-target',\n            any: ['check-context-has-assets'],\n            preload: {\n              assets: ['cssom']\n            }\n          }\n        ],\n        checks: [\n          {\n            id: 'check-context-exists',\n            evaluate: customCheckEvalFn\n          },\n          {\n            id: 'check-context-has-assets',\n            evaluate: customCheckEvalFn\n          }\n        ]\n      });\n\n      // load stylesheets\n      stylesForPage = [\n        styleSheets.crossOriginLinkHref,\n        styleSheets.crossOriginLinkHrefMediaPrint,\n        styleSheets.styleTag\n      ];\n      attachStylesheets({ styles: stylesForPage }, done);\n    });\n\n    after(done => {\n      detachStylesheets(done);\n    });\n\n    it(\"returns preloaded assets to the check's evaluate fn for the rule which has `preload:true`\", done => {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['run-later-rule']\n          },\n          preload: true\n        },\n        (err, res) => {\n          assert.isNull(err);\n          assert.isDefined(res);\n          assert.property(res, 'passes');\n          assert.lengthOf(res.passes, 1);\n\n          const checkData = res.passes[0].nodes[0].any[0].data;\n          assert.property(checkData, 'cssom');\n\n          const cssom = checkData.cssom;\n\n          // ignores all media='print' styleSheets\n          assert.lengthOf(cssom, 2);\n\n          // there should be no external sheet returned\n          const crossOriginSheet = cssom.filter(s => {\n            return s.isCrossOrigin;\n          });\n          assert.lengthOf(crossOriginSheet, 1);\n\n          // verify content of stylesheet\n          const inlineStylesheet = cssom.filter(s => {\n            return s.sheet.cssRules.length === 1 && !s.isCrossOrigin;\n          })[0].sheet;\n          axe.testUtils.assertStylesheet(\n            inlineStylesheet,\n            '.inline-css-test',\n            '.inline-css-test{font-size:inherit;}'\n          );\n\n          done();\n        }\n      );\n    });\n\n    it(\"returns NO preloaded assets to the check which does not require preload'\", done => {\n      axe.run(\n        {\n          runOnly: {\n            type: 'rule',\n            values: ['run-now-rule']\n          },\n          preload: true\n        },\n        (err, res) => {\n          assert.isNull(err);\n          assert.isDefined(res);\n          assert.property(res, 'passes');\n          assert.lengthOf(res.passes, 1);\n\n          const checkData = res.passes[0].nodes[0].any[0];\n          assert.notProperty(checkData, 'cssom');\n          done();\n        }\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/preload-cssom/base.css",
    "content": "/* stylesheet used for preload cssom testing */\n\n.style-from-base-css {\n  font-size: 100%;\n}\n"
  },
  {
    "path": "test/integration/full/preload-cssom/cyclic-cross-origin-import-1.css",
    "content": "/* calls a stylesheets, which inturn refers back to this one , but also imports a cross-origin stylesheet */\n\n@import 'cyclic-cross-origin-import-2.css';\n@import 'https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/cyclic-cross-origin-import-2.css",
    "content": "/* calls a stylesheets, which inturn refers back to this one */\n\n@import 'cyclic-cross-origin-import-1.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/cyclic-import-1.css",
    "content": "/* calls a stylesheets, which inturn refers back to this one */\n\n@import 'cyclic-import-2.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/cyclic-import-2.css",
    "content": "/* calls a stylesheet, from which this stylesheet was called (cyclic references) */\n\n@import 'cyclic-import-1.css';\n\n.style-from-cyclic-import-2-css {\n  font-family: inherit;\n}\n"
  },
  {
    "path": "test/integration/full/preload-cssom/frames/level1.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/pure/1.0.0/pure-min.css\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://unpkg.com/gutenberg-css@0.4\"\n      media=\"print\"\n    />\n    <style>\n      .style-from-nested-iframe {\n        font-size: inherit;\n      }\n    </style>\n    <script src=\"/axe.js\"></script>\n  </head>\n</html>\n"
  },
  {
    "path": "test/integration/full/preload-cssom/import-non-existing-cross-origin.css",
    "content": "/* loads a non existing cross origin stylesheet */\n\n@import 'https://non.existing/style.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/multiple-import-1.css",
    "content": "/* import multiple stylesheets */\n\n@import 'multiple-import-2.css';\n@import 'multiple-import-3.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/multiple-import-2.css",
    "content": "/* this is referenced by multiple-import-1.css */\n\n.style-from-multiple-import-2-css {\n  font-size: 100%;\n}\n"
  },
  {
    "path": "test/integration/full/preload-cssom/multiple-import-3.css",
    "content": "/* this is referenced by multiple-import-1.css */\n\n.style-from-multiple-import-3-css {\n  font-size: 100%;\n}\n"
  },
  {
    "path": "test/integration/full/preload-cssom/nested-import-1.css",
    "content": "/* entry point for nested import scenario */\n\n@import 'nested-import-2.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/nested-import-2.css",
    "content": "/* referred by nested-import-1.css */\n\n@import 'nested-import-3.css';\n"
  },
  {
    "path": "test/integration/full/preload-cssom/nested-import-3.css",
    "content": "/* referred by nested-import-2.css */\n\n.style-from-nested-import-3-css {\n  font-size: inherit;\n}\n"
  },
  {
    "path": "test/integration/full/preload-cssom/preload-cssom.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <!-- \n\t\tNote:\n\t\tVarious styles -> `link`, `style` etc., are appended dynamically to this page. \n\t\tTo assert the right count of sheets, removing `mocha` styles \n\t\t-->\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <div class=\"blue\">blue on parent page</div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"preload-cssom.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/preload-cssom/preload-cssom.js",
    "content": "describe('preload cssom integration test', function () {\n  'use strict';\n\n  // axe-core preload timeout is set to 10s so we need a\n  // timeout slightly above that to ensure there's enough\n  // time to resolve tests that we want to throw\n  this.timeout(15000);\n\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n  const styleSheets = {\n    crossOriginLinkHref: {\n      id: 'crossOriginLinkHref',\n      href: 'https://stackpath.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'\n    },\n    crossOriginLinkHrefMediaPrint: {\n      id: 'crossOriginLinkHrefMediaPrint',\n      href: 'https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css',\n      mediaPrint: true\n    },\n    styleTag: {\n      id: 'styleTag',\n      text: '.inline-css-test{font-size:inherit;}'\n    },\n    styleTagWithOneImport: {\n      id: 'styleTagWithOneImport',\n      text: '@import \"base.css\";'\n    },\n    styleTagWithMultipleImports: {\n      id: 'styleTagWithMultipleImports',\n      text: '@import \"multiple-import-1.css\";'\n    },\n    styleTagWithNestedImports: {\n      id: 'styleTagWithNestedImports',\n      text: '@import \"nested-import-1.css\";'\n    },\n    styleTagWithCyclicImports: {\n      id: 'styleTagWithCyclicImports',\n      text: '@import \"cyclic-import-1.css\";'\n    },\n    styleTagWithCyclicCrossOriginImports: {\n      id: 'styleTagWithCyclicCrossOriginImports',\n      text: '@import \"cyclic-cross-origin-import-1.css\";'\n    },\n    styleTagWithNonExistentImport: {\n      id: 'styleTagWithNonExistentImport',\n      text: '@import \"non-existent-import.css\";'\n    }\n  };\n  let stylesForPage;\n  let nestedFrame;\n\n  before(done => {\n    axe.testUtils.awaitNestedLoad(() => {\n      nestedFrame = document.getElementById('frame1').contentDocument;\n      done();\n    });\n  });\n\n  function attachStylesheets(options, callback) {\n    axe.testUtils\n      .addStyleSheets(options.styles, options.root)\n      .then(() => {\n        callback();\n      })\n      .catch(error => {\n        callback(new Error('Could not load stylesheets for testing. ' + error));\n      });\n  }\n\n  function detachStylesheets(done) {\n    if (!stylesForPage) {\n      done();\n    }\n    axe.testUtils\n      .removeStyleSheets(stylesForPage)\n      .then(() => {\n        done();\n        stylesForPage = undefined;\n      })\n      .catch(err => {\n        done(err);\n        stylesForPage = undefined;\n      });\n  }\n\n  function getPreloadCssom(root) {\n    const treeRoot = axe.utils.getFlattenedTree(root ? root : document);\n    return axe.utils.preloadCssom({ treeRoot: treeRoot });\n  }\n\n  function commonTestsForRootNodeAndNestedFrame(root) {\n    it('returns cross-origin stylesheet', done => {\n      stylesForPage = [styleSheets.crossOriginLinkHref];\n      attachStylesheets(\n        {\n          root: root,\n          styles: stylesForPage\n        },\n        err => {\n          if (err) {\n            done(err);\n          }\n          getPreloadCssom(root)\n            .then(sheets => {\n              assert.lengthOf(sheets, 1);\n              const sheetData = sheets[0].sheet;\n              axe.testUtils.assertStylesheet(\n                sheetData,\n                ':root',\n                sheetData.cssRules[0].cssText,\n                true\n              );\n              done();\n            })\n            .catch(() => {\n              done(new Error('Expected getPreload to resolve.'));\n            });\n        },\n        root\n      );\n    });\n\n    it('returns no stylesheets when cross-origin stylesheets are of media=print', done => {\n      stylesForPage = [styleSheets.crossOriginLinkHrefMediaPrint];\n      attachStylesheets(\n        {\n          root: root,\n          styles: stylesForPage\n        },\n        err => {\n          if (err) {\n            done(err);\n          }\n          getPreloadCssom(root)\n            .then(sheets => {\n              assert.lengthOf(sheets, 0);\n              done();\n            })\n            .catch(() => {\n              done(new Error('Expected getPreload to resolve.'));\n            });\n        },\n        root\n      );\n    });\n\n    it('throws if cross-origin stylesheet fail to load', done => {\n      stylesForPage = [styleSheets.styleTagWithNonExistentImport];\n      attachStylesheets(\n        {\n          root: root,\n          styles: stylesForPage\n        },\n        err => {\n          if (err) {\n            done(err);\n          }\n          getPreloadCssom(root)\n            .then(sheets => {\n              done(\n                new Error(\n                  `Expected getPreload to reject, but it resolved with ${JSON.stringify(sheets)}.`\n                )\n              );\n            })\n            .catch(e => {\n              assert.isDefined(e);\n              done();\n            });\n        },\n        root\n      );\n    });\n  }\n\n  describe('tests for root (document)', () => {\n    let shadowFixture;\n\n    beforeEach(() => {\n      const shadowNode = document.createElement('div');\n      shadowNode.id = 'shadow-fixture';\n      document.body.appendChild(shadowNode);\n      shadowFixture = document.getElementById('shadow-fixture');\n    });\n\n    afterEach(done => {\n      if (shadowFixture) {\n        document.body.removeChild(shadowFixture);\n      }\n      detachStylesheets(done);\n    });\n\n    it('returns stylesheets defined via <style> tag', done => {\n      stylesForPage = [styleSheets.styleTag];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 1);\n            const sheetData = sheets[0].sheet;\n            axe.testUtils.assertStylesheet(\n              sheetData,\n              '.inline-css-test',\n              stylesForPage[0].text\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    it('returns stylesheets with in same-origin', done => {\n      stylesForPage = [styleSheets.styleTagWithOneImport];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 1);\n            const nonCrossOriginSheets = sheets.filter(s => {\n              return !s.isCrossOrigin;\n            });\n            assert.lengthOf(nonCrossOriginSheets, 1);\n            axe.testUtils.assertStylesheet(\n              nonCrossOriginSheets[0].sheet,\n              '.style-from-base-css',\n              '.style-from-base-css { font-size: 100%; }'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    (shadowSupported ? it : xit)(\n      'returns styles from shadow DOM (handles @import in <style>)',\n      done => {\n        const shadow = shadowFixture.attachShadow({ mode: 'open' });\n        shadow.innerHTML =\n          '<style>' +\n          // stylesheet -> 1\n          '@import \"https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.css\";' +\n          // stylesheet -> 2\n          '.green { background-color: green; } ' +\n          '</style>' +\n          '<div class=\"initialism\">Some text</div>';\n        getPreloadCssom(shadowFixture)\n          .then(sheets => {\n            assert.lengthOf(sheets, 2);\n            const nonCrossOriginSheetsWithInShadowDOM = sheets\n              .filter(s => {\n                return !s.isCrossOrigin;\n              })\n              .filter(s => {\n                return s.shadowId;\n              });\n            axe.testUtils.assertStylesheet(\n              nonCrossOriginSheetsWithInShadowDOM[\n                nonCrossOriginSheetsWithInShadowDOM.length - 1\n              ].sheet,\n              '.green',\n              '.green{background-color:green;}'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      }\n    );\n\n    (shadowSupported ? it : xit)(\n      'returns styles from base document and shadow DOM with right priority',\n      done => {\n        const shadow = shadowFixture.attachShadow({ mode: 'open' });\n        shadow.innerHTML =\n          '<style>' +\n          // stylesheet -> 1 -> inside shadow DOM\n          '@import \"base.css\"' +\n          '</style>' +\n          '<h1>Heading</h1>';\n\n        // sheet appended to root document\n        stylesForPage = [styleSheets.styleTag];\n        attachStylesheets({ styles: stylesForPage }, err => {\n          if (err) {\n            done(err);\n          }\n          getPreloadCssom(shadowFixture)\n            .then(sheets => {\n              assert.lengthOf(sheets, 2);\n\n              const shadowDomStyle = sheets.filter(s => {\n                return s.shadowId;\n              })[0];\n              axe.testUtils.assertStylesheet(\n                shadowDomStyle.sheet,\n                '.style-from-base-css',\n                '.style-from-base-css { font-size: 100%; }'\n              );\n\n              const rootDocumentStyle = sheets.filter(s => {\n                return !s.shadowId;\n              })[0];\n              assert.isAbove(\n                shadowDomStyle.priority[0],\n                rootDocumentStyle.priority[0]\n              );\n              done();\n            })\n            .catch(() => {\n              done(new Error('Expected getPreload to resolve.'));\n            });\n        });\n      }\n    );\n\n    it('returns styles from various @import(ed) styles from an @import(ed) stylesheet', done => {\n      stylesForPage = [\n        styleSheets.styleTagWithMultipleImports // this imports 2 other stylesheets\n      ];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 2);\n            const nonCrossOriginSheets = sheets.filter(s => {\n              return !s.isCrossOrigin;\n            });\n            assert.lengthOf(nonCrossOriginSheets, 2);\n            axe.testUtils.assertStylesheet(\n              nonCrossOriginSheets[0].sheet,\n              '.style-from-multiple-import-2-css',\n              '.style-from-multiple-import-2-css { font-size: 100%; }'\n            );\n            axe.testUtils.assertStylesheet(\n              nonCrossOriginSheets[1].sheet,\n              '.style-from-multiple-import-3-css',\n              '.style-from-multiple-import-3-css { font-size: 100%; }'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    it('returns style from nested @import (3 levels deep)', done => {\n      stylesForPage = [styleSheets.styleTagWithNestedImports];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 1);\n            const nonCrossOriginSheets = sheets.filter(s => {\n              return !s.isCrossOrigin;\n            });\n            assert.lengthOf(nonCrossOriginSheets, 1);\n            axe.testUtils.assertStylesheet(\n              nonCrossOriginSheets[0].sheet,\n              '.style-from-nested-import-3-css',\n              '.style-from-nested-import-3-css { font-size: inherit; }'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    it('returns style from cyclic @import (exits recursion successfully)', done => {\n      stylesForPage = [styleSheets.styleTagWithCyclicImports];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 1);\n            axe.testUtils.assertStylesheet(\n              sheets[0].sheet,\n              '.style-from-cyclic-import-2-css',\n              '.style-from-cyclic-import-2-css { font-family: inherit; }'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    it('returns style from cyclic @import which only imports one cross-origin stylesheet', done => {\n      stylesForPage = [styleSheets.styleTagWithCyclicCrossOriginImports];\n      attachStylesheets({ styles: stylesForPage }, err => {\n        if (err) {\n          done(err);\n        }\n        getPreloadCssom()\n          .then(sheets => {\n            assert.lengthOf(sheets, 1);\n            axe.testUtils.assertStylesheet(\n              sheets[0].sheet,\n              '.container',\n              '.container { position: relative; width: 100%; max-width: 960px; margin: 0px auto; padding: 0px 20px; box-sizing: border-box; }'\n            );\n            done();\n          })\n          .catch(() => {\n            done(new Error('Expected getPreload to resolve.'));\n          });\n      });\n    });\n\n    commonTestsForRootNodeAndNestedFrame();\n  });\n\n  describe('tests for nested document', () => {\n    afterEach(done => {\n      detachStylesheets(done);\n    });\n\n    it('returns styles defined using <style> tag', done => {\n      getPreloadCssom(nestedFrame)\n        .then(sheets => {\n          assert.lengthOf(sheets, 2);\n\n          const nonCrossOriginSheet = sheets.filter(s => {\n            return !s.isCrossOrigin;\n          })[0].sheet;\n          axe.testUtils.assertStylesheet(\n            nonCrossOriginSheet,\n            '.style-from-nested-iframe',\n            '.style-from-nested-iframe {font-size: inherit; }'\n          );\n          done();\n        })\n        .catch(() => {\n          done(new Error('Expected getPreload to resolve.'));\n        });\n    });\n\n    commonTestsForRootNodeAndNestedFrame(nestedFrame);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/region/frames/region-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p id=\"wrapper\">Region content no region</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/frames/region-nested.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"region-fail.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/frames/region.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"violation2\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <section aria-label=\"region\">\n      <p>Region content</p>\n    </section>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-fail-iframe.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe src=\"frames/region-fail.html\"></iframe>\n    <main id=\"mocha\"></main>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-fail-iframe.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-fail-iframe.js",
    "content": "describe('region fail test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['region'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find one', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n\n    it('should find wrapper', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'iframe',\n        '#wrapper'\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/region/region-fail-nested-iframe.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe src=\"frames/region-nested.html\"></iframe>\n    <main id=\"mocha\"></main>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-fail-nested-iframe.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-fail-nested-iframe.js",
    "content": "describe('region fail test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['region'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find one', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n\n    it('should find wrapper', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, [\n        'iframe',\n        'iframe',\n        '#wrapper'\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/region/region-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"wrapper\">\n      <a href=\"stuff.html#mainheader\" id=\"notskiplink\"\n        >This is not a skip link.</a\n      >\n      <form>\n        <div>\n          <h1 id=\"mainheader\" tabindex=\"0\">This is a header.</h1>\n        </div>\n      </form>\n      <ul>\n        <li>Home</li>\n        <li>Other</li>\n      </ul>\n      <section>\n        <p>Content</p>\n      </section>\n      <section title=\"section 3\">\n        <p>Content</p>\n      </section>\n    </div>\n\n    <div id=\"img-no-alt\">\n      <img src=\"#\" />\n    </div>\n    <div id=\"img-focusable\">\n      <img src=\"#\" tabindex=\"0\" alt=\"\" />\n    </div>\n    <div id=\"img-aria-global\">\n      <img src=\"#\" aria-atomic=\"true\" alt=\"\" />\n    </div>\n    <div id=\"labeled-object\">\n      <object aria-label=\"bar\"></object>\n    </div>\n    <div id=\"none-role-div\">\n      <div id=\"target\" role=\"none\">apples</div>\n    </div>\n\n    This should be ignored\n\n    <main id=\"mocha\"></main>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-fail.js",
    "content": "describe('region fail test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['region'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find all violations', function () {\n      assert.lengthOf(results.violations[0].nodes, 6);\n    });\n\n    it('should find wrapper', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['#wrapper']);\n    });\n\n    it('should find image without an alt tag', function () {\n      assert.deepEqual(results.violations[0].nodes[1].target, ['#img-no-alt']);\n    });\n\n    it('should find focusable image', function () {\n      assert.deepEqual(results.violations[0].nodes[2].target, [\n        '#img-focusable'\n      ]);\n    });\n\n    it('should find image with global aria attr', function () {\n      assert.deepEqual(results.violations[0].nodes[3].target, [\n        '#img-aria-global'\n      ]);\n    });\n\n    it('should find object with a label', function () {\n      assert.deepEqual(results.violations[0].nodes[4].target, [\n        '#labeled-object'\n      ]);\n    });\n\n    it('should find div with an role of none', function () {\n      assert.deepEqual(results.violations[0].nodes[5].target, [\n        '#none-role-div'\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/region/region-iframe-with-landmarks.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main id=\"mocha\"></main>\n    <iframe id=\"region-frame\" src=\"frames/region.html\"></iframe>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-iframe-within-landmark.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"wrapper\">\n      <main id=\"mocha\">\n        <iframe id=\"region-frame\" src=\"frames/region-fail.html\"></iframe>\n      </main>\n    </div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-pass-nested-iframe.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"wrapper\">\n      <main id=\"mocha\">\n        <iframe id=\"region-frame\" src=\"frames/region-nested.html\"></iframe>\n      </main>\n    </div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Region Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <a href=\"#mainheader\" id=\"skiplink\">This is a skip link.</a>\n    <!-- Decorative image -->\n    <img src=\"#\" alt=\"\" />\n    <!-- Decorative image -->\n    <img src=\"#\" role=\"presentation\" />\n    <!-- SVG's may be outside of a landmark -->\n    <svg role=\"none\" aria-label=\"foo\"></svg>\n    <!-- Div with a role of none may be outside a landmark -->\n    <div role=\"none\"></div>\n    <!-- Canvas with a role of none may be outside a landmark -->\n    <canvas role=\"none\" />\n    <div id=\"wrapper\">\n      <div role=\"main\">\n        <h1 id=\"mainheader\" tabindex=\"0\">This is a header.</h1>\n      </div>\n    </div>\n    <form aria-label=\"My wonderful form\">\n      <p>My form!</p>\n    </form>\n    <ul role=\"navigation\">\n      <li>Home</li>\n      <li>Other</li>\n    </ul>\n    <section aria-labelledby=\"hdng\">\n      <h1 id=\"hdng\">Section heading</h1>\n      <p>Content</p>\n    </section>\n    <section aria-label=\"section 2\">\n      <p>Content</p>\n    </section>\n    <iframe id=\"region-frame\" src=\"frames/region.html\"></iframe>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"region-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/region/region-pass.js",
    "content": "describe('region pass test', function () {\n  'use strict';\n  var results;\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run({ runOnly: ['region'] }, function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      });\n    });\n  });\n\n  describe('passes', function () {\n    it('should pass nodes', function () {\n      // it seems CircleCI and localhost have different number of DOM nodes,\n      // so as long as everything passes and nothing fails, the rule is working\n      assert.isTrue(results.passes[0].nodes.length > 0);\n    });\n  });\n\n  describe('violations', function () {\n    it('should find none', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/rerun/frames/frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1>Just a frame</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/rerun/rerun.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Rerun Test</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"frames/frame.html\"></iframe>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"rerun.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/rerun/rerun.js",
    "content": "describe('rerun axe in the same tick' + window.location.pathname, function () {\n  'use strict';\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  it('can run multiple times without interfering with itself', function (done) {\n    var options = {\n      runOnly: {\n        type: 'rule',\n        values: ['html-has-lang']\n      }\n    };\n\n    // First run\n    axe.run(options, function (err1, res1) {\n      try {\n        assert.isNull(err1);\n\n        // Second run, on the same tick\n        axe.run(options, function (err2, res2) {\n          try {\n            assert.isNull(err2);\n            axe.testUtils.assertResultsDeepEqual(res1, res2);\n            done();\n          } catch (e) {\n            done(e);\n          }\n        });\n      } catch (e) {\n        done(e);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/run-partial/after-method.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>runPartial, test after methods</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <h1>Heading</h1>\n    <iframe src=\"frames/heading-frame-h3.html\" id=\"frame1\"></iframe>\n    <h4>Heading</h4>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"after-method.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/run-partial/after-method.js",
    "content": "describe('run-partial, after-method', function () {\n  'use strict';\n  var ruleName = 'heading-order';\n  var runPartialRecursive = axe.testUtils.runPartialRecursive;\n  var clone = axe.utils.clone;\n\n  beforeEach(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  it('gives the same passed results as axe.run with a complex \"after\" method', function (done) {\n    var options = { runOnly: ruleName };\n    var context = { exclude: [] };\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n        assert.lengthOf(axeRunPartialResult.violations, 0);\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('gives the same failed results as axe.run with a complex \"after\" method', function (done) {\n    var options = { runOnly: ruleName };\n    var context = { exclude: [['#frame1', '#frame1a']] };\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n\n        assert.isObject(axeRunPartialResult);\n        assert.isObject(axeRunResult);\n        // Check the node is the one we expect\n        var nodes = axeRunPartialResult.violations[0].nodes;\n        assert.lengthOf(nodes, 1);\n        assert.deepEqual(nodes[0].target, ['#frame1', 'h3']);\n\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/run-partial/context-size-focusable.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>runPartial, test context.size and context.focusable</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"frames/link.html\" id=\"fail1\" tabindex=\"-1\"></iframe>\n    <iframe src=\"frames/link-nested.html\" id=\"fail2\" tabindex=\"-1000\"></iframe>\n\n    <iframe\n      src=\"frames/link.html\"\n      id=\"pass1\"\n      tabindex=\"-1\"\n      height=\"1\"\n      width=\"1\"\n    ></iframe>\n    <iframe src=\"frames/heading-h2.html\" id=\"pass2\" tabindex=\"-1\"></iframe>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"context-size-focusable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/run-partial/context-size-focusable.js",
    "content": "describe('run-partial, context-size-focusable', function () {\n  'use strict';\n  var ruleName = 'frame-focusable-content';\n  var runPartialRecursive = axe.testUtils.runPartialRecursive;\n  var clone = axe.utils.clone;\n\n  beforeEach(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  it('gives the same passed results as axe.run when context.size and context.focusable are used', function (done) {\n    var options = { runOnly: ruleName };\n    var context = {\n      exclude: [['#fail1'], ['#fail2', 'iframe']]\n    };\n\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n        assert.lengthOf(axeRunPartialResult.violations, 0);\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('gives the same failed results as axe.run when context.size and context.focusable are used', function (done) {\n    var options = { runOnly: ruleName };\n    Promise.all(runPartialRecursive({ exclude: [] }, options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run({ exclude: [] }, options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n\n        assert.isObject(axeRunPartialResult);\n        assert.isObject(axeRunResult);\n        // Check the node is the one we expect\n        var nodes = axeRunPartialResult.violations[0].nodes;\n        assert.lengthOf(nodes, 2);\n        assert.deepEqual(nodes[0].target, ['#fail1', 'html']);\n        assert.deepEqual(nodes[1].target, ['#fail2', 'iframe', 'html']);\n\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/run-partial/frames/heading-frame-h3.html",
    "content": "<script src=\"/axe.js\"></script>\n<iframe src=\"heading-h2.html\" id=\"frame1a\"></iframe>\n<h3>Hello</h3>\n"
  },
  {
    "path": "test/integration/full/run-partial/frames/heading-h2.html",
    "content": "<script src=\"/axe.js\"></script>\n<h2>Hello</h2>\n"
  },
  {
    "path": "test/integration/full/run-partial/frames/link-nested.html",
    "content": "<script src=\"/axe.js\"></script>\n<iframe src=\"link.html\"></iframe>\n"
  },
  {
    "path": "test/integration/full/run-partial/frames/link.html",
    "content": "<script src=\"/axe.js\"></script>\n<a href=\"https://deque.com/\">Deque</a>\n"
  },
  {
    "path": "test/integration/full/run-partial/initiator.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>runPartial, test context.initiator</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"frames/heading-frame-h3.html\" id=\"frame1\"></iframe>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"initiator.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/run-partial/initiator.js",
    "content": "describe('run-partial, initiator', function () {\n  'use strict';\n  var ruleName = 'document-title';\n  var runPartialRecursive = axe.testUtils.runPartialRecursive;\n  var clone = axe.utils.clone;\n\n  beforeEach(function (done) {\n    axe.testUtils.awaitNestedLoad(done);\n  });\n\n  it('gives the same results as axe.run when context.initiator is used', function (done) {\n    var options = { runOnly: ruleName };\n    var context = { exclude: [] };\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n        assert.lengthOf(axeRunPartialResult.violations, 0);\n        axeRunPartialResult.testEnvironment = axeRunResult.testEnvironment;\n        axeRunPartialResult.timestamp = axeRunResult.timestamp;\n        assert.deepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/run-partial/page-level.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>runPartial, test rule.pageLevel</title>\n\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n    <script src=\"/test/integration/no-ui-reporter.js\"></script>\n  </head>\n  <body>\n    <article>\n      <p>No h1 content</p>\n      <iframe id=\"frame1\" src=\"frames/link.html\"></iframe>\n      <a href=\"http://www.deque.com\">stuff</a>\n    </article>\n\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"page-level.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/run-partial/page-level.js",
    "content": "describe('run-partial, page-level', function () {\n  'use strict';\n  var ruleName = 'bypass';\n  var runPartialRecursive = axe.testUtils.runPartialRecursive;\n  var clone = axe.utils.clone;\n\n  beforeEach(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Stop messing with my tests Mocha!\n      var heading = document.querySelector('#mocha h1');\n      if (heading) {\n        heading.outerHTML = '<div><b>bypass iframe test fail</b></div>';\n      }\n      done();\n    });\n  });\n\n  it('gives the same empty results as axe.run with a pageLevel rule', function (done) {\n    var options = { runOnly: ruleName };\n    // pageLevel rules are inapplicable when they don't test the entire page\n    var context = { include: ['article'] };\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n        assert.lengthOf(axeRunPartialResult.incomplete, 0);\n        assert.lengthOf(axeRunPartialResult.passes, 0);\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n\n  it('gives the same failed results as axe.run with a pageLevel rule', function (done) {\n    var options = { runOnly: ruleName };\n    var context = { exclude: [] };\n    Promise.all(runPartialRecursive(clone(context), options))\n      .then(function (partialResults) {\n        return Promise.all([\n          axe.finishRun(partialResults, options),\n          axe.run(clone(context), options)\n        ]);\n      })\n      .then(function (results) {\n        var axeRunPartialResult = results[0];\n        var axeRunResult = results[1];\n        assert.isObject(axeRunPartialResult);\n        assert.isObject(axeRunResult);\n\n        // Check the node is the one we expect\n        var nodes = axeRunPartialResult.incomplete[0].nodes;\n        assert.lengthOf(nodes, 1);\n        assert.deepEqual(nodes[0].target, ['html']);\n\n        axe.testUtils.assertResultsDeepEqual(axeRunPartialResult, axeRunResult);\n        done();\n      })\n      .catch(done);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/serializer/custom-source-serializer.js",
    "content": "axe.utils.nodeSerializer.update({\n  toSpec(dqElm) {\n    const result = dqElm.toJSON();\n    result.source = dqElm.element.id;\n    return result;\n  },\n  mergeSpecs(childSpec, parentSpec) {\n    const result = axe.utils.DqElement.mergeSpecs(childSpec, parentSpec);\n    result.source = `${parentSpec.source} > ${childSpec.source}`;\n    return result;\n  }\n});\n"
  },
  {
    "path": "test/integration/full/serializer/frames/level1.html",
    "content": "<!doctype html>\n<html id=\"level1\" lang=\"INVALID\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n    <script src=\"../custom-source-serializer.js\"></script>\n  </head>\n  <body>\n    <iframe id=\"frame2-a\" src=\"level2-a.html\"></iframe>\n    <iframe id=\"frame2-b\" src=\"level2-b.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/serializer/frames/level2-a.html",
    "content": "<!doctype html>\n<html id=\"level2-a\" lang=\"INVALID\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n    <script src=\"../custom-source-serializer.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/serializer/frames/level2-b.html",
    "content": "<!doctype html>\n<html id=\"level2-b\" xml:lang=\"INVALID\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n    <script src=\"../custom-source-serializer.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/full/serializer/serializer.html",
    "content": "<!doctype html>\n<html id=\"level0\" lang=\"INVALID\">\n  <head>\n    <title>Serializer Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script src=\"custom-source-serializer.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <iframe id=\"frame1\" src=\"frames/level1.html\"></iframe>\n    <main id=\"mocha\"></main>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"serializer.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/serializer/serializer.js",
    "content": "describe('serializer', () => {\n  const { awaitNestedLoad, runPartialRecursive } = axe.testUtils;\n\n  beforeEach(async () => {\n    await new Promise(res => awaitNestedLoad(res));\n  });\n\n  const runOptions = { runOnly: 'html-lang-valid' };\n  const expectedCustomNodeSources = [\n    'level0',\n    'frame1 > level1',\n    'frame1 > frame2-a > level2-a',\n    'frame1 > frame2-b > level2-b'\n  ];\n\n  it('applies serializer hooks with axe.runPartial/finishRun', async () => {\n    const partialResults = await Promise.all(\n      runPartialRecursive(document, runOptions)\n    );\n    const results = await axe.finishRun(partialResults, runOptions);\n    const nodesHtml = results.violations[0].nodes.map(n => n.html);\n    assert.deepStrictEqual(nodesHtml, expectedCustomNodeSources);\n  });\n\n  it('applies serializer hooks with axe.run', async () => {\n    const results = await axe.run(document, runOptions);\n    const nodesHtml = results.violations[0].nodes.map(n => n.html);\n    assert.deepStrictEqual(nodesHtml, expectedCustomNodeSources);\n  });\n\n  it('still supports axe.run with options.elementRef', async () => {\n    const results = await axe.run(document, {\n      ...runOptions,\n      elementRef: true\n    });\n    const nodeElements = results.violations[0].nodes.map(n => n.element);\n\n    assert.deepStrictEqual(nodeElements, [\n      document.querySelector('html'),\n      // as usual, elementRef only works for the top frame\n      undefined,\n      undefined,\n      undefined\n    ]);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/skip-link/skip-link-fail.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>skip-link test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <a href=\"#fail1-tgt\" style=\"position: absolute; margin: -10000px\" id=\"fail1\"\n      >bad link 1</a\n    >\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"skip-link-fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/skip-link/skip-link-fail.js",
    "content": "describe('skip-link test pass', function () {\n  'use strict';\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['skip-link'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 1', function () {\n      assert.lengthOf(results.violations, 1);\n    });\n\n    it('should find 1 nodes', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.passes, 0);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/skip-link/skip-link-pass.html",
    "content": "<!doctype html>\n<html lang=\"en\" id=\"pass1\">\n  <head>\n    <title>skip-link test</title>\n    <meta charset=\"utf8\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <style type=\"text/css\">\n      .sr-only {\n        border: 0;\n        clip: rect(0 0 0 0);\n        clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);\n        height: 1px;\n        margin: -1px;\n        overflow: hidden;\n        padding: 0;\n        position: absolute;\n        width: 1px;\n        white-space: nowrap;\n      }\n    </style>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"pass1-tgt\"></div>\n    <a href=\"#pass1-tgt\" class=\"sr-only\" id=\"pass1\">Link</a>\n\n    <a href=\"#pass2-tgt\" style=\"position: absolute; left: -10000px\" id=\"pass2\"\n      >Link</a\n    >\n\n    <div id=\"pass3-tgt\"></div>\n    <a href=\"/#pass3-tgt\" class=\"sr-only\" id=\"pass3\">Link (angular)</a>\n\n    <div id=\"canttell1-tgt\" style=\"display: none\"></div>\n    <a href=\"#canttell1-tgt\" class=\"sr-only\" id=\"canttell1\">Link</a>\n\n    <!-- since these elements are page links, they needs to be at the bottom\n       of the test so all the prior links are considered skip links -->\n    <header>\n      <ul>\n        <li><a id=\"ignore1\" href=\"/#about\">About</a></li>\n        <li><a id=\"ignore2\" href=\"/#contact\">Contact</a></li>\n      </ul>\n    </header>\n\n    <h2 name=\"pass2-tgt\">Heading</h2>\n\n    <a href=\"foo#bar\" id=\"ignore3\">link</a>\n    <a href=\"#\" id=\"ignore4\">link</a>\n    <div id=\"mocha\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"skip-link-pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/skip-link/skip-link-pass.js",
    "content": "describe('skip-link test pass', function () {\n  'use strict';\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      axe.run(\n        { runOnly: { type: 'rule', values: ['skip-link'] } },\n        function (err, r) {\n          assert.isNull(err);\n          results = r;\n          done();\n        }\n      );\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 3', function () {\n      assert.lengthOf(results.passes[0].nodes, 3);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 1 incomplete', function () {\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/strict-csp/strict-csp.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <script id=\"strict-csp\">\n      var thisScript = document.getElementById('strict-csp');\n      var meta = document.createElement('meta');\n      meta.setAttribute('http-equiv', 'Content-Security-Policy');\n      meta.setAttribute(\n        'content',\n        \"script-src 'nonce-4AEemGb0xJptoIGFP3Nd'; style-src 'nonce-4AEemGb0xJptoIGFP3Nd'\"\n      );\n      document.head.appendChild(meta);\n    </script>\n    <link\n      nonce=\"4AEemGb0xJptoIGFP3Nd\"\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script\n      nonce=\"4AEemGb0xJptoIGFP3Nd\"\n      src=\"/node_modules/mocha/mocha.js\"\n    ></script>\n    <script\n      nonce=\"4AEemGb0xJptoIGFP3Nd\"\n      src=\"/node_modules/chai/chai.js\"\n    ></script>\n    <script nonce=\"4AEemGb0xJptoIGFP3Nd\" src=\"/axe.js\"></script>\n    <script nonce=\"4AEemGb0xJptoIGFP3Nd\">\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <div id=\"mocha\"></div>\n    <script nonce=\"4AEemGb0xJptoIGFP3Nd\" src=\"/test/testutils.js\"></script>\n    <script nonce=\"4AEemGb0xJptoIGFP3Nd\" src=\"strict-csp.js\"></script>\n    <script\n      nonce=\"4AEemGb0xJptoIGFP3Nd\"\n      src=\"/test/integration/adapter.js\"\n    ></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/strict-csp/strict-csp.js",
    "content": "describe('strict-csp', function () {\n  'use strict';\n\n  it('should parse without errors', function () {\n    (assert.isDefined(window.axe), 'axe is not defined');\n    assert.isDefined(window.axe.run, 'axe.run is not defined');\n  });\n\n  it('should run without errors', function (done) {\n    axe.run(function (err, results) {\n      assert.isNull(err);\n      assert.isDefined(results);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/target-size/shadow-dom.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Target-size shadow DOM test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main id=\"mocha\"></main>\n    <script>\n      const shadowTemplate = document.createElement('template');\n      shadowTemplate.innerHTML =\n        '<div id=\"shadow-container\"><slot></slot></div>';\n      class ShadowOpenWebComponent extends HTMLElement {\n        connectedCallback() {\n          const shadowRoot = this.attachShadow({ mode: 'open' });\n          shadowRoot.appendChild(shadowTemplate.content.cloneNode(true));\n        }\n      }\n\n      customElements.define('shadow-open', ShadowOpenWebComponent);\n    </script>\n    <style>\n      #container {\n        font-size: 16px;\n      }\n      #title {\n        background-color: #cff;\n      }\n      #content {\n        background-color: #cfc;\n        margin-top: 10px;\n      }\n    </style>\n    <div id=\"container\">\n      <div id=\"title\">\n        <a id=\"title-link\" href=\"#target\">\n          <shadow-open>\n            <div>Title</div>\n          </shadow-open>\n        </a>\n      </div>\n      <div id=\"content\">\n        <a id=\"content-link\" href=\"#target\">\n          <div>Content</div>\n        </a>\n      </div>\n    </div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"shadow-dom.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/target-size/shadow-dom.js",
    "content": "describe('target-size shadow dom test', () => {\n  'use strict';\n  let results;\n\n  before(done => {\n    axe.testUtils.awaitNestedLoad(async () => {\n      const options = {\n        runOnly: ['target-size'],\n        elementRef: true\n      };\n      const context = {\n        // ignore the mocha links\n        exclude: '#mocha'\n      };\n      results = await axe.run(context, options);\n      done();\n    });\n  });\n\n  describe('violations', function () {\n    it('should find 0', function () {\n      assert.lengthOf(results.violations, 0);\n    });\n  });\n\n  describe('passes', function () {\n    it('should find 2', function () {\n      assert.lengthOf(results.passes[0].nodes, 2);\n    });\n  });\n\n  it('should find 0 inapplicable', function () {\n    assert.lengthOf(results.inapplicable, 0);\n  });\n\n  it('should find 0 incomplete', function () {\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/target-size/target-size.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Target-size Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main id=\"mocha\"></main>\n    <style>\n      /* color */\n      .failed.passes {\n        background: blueviolet !important;\n      }\n      .incomplete {\n        background: yellow !important;\n      }\n      .passed.violations {\n        background: blueviolet !important;\n      }\n      [hidden] {\n        display: none !important;\n      }\n      p {\n        font-size: 12px;\n        margin: 1.3em 0 0.3em;\n      }\n      .sample {\n        display: flex;\n      }\n      .sample > div {\n        outline: solid 1px #ddd;\n        padding: 12px 6px;\n        flex-basis: 95px;\n        justify-content: center;\n      }\n      .sample div {\n        position: relative;\n        display: flex;\n      }\n      .sample > div span {\n        box-shadow:\n          inset -2px -2px 0 rgba(0, 0, 0, 0.4),\n          inset 2px 2px 0 rgba(0, 0, 0, 0.4);\n        display: flex;\n        z-index: 1;\n      }\n      .sample p {\n        position: absolute;\n        font-size: 10px;\n        top: -9px;\n        left: 3px;\n        color: #333;\n      }\n      .rnd-50 {\n        border-radius: 50%;\n      }\n      .rnd-0-25 {\n        border-radius: 0 25%;\n      }\n      .rnd-0-50 {\n        border-radius: 0 50%;\n      }\n      .rnd-0-75 {\n        border-radius: 0 75%;\n      }\n      .rnd-0-100 {\n        border-radius: 0 100%;\n      }\n      .rot30 {\n        transform: rotate(-30deg);\n      }\n      .rot45 {\n        transform: rotate(-45deg);\n      }\n\n      svg path {\n        stroke: rgba(0, 0, 0, 0.4);\n        stroke-width: 2px;\n      }\n\n      /* color */\n      .passed {\n        background: linear-gradient(90deg, darkgreen 0%, lightgreen 100%);\n      }\n      .failed {\n        background: linear-gradient(90deg, pink 0%, darkred 100%);\n      }\n\n      /* height */\n      .h1 {\n        height: 6px;\n      }\n      .h2 {\n        height: 12px;\n      }\n      .h3 {\n        height: 18px;\n      }\n      .h4 {\n        height: 24px;\n      }\n      .h6 {\n        height: 36px;\n      }\n      .h7 {\n        height: 42px;\n      }\n      .h8 {\n        height: 48px;\n      }\n      .h10 {\n        height: 60px;\n      }\n      /* With */\n      .w1 {\n        width: 6px;\n      }\n      .w2 {\n        width: 12px;\n      }\n      .w3 {\n        width: 18px;\n      }\n      .w4 {\n        width: 24px;\n      }\n      .w5 {\n        width: 30px;\n      }\n      .w6 {\n        width: 36px;\n      }\n      .w7 {\n        width: 42px;\n      }\n      .w8 {\n        width: 48px;\n      }\n      .w10 {\n        width: 60px;\n      }\n      .w11 {\n        width: 66px;\n      }\n      /* Margins */\n      .m4-n2 {\n        margin-right: -12px;\n      }\n      .m4-n1 {\n        margin-right: -6px;\n      }\n      .mr1 {\n        margin-right: 6px;\n      }\n      .mr2 {\n        margin-right: 12px;\n      }\n      .mr3 {\n        margin-right: 18px;\n      }\n      .mr4 {\n        margin-right: 24px;\n      }\n      .ml-n6 {\n        margin-left: -36px;\n      }\n      .ml-n5 {\n        margin-left: -30px;\n      }\n      .ml-n4 {\n        margin-left: -24px;\n      }\n      .ml-n3 {\n        margin-left: -18px;\n      }\n      .ml-n2 {\n        margin-left: -12px;\n      }\n      .ml-n1 {\n        margin-left: -6px;\n      }\n      .ml1 {\n        margin-left: 6px;\n      }\n      .ml2 {\n        margin-left: 12px;\n      }\n      .ml3 {\n        margin-left: 18px;\n      }\n      .ml4 {\n        margin-left: 24px;\n      }\n      .ml5 {\n        margin-left: 30px;\n      }\n      .ml6 {\n        margin-left: 36px;\n      }\n      .mt-n1 {\n        margin-top: -6px;\n      }\n      .mt1 {\n        margin-top: 6px;\n      }\n      .mt2 {\n        margin-top: 12px;\n      }\n      .mt3 {\n        margin-top: 18px;\n      }\n      .mt4 {\n        margin-top: 24px;\n      }\n      .mt5 {\n        margin-top: 30px;\n      }\n      .mt6 {\n        margin-top: 36px;\n      }\n      .mt8 {\n        margin-top: 48px;\n      }\n      .mb-n1 {\n        margin-bottom: -6px;\n      }\n      .mb1 {\n        margin-bottom: 6px;\n      }\n      .mb2 {\n        margin-bottom: 12px;\n      }\n      .mb3 {\n        margin-bottom: 18px;\n      }\n      .mb4 {\n        margin-bottom: 24px;\n      }\n      .mb5 {\n        margin-bottom: 30px;\n      }\n      .mb6 {\n        margin-bottom: 36px;\n      }\n    </style>\n\n    <p>Examples A1 - A4 all pass.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h4 w4\"></span>\n        <span class=\"passed h4 w4\"></span>\n        <span class=\"passed h4 w4\"></span>\n      </div>\n      <div>\n        <span class=\"passed h3 w4\"></span>\n        <span class=\"passed h3 w4\"></span>\n        <span class=\"passed h3 w4\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w4\"></span>\n        <span class=\"passed h2 w4\"></span>\n        <span class=\"passed h2 w4\"></span>\n      </div>\n      <div>\n        <span class=\"passed h1 w4\"></span>\n        <span class=\"passed h1 w4\"></span>\n        <span class=\"passed h1 w4\"></span>\n      </div>\n    </section>\n\n    <p>Examples B1 - B3 pass, B4 fails.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w3\"></span>\n        <span class=\"passed h2 w3 ml3 mr3\"></span>\n        <span class=\"passed h2 w3\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w3\"></span>\n        <span class=\"passed h2 w3 ml2 mr2\"></span>\n        <span class=\"passed h2 w3\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w3\"></span>\n        <span class=\"passed h2 w3 ml1 mr1\"></span>\n        <span class=\"passed h2 w3\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w3\"></span>\n        <span class=\"failed h2 w3\"></span>\n        <span class=\"failed h2 w3\"></span>\n      </div>\n    </section>\n\n    <p>Examples C1 and C2 pass, C3 and C4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w2\"></span>\n        <span class=\"passed h2 w2 ml3 mr3\"></span>\n        <span class=\"passed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w2\"></span>\n        <span class=\"passed h2 w2 ml2 mr2\"></span>\n        <span class=\"passed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w2\"></span>\n        <span class=\"failed h2 w2 ml1 mr1\"></span>\n        <span class=\"failed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w2\"></span>\n        <span class=\"failed h2 w2\"></span>\n        <span class=\"failed h2 w2\"></span>\n      </div>\n    </section>\n\n    <p>Example D1 passes, D2 - D4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w1\"></span>\n        <span class=\"passed h2 w1 ml3 mr3\"></span>\n        <span class=\"passed h2 w1\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w1\"></span>\n        <span class=\"failed h2 w1 ml2 mr2\"></span>\n        <span class=\"failed h2 w1\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w1\"></span>\n        <span class=\"failed h2 w1 ml1 mr1\"></span>\n        <span class=\"failed h2 w1\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w1\"></span>\n        <span class=\"failed h2 w1\"></span>\n        <span class=\"failed h2 w1\"></span>\n      </div>\n    </section>\n\n    <p>Example E1 - E3 pass, E4 fails.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w2\"></span>\n        <span class=\"passed h2 w4 ml3 mr3\"></span>\n        <span class=\"passed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w2\"></span>\n        <span class=\"passed h2 w4 ml2 mr2\"></span>\n        <span class=\"passed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w2\"></span>\n        <span class=\"passed h2 w4 ml1 mr1\"></span>\n        <span class=\"passed h2 w2\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w2\"></span>\n        <span class=\"failed h2 w4\"></span>\n        <span class=\"failed h2 w2\"></span>\n      </div>\n    </section>\n\n    <p>Example F1 - F3 pass, F4 fails.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w4\"></span>\n        <span class=\"passed h2 w2 ml3 mr3\"></span>\n        <span class=\"passed h2 w4\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w4\"></span>\n        <span class=\"passed h2 w2 ml2 mr2\"></span>\n        <span class=\"passed h2 w4\"></span>\n      </div>\n      <div>\n        <span class=\"passed h2 w4\"></span>\n        <span class=\"passed h2 w2 ml1 mr1\"></span>\n        <span class=\"passed h2 w4\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w4\"></span>\n        <span class=\"failed h2 w2\"></span>\n        <span class=\"failed h2 w4\"></span>\n      </div>\n    </section>\n\n    <p>Example G1 - G3 pass, the outer element of G4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w6 ml4\">\n          <span class=\"passed h4 w4 mt1 ml-n4\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w6 ml3\">\n          <span class=\"passed h4 w4 mt1 ml-n3\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w6 ml2\">\n          <span class=\"passed h4 w4 mt1 ml-n2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w6 ml1\">\n          <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example H1 and H2 pass, the outer element of H3 and H4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w10\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w10\">\n          <span class=\"passed h4 w4 mt1 ml2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w10\">\n          <span class=\"passed h4 w4 mt1 ml3\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w10\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n          <span class=\"passed h4 w4 mt1\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example I1 and I2 pass, I3 and I4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h4 w6 ml6\">\n          <span class=\"passed h2 w6 mt1 ml-n6\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h4 w6 ml4\">\n          <span class=\"passed h2 w6 mt1 ml-n5\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6 ml2\">\n          <span class=\"failed h2 w6 mt1 ml-n2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6\">\n          <span class=\"failed h2 w6 mt1\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example J1 and J2 pass, J3 and J4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h4 w6 ml6\">\n          <span class=\"passed h2 w6 ml-n6\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h4 w6 ml4\">\n          <span class=\"passed h2 w6 ml-n5\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6 ml2\">\n          <span class=\"failed h2 w6 ml-n2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6\">\n          <span class=\"failed h2 w6\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example K1 and K2 pass, K3 and K4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h4 w6 ml4\">\n          <span class=\"passed h2 w5 mt1 ml-n5\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h4 w6 ml4\">\n          <span class=\"passed h2 w6 mt1 ml-n5\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6 ml4\">\n          <span class=\"failed h2 w7 mt1 ml-n4\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h4 w6 ml4\">\n          <span class=\"failed h2 w8 mt1 ml-n5\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example L1 and L2 pass, the outer element of L3 and L4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h2 w2 rnd-50\"></span>\n        <span class=\"passed h2 w2 mt4 ml-n2 rnd-50\"></span>\n        <span class=\"passed h2 w2 mt8 ml-n2 rnd-50\"></span>\n      </div>\n      <div>\n        <div class=\"rot30\">\n          <span class=\"passed h2 w2 rnd-50\"></span>\n          <span class=\"passed h2 w2 mt4 ml-n2 rnd-50\"></span>\n          <span class=\"passed h2 w2 mt8 ml-n2 rnd-50\"></span>\n        </div>\n      </div>\n      <div>\n        <span class=\"passed h2 w2 rnd-50\"></span>\n        <span class=\"passed h2 w2 mt3 ml1 rnd-50\"></span>\n        <span class=\"passed h2 w2 mt6 ml1 rnd-50\"></span>\n      </div>\n      <div>\n        <span class=\"failed h2 w2 rnd-50\"></span>\n        <span class=\"failed h2 w2 mt3 rnd-50\"></span>\n        <span class=\"failed h2 w2 mt6 rnd-50\"></span>\n      </div>\n    </section>\n\n    <p>Example M1 - M3 pass, the outer element of M4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w6 ml3 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml-n3\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w6 ml2 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml-n2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w6 ml1 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w6 ml1 rnd-50\">\n          <span class=\"failed h2 w2 mt2 ml1\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example N1 - N3 pass, the outer element of N4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w10 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w10 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml2\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"failed h6 w10 rnd-50\">\n          <span class=\"passed h4 w4 mt1 ml3\"></span>\n        </span>\n      </div>\n      <div>\n        <!-- this should technically fail the target-size rule if we were implementing the size based on the largest inscribed square -->\n        <span class=\"passed h6 w10 rnd-50\">\n          <span class=\"failed h2 w2 mt2 ml4\"></span>\n        </span>\n      </div>\n    </section>\n\n    <p>Example O1 and O2 pass, the inner element of O3 and O4 fail.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w6 ml4\">\n          <span class=\"passed h4 w4 mt1 ml-n4\"></span>\n        </span>\n        <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n      </div>\n      <div>\n        <span class=\"passed h6 w6 ml3\">\n          <span class=\"passed h4 w4 mt1 ml-n3\"></span>\n        </span>\n        <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n      </div>\n      <div>\n        <span class=\"failed h6 w6 ml2\">\n          <span class=\"passed h4 w4 mt1 ml-n2\"></span>\n        </span>\n        <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n      </div>\n      <div>\n        <span class=\"failed h6 w6 ml1\">\n          <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n        </span>\n        <span class=\"passed h4 w4 mt1 ml-n1\"></span>\n      </div>\n    </section>\n\n    <p>Example P.</p>\n    <section class=\"sample\">\n      <div>\n        <span class=\"passed h6 w10 rnd-0-25\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w10 rnd-0-50\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n      <div>\n        <span class=\"passed h6 w10 rnd-0-75\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n      <div>\n        <!-- this should technically fail the target-size rule if we were implementing the size based on the largest inscribed square -->\n        <span class=\"passed h6 w10 rnd-0-100\">\n          <span class=\"passed h4 w4 mt1 ml1\"></span>\n        </span>\n      </div>\n    </section>\n\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"target-size.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/target-size/target-size.js",
    "content": "describe('target-size test', function () {\n  'use strict';\n  var results;\n\n  before(function (done) {\n    axe.testUtils.awaitNestedLoad(function () {\n      // Add necessary markup for axe to recognize these as components:\n      document.querySelectorAll('section span').forEach(function (link) {\n        link.setAttribute('role', 'link');\n        link.setAttribute('tabindex', '0');\n      });\n\n      var options = {\n        runOnly: ['target-size'],\n        elementRef: true\n      };\n      axe.run('section', options, function (err, r) {\n        if (err) {\n          done(err);\n        }\n        results = r;\n        // Add some highlighting for visually identifying issues.\n        // There are too many test cases to just do this by selector.\n        results.violations[0] &&\n          results.violations[0].nodes.forEach(function (node) {\n            node.element.className += ' violations';\n          });\n        results.passes[0] &&\n          results.passes[0].nodes.forEach(function (node) {\n            node.element.className += ' passes';\n          });\n        console.log(results);\n        done();\n      });\n    });\n  });\n\n  it('finds all passing nodes', function () {\n    var passResults = results.passes[0] ? results.passes[0].nodes : [];\n    var passedElms = document.querySelectorAll(\n      'section:not([hidden]) div:not([hidden]) .passed'\n    );\n    passResults.forEach(function (result) {\n      assert.include(passedElms, result.element);\n    });\n    assert.lengthOf(passResults, passedElms.length);\n  });\n\n  it('finds all failed nodes', function () {\n    var failResults = results.violations[0] ? results.violations[0].nodes : [];\n    var failedElms = document.querySelectorAll(\n      'section:not([hidden]) div:not([hidden])  .failed'\n    );\n    failResults.forEach(function (result) {\n      assert.include(failedElms, result.element);\n    });\n    assert.lengthOf(failResults, failedElms.length);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/target-size/too-many-rects.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Target-size too many rects Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <main id=\"mocha\"></main>\n\n    <section id=\"incomplete\">\n      <div\n        id=\"incomplete-many-rects\"\n        role=\"button\"\n        tabindex=\"0\"\n        style=\"display: inline-block\"\n      >\n        <table id=\"tab-table\"></table>\n      </div>\n    </section>\n\n    <script>\n      let html = '';\n      for (let i = 0; i < 100; i++) {\n        html += `\n        <tr>\n          <td><a href=\"#\">A</a></td>\n          <td><button>B</button></td>\n          <td><button>C</button></td>\n          <td><button>D</button></td>\n        </tr>`;\n      }\n      document.querySelector('#tab-table').innerHTML = html;\n    </script>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"too-many-rects.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/target-size/too-many-rects.js",
    "content": "describe('target-size too many rects test', () => {\n  'use strict';\n  let results;\n\n  before(done => {\n    axe.testUtils.awaitNestedLoad(() => {\n      const options = {\n        runOnly: ['target-size'],\n        elementRef: true\n      };\n      const context = {\n        include: '#incomplete',\n        // ignore the incomplete table results\n        exclude: 'table tr'\n      };\n      axe.run(context, options, (err, r) => {\n        if (err) {\n          done(err);\n        }\n        results = r;\n        console.log(results);\n        done();\n      });\n    });\n  });\n\n  it('incompletes with too many rects', () => {\n    const incompleteNodes = results.incomplete[0]\n      ? results.incomplete[0].nodes\n      : [];\n    assert.lengthOf(incompleteNodes, 1);\n    assert.include(\n      incompleteNodes[0].element,\n      document.querySelector('#incomplete-many-rects')\n    );\n  });\n});\n"
  },
  {
    "path": "test/integration/full/test-webdriver.js",
    "content": "const { globSync } = require('glob');\nconst chrome = require('selenium-webdriver/chrome');\nconst firefox = require('selenium-webdriver/firefox');\nconst chromedriver =\n  process.env.CHROMEDRIVER_BIN ?? require('chromedriver').path;\n\nconst args = process.argv.slice(2);\n\n// allow running certain browsers through command line args\n// (only one browser supported, run multiple times for more browsers)\nlet browserArg = 'chrome';\nargs.forEach(arg => {\n  // pattern: browsers=Chrome\n  const parts = arg.split('=');\n  if (parts[0] === 'browser') {\n    browserArg = parts[1].toLowerCase();\n  }\n});\n\n/**\n * Keep injecting scripts until window.mochaResults is set\n */\nfunction collectTestResults(driver) {\n  // inject a script that waits half a second\n  return driver\n    .executeAsyncScript(() => {\n      const callback = arguments[arguments.length - 1];\n      setTimeout(() => {\n        if (!window.mocha) {\n          callback('mocha-missing;' + window.location.href);\n        }\n        // return the mocha results (or undefined if not finished)\n        callback(window.mochaResults);\n      }, 500);\n    })\n    .then(result => {\n      // If there are no results, listen a little longer\n      if (typeof result === 'string' && result.includes('mocha-missing')) {\n        throw new Error(\n          'Mocha does not exist in: ' +\n            result.split(';')[1] +\n            '\\nIf using a frame, put the file in a subdirectory'\n        );\n      }\n      if (!result) {\n        return collectTestResults(driver);\n\n        // if there are, return them\n      } else {\n        return Promise.resolve(result);\n      }\n    });\n}\n\n/**\n * Test each URL\n */\nfunction runTestUrls(driver, isMobile, urls, errors) {\n  const url = urls.shift();\n  errors = errors || [];\n\n  return (\n    driver\n      .get(url)\n      // Get results\n      .then(() => {\n        return Promise.all([\n          driver.getCapabilities(),\n          collectTestResults(driver)\n        ]);\n      })\n      // And process them\n      .then(promiseResults => {\n        const capabilities = promiseResults[0];\n        const result = promiseResults[1];\n        const browserName =\n          capabilities.get('browserName') +\n          (capabilities.get('mobileEmulationEnabled') ? '-mobile' : '');\n        console.log(url + ' [' + browserName + ']');\n\n        // Remember the errors\n        (result.reports || []).forEach(err => {\n          console.log(err.message);\n          err.url = url;\n          err.browser = browserName;\n          errors.push(err);\n        });\n\n        // Log the result of the page tests\n        console[result.failures ? 'error' : 'log'](\n          'passes: ' +\n            result.passes +\n            ', ' +\n            'failures: ' +\n            result.failures +\n            ', ' +\n            'duration: ' +\n            result.duration / 1000 +\n            's'\n        );\n        console.log();\n      })\n      .then(() => {\n        // Start the next job, if any\n        if (urls.length > 0) {\n          return runTestUrls(driver, isMobile, urls, errors);\n        } else {\n          driver.quit();\n          return Promise.resolve(errors);\n        }\n      })\n  );\n}\n\n/*\n * Build web driver depends whether REMOTE_SELENIUM_URL is set\n */\nfunction buildWebDriver(browser) {\n  let webdriver;\n  const mobileBrowser = browser.split('-mobile');\n\n  // fix chrome DevToolsActivePort file doesn't exist in CircleCI (as well as a\n  // host of other problems with starting Chrome). the only thing that seems to\n  // allow Chrome to start without problems consistently is using ChromeHeadless\n  // @see https://stackoverflow.com/questions/50642308/webdriverexception-unknown-error-devtoolsactiveport-file-doesnt-exist-while-t\n  if (browser === 'chrome') {\n    const service = new chrome.ServiceBuilder(chromedriver).build();\n\n    const options = new chrome.Options().addArguments([\n      '--headless',\n      '--no-sandbox',\n      '--disable-extensions',\n      '--disable-dev-shm-usage'\n    ]);\n\n    if (process.env.CHROME_BIN) {\n      options.setBinaryPath(process.env.CHROME_BIN);\n    }\n\n    webdriver = chrome.Driver.createSession(options, service);\n  } else if (browser === 'firefox') {\n    const options = new firefox.Options().addArguments('--headless');\n\n    if (process.env.FIREFOX_BIN) {\n      options.setBinary(process.env.FIREFOX_BIN);\n    }\n\n    webdriver = firefox.Driver.createSession(options);\n  }\n\n  if (process.env.REMOTE_SELENIUM_URL) {\n    webdriver.usingServer(process.env.REMOTE_SELENIUM_URL);\n  }\n\n  return {\n    driver: webdriver,\n    isMobile: mobileBrowser.length > 1\n  };\n}\n\nfunction start(options) {\n  // yes, really, and this isn't documented anywhere either.\n  options.browser =\n    options.browser === 'edge' ? 'MicrosoftEdge' : options.browser;\n\n  const testUrls = globSync(['test/integration/full/**/*.{html,xhtml}'], {\n    ignore: '**/frames/**/*.{html,xhtml}'\n  }).map(url => {\n    return 'http://localhost:9876/' + url;\n  });\n\n  if (\n    (process.platform === 'win32' && options.browser === 'safari') ||\n    (process.platform === 'darwin' &&\n      ['ie', 'MicrosoftEdge'].indexOf(options.browser) !== -1) ||\n    ((process.platform === 'linux' || process.env.REMOTE_SELENIUM_URL) &&\n      ['ie', 'MicrosoftEdge', 'safari'].indexOf(options.browser) !== -1)\n  ) {\n    console.log();\n    console.log(\n      'Skipped ' + options.browser + ' as it is not supported on this platform'\n    );\n    return process.exit();\n  }\n\n  // try to load the browser\n  const webDriver = buildWebDriver(options.browser);\n  const driver = webDriver.driver;\n  const isMobile = webDriver.isMobile;\n\n  driver.manage().setTimeouts({\n    pageLoad: 50000,\n    script: !isMobile ? 60000 * 5 : 60000 * 10\n  });\n\n  // Test all pages\n  runTestUrls(driver, isMobile, testUrls)\n    .then(testErrors => {\n      // log each error and abort\n      testErrors.forEach(err => {\n        console.log();\n        console.log('URL: ' + err.url);\n        console.log('Browser: ' + err.browser);\n        console.log('Describe: ' + err.titles.join(' > '));\n        console.log('it ' + err.name);\n        console.log(err.stack);\n        console.log();\n      });\n\n      process.exit(testErrors.length);\n\n      // catch any potential problems\n    })\n    .catch(err => {\n      console.log(err);\n      process.exit(1);\n    });\n}\n\nstart({ browser: browserArg });\n"
  },
  {
    "path": "test/integration/full/umd/mock-define.js",
    "content": "/* eslint no-unused-vars: 0 */\n\n// Mock define method to collect calls to define\nvar defineCalls = [];\nfunction define() {\n  'use strict';\n  defineCalls.push(arguments);\n}\ndefine.amd = true;\n"
  },
  {
    "path": "test/integration/full/umd/mock-module-exports.js",
    "content": "/* eslint no-unused-vars: 0 */\n\n// Mock module object to test module.export being set\nvar module = {\n  exports: {}\n};\n"
  },
  {
    "path": "test/integration/full/umd/umd-define.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>UMD define Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"mock-define.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"umd-define.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/umd/umd-define.js",
    "content": "/* global defineCalls */\ndescribe('UMD define', function () {\n  'use strict';\n\n  it('should have atleast one umd global', function () {\n    assert.isAtLeast(defineCalls.length, 1);\n  });\n\n  it('calls define and passes it axe', function () {\n    var call = defineCalls[defineCalls.length - 1];\n    assert.isFunction(call[2]);\n    assert.strictEqual(call[2](), axe);\n  });\n\n  it('defines module name as axe-core', function () {\n    var call = defineCalls[defineCalls.length - 1];\n    assert.equal(call[0], 'axe-core');\n  });\n});\n"
  },
  {
    "path": "test/integration/full/umd/umd-module-exports.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>UMD module.exports Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"mock-module-exports.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"umd-module-exports.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/umd/umd-module-exports.js",
    "content": "/* global module */\ndescribe('UMD module.export', function () {\n  'use strict';\n\n  it('registers axe to module.exports', function () {\n    assert.strictEqual(module.exports, axe);\n  });\n\n  it('does not use `require` functions', function () {\n    var result;\n    var requireRegex = /[^.]require\\(([^\\)])\\)/g;\n\n    // This is to avoid colliding with Cypress.js which overloads all\n    // uses of variables named `require`.\n    while ((result = requireRegex.exec(axe.source)) !== null) {\n      // Allow 'crypto' as it is used in an unobtrusive way.\n      assert.includes(\n        result[1],\n        'crypto',\n        'Axe source should not contain `require` variables'\n      );\n    }\n  });\n\n  it('should include doT', function () {\n    var doT = axe.imports.doT;\n    assert(doT, 'doT is registered on axe.imports');\n    assert.equal(doT.name, 'doT');\n  });\n});\n"
  },
  {
    "path": "test/integration/full/umd/umd-window.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>UMD window.axe Test</title>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <script src=\"umd-window.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/umd/umd-window.js",
    "content": "/*global Promise */\ndescribe('UMD window', function () {\n  'use strict';\n\n  it('should expose axe as a property of window', function () {\n    assert.property(window, 'axe');\n  });\n\n  it('should expose Promise as a property of window', function () {\n    assert.property(window, 'Promise');\n  });\n\n  it('should resolve Promise(s)', function (done) {\n    var p1 = new Promise(function (resolve) {\n      setTimeout(function () {\n        resolve('Hello');\n      });\n    });\n    var p2 = new Promise(function (resolve) {\n      setTimeout(function () {\n        resolve('World!');\n      });\n    });\n    Promise.all([p1, p2])\n      .then(function (values) {\n        assert.lengthOf(values, 2);\n        assert.equal(values.join(' '), 'Hello World!');\n        done();\n      })\n      .catch(function () {\n        done(new Error('Expected to resolve.'));\n      });\n  });\n  it('should reject Promise', function (done) {\n    new Promise(function (resolve, reject) {\n      setTimeout(function () {\n        reject(new Error('Boom!'));\n      });\n    })\n      .then(function () {\n        done(new Error('Expected to reject.'));\n      })\n      .catch(function (err) {\n        assert.isDefined(err);\n        done();\n      });\n  });\n\n  it('should ensure axe has prototype chained keys', function () {\n    assert.hasAnyKeys(axe, ['utils', 'commons', 'core']);\n  });\n});\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail.js",
    "content": "describe('html-xml-lang-mismatch test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['html-xml-lang-mismatch']\n        }\n      },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  });\n\n  describe('violations', function () {\n    it('should find one', function () {\n      assert.lengthOf(results.violations[0].nodes, 1);\n    });\n\n    it('should find html', function () {\n      assert.deepEqual(results.violations[0].nodes[0].target, ['html']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail1.html",
    "content": "<!doctype html>\n<html lang=\"fr\" xml:lang=\"en\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.fail2.html",
    "content": "<!doctype html>\n<html lang=\"fr-CA\" xml:lang=\"en-CA\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.fail.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable.js",
    "content": "describe('html-xml-lang-mismatch test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['html-xml-lang-mismatch']\n        }\n      },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  });\n\n  describe('inapplicable', function () {\n    it('should find one', function () {\n      assert.lengthOf(results.inapplicable, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable1.html",
    "content": "<!doctype html>\n<html lang=\"fr\" xml:lang=\"\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.inapplicable2.html",
    "content": "<!doctype html>\n<html lang=\"\" xml:lang=\"\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.inapplicable.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass.js",
    "content": "describe('html-xml-lang-mismatch test', function () {\n  'use strict';\n\n  var results;\n  before(function (done) {\n    axe.run(\n      {\n        runOnly: {\n          type: 'rule',\n          values: ['html-xml-lang-mismatch']\n        }\n      },\n      function (err, r) {\n        assert.isNull(err);\n        results = r;\n        done();\n      }\n    );\n  });\n\n  describe('passes', function () {\n    it('should find one', function () {\n      assert.lengthOf(results.passes[0].nodes, 1);\n    });\n\n    it('should find html', function () {\n      assert.deepEqual(results.passes[0].nodes[0].target, ['html']);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass1.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"en\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass2.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"En\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass3.html",
    "content": "<!doctype html>\n<html lang=\"en\" xml:lang=\"en-GB\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass4.html",
    "content": "<!doctype html>\n<html lang=\"en-GB\" xml:lang=\"en\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/full/xml-lang-mismatch/xml-lang-mismatch.pass5.html",
    "content": "<!doctype html>\n<html lang=\"en-XYZ\" xml:lang=\"en\">\n  <head>\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"/node_modules/mocha/mocha.css\"\n    />\n    <script src=\"/node_modules/mocha/mocha.js\"></script>\n    <script src=\"/node_modules/chai/chai.js\"></script>\n    <script src=\"/axe.js\"></script>\n    <script>\n      mocha.setup({\n        timeout: 10000,\n        ui: 'bdd'\n      });\n      var assert = chai.assert;\n    </script>\n  </head>\n  <body>\n    <div id=\"mocha\" role=\"complementary\"></div>\n    <script src=\"/test/testutils.js\"></script>\n    <script src=\"xml-lang-mismatch.pass.js\"></script>\n    <script src=\"/test/integration/adapter.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/no-ui-reporter.js",
    "content": "/*global mocha, console */\n(function () {\n  'use strict';\n\n  if (!mocha || !mocha.reporter || !mocha.reporter('base')) {\n    return;\n  }\n\n  var Base = mocha.reporter('base')._reporter;\n  mocha.reporter(function (runner) {\n    Base.call(this, runner);\n    var passes = 0;\n    var failures = 0;\n\n    runner.on('pass', function (test) {\n      passes++;\n      console.log('pass: %s', test.fullTitle());\n    });\n\n    runner.on('fail', function (test, err) {\n      failures++;\n      console.error('fail: %s -- error: %s', test.fullTitle(), err.message);\n    });\n\n    runner.on('end', function () {\n      console.log('end: %d/%d', passes, passes + failures);\n      var mochaFixture = document.getElementById('mocha');\n      if (mochaFixture) {\n        var html = '<div style=\"color: ' + (failures ? 'red' : 'green') + '\">';\n        html += passes + '/' + (failures + passes);\n        html += ' tests passed</div>';\n        mochaFixture.innerHTML = html;\n      }\n    });\n  });\n})();\n"
  },
  {
    "path": "test/integration/rules/README.md",
    "content": "# Rule Integration Tests\n\nRule Integration tests take an HTML snippet file and runs an axe-core rule against it. The results for the run are then compared against the companion JSON file to ensure that every node returns as the expected result (passes, violation, incomplete, inapplicable).\n\nTo run the rule integration tests, run `npm run test:unit:integration`. You can run and debug the tests in a non-headless browser by running `npm run test:debug -- testDirs=integration`. You can either use that browser's debugger or attach an external debugger on port 9765; [a VS Code launch profile](../../../.vscode/launch.json) is provided.\n\nWhen the tests are run, each JSON file is converted into a test suite file using [Karmas preprocessor](https://karma-runner.github.io/latest/config/preprocessors.html) and [runner.js](./runner.js) as the test suite template.\n\nThe JSON file for a rule integration test contains the following information:\n\n- `description` - string(required). The title of the test. Used in the `describe` block of the test suite\n- `rule` - string(required) The axe-core rule to run\n- `violations` array(optional). List of axe-core selectors of nodes that should return as Violations\n- `passes` array(optional). List of axe-core selectors of nodes that should return as Passes\n- `incomplete` array(optional). List of axe-core selectors of nodes that should return as Needs Review\n\nThe JSON file should have at least one of the `violations`, `passes`, or `incomplete` arrays. Inapplicable results are not listed as the test will fail if any node is found in one of the 3 arrays that is not explicitly listed.\n"
  },
  {
    "path": "test/integration/rules/accesskeys/accesskeys.html",
    "content": "<div id=\"violation1\" accesskey=\"B\">violation 1</div>\n<div id=\"violation2\" accesskey=\"A\">violation 2</div>\n<div id=\"pass1\" accesskey=\"C\">pass 1</div>\n<div accesskey=\"B\"></div>\n<iframe\n  src=\"/integration/rules/accesskeys/frame.html\"\n  title=\"violation test\"\n  id=\"frame\"\n></iframe>\n\n<div id=\"ignore1\" accesskey=\"C\" style=\"display: none\">ignore 1</div>\n"
  },
  {
    "path": "test/integration/rules/accesskeys/accesskeys.json",
    "content": "{\n  \"description\": \"accesskeys tests\",\n  \"rule\": \"accesskeys\",\n  \"violations\": [[\"#violation1\"], [\"#violation2\"]],\n  \"passes\": [[\"#pass1\"], [\"#frame\", \"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/accesskeys/frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>ACCCESKEYS test</title>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <div accesskey=\"A\"></div>\n    <div id=\"pass2\" accesskey=\"D\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/area-alt/area-alt.html",
    "content": "<div id=\"monkeys\">Bananas</div>\n<map>\n  <area href=\"#\" id=\"violation1\" alt=\"\" />\n  <area href=\"#\" id=\"violation2\" aria-label=\"\" />\n  <area href=\"#\" id=\"violation3\" aria-labelledby=\"nomatchy\" />\n  <area href=\"#\" id=\"violation4\" aria-labelledby=\"\" />\n  <area href=\"#\" id=\"pass1\" alt=\"monkeys\" />\n  <area href=\"#\" id=\"pass2\" aria-label=\"monkeys\" />\n  <area href=\"#\" id=\"pass3\" aria-labelledby=\"monkeys\" />\n\n  <!-- no HREF, not violations -->\n  <area alt=\"\" />\n  <area aria-label=\"\" />\n  <area aria-labelledby=\"\" />\n  <area aria-labelledby=\"nomatchy\" />\n</map>\n"
  },
  {
    "path": "test/integration/rules/area-alt/area-alt.json",
    "content": "{\n  \"description\": \"area-alt tests\",\n  \"rule\": \"area-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/failures.html",
    "content": "<div role=\"alert\" aria-selected=\"true\" id=\"fail1\"></div>\n<div role=\"link\" aria-selected=\"true\" id=\"fail2\"></div>\n<div role=\"row\" aria-colcount=\"value\" id=\"fail3\"></div>\n<div role=\"row\" aria-rowcount=\"value\" id=\"fail4\"></div>\n\n<audio\n  src=\"/test/assets/moon-speech.mp3\"\n  controls\n  aria-orientation=\"horizontal\"\n  id=\"fail5\"\n></audio>\n\n<div id=\"fail6\" role=\"combobox\" aria-multiline=\"false\"></div>\n<div id=\"fail7\" role=\"combobox\" aria-multiline=\"true\" contenteditable></div>\n<div id=\"fail8\" role=\"combobox\" aria-multiline=\"true\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/failures.json",
    "content": "{\n  \"description\": \"aria-allowed-attr failing tests\",\n  \"rule\": \"aria-allowed-attr\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/incomplete.html",
    "content": "<my-custom-elm id=\"incomplete1\" aria-expanded=\"true\">Foo</my-custom-elm>\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/incomplete.json",
    "content": "{\n  \"description\": \"aria-allowed-attr incomplete tests\",\n  \"rule\": \"aria-allowed-attr\",\n  \"incomplete\": [[\"#incomplete1\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/passes.html",
    "content": "<div role=\"dialog\" aria-cats=\"meow\" aria-scale=\"2.4\" id=\"pass0\">\n  should not report on invalid attributes\n</div>\n\n<div\n  role=\"alert\"\n  id=\"pass1\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"alertdialog\"\n  id=\"pass2\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-modal=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"application\"\n  id=\"pass3\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"article\"\n  id=\"pass4\"\n  aria-expanded=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"banner\"\n  id=\"pass5\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"button\"\n  id=\"pass6\"\n  aria-expanded=\"value\"\n  aria-pressed=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"checkbox\"\n  id=\"pass7\"\n  aria-checked=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-readonly=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"columnheader\"\n  id=\"pass8\"\n  aria-colindex=\"value\"\n  aria-colspan=\"value\"\n  aria-expanded=\"value\"\n  aria-rowindex=\"value\"\n  aria-rowspan=\"value\"\n  aria-sort=\"value\"\n  aria-readonly=\"value\"\n  aria-selected=\"value\"\n  aria-required=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"combobox\"\n  id=\"pass9\"\n  aria-autocomplete=\"value\"\n  aria-required=\"value\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"complementary\"\n  id=\"pass10\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"contentinfo\"\n  id=\"pass11\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"definition\"\n  id=\"pass12\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"dialog\"\n  id=\"pass13\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-modal=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"directory\"\n  id=\"pass14\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"document\"\n  id=\"pass15\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"feed\"\n  id=\"pass16\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"form\"\n  id=\"pass17\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"grid\"\n  id=\"pass18\"\n  aria-level=\"value\"\n  aria-multiselectable=\"value\"\n  aria-readonly=\"value\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-colcount=\"value\"\n  aria-rowcount=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"gridcell\"\n  id=\"pass19\"\n  aria-colindex=\"value\"\n  aria-colspan=\"value\"\n  aria-rowindex=\"value\"\n  aria-rowspan=\"value\"\n  aria-selected=\"value\"\n  aria-readonly=\"value\"\n  aria-expanded=\"value\"\n  aria-required=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"group\"\n  id=\"pass20\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"heading\"\n  id=\"pass21\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"img\"\n  id=\"pass22\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"link\"\n  id=\"pass23\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"list\"\n  id=\"pass24\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"listbox\"\n  id=\"pass25\"\n  aria-activedescendant=\"value\"\n  aria-multiselectable=\"value\"\n  aria-required=\"value\"\n  aria-expanded=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-readonly=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"listitem\"\n  id=\"pass26\"\n  aria-level=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"log\"\n  id=\"pass27\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"main\"\n  id=\"pass28\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"marquee\"\n  id=\"pass29\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"math\"\n  id=\"pass30\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"menu\"\n  id=\"pass31\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"menubar\"\n  id=\"pass32\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"menuitem\"\n  id=\"pass33\"\n  aria-posinset=\"value\"\n  aria-expanded=\"value\"\n  aria-setsize=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"menuitemcheckbox\"\n  id=\"pass34\"\n  aria-checked=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-expanded=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"menuitemradio\"\n  id=\"pass35\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-checked=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-expanded=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"navigation\"\n  id=\"pass36\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"note\"\n  id=\"pass37\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"option\"\n  id=\"pass38\"\n  aria-selected=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-checked=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"progressbar\"\n  id=\"pass39\"\n  aria-valuetext=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-expanded=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"radio\"\n  id=\"pass40\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-checked=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"radiogroup\"\n  id=\"pass41\"\n  aria-activedescendant=\"value\"\n  aria-required=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-readonly=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"region\"\n  id=\"pass42\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"row\"\n  id=\"pass43\"\n  aria-colindex=\"value\"\n  aria-rowindex=\"value\"\n  aria-level=\"value\"\n  aria-selected=\"value\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"rowgroup\"\n  id=\"pass44\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"rowheader\"\n  id=\"pass45\"\n  aria-colindex=\"value\"\n  aria-colspan=\"value\"\n  aria-rowindex=\"value\"\n  aria-rowspan=\"value\"\n  aria-sort=\"value\"\n  aria-required=\"value\"\n  aria-readonly=\"value\"\n  aria-expanded=\"value\"\n  aria-selected=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"scrollbar\"\n  id=\"pass46\"\n  aria-valuetext=\"value\"\n  aria-controls=\"value\"\n  aria-orientation=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"search\"\n  id=\"pass47\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"separator\"\n  id=\"pass48\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n  aria-valuetext=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"slider\"\n  id=\"pass49\"\n  aria-valuetext=\"value\"\n  aria-orientation=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-readonly=\"value\"\n  aria-required=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"spinbutton\"\n  id=\"pass50\"\n  aria-valuetext=\"value\"\n  aria-required=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-readonly=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"status\"\n  id=\"pass51\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"tab\"\n  id=\"pass52\"\n  aria-selected=\"value\"\n  aria-expanded=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"tablist\"\n  id=\"pass53\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-level=\"value\"\n  aria-multiselectable=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"tabpanel\"\n  id=\"pass54\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"textbox\"\n  id=\"pass55\"\n  aria-activedescendant=\"value\"\n  aria-autocomplete=\"value\"\n  aria-multiline=\"value\"\n  aria-readonly=\"value\"\n  aria-required=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-placeholder=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"timer\"\n  id=\"pass56\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"toolbar\"\n  id=\"pass57\"\n  aria-activedescendant=\"value\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-orientation=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"tooltip\"\n  id=\"pass58\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"tree\"\n  id=\"pass59\"\n  aria-activedescendant=\"value\"\n  aria-multiselectable=\"value\"\n  aria-required=\"value\"\n  aria-expanded=\"value\"\n  aria-orientation=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"treegrid\"\n  id=\"pass60\"\n  aria-activedescendant=\"value\"\n  aria-colcount=\"value\"\n  aria-expanded=\"value\"\n  aria-level=\"value\"\n  aria-multiselectable=\"value\"\n  aria-orientation=\"value\"\n  aria-readonly=\"value\"\n  aria-required=\"value\"\n  aria-rowcount=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"treeitem\"\n  id=\"pass61\"\n  aria-checked=\"value\"\n  aria-selected=\"value\"\n  aria-expanded=\"value\"\n  aria-level=\"value\"\n  aria-posinset=\"value\"\n  aria-setsize=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"switch\"\n  id=\"pass62\"\n  aria-expanded=\"value\"\n  aria-checked=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-required=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"cell\"\n  id=\"pass63\"\n  aria-colindex=\"value\"\n  aria-colspan=\"value\"\n  aria-rowindex=\"value\"\n  aria-rowspan=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"searchbox\"\n  id=\"pass64\"\n  aria-activedescendant=\"value\"\n  aria-autocomplete=\"value\"\n  aria-multiline=\"value\"\n  aria-readonly=\"value\"\n  aria-required=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n  aria-placeholder=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"term\"\n  id=\"pass65\"\n  aria-expanded=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"text\"\n  id=\"pass66\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<div\n  role=\"table\"\n  id=\"pass67\"\n  aria-colcount=\"value\"\n  aria-rowcount=\"value\"\n  aria-atomic=\"value\"\n  aria-braillelabel=\"value\"\n  aria-brailleroledescription=\"value\"\n  aria-busy=\"value\"\n  aria-controls=\"value\"\n  aria-describedby=\"value\"\n  aria-description=\"value\"\n  aria-details=\"value\"\n  aria-disabled=\"value\"\n  aria-dropeffect=\"value\"\n  aria-flowto=\"value\"\n  aria-grabbed=\"value\"\n  aria-haspopup=\"value\"\n  aria-hidden=\"value\"\n  aria-invalid=\"value\"\n  aria-keyshortcuts=\"value\"\n  aria-label=\"value\"\n  aria-labelledby=\"value\"\n  aria-live=\"value\"\n  aria-owns=\"value\"\n  aria-relevant=\"value\"\n>\n  ok\n</div>\n\n<input type=\"reset\" id=\"pass68\" value=\"Reset\" aria-pressed=\"true\" />\n\n<input type=\"radio\" aria-required=\"true\" id=\"pass69\" />\n<div role=\"radio\" aria-required=\"true\" id=\"pass70\">ok</div>\n\n<input type=\"checkbox\" aria-required=\"true\" id=\"pass71\" />\n<div role=\"checkbox\" aria-required=\"true\" id=\"pass72\">ok</div>\n\n<button id=\"pass73\" aria-roledescription=\"attachment button\"></button>\n<input\n  type=\"checkbox\"\n  id=\"pass74\"\n  aria-roledescription=\"cuisine type checkbox\"\n  aria-expanded=\"false\"\n/>\n\n<span role=\"radio\" id=\"pass75\" aria-checked=\"false\">I am RED!</span>\n<span role=\"radio\" id=\"pass76\" aria-checked=\"true\">I am GREEN!</span>\n\n<table role=\"grid\">\n  <td aria-selected=\"false\" tabindex=\"0\" id=\"pass77\">target 1</td>\n</table>\n\n<table role=\"treegrid\">\n  <td aria-selected=\"false\" tabindex=\"0\" id=\"pass78\">target 1</td>\n</table>\n\n<div id=\"pass79\" aria-label=\"  \">Foo</div>\n<div id=\"pass80\" aria-labelledby=\"  \">Foo</div>\n\n<audio aria-label=\"value\" id=\"pass81\" controls></audio>\n<iframe aria-label=\"value\" id=\"pass82\" src=\"/test/playground.html\"></iframe>\n<canvas aria-label=\"value\" id=\"pass83\"></canvas>\n<dl aria-label=\"value\" id=\"pass84\"></dl>\n<input aria-label=\"value\" id=\"pass85\" />\n<label aria-label=\"value\" id=\"pass86\"></label>\n<meter\n  id=\"pass87\"\n  aria-label=\"value\"\n  aria-valuemin=\"0\"\n  aria-valuemax=\"0\"\n  aria-valuenow=\"40\"\n  aria-valuetext=\"40%\"\n>\n  40\n</meter>\n<object aria-label=\"value\" id=\"pass88\"></object>\n<svg aria-label=\"value\" id=\"pass89\"></svg>\n<video aria-label=\"value\" id=\"pass90\" controls></video>\n\n<div\n  id=\"pass91\"\n  role=\"comment\"\n  aria-level=\"1\"\n  aria-posinset=\"1\"\n  aria-setsize=\"16\"\n>\n  ok\n</div>\n\n<!-- Prohibited attributes fail in aria-prohibited-attr -->\n<div role=\"caption\" aria-label=\"value\" id=\"pass92\"></div>\n<div role=\"caption\" aria-labelledby=\"value\" id=\"pass93\"></div>\n<div role=\"paragraph\" aria-labelledby=\"value\" id=\"pass94\"></div>\n<div role=\"strong\" aria-label=\"value\" id=\"pass95\"></div>\n\n<!-- Conditional ARIA attributes tested under aria-conditional-attr: -->\n<div role=\"table\">\n  <div role=\"row\" aria-expanded=\"false\" id=\"pass96\"></div>\n</div>\n<table role=\"treegrid\">\n  <tr\n    id=\"pass97\"\n    role=\"row\"\n    aria-level=\"1\"\n    aria-posinset=\"1\"\n    aria-setsize=\"1\"\n    aria-expanded=\"true\"\n  >\n    <td role=\"gridcell\">Treegrids are awesome</td>\n  </tr>\n</table>\n\n<input type=\"checkbox\" aria-checked=\"false\" id=\"pass98\" />\n<input type=\"checkbox\" aria-checked=\"mixed\" id=\"pass99\" />\n\n<search aria-expanded=\"true\" id=\"pass100\"></search>\n\n<!-- Ignored ARIA attributes -->\n<button id=\"pass101\" aria-required=\"false\"></button>\n<div id=\"pass102\" role=\"combobox\" aria-multiline=\"false\" contenteditable></div>\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-attr/passes.json",
    "content": "{\n  \"description\": \"aria-allowed-attr passing tests\",\n  \"rule\": \"aria-allowed-attr\",\n  \"passes\": [\n    [\"#pass0\"],\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass25\"],\n    [\"#pass26\"],\n    [\"#pass27\"],\n    [\"#pass28\"],\n    [\"#pass29\"],\n    [\"#pass30\"],\n    [\"#pass31\"],\n    [\"#pass32\"],\n    [\"#pass33\"],\n    [\"#pass34\"],\n    [\"#pass35\"],\n    [\"#pass36\"],\n    [\"#pass37\"],\n    [\"#pass38\"],\n    [\"#pass39\"],\n    [\"#pass40\"],\n    [\"#pass41\"],\n    [\"#pass42\"],\n    [\"#pass43\"],\n    [\"#pass44\"],\n    [\"#pass45\"],\n    [\"#pass46\"],\n    [\"#pass47\"],\n    [\"#pass48\"],\n    [\"#pass49\"],\n    [\"#pass50\"],\n    [\"#pass51\"],\n    [\"#pass52\"],\n    [\"#pass53\"],\n    [\"#pass54\"],\n    [\"#pass55\"],\n    [\"#pass56\"],\n    [\"#pass57\"],\n    [\"#pass58\"],\n    [\"#pass59\"],\n    [\"#pass60\"],\n    [\"#pass61\"],\n    [\"#pass62\"],\n    [\"#pass63\"],\n    [\"#pass64\"],\n    [\"#pass65\"],\n    [\"#pass66\"],\n    [\"#pass67\"],\n    [\"#pass68\"],\n    [\"#pass69\"],\n    [\"#pass70\"],\n    [\"#pass71\"],\n    [\"#pass72\"],\n    [\"#pass73\"],\n    [\"#pass74\"],\n    [\"#pass75\"],\n    [\"#pass76\"],\n    [\"#pass77\"],\n    [\"#pass78\"],\n    [\"#pass79\"],\n    [\"#pass80\"],\n    [\"#pass81\"],\n    [\"#pass82\"],\n    [\"#pass83\"],\n    [\"#pass84\"],\n    [\"#pass85\"],\n    [\"#pass86\"],\n    [\"#pass87\"],\n    [\"#pass88\"],\n    [\"#pass89\"],\n    [\"#pass90\"],\n    [\"#pass91\"],\n    [\"#pass92\"],\n    [\"#pass93\"],\n    [\"#pass94\"],\n    [\"#pass95\"],\n    [\"#pass96\"],\n    [\"#pass97\"],\n    [\"#pass98\"],\n    [\"#pass99\"],\n    [\"#pass100\"],\n    [\"#pass101\"],\n    [\"#pass102\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-role/aria-allowed-role.html",
    "content": "<section id=\"pass-section-role-alert\" role=\"alert\"></section>\n<dialog id=\"pass-dialog-role-alertdialog\" role=\"alertdialog\"></dialog>\n<section id=\"pass-section-role-banner\" role=\"banner\"></section>\n<section id=\"pass-section-role-complementary\" role=\"complementary\"></section>\n<object data=\"\" role=\"document\" type=\"\" id=\"pass-object-role-document\"></object>\n<section id=\"pass-section-role-doc-afterword\" role=\"doc-afterword\"></section>\n<section id=\"pass-section-role-doc-bib\" role=\"doc-bibliography\"></section>\n<ul>\n  <li id=\"pass-li-role-doc-biblioentry\" role=\"doc-biblioentry\"></li>\n</ul>\n<aside id=\"pass-aside-doc-example\" role=\"doc-example\"></aside>\n<aside id=\"pass-aside-doc-glossary\" role=\"doc-glossary\"></aside>\n<div id=\"pass-div-has-any-role\" role=\"contentinfo\"></div>\n<div id=\"pass-div-valid-role\" role=\"link\">ok</div>\n<ol id=\"pass-ol-valid-role\" role=\"directory\"></ol>\n<nav id=\"pass-nav-role-doc-index\" role=\"doc-index\"></nav>\n<h1 id=\"pass-h1-role-doc-subtitle\" role=\"doc-subtitle\"></h1>\n<video id=\"pass-video-valid-role\" role=\"application\"></video>\n<a href=\"\" role=\"treeitem\" id=\"pass-a-valid-role-tree-item\"></a>\n<a href=\"\" role=\"button\" id=\"pass-a-valid-role-button\"></a>\n<a href=\"\" role=\"doc-backlink\" id=\"pass-a-valid-role-doc-backlink\"></a>\n<ul id=\"pass-ul-valid-role\" role=\"toolbar\">\n  <li></li>\n</ul>\n<ol id=\"pass-ol-role-listbox\" role=\"listbox\">\n  <li></li>\n</ol>\n<section id=\"pass-section-role-marquee\" role=\"marquee\"></section>\n<figure id=\"pass-figure-role-group\" role=\"group\"></figure>\n<section id=\"pass-section-valid-role-application\" role=\"application\"></section>\n<section id=\"pass-section-valid-role-content-info\" role=\"contentinfo\"></section>\n<section id=\"pass-section-valid-role-dialog\" role=\"dialog\"></section>\n<button\n  type=\"button\"\n  role=\"checkbox\"\n  id=\"pass-button-valid-role-checkbox\"\n></button>\n<header id=\"pass-header-valid-role\" role=\"group\"></header>\n<footer id=\"pass-footer-valid-role\" role=\"group\"></footer>\n<embed id=\"pass-embed-valid-role\" role=\"img\" />\n<input type=\"text\" role=\"textbox\" id=\"pass-input-text-redundant-role\" />\n<input type=\"text\" role=\"textbox combobox\" id=\"pass-input-multiple-roles\" />\n<input\n  type=\"text\"\n  role=\"searchbox invalidrole\"\n  id=\"pass-input-multiple-valid-and-invalid-roles\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"searchbox\"\n  type=\"text\"\n  aria-expanded=\"true\"\n  id=\"pass-input-text-role-searchbox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"text\"\n  aria-expanded=\"true\"\n  id=\"pass-input-text-role-combobox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"search\"\n  aria-expanded=\"true\"\n  id=\"pass-input-search-role-combobox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"tel\"\n  aria-expanded=\"true\"\n  id=\"pass-input-tel-role-combobox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"url\"\n  aria-expanded=\"true\"\n  id=\"pass-input-url-role-combobox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"email\"\n  aria-expanded=\"true\"\n  id=\"pass-input-email-role-combobox\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"spinbutton\"\n  type=\"text\"\n  aria-expanded=\"true\"\n  id=\"pass-input-text-role-spinbutton\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"spinbutton\"\n  type=\"number\"\n  aria-expanded=\"true\"\n  id=\"pass-input-number-role-spinbutton\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"spinbutton\"\n  type=\"tel\"\n  aria-expanded=\"true\"\n  id=\"pass-input-tel-role-spinbutton\"\n/>\n<input\n  aria-autocomplete=\"list\"\n  value=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"button\"\n  aria-expanded=\"true\"\n  id=\"pass-input-button-role-combobox\"\n/>\n<button\n  aria-autocomplete=\"list\"\n  aria-label=\"some label\"\n  autocomplete=\"off\"\n  role=\"combobox\"\n  type=\"button\"\n  aria-expanded=\"true\"\n  id=\"pass-button-role-combobox\"\n>\n  ok\n</button>\n<input\n  value=\"some label\"\n  role=\"checkbox\"\n  type=\"button\"\n  aria-checked=\"true\"\n  id=\"pass-input-button-role-checkbox\"\n/>\n<input type=\"image\" role=\"link\" id=\"pass-input-image-valid-role\" />\n<input\n  type=\"checkbox\"\n  role=\"menuitemcheckbox\"\n  id=\"pass-input-checkbox-valid-role\"\n/>\n<h1 id=\"pass-h1-valid-role\" role=\"none\"></h1>\n<img alt=\"\" role=\"presentation\" id=\"pass-img-valid-role\" />\n<button id=\"pass-button-role-radio\" role=\"radio\"></button>\n<aside id=\"pass-aside-role-region\" role=\"region\"></aside>\n<form action=\"\" id=\"pass-form-role-search\" role=\"search\"></form>\n<ul>\n  <li id=\"pass-li-role-sep\" role=\"separator\"></li>\n</ul>\n<iframe\n  src=\"dummy.html\"\n  role=\"presentation\"\n  id=\"pass-iframe-presentation\"\n></iframe>\n<iframe src=\"dummy.html\" role=\"none\" id=\"pass-iframe-none\"></iframe>\n<ul id=\"pass-ul-role-none\" role=\"none\"></ul>\n<ul id=\"pass-ul-role-presentation\" role=\"presentation\"></ul>\n<ol id=\"pass-ol-role-none\" role=\"none\"></ol>\n<ol id=\"pass-ol-role-presentation\" role=\"presentation\"></ol>\n<my-custom-element\n  id=\"pass-custom-element-any-role\"\n  role=\"navigation\"\n></my-custom-element>\n<!-- links -->\n<a id=\"pass-dpub-1\" role=\"doc-backlink\" href=\"#\">ok</a>\n<a id=\"pass-dpub-2\" role=\"doc-biblioref\" href=\"#\">ok</a>\n<a id=\"pass-dpub-3\" role=\"doc-glossref\" href=\"#\">ok</a>\n<a id=\"pass-dpub-4\" role=\"doc-noteref\" href=\"#\">ok</a>\n<!-- images -->\n<img alt=\"foo\" id=\"pass-dpub-5\" role=\"doc-cover\" />\n<!-- listitems -->\n<ul>\n  <li id=\"pass-dpub-6\" role=\"doc-biblioentry\">ok</li>\n</ul>\n<ul>\n  <li id=\"pass-dpub-7\" role=\"doc-endnote\">ok</li>\n</ul>\n<hr id=\"hr-presentation\" role=\"presentation\" />\n<hr id=\"hr-none\" role=\"none\" />\n<span role=\"text\" id=\"span-text\" />\n<div role=\"text\" id=\"div-text\" />\n<p role=\"text\" id=\"p-text\" />\n<div\n  id=\"pass-graphics-document\"\n  role=\"graphics-document\"\n  aria-label=\"doc\"\n></div>\n<div id=\"pass-graphics-object\" role=\"graphics-object\"></div>\n<div id=\"pass-graphics-symbol\" role=\"graphics-symbol\"></div>\n<button\n  id=\"pass-button-role-gridcell\"\n  role=\"gridcell\"\n  title=\"IconCheckmark\"\n  aria-label=\"IconCheckmark icon\"\n></button>\n\n<dd id=\"fail-dd-no-role\" role=\"link\"></dd>\n<dt id=\"fail-dt-no-role\" role=\"banner\"></dt>\n<label id=\"fail-label-no-role\" role=\"presentation\"></label>\n<ol id=\"fail-ol-invalid-role\" role=\"banner\"></ol>\n<a href=\"\" role=\"doc-acknowledgments\" id=\"fail-a-invalid-role\"></a>\n<section id=\"fail-section-invalid-role\" role=\"doc-subtitle\"></section>\n<embed id=\"fail-embed-invalid-role\" role=\"doc-abstract\" />\n<input type=\"image\" id=\"fail-input-image-invalid-role\" role=\"doc-afterword\" />\n<button id=\"fail-button-role-cell\" role=\"cell\"></button>\n<aside id=\"fail-aside-doc-foreword\" role=\"doc-foreword\"></aside>\n<aside id=\"fail-aside-role-tab\" role=\"tab\"></aside>\n\n<input id=\"fail-input-role-gridcell-multiple-role\" role=\"gridcell combobox\" />\n<div style=\"display: none\">\n  <button\n    id=\"incomplete1\"\n    class=\"mm-tabstart\"\n    type=\"button\"\n    role=\"presentation\"\n  ></button>\n</div>\n<button\n  id=\"incomplete2\"\n  type=\"button\"\n  aria-hidden=\"true\"\n  role=\"presentation\"\n></button>\n<!-- links -->\n<div id=\"fail-dpub-1\" role=\"doc-backlink\">ok</div>\n<div id=\"fail-dpub-2\" role=\"doc-biblioref\">ok</div>\n<div id=\"fail-dpub-3\" role=\"doc-glossref\">ok</div>\n<div id=\"fail-dpub-4\" role=\"doc-noteref\">ok</div>\n<!-- images -->\n<div id=\"fail-dpub-5\" role=\"doc-cover\">ok</div>\n<img\n  src=\"#\"\n  role=\"meter\"\n  aria-valuenow=\"0\"\n  aria-valuemin=\"0\"\n  aria-valuemax=\"100\"\n  alt=\"test\"\n  id=\"pass-img-valid-role-meter\"\n/>\n<img role=\"doc-cover\" aria-label=\"foo\" id=\"pass-img-valid-role-aria-label\" />\n<img role=\"math\" aria-label=\"test\" id=\"pass-img-valid-role-math\" />\n<img role=\"menuitem\" title=\"bar\" id=\"pass-img-valid-role-title\" />\n<div id=\"image-baz\">hazaar</div>\n<img\n  role=\"switch\"\n  aria-labelledby=\"image-baz\"\n  id=\"pass-img-valid-role-aria-labelledby\"\n/>\n<img role=\"radio\" aria-label=\"test\" id=\"pass-img-valid-role-radio\" />\n<img role=\"text\" aria-label=\"foo\" id=\"fail-img-invalid-role-aria-label\" />\n<img role=\"spinbutton\" title=\"bar\" id=\"fail-img-invalid-role-title\" />\n<img\n  role=\"combobox\"\n  aria-labelledby=\"image-baz\"\n  id=\"fail-img-invalid-role-aria-labelledby\"\n/>\n<img role=\"button\" id=\"fail-img-no-accessible-name-present\" />\n\n<!-- listitems -->\n<div id=\"fail-dpub-6\" role=\"doc-biblioentry\">ok</div>\n<div id=\"fail-dpub-7\" role=\"doc-endnote\">ok</div>\n<h1 role=\"text\" id=\"fail-text-1\">ok</h1>\n<button role=\"text\" id=\"fail-text-2\">ok</button>\n<a href=\"#\" role=\"text\" id=\"fail-text-3\">ok</a>\n<main role=\"text\" id=\"fail-text-4\">ok</main>\n\n<!-- landmarks -->\n<main><header role=\"banner\" id=\"pass-header-banner\">ok</header></main>\n<main><footer role=\"contentinfo\" id=\"pass-footer-contentinfo\">ok</footer></main>\n<nav role=\"none\" id=\"pass-navnone-1\">ok</nav>\n<nav role=\"presentation\" id=\"pass-navnone-2\">ok</nav>\n\n<img usemap=\"#my-map\" />\n<map name=\"my-map\">\n  <area shape=\"circle\" coords=\"5,5,10\" role=\"button\" id=\"pass-imgmap-1\" />\n  <area shape=\"circle\" coords=\"5,15,10\" role=\"link\" id=\"pass-imgmap-2\" />\n  <area\n    shape=\"circle\"\n    coords=\"15,5,10\"\n    href=\"#\"\n    role=\"button\"\n    id=\"fail-imgmap-1\"\n  />\n  <area shape=\"circle\" coords=\"15,15,10\" role=\"checkbox\" id=\"fail-imgmap-2\" />\n</map>\n\n<search role=\"form\" id=\"pass-search-elm\"></search>\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-role/aria-allowed-role.json",
    "content": "{\n  \"description\": \"aria-allowed-role integration tests\",\n  \"rule\": \"aria-allowed-role\",\n  \"passes\": [\n    [\"#pass-section-role-alert\"],\n    [\"#pass-dialog-role-alertdialog\"],\n    [\"#pass-section-role-banner\"],\n    [\"#pass-section-role-complementary\"],\n    [\"#pass-object-role-document\"],\n    [\"#pass-section-role-doc-afterword\"],\n    [\"#pass-section-role-doc-bib\"],\n    [\"#pass-li-role-doc-biblioentry\"],\n    [\"#pass-aside-doc-example\"],\n    [\"#pass-aside-doc-glossary\"],\n    [\"#pass-div-valid-role\"],\n    [\"#pass-ol-valid-role\"],\n    [\"#pass-nav-role-doc-index\"],\n    [\"#pass-h1-role-doc-subtitle\"],\n    [\"#pass-video-valid-role\"],\n    [\"#pass-a-valid-role-tree-item\"],\n    [\"#pass-a-valid-role-button\"],\n    [\"#pass-a-valid-role-doc-backlink\"],\n    [\"#pass-ul-valid-role\"],\n    [\"#pass-ol-role-listbox\"],\n    [\"#pass-section-role-marquee\"],\n    [\"#pass-figure-role-group\"],\n    [\"#pass-section-valid-role-application\"],\n    [\"#pass-section-valid-role-content-info\"],\n    [\"#pass-section-valid-role-dialog\"],\n    [\"#pass-button-valid-role-checkbox\"],\n    [\"#pass-header-valid-role\"],\n    [\"#pass-footer-valid-role\"],\n    [\"#pass-embed-valid-role\"],\n    [\"#pass-div-has-any-role\"],\n    [\"#pass-input-text-redundant-role\"],\n    [\"#pass-input-multiple-roles\"],\n    [\"#pass-input-multiple-valid-and-invalid-roles\"],\n    [\"#pass-input-text-role-searchbox\"],\n    [\"#pass-input-text-role-combobox\"],\n    [\"#pass-input-search-role-combobox\"],\n    [\"#pass-input-tel-role-combobox\"],\n    [\"#pass-input-url-role-combobox\"],\n    [\"#pass-input-email-role-combobox\"],\n    [\"#pass-input-text-role-spinbutton\"],\n    [\"#pass-input-number-role-spinbutton\"],\n    [\"#pass-input-tel-role-spinbutton\"],\n    [\"#pass-input-image-valid-role\"],\n    [\"#pass-input-checkbox-valid-role\"],\n    [\"#pass-button-role-combobox\"],\n    [\"#pass-input-button-role-checkbox\"],\n    [\"#pass-input-button-role-combobox\"],\n    [\"#pass-h1-valid-role\"],\n    [\"#pass-img-valid-role\"],\n    [\"#pass-button-role-radio\"],\n    [\"#pass-aside-role-region\"],\n    [\"#pass-form-role-search\"],\n    [\"#pass-li-role-sep\"],\n    [\"#pass-iframe-presentation\"],\n    [\"#pass-iframe-none\"],\n    [\"#pass-ul-role-none\"],\n    [\"#pass-ul-role-presentation\"],\n    [\"#pass-ol-role-none\"],\n    [\"#pass-ol-role-presentation\"],\n    [\"#pass-custom-element-any-role\"],\n    [\"#pass-dpub-1\"],\n    [\"#pass-dpub-2\"],\n    [\"#pass-dpub-3\"],\n    [\"#pass-dpub-4\"],\n    [\"#pass-dpub-5\"],\n    [\"#pass-dpub-6\"],\n    [\"#pass-dpub-7\"],\n    [\"#hr-presentation\"],\n    [\"#hr-none\"],\n    [\"#span-text\"],\n    [\"#div-text\"],\n    [\"#p-text\"],\n    [\"#pass-graphics-document\"],\n    [\"#pass-graphics-object\"],\n    [\"#pass-graphics-symbol\"],\n    [\"#pass-header-banner\"],\n    [\"#pass-footer-contentinfo\"],\n    [\"#pass-img-valid-role-aria-label\"],\n    [\"#pass-img-valid-role-title\"],\n    [\"#pass-img-valid-role-aria-labelledby\"],\n    [\"#pass-img-valid-role-radio\"],\n    [\"#pass-img-valid-role-math\"],\n    [\"#pass-img-valid-role-meter\"],\n    [\"#pass-imgmap-1\"],\n    [\"#pass-imgmap-2\"],\n    [\"#pass-navnone-1\"],\n    [\"#pass-navnone-2\"],\n    [\"#pass-search-elm\"],\n    [\"#pass-button-role-gridcell\"]\n  ],\n  \"violations\": [\n    [\"#fail-dd-no-role\"],\n    [\"#fail-dt-no-role\"],\n    [\"#fail-label-no-role\"],\n    [\"#fail-ol-invalid-role\"],\n    [\"#fail-a-invalid-role\"],\n    [\"#fail-section-invalid-role\"],\n    [\"#fail-embed-invalid-role\"],\n    [\"#fail-input-image-invalid-role\"],\n    [\"#fail-button-role-cell\"],\n    [\"#fail-aside-doc-foreword\"],\n    [\"#fail-aside-role-tab\"],\n    [\"#fail-input-role-gridcell-multiple-role\"],\n    [\"#fail-dpub-1\"],\n    [\"#fail-dpub-2\"],\n    [\"#fail-dpub-3\"],\n    [\"#fail-dpub-4\"],\n    [\"#fail-dpub-5\"],\n    [\"#fail-dpub-6\"],\n    [\"#fail-dpub-7\"],\n    [\"#fail-text-1\"],\n    [\"#fail-text-2\"],\n    [\"#fail-text-3\"],\n    [\"#fail-text-4\"],\n    [\"#fail-img-invalid-role-aria-label\"],\n    [\"#fail-img-invalid-role-title\"],\n    [\"#fail-img-invalid-role-aria-labelledby\"],\n    [\"#fail-img-no-accessible-name-present\"],\n    [\"#fail-imgmap-1\"],\n    [\"#fail-imgmap-2\"]\n  ],\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-allowed-role/dummy.html",
    "content": ""
  },
  {
    "path": "test/integration/rules/aria-braille-equivalent/aria-braille-equivalent.html",
    "content": "<button id=\"pass1\" aria-braillelabel=\"hello\">Hello</button>\n<button id=\"pass2\" aria-braillelabel=\"\"></button>\n<button id=\"incomplete1\" aria-braillelabel=\"hello\"></button>\n\n<aside\n  id=\"pass3\"\n  aria-roledescription=\"table of contents\"\n  aria-brailleroledescription=\"\"\n></aside>\n\n<aside\n  id=\"pass4\"\n  aria-roledescription=\"table of contents\"\n  aria-brailleroledescription=\"table of contents\"\n></aside>\n\n<aside\n  id=\"pass5\"\n  aria-roledescription=\"\"\n  aria-brailleroledescription=\"\"\n></aside>\n\n<aside\n  id=\"incomplete2\"\n  aria-roledescription=\"\"\n  aria-brailleroledescription=\"table of contents\"\n></aside>\n"
  },
  {
    "path": "test/integration/rules/aria-braille-equivalent/aria-braille-equivalent.json",
    "content": "{\n  \"description\": \"aria-braille-equivalent tests\",\n  \"rule\": \"aria-braille-equivalent\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]],\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]],\n  \"violation\": []\n}\n"
  },
  {
    "path": "test/integration/rules/aria-command-name/aria-command-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"link\">Home</div>\n<div id=\"pass2\" role=\"link\" title=\"About\"></div>\n<div id=\"pass3\" role=\"link\" aria-label=\"Products\"></div>\n<div id=\"pass4\" role=\"link\" aria-labelledby=\"contact\"></div>\n<div id=\"contact\">Contact us</div>\n\n<div id=\"pass5\" role=\"button\">Submit</div>\n<div id=\"pass6\" role=\"button\" title=\"Upload\"></div>\n<div id=\"pass7\" role=\"button\" aria-label=\"Download\"></div>\n<div id=\"pass8\" role=\"button\" aria-labelledby=\"print\"></div>\n\n<div role=\"menu\">\n  <div id=\"pass9\" role=\"menuitem\">New</div>\n  <div id=\"pass10\" role=\"menuitem\" title=\"Open\"></div>\n  <div id=\"pass11\" role=\"menuitem\" aria-label=\"Save\"></div>\n  <div id=\"pass12\" role=\"menuitem\" aria-labelledby=\"print\"></div>\n</div>\n\n<div id=\"print\">print file</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"link\"></div>\n<div id=\"fail2\" role=\"link\" aria-labelledby=\"non-existant\"></div>\n<div id=\"fail3\" role=\"link\" aria-labelledby=\"div-empty\"></div>\n\n<div id=\"fail4\" role=\"button\"></div>\n<div id=\"fail5\" role=\"button\" aria-labelledby=\"non-existant\"></div>\n<div id=\"fail6\" role=\"button\" aria-labelledby=\"div-empty\"></div>\n\n<div role=\"menu\">\n  <div id=\"fail7\" role=\"menuitem\"></div>\n  <div id=\"fail8\" role=\"menuitem\" aria-labelledby=\"non-existant\"></div>\n  <div id=\"fail9\" role=\"menuitem\" aria-labelledby=\"div-empty\"></div>\n</div>\n\n<div id=\"div-empty\"></div>\n\n<!-- INAPPLICABLE -->\n<a href=\"/\" role=\"link\">Home</a>\n<button role=\"button\">Save</button>\n<img role=\"button\" alt=\"Send\" id=\"inapplicable1\" />\n<div role=\"menu\">\n  <input role=\"menuitem\" title=\"Label\" id=\"inapplicable2\" />\n  <button role=\"menuitem\" title=\"Label\" id=\"inapplicable3\"></button>\n  <a href=\"#\" role=\"menuitem\" title=\"Label\" id=\"inapplicable4\"></a>\n  <select role=\"menuitem\" title=\"Label\" id=\"inapplicable5\">\n    <option value=\"volvo\">Volvo</option>\n    <option value=\"saab\">Saab</option>\n    <option value=\"opel\">Opel</option>\n  </select>\n  <textarea role=\"menuitem\" id=\"inapplicable6\" title=\"Label\"></textarea>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-command-name/aria-command-name.json",
    "content": "{\n  \"description\": \"aria-command-name test\",\n  \"rule\": \"aria-command-name\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"]\n  ],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-conditional-attr/aria-conditional-attr.html",
    "content": "<input type=\"checkbox\" aria-checked=\"false\" id=\"pass1\" />\n<input type=\"checkbox\" aria-checked=\"true\" checked id=\"pass2\" />\n<table role=\"treegrid\">\n  <tr\n    id=\"pass3\"\n    role=\"row\"\n    aria-level=\"1\"\n    aria-posinset=\"1\"\n    aria-setsize=\"1\"\n    aria-expanded=\"true\"\n  >\n    <td role=\"gridcell\">Treegrids are awesome</td>\n  </tr>\n</table>\n\n<div role=\"table\">\n  <div role=\"row\" aria-expanded=\"false\" id=\"fail1\"></div>\n  <div role=\"row\" aria-posinset=\"1\" id=\"fail2\"></div>\n  <div role=\"row\" aria-setsize=\"10\" id=\"fail3\"></div>\n  <div role=\"row\" aria-level=\"1\" id=\"fail4\"></div>\n</div>\n\n<div role=\"grid\">\n  <div role=\"row\" aria-expanded=\"false\" id=\"fail5\"></div>\n  <div role=\"row\" aria-posinset=\"1\" id=\"fail6\"></div>\n  <div role=\"row\" aria-setsize=\"10\" id=\"fail7\"></div>\n  <div role=\"row\" aria-level=\"1\" id=\"fail8\"></div>\n</div>\n\n<input type=\"checkbox\" aria-checked=\"mixed\" id=\"fail9\" />\n<input type=\"checkbox\" aria-checked=\"true\" id=\"fail10\" />\n<input type=\"checkbox\" aria-checked=\"false\" checked id=\"fail11\" />\n"
  },
  {
    "path": "test/integration/rules/aria-conditional-attr/aria-conditional-attr.json",
    "content": "{\n  \"description\": \"aria-conditional-attr tests\",\n  \"rule\": \"aria-conditional-attr\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-deprecated-role/aria-deprecated-role.html",
    "content": "<div role=\"alert\" id=\"pass1\">ok</div>\n<div role=\"alertdialog\" id=\"pass2\">ok</div>\n<div role=\"application\" id=\"pass3\">ok</div>\n<!-- fallback roles -->\n<div role=\"button alert\" id=\"pass4\">fail</div>\n<!-- abstract roles -->\n<div role=\"command\" id=\"pass5\">fail</div>\n<div role=\"composite\" id=\"pass6\">fail</div>\n<!-- invalid roles -->\n<div role=\"lol\" id=\"pass7\">fail</div>\n\n<!-- deprecated roles-->\n<div role=\"doc-biblioentry\" id=\"fail1\">fail</div>\n<div role=\"doc-endnote\" id=\"fail2\">fail</div>\n<div role=\"directory\" id=\"fail3\">ok</div>\n\n<!-- inapplicable  -->\n<div id=\"inapplicable1\">no role attribute</div>\n<div id=\"inapplicable2\" role>role not defined</div>\n"
  },
  {
    "path": "test/integration/rules/aria-deprecated-role/aria-deprecated-role.json",
    "content": "{\n  \"description\": \"aria-deprecated-role tests\",\n  \"rule\": \"aria-deprecated-role\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-dialog-name/aria-dialog-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"dialog\" aria-label=\"Cookies\"></div>\n<div id=\"pass2\" role=\"dialog\" title=\"Cookies\"></div>\n<div id=\"pass3\" role=\"dialog\" aria-labelledby=\"dialog-div-heading\">\n  <h2 id=\"dialog-div-heading\">Cookies</h2>\n</div>\n<dialog id=\"pass4\" open role=\"alertdialog\" aria-label=\"Cookies\"></dialog>\n<dialog id=\"pass5\" open role=\"alertdialog\" title=\"Cookies\"></dialog>\n<dialog\n  id=\"pass6\"\n  open\n  role=\"alertdialog\"\n  aria-labelledby=\"alertdialog-heading\"\n>\n  <h2 id=\"alertdialog-heading\">Cookies</h2>\n</dialog>\n<div id=\"pass7\" role=\"alertdialog\" aria-label=\"Cookies\"></div>\n<div id=\"pass8\" role=\"alertdialog\" title=\"Cookies\"></div>\n<div id=\"pass9\" role=\"alertdialog\" aria-labelledby=\"alertdialog-div-heading\">\n  <h2 id=\"alertdialog-div-heading\">Cookies</h2>\n</div>\n<div id=\"pass10\" role=\"dialog\" aria-labelledby=\"pass10-heading\">\n  <header id=\"pass10-heading\">Named from author element</header>\n</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"dialog\"></div>\n<div id=\"fail2\" role=\"dialog\" aria-labelledby=\"non-existent\"></div>\n<dialog id=\"fail3\" open role=\"alertdialog\"></dialog>\n<dialog\n  id=\"fail4\"\n  open\n  role=\"alertdialog\"\n  aria-labelledby=\"non-existent\"\n></dialog>\n<div id=\"fail5\" role=\"alertdialog\"></div>\n<div id=\"fail6\" role=\"alertdialog\" aria-labelledby=\"non-existent\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-dialog-name/aria-dialog-name.json",
    "content": "{\n  \"description\": \"aria-dialog-name test\",\n  \"rule\": \"aria-dialog-name\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"]\n  ],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-hidden-focus/aria-hidden-focus.html",
    "content": "<!-- ///////////////// -->\n<!-- Pass -->\n<!-- ///////////////// -->\n<p id=\"pass1\" aria-hidden=\"true\">Some text</p>\n\n<div id=\"pass2\" aria-hidden=\"true\">\n  <a href=\"/\" style=\"display: none\">Link</a>\n</div>\n\n<div id=\"pass3\" aria-hidden=\"true\">\n  <button tabindex=\"-1\">Some button</button>\n</div>\n\n<input id=\"pass4\" disabled aria-hidden=\"true\" />\n\n<div id=\"pass5\" aria-hidden=\"true\">\n  <!-- aria-hidden=false does not negate aria-hidden true -->\n  <div aria-hidden=\"false\">\n    <button tabindex=\"-1\">Some button</button>\n  </div>\n</div>\n\n<div id=\"pass6\" aria-hidden=\"true\">\n  <label>\n    Enter your comments:\n    <textarea tabindex=\"-1\"></textarea>\n  </label>\n</div>\n\n<div id=\"pass7\" aria-hidden=\"true\">\n  <div inert>\n    <button>hello</button>\n  </div>\n</div>\n\n<!-- ///////////////// -->\n<!-- Fail -->\n<!-- ///////////////// -->\n<div id=\"violation1\" aria-hidden=\"true\">\n  <a href=\"/\" style=\"position: absolute; top: -999em\">Link</a>\n</div>\n\n<div id=\"violation2\" aria-hidden=\"true\">\n  <input aria-disabled=\"true\" />\n</div>\n\n<p id=\"violation3\" tabindex=\"0\" aria-hidden=\"true\">Some text</p>\n\n<details id=\"violation4\" aria-hidden=\"true\">\n  <summary>Some button</summary>\n  <p>Some details</p>\n</details>\n\n<div id=\"violation5\" aria-hidden=\"true\">\n  <label>\n    Choose:\n    <select>\n      <option selected=\"selected\">Chosen</option>\n      <option>Not Selected</option>\n    </select>\n  </label>\n</div>\n\n<main id=\"violation6\" aria-hidden=\"true\">\n  <map name=\"infographic\">\n    <area\n      shape=\"rect\"\n      coords=\"184,6,253,27\"\n      href=\"https://mozilla.org\"\n      target=\"_blank\"\n      alt=\"Mozilla\"\n    />\n  </map>\n</main>\n\n<div id=\"violation7\" aria-hidden=\"true\">\n  <a href=\"\">foo</a><button>bar</button>\n</div>\n\n<!-- ///////////////// -->\n<!-- Incomplete -->\n<!-- ///////////////// -->\n<div>\n  <div\n    id=\"incomplete1\"\n    aria-hidden=\"true\"\n    tabindex=\"0\"\n    style=\"pointer-events: none; position: fixed\"\n  ></div>\n  <div class=\"test-div\">\n    <button>hello</button>\n  </div>\n  <div\n    id=\"incomplete2\"\n    aria-hidden=\"true\"\n    tabindex=\"0\"\n    data-is-visible=\"true\"\n    style=\"pointer-events: none; position: fixed\"\n  ></div>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-hidden-focus/aria-hidden-focus.json",
    "content": "{\n  \"description\": \"aria-hidden-focus tests\",\n  \"rule\": \"aria-hidden-focus\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ],\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-input-field-name/aria-input-field-name.html",
    "content": "<!-- PASS -->\n<!-- combobox -->\n<div\n  id=\"pass1\"\n  aria-label=\"country\"\n  role=\"combobox\"\n  aria-expanded=\"true\"\n  aria-controls=\"inapplicable1\"\n>\n  England\n</div>\n\n<!-- Controlled by combobox: -->\n<ul role=\"listbox\" id=\"inapplicable1\">\n  <li role=\"option\">Zebra</li>\n  <li role=\"option\" id=\"selected_option\">Zoom</li>\n</ul>\n\n<!-- listbox -->\n<p id=\"pass2Label\">Select a color:</p>\n<div id=\"pass2\" role=\"listbox\" aria-labelledby=\"pass2Label\">\n  <div role=\"option\">Orange</div>\n</div>\n<!-- searchbox -->\n<p id=\"pass3Label\">Search currency pairs:</p>\n<div\n  id=\"pass3\"\n  role=\"searchbox\"\n  contenteditable=\"true\"\n  aria-labelledby=\"pass3Label\"\n></div>\n<!-- slider -->\n<div\n  id=\"pass4\"\n  role=\"slider\"\n  aria-label=\"Choose a value\"\n  aria-valuemin=\"1\"\n  aria-valuemax=\"7\"\n  aria-valuenow=\"2\"\n></div>\n<!-- spinbutton -->\n<div\n  id=\"pass5\"\n  role=\"spinbutton\"\n  aria-valuemin=\"0\"\n  aria-valuemax=\"10\"\n  aria-valuenow=\"8\"\n  aria-label=\"Enter quantity:\"\n></div>\n<!-- textbox -->\n<label id=\"foo\">\n  foo\n  <div id=\"pass6\" role=\"textbox\" aria-labelledby=\"foo\"></div>\n</label>\n\n<!-- FAIL -->\n<!-- aria-label with empty text string -->\n<div id=\"fail1\" aria-label=\" \" role=\"combobox\">England</div>\n<!-- The label does not exist. -->\n<div id=\"fail2\" aria-labelledby=\"non-existing\" role=\"combobox\">England</div>\n<!-- The implicit label is not supported on div elements. -->\n<label>\n  first name\n  <div id=\"fail3\" role=\"textbox\"></div>\n</label>\n<!-- explicit label -->\n<label for=\"fail4\">first name</label>\n<div role=\"textbox\" id=\"fail4\"></div>\n<!-- combobox -->\n<div id=\"fail5\" role=\"combobox\">England</div>\n<!-- listbox -->\n<div id=\"fail6\" role=\"listbox\" aria-labelledby=\"label-does-not-exist\">\n  <div role=\"option\">Orange</div>\n</div>\n<!-- searchbox -->\n<div\n  id=\"fail7\"\n  role=\"searchbox\"\n  contenteditable=\"true\"\n  aria-labelledby=\"unknown-label\"\n></div>\n<!-- slider -->\n<div\n  id=\"fail8\"\n  role=\"slider\"\n  aria-valuemin=\"1\"\n  aria-valuemax=\"7\"\n  aria-valuenow=\"2\"\n></div>\n<!-- spinbutton -->\n<div\n  id=\"fail9\"\n  role=\"spinbutton\"\n  aria-valuemin=\"0\"\n  aria-valuemax=\"10\"\n  aria-valuenow=\"8\"\n></div>\n<!-- textbox -->\n<label>\n  foo\n  <div id=\"fail10\" role=\"textbox\"></div>\n</label>\n\n<!-- INAPPLICABLE -->\n<input id=\"inapplicable2\" />\n<select id=\"inapplicable3\">\n  <option value=\"volvo\">Volvo</option>\n  <option value=\"saab\">Saab</option>\n  <option value=\"opel\">Opel</option>\n</select>\n<textarea id=\"inapplicable4\" title=\"Label\"></textarea>\n\n<!-- INCOMPLETE -->\n<!-- Implicit label -->\n<label>\n  first name\n  <div id=\"canttell1\" role=\"textbox\" aria-label=\"name\"></div>\n</label>\n\n<!--  Explicit label -->\n<label for=\"canttell2\">first name</label>\n<div role=\"textbox\" id=\"canttell2\" aria-label=\"name\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-input-field-name/aria-input-field-name.json",
    "content": "{\n  \"description\": \"aria-input-field-name test\",\n  \"rule\": \"aria-input-field-name\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"]\n  ],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"]\n  ],\n  \"incomplete\": [[\"#canttell1\"], [\"#canttell2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-meter-name/aria-meter-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"meter\" title=\"CPU usage\"></div>\n<div id=\"pass2\" role=\"meter\" aria-label=\"CPU usage\"></div>\n<div id=\"pass3\" role=\"meter\" aria-labelledby=\"usage\"></div>\n\n<div id=\"usage\">CPU usage</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"meter\">CPU usage</div>\n<div id=\"fail2\" role=\"meter\"></div>\n<div id=\"fail3\" role=\"meter\" aria-labelledby=\"usage-non-existant\"></div>\n<div id=\"fail4\" role=\"meter\" aria-labelledby=\"usage-empty\"></div>\n\n<div id=\"usage-empty\"></div>\n\n<!-- INAPPLICABLE -->\n<img role=\"meter\" alt=\"Label\" id=\"inapplicable1\" />\n<input role=\"meter\" title=\"Label\" id=\"inapplicable2\" />\n<button role=\"meter\" title=\"Label\" id=\"inapplicable3\"></button>\n<a href=\"#\" role=\"meter\" title=\"Label\" id=\"inapplicable4\"></a>\n<select role=\"meter\" title=\"Label\" id=\"inapplicable5\">\n  <option value=\"volvo\">Volvo</option>\n  <option value=\"saab\">Saab</option>\n  <option value=\"opel\">Opel</option>\n</select>\n<textarea role=\"meter\" id=\"inapplicable6\" title=\"Label\"></textarea>\n"
  },
  {
    "path": "test/integration/rules/aria-meter-name/aria-meter-name.json",
    "content": "{\n  \"description\": \"aria-meter-name test\",\n  \"rule\": \"aria-meter-name\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-progressbar-name/aria-progressbar-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"progressbar\" title=\"Loading, please wait\"></div>\n<div id=\"pass2\" role=\"progressbar\" aria-label=\"loading, please wait\"></div>\n<div id=\"pass3\" role=\"progressbar\" aria-labelledby=\"loading\"></div>\n\n<div id=\"loading\">loading, please wait</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"progressbar\">loading, please wait</div>\n<div id=\"fail2\" role=\"progressbar\"></div>\n<div id=\"fail3\" role=\"progressbar\" aria-labelledby=\"loading-non-existant\"></div>\n<div id=\"fail4\" role=\"progressbar\" aria-labelledby=\"loading-empty\"></div>\n\n<div id=\"loading-empty\"></div>\n\n<!-- INAPPLICABLE -->\n<img role=\"progressbar\" alt=\"Label\" id=\"inapplicable1\" />\n<input role=\"progressbar\" title=\"Label\" id=\"inapplicable2\" />\n<button role=\"progressbar\" title=\"Label\" id=\"inapplicable3\"></button>\n<a href=\"#\" role=\"progressbar\" title=\"Label\" id=\"inapplicable4\"></a>\n<select role=\"progressbar\" title=\"Label\" id=\"inapplicable5\">\n  <option value=\"volvo\">Volvo</option>\n  <option value=\"saab\">Saab</option>\n  <option value=\"opel\">Opel</option>\n</select>\n<textarea role=\"progressbar\" id=\"inapplicable6\" title=\"Label\"></textarea>\n"
  },
  {
    "path": "test/integration/rules/aria-progressbar-name/aria-progressbar-name.json",
    "content": "{\n  \"description\": \"aria-progressbar-name test\",\n  \"rule\": \"aria-progressbar-name\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.html",
    "content": "<div role=\"checkbox\" aria-required=\"true\" id=\"pass1\">ok</div>\n<div id=\"pass2\" aria-label=\"  \">Foo</div>\n<div id=\"pass3\" aria-labelledby=\"  \">Foo</div>\n<div role=\"alert\" aria-selected=\"true\" id=\"pass4\"></div>\n<div role=\"row\" aria-colcount=\"value\" id=\"pass5\"></div>\n<div role=\"button\"><span id=\"pass6\" aria-label=\"value\"></span></div>\n<div role=\"button\"><span id=\"pass7\" aria-labelledby=\"value\"></span></div>\n<div role=\"foo dialog\" aria-label=\"foo\" id=\"pass8\"></div>\n\n<div role=\"caption\" aria-label=\"value\" id=\"fail1\"></div>\n<div role=\"caption\" aria-labelledby=\"value\" id=\"fail2\"></div>\n<div role=\"code\" aria-label=\"value\" id=\"fail3\"></div>\n<div role=\"code\" aria-labelledby=\"value\" id=\"fail4\"></div>\n<div role=\"deletion\" aria-label=\"value\" id=\"fail5\"></div>\n<div role=\"deletion\" aria-labelledby=\"value\" id=\"fail6\"></div>\n<div role=\"emphasis\" aria-label=\"value\" id=\"fail7\"></div>\n<div role=\"emphasis\" aria-labelledby=\"value\" id=\"fail8\"></div>\n<div role=\"insertion\" aria-label=\"value\" id=\"fail9\"></div>\n<div role=\"insertion\" aria-labelledby=\"value\" id=\"fail10\"></div>\n<div role=\"paragraph\" aria-label=\"value\" id=\"fail11\"></div>\n<div role=\"paragraph\" aria-labelledby=\"value\" id=\"fail12\"></div>\n<div role=\"strong\" aria-label=\"value\" id=\"fail13\"></div>\n<div role=\"strong\" aria-labelledby=\"value\" id=\"fail14\"></div>\n<div role=\"subscript\" aria-label=\"value\" id=\"fail15\"></div>\n<div role=\"subscript\" aria-labelledby=\"value\" id=\"fail16\"></div>\n<div role=\"superscript\" aria-label=\"value\" id=\"fail17\"></div>\n<div role=\"superscript\" aria-labelledby=\"value\" id=\"fail18\"></div>\n<div aria-label=\"value\" id=\"fail19\"></div>\n<div aria-labelledby=\"value\" id=\"fail20\"></div>\n<!- aria-label(ledby) is prohibited on none / presentation. Axe-core considers this to trigger presentation role\nconflict, which was true in ARIA 1.1. This changed in ARIA 1.2 but so far has only been implemented in Chomium. -->\n<span aria-label=\"value\" id=\"fail21\"></span>\n<strong aria-label=\"value\" id=\"fail22\"></strong>\n<kbd aria-label=\"value\" id=\"fail23\"></kbd>\n<abbr aria-label=\"value\" id=\"fail24\"></abbr>\n<custom-elm aria-label=\"value\" id=\"fail25\"></custom-elm>\n<div role=\"mark\" aria-label=\"value\" id=\"fail26\"></div>\n<div role=\"mark\" aria-labelledby=\"value\" id=\"fail27\"></div>\n<div role=\"suggestion\" aria-label=\"value\" id=\"fail28\"></div>\n<div role=\"suggestion\" aria-labelledby=\"value\" id=\"fail29\"></div>\n<div role=\"grid\"><span id=\"fail30\" aria-label=\"value\"></span></div>\n<div role=\"grid\"><span id=\"fail31\" aria-labelledby=\"value\"></span></div>\n<div role=\"foo\" aria-label=\"foo\" id=\"fail32\"></div>\n<div role=\"foo bar\" aria-label=\"foo\" id=\"fail33\"></div>\n\n<div id=\"incomplete1\" aria-label=\"foo\">Foo</div>\n<div id=\"incomplete2\" aria-labelledby=\"missing\">Foo</div>\n<div id=\"incomplete3\" aria-label=\"foo\" role=\"code\">Foo</div>\n"
  },
  {
    "path": "test/integration/rules/aria-prohibited-attr/aria-prohibited-attr.json",
    "content": "{\n  \"description\": \"aria-prohibited-attr tests\",\n  \"rule\": \"aria-prohibited-attr\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"]\n  ],\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"], [\"#incomplete3\"]],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"],\n    [\"#fail12\"],\n    [\"#fail13\"],\n    [\"#fail14\"],\n    [\"#fail15\"],\n    [\"#fail16\"],\n    [\"#fail17\"],\n    [\"#fail18\"],\n    [\"#fail19\"],\n    [\"#fail20\"],\n    [\"#fail21\"],\n    [\"#fail22\"],\n    [\"#fail23\"],\n    [\"#fail24\"],\n    [\"#fail25\"],\n    [\"#fail26\"],\n    [\"#fail27\"],\n    [\"#fail28\"],\n    [\"#fail29\"],\n    [\"#fail30\"],\n    [\"#fail31\"],\n    [\"#fail32\"],\n    [\"#fail33\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-required-attr/required-attr.html",
    "content": "<div\n  role=\"scrollbar\"\n  id=\"pass1\"\n  aria-controls=\"value\"\n  aria-orientation=\"value\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n>\n  ok\n</div>\n<div\n  role=\"slider\"\n  id=\"pass2\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n>\n  ok\n</div>\n<div\n  role=\"spinbutton\"\n  id=\"pass3\"\n  aria-valuenow=\"value\"\n  aria-valuemax=\"value\"\n  aria-valuemin=\"value\"\n>\n  ok\n</div>\n<div role=\"heading\" id=\"pass4\" aria-level=\"1\">ok</div>\n<div\n  role=\"combobox\"\n  id=\"pass5\"\n  aria-expanded=\"true\"\n  aria-controls=\"controlsElement\"\n>\n  ok\n</div>\n<div\n  role=\"combobox\"\n  id=\"comboboxWithOwns\"\n  aria-expanded=\"true\"\n  aria-owns=\"ownsElement\"\n>\n  ok\n</div>\n<span role=\"radio\" id=\"pass6\" aria-checked=\"false\">I am RED!</span>\n<span role=\"radio\" id=\"pass7\" aria-checked=\"true\">I am GREEN!</span>\n<span role=\"radio\" id=\"pass8\" aria-checked=\"true\">I am GREEN!</span>\n<input type=\"number\" role=\"spinbutton\" id=\"pass9\" value=\"10\" />\n<input type=\"number\" role=\"spinbutton\" id=\"pass10\" />\n<div role=\"spinbutton\" id=\"pass11\">fail</div>\n<div role=\"separator\" id=\"pass12\"></div>\n<div role=\"separator\" id=\"pass13\" tabindex=\"0\" aria-valuenow=\"foo\"></div>\n<div\n  role=\"slider\"\n  id=\"pass14-aria-valuetext-slider\"\n  aria-valuetext=\"3 minutes 20 second\"\n>\n  ok\n</div>\n\n<div role=\"scrollbar\" id=\"violation1\">fail</div>\n<div role=\"slider\" id=\"violation2\">fail</div>\n<div role=\"heading\" id=\"violation3\">fail</div>\n<div role=\"combobox\" id=\"violation4\">fail</div>\n<span role=\"radio\" id=\"violation5\">I am BLUE!</span>\n<div role=\"separator\" id=\"violation6\" tabindex=\"0\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-required-attr/required-attr.json",
    "content": "{\n  \"description\": \"aria-required-attr tests\",\n  \"rule\": \"aria-required-attr\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#comboboxWithOwns\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14-aria-valuetext-slider\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-required-children/aria-required-children.html",
    "content": "<div role=\"list\" id=\"pass1\">\n  <div role=\"listitem\" id=\"ignore1\">Item 1</div>\n</div>\n<div role=\"list\" id=\"pass2\" aria-owns=\"ignore2\"></div>\n<div role=\"listitem\" id=\"ignore2\"></div>\n<div role=\"list\" id=\"fail1\">\n  <div role=\"menuitem\" id=\"ignore3\"></div>\n</div>\n<div role=\"list\" id=\"fail2\" aria-owns=\"ingore4\"></div>\n<div role=\"row\" id=\"fail3\"></div>\n<div role=\"menuitem\" id=\"ingore4\"></div>\n<div role=\"list\" id=\"pass3\" aria-owns=\"parent\"></div>\n<div id=\"parent\">\n  <div role=\"listitem\" id=\"ignore5\"></div>\n</div>\n<div role=\"grid\" id=\"incomplete1\"></div>\n<div role=\"list\" id=\"incomplete2\"></div>\n<div role=\"listbox\" id=\"incomplete3\"></div>\n<div role=\"table\" id=\"incomplete4\"></div>\n<div role=\"tablist\" id=\"incomplete5\"></div>\n<div role=\"tree\" id=\"incomplete6\"></div>\n<div role=\"treegrid\" id=\"incomplete7\"></div>\n<div role=\"rowgroup\" id=\"incomplete8\"></div>\n<div role=\"listbox\" id=\"incomplete9\">\n  <div></div>\n</div>\n<div role=\"menu\" id=\"incomplete10\">\n  <!-- -->\n  <span role=\"menuitem\" hidden></span>\n  <span role=\"none\">\n    <span role=\"menuitem\" aria-hidden=\"true\">hidden</span>\n  </span>\n</div>\n<div role=\"menubar\" id=\"incomplete11\"></div>\n<div role=\"list\" id=\"fail4\">\n  <div role=\"tabpanel\" id=\"ignore6\">\n    <div role=\"listitem\" id=\"ignore7\">List item 1</div>\n  </div>\n</div>\n<div role=\"grid\" id=\"pass4\">\n  <div role=\"row\" id=\"fail5\">\n    <span>Item 1</span>\n  </div>\n</div>\n<div role=\"grid\" id=\"pass5\">\n  <div role=\"presentation\" id=\"ignore8\">\n    <div role=\"row\" id=\"pass6\">\n      <div role=\"none\" id=\"ingore9\">\n        <span role=\"cell\" id=\"ignore10\">Item 1</span>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div role=\"menu\" id=\"pass7\">\n  <ul role=\"group\" id=\"ignore11\">\n    <li role=\"menuitem\" id=\"ignore12\">>Inbox</li>\n    <li role=\"separator\" id=\"ignored13\"></li>\n    <li role=\"menuitem\" id=\"ignore14\">>Archive</li>\n    <li role=\"menuitem\" id=\"ignore15\">>Trash</li>\n  </ul>\n</div>\n\n<div role=\"suggestion\" id=\"pass8\">\n  <span role=\"deletion\">option</span>\n  <span role=\"insertion\">option</span>\n</div>\n\n<div role=\"suggestion\" id=\"fail6\"></div>\n\n<div role=\"listbox\" id=\"pass9\">\n  <div role=\"group\">\n    <div role=\"option\">option</div>\n  </div>\n</div>\n\n<div role=\"list\" id=\"pass10\" aria-busy=\"true\"></div>\n<div role=\"suggestion\" id=\"pass11\" aria-busy=\"true\"></div>\n<div role=\"menu\" id=\"pass12\" aria-busy=\"true\"></div>\n<div role=\"listbox\" id=\"pass13\" aria-busy=\"true\"></div>\n\n<div role=\"list\" id=\"pass14\">\n  <script></script>\n  <style></style>\n  <canvas hidden></canvas>\n  <div><div role=\"listitem\">Item 1</div></div>\n  <div role=\"generic\"><div role=\"listitem\">item 2</div></div>\n  <div role=\"presentation\"><div role=\"listitem\">item 2</div></div>\n  <div role=\"none\"><div role=\"listitem\">item 2</div></div>\n</div>\n\n<div role=\"suggestion\" id=\"fail7\">\n  <div aria-busy=\"true\"></div>\n</div>\n<div role=\"suggestion\" id=\"fail8\" aria-busy=\"false\"></div>\n<div role=\"list\" id=\"fail9\">\n  <li>Item 1</li>\n  <span role=\"link\">Item 2</span>\n</div>\n<div role=\"list\" id=\"fail10\">\n  <div aria-live=\"polite\">\n    <div role=\"listitem\">List item 1</div>\n    <div role=\"listitem\">List item 2</div>\n  </div>\n</div>\n<div role=\"list\" id=\"fail11\">\n  <div tabindex=\"0\">\n    <div role=\"listitem\">List item 1</div>\n    <div role=\"listitem\">List item 2</div>\n  </div>\n</div>\n\n<div role=\"list\" id=\"fail12\">\n  <div aria-busy=\"true\"></div>\n</div>\n\n<div role=\"list\" id=\"fail13\" aria-busy=\"true\">\n  <div role=\"alert\">unallowed role</div>\n</div>\n\n<div role=\"doc-bibliography\" id=\"inapplicable1\"></div>\n<div role=\"doc-endnotes\" id=\"inapplicable2\"></div>\n<div role=\"radiogroup\" id=\"inapplicable3\">\n  <div role=\"heading\" aria-level=\"2\">Heading</div>\n  <ul>\n    <li><div role=\"radio\"></div></li>\n    <li><div role=\"radio\"></div></li>\n  </ul>\n</div>\n\n<ul role=\"menu\" id=\"pass15\">\n  <li role=\"none\">\n    <a href=\"#\" role=\"menuitem\">Foo</a>\n    <ul role=\"menu\" id=\"pass16\">\n      <li role=\"none\">\n        <a href=\"#\" role=\"menuitem\">Bar</a>\n      </li>\n    </ul>\n  </li>\n</ul>\n\n<nav role=\"menubar\" id=\"pass17\">\n  <a role=\"menuitem\" href=\"\">Item 1</a>\n  <a role=\"menuitem\" href=\"\">Item 2</a>\n  <span role=\"separator\"></span>\n  <a role=\"menuitem\" href=\"\">Item 3</a>\n</nav>\n\n<div role=\"menu\" id=\"pass18\">\n  <div role=\"menuitem\">menu item</div>\n  <div aria-hidden=\"true\">shouldn't be flagged but is</div>\n</div>\n\n<div role=\"list\" id=\"pass19\">\n  <div role=\"presentation\">\n    <div style=\"display: none\">ignore</div>\n    <div style=\"visibility: hidden\" aria-hidden=\"true\">ignore</div>\n    <li>item 1</li>\n    <li>item 2</li>\n  </div>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-required-children/aria-required-children.json",
    "content": "{\n  \"description\": \"aria-required-children test\",\n  \"rule\": \"aria-required-children\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"],\n    [\"#fail12\"],\n    [\"#fail13\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"],\n    [\"#incomplete9\"],\n    [\"#incomplete10\"],\n    [\"#incomplete11\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-required-parent/aria-required-parent.html",
    "content": "<div role=\"list\" id=\"ignore1\"><div role=\"listitem\" id=\"pass1\">Item 1</div></div>\n<div role=\"list\" id=\"ignore2\" aria-owns=\"pass2\"></div>\n<div role=\"listitem\" id=\"pass2\"></div>\n<div role=\"listitem\" id=\"fail1\"></div>\n<div role=\"menu\" id=\"ignore3\"><div role=\"listitem\" id=\"fail2\"></div></div>\n<div role=\"menu\" id=\"ignore4\" aria-owns=\"fail3\"></div>\n<div role=\"listitem\" id=\"fail3\"></div>\n<div role=\"list\" id=\"ignore5\" aria-owns=\"parent\"></div>\n<div id=\"parent\"><div role=\"listitem\" id=\"pass3\"></div></div>\n<div role=\"grid\" id=\"ignore6\"><div role=\"rowgroup\" id=\"pass4\">Item 1</div></div>\n<div role=\"table\" id=\"ignore7\">\n  <div role=\"rowgroup\" id=\"pass5\">Item 1</div>\n</div>\n<div role=\"treegrid\" id=\"ignore8\">\n  <div role=\"rowgroup\" id=\"pass6\">Item 1</div>\n</div>\n\n<div role=\"treegrid\" id=\"ignore9\">\n  <div role=\"listitem\" id=\"fail4\">\n    <div role=\"rowgroup\" id=\"fail5\">Item 1</div>\n  </div>\n</div>\n\n<div role=\"treegrid\" id=\"ignore10\">\n  <div role=\"presentation\" id=\"ignore11\">\n    <div role=\"rowgroup\" id=\"pass7\">Item 1</div>\n  </div>\n</div>\n\n<div role=\"treegrid\" id=\"ignore12\">\n  <div role=\"none\" id=\"ignore13\">\n    <div role=\"rowgroup\" id=\"pass8\">Item 1</div>\n  </div>\n</div>\n\n<div role=\"treegrid\" id=\"ignore14\" aria-owns=\"pass9\">\n  <div role=\"list\" id=\"ignore15\">\n    <div role=\"rowgroup\" id=\"pass9\">Item 1</div>\n  </div>\n</div>\n\n<div role=\"tree\">\n  <div role=\"treeitem\" id=\"pass10\">\n    <div role=\"group\">\n      <div role=\"treeitem\" id=\"pass11\">tree item</div>\n    </div>\n  </div>\n</div>\n\n<div role=\"group\">\n  <div role=\"treeitem\" id=\"fail6\">tree item</div>\n</div>\n\n<div role=\"menu\">\n  <div role=\"menuitem\" id=\"pass12\">\n    <div role=\"group\">\n      <div role=\"menuitem\" id=\"fail7\">menu item</div>\n    </div>\n  </div>\n</div>\n\n<div role=\"listbox\">\n  <div role=\"group\">\n    <div role=\"option\" id=\"pass13\">option</div>\n  </div>\n</div>\n\n<div role=\"tree\">\n  <ul role=\"group\">\n    <li role=\"none\">\n      <ul role=\"group\">\n        <li role=\"none\">\n          <div role=\"treeitem\" id=\"pass14\">tree item</div>\n        </li>\n      </ul>\n    </li>\n  </ul>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-required-parent/aria-required-parent.json",
    "content": "{\n  \"description\": \"aria-required-parent test\",\n  \"rule\": \"aria-required-parent\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-roledescription/aria-roledescription.html",
    "content": "<button aria-roledescription=\"my button\" id=\"pass1\">button</button>\n<img aria-roledescription=\"my img\" src=\"foo.png\" id=\"pass2\" />\n<div role=\"checkbox\" aria-roledescription=\"my checkbox\" id=\"pass3\"></div>\n<div role=\"radio\" aria-roledescription=\"my radio\" id=\"pass4\"></div>\n<div role=\"combobox\" aria-roledescription=\"my combobox\" id=\"pass5\"></div>\n<div\n  role=\"menuitemcheckbox\"\n  aria-roledescription=\"my menuitemcheckbox\"\n  id=\"pass6\"\n></div>\n<div\n  role=\"menuitemradio\"\n  aria-roledescription=\"my menuitemradio\"\n  id=\"pass7\"\n></div>\n<input type=\"checkbox\" aria-roledescription=\"my checkbox\" id=\"pass8\" />\n<input type=\"radio\" aria-roledescription=\"my radio\" id=\"pass9\" />\n\n<h1 aria-roledescription=\"my heading\" id=\"incomplete1\">heading</h1>\n<div role=\"rowgroup\" aria-roledescription=\"my row\" id=\"incomplete2\"></div>\n\n<p aria-roledescription=\"my paragraph\" id=\"fail1\">paragraph</p>\n<div aria-roledescription=\"my div\" id=\"fail2\">div</div>\n<div\n  role=\"presentation\"\n  aria-roledescription=\"my presentation\"\n  id=\"fail3\"\n></div>\n<div role=\"none\" aria-roledescription=\"my none\" id=\"fail4\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-roledescription/aria-roledescription.json",
    "content": "{\n  \"description\": \"aria-roledescription test\",\n  \"rule\": \"aria-roledescription\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"]\n  ],\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-roles/aria-roles.html",
    "content": "<div id=\"ok\">\n  <div role=\"alert\" id=\"pass1\">ok</div>\n  <div role=\"alertdialog\" id=\"pass2\">ok</div>\n  <div role=\"application\" id=\"pass3\">ok</div>\n  <div role=\"article\" id=\"pass4\">ok</div>\n  <div role=\"banner\" id=\"pass5\">ok</div>\n  <div role=\"button\" id=\"pass6\">ok</div>\n  <div role=\"checkbox\" id=\"pass7\">ok</div>\n  <div role=\"columnheader\" id=\"pass8\">ok</div>\n  <div role=\"combobox\" id=\"pass9\">ok</div>\n  <div role=\"complementary\" id=\"pass10\">ok</div>\n  <div role=\"contentinfo\" id=\"pass11\">ok</div>\n  <div role=\"definition\" id=\"pass12\">ok</div>\n  <div role=\"dialog\" id=\"pass13\">ok</div>\n  <div role=\"document\" id=\"pass15\">ok</div>\n  <div role=\"feed\" id=\"pass16\">ok</div>\n  <div role=\"form\" id=\"pass17\">ok</div>\n  <div role=\"grid\" id=\"pass18\">ok</div>\n  <div role=\"gridcell\" id=\"pass19\">ok</div>\n  <div role=\"group\" id=\"pass20\">ok</div>\n  <div role=\"heading\" id=\"pass21\">ok</div>\n  <div role=\"img\" id=\"pass22\">ok</div>\n  <div role=\"link\" id=\"pass23\">ok</div>\n  <div role=\"list\" id=\"pass24\">ok</div>\n  <div role=\"listbox\" id=\"pass25\">ok</div>\n  <div role=\"listitem\" id=\"pass26\">ok</div>\n  <div role=\"log\" id=\"pass27\">ok</div>\n  <div role=\"main\" id=\"pass28\">ok</div>\n  <div role=\"marquee\" id=\"pass29\">ok</div>\n  <div role=\"math\" id=\"pass30\">ok</div>\n  <div role=\"menu\" id=\"pass31\">ok</div>\n  <div role=\"menubar\" id=\"pass32\">ok</div>\n  <div role=\"menuitem\" id=\"pass33\">ok</div>\n  <div role=\"menuitemcheckbox\" id=\"pass34\">ok</div>\n  <div role=\"menuitemradio\" id=\"pass35\">ok</div>\n  <div role=\"navigation\" id=\"pass36\">ok</div>\n  <div role=\"note\" id=\"pass37\">ok</div>\n  <div role=\"option\" id=\"pass38\">ok</div>\n  <div role=\"presentation\" id=\"pass39\">ok</div>\n  <div role=\"progressbar\" id=\"pass40\">ok</div>\n  <div role=\"radio\" id=\"pass41\">ok</div>\n  <div role=\"radiogroup\" id=\"pass42\">ok</div>\n  <div role=\"region\" id=\"pass43\">ok</div>\n  <div role=\"row\" id=\"pass44\">ok</div>\n  <div role=\"rowgroup\" id=\"pass45\">ok</div>\n  <div role=\"rowheader\" id=\"pass46\">ok</div>\n  <div role=\"scrollbar\" id=\"pass47\">ok</div>\n  <div role=\"search\" id=\"pass48\">ok</div>\n  <div role=\"separator\" id=\"pass49\">ok</div>\n  <div role=\"slider\" id=\"pass50\">ok</div>\n  <div role=\"spinbutton\" id=\"pass51\">ok</div>\n  <div role=\"status\" id=\"pass52\">ok</div>\n  <div role=\"tab\" id=\"pass53\">ok</div>\n  <div role=\"tablist\" id=\"pass54\">ok</div>\n  <div role=\"tabpanel\" id=\"pass55\">ok</div>\n  <div role=\"term\" id=\"pass56\">ok</div>\n  <div role=\"textbox\" id=\"pass57\">ok</div>\n  <div role=\"timer\" id=\"pass58\">ok</div>\n  <div role=\"toolbar\" id=\"pass59\">ok</div>\n  <div role=\"tooltip\" id=\"pass60\">ok</div>\n  <div role=\"tree\" id=\"pass61\">ok</div>\n  <div role=\"treegrid\" id=\"pass62\">ok</div>\n  <div role=\"treeitem\" id=\"pass63\">ok</div>\n  <div role=\"switch\" id=\"pass64\">ok</div>\n  <div role=\"none\" id=\"pass65\">ok</div>\n  <div role=\"cell\" id=\"pass66\">ok</div>\n  <div role=\"searchbox\" id=\"pass67\">ok</div>\n  <div role=\"table\" id=\"pass69\">ok</div>\n  <div role=\"doc-abstract\" id=\"pass70\">ok</div>\n  <div role=\"doc-acknowledgments\" id=\"pass71\">ok</div>\n  <div role=\"doc-afterword\" id=\"pass72\">ok</div>\n  <div role=\"doc-appendix\" id=\"pass73\">ok</div>\n  <div role=\"doc-backlink\" id=\"pass74\">ok</div>\n  <div role=\"doc-bibliography\" id=\"pass76\">ok</div>\n  <div role=\"doc-biblioref\" id=\"pass77\">ok</div>\n  <div role=\"doc-chapter\" id=\"pass78\">ok</div>\n  <div role=\"doc-colophon\" id=\"pass79\">ok</div>\n  <div role=\"doc-conclusion\" id=\"pass80\">ok</div>\n  <div role=\"doc-cover\" id=\"pass81\">ok</div>\n  <div role=\"doc-credit\" id=\"pass82\">ok</div>\n  <div role=\"doc-credits\" id=\"pass83\">ok</div>\n  <div role=\"doc-dedication\" id=\"pass84\">ok</div>\n  <div role=\"doc-endnotes\" id=\"pass85\">ok</div>\n  <div role=\"doc-epigraph\" id=\"pass87\">ok</div>\n  <div role=\"doc-epilogue\" id=\"pass88\">ok</div>\n  <div role=\"doc-errata\" id=\"pass89\">ok</div>\n  <div role=\"doc-example\" id=\"pass90\">ok</div>\n  <div role=\"doc-footnote\" id=\"pass91\">ok</div>\n  <div role=\"doc-foreword\" id=\"pass92\">ok</div>\n  <div role=\"doc-glossary\" id=\"pass93\">ok</div>\n  <div role=\"doc-glossref\" id=\"pass94\">ok</div>\n  <div role=\"doc-index\" id=\"pass95\">ok</div>\n  <div role=\"doc-introduction\" id=\"pass96\">ok</div>\n  <div role=\"doc-noteref\" id=\"pass97\">ok</div>\n  <div role=\"doc-notice\" id=\"pass98\">ok</div>\n  <div role=\"doc-pagebreak\" id=\"pass99\">ok</div>\n  <div role=\"doc-pagelist\" id=\"pass100\">ok</div>\n  <div role=\"doc-part\" id=\"pass101\">ok</div>\n  <div role=\"doc-preface\" id=\"pass102\">ok</div>\n  <div role=\"doc-prologue\" id=\"pass103\">ok</div>\n  <div role=\"doc-pullquote\" id=\"pass104\">ok</div>\n  <div role=\"doc-qna\" id=\"pass105\">ok</div>\n  <div role=\"doc-subtitle\" id=\"pass106\">ok</div>\n  <div role=\"doc-tip\" id=\"pass107\">ok</div>\n  <div role=\"doc-toc\" id=\"pass108\">ok</div>\n  <div role=\"figure\" id=\"pass109\">ok</div>\n  <div role=\"text\" id=\"pass110\">ok</div>\n  <div role=\"graphics-document\" id=\"pass111\">ok</div>\n  <div role=\"graphics-object\" id=\"pass112\">ok</div>\n  <div role=\"graphics-symbol\" id=\"pass113\">ok</div>\n  <div role=\"comment\" id=\"pass114\">ok</div>\n  <div role=\"mark\" id=\"pass115\">ok</div>\n  <div role=\"suggestion\" id=\"pass116\">ok</div>\n  <!-- fallback roles -->\n  <div role=\"button alert\" id=\"pass117\">fail</div>\n</div>\n<div id=\"violation\">\n  <!-- abstract roles -->\n  <div role=\"command\" id=\"fail1\">fail</div>\n  <div role=\"composite\" id=\"fail2\">fail</div>\n  <div role=\"input\" id=\"fail3\">fail</div>\n  <div role=\"landmark\" id=\"fail4\">fail</div>\n  <div role=\"range\" id=\"fail5\">fail</div>\n  <div role=\"roletype\" id=\"fail6\">fail</div>\n  <div role=\"section\" id=\"fail7\">fail</div>\n  <div role=\"sectionhead\" id=\"fail8\">fail</div>\n  <div role=\"select\" id=\"fail9\">fail</div>\n  <div role=\"structure\" id=\"fail10\">fail</div>\n  <div role=\"widget\" id=\"fail11\">fail</div>\n  <div role=\"window\" id=\"fail12\">fail</div>\n  <!-- invalid roles -->\n  <div role=\"lol\" id=\"fail13\">fail</div>\n  <!-- unsupported roles -->\n  <!-- deprecated roles-->\n  <div role=\"doc-biblioentry\" id=\"pass118\">fail</div>\n  <div role=\"doc-endnote\" id=\"pass119\">fail</div>\n  <div role=\"directory\" id=\"pass120\">ok</div>\n</div>\n\n<!-- inapplicable  -->\n<div id=\"inapplicable1\">no role attribute</div>\n<div id=\"inapplicable2\" role>role not defined</div>\n<div id=\"inapplicable3\" role=\"\">empty role</div>\n<div id=\"inapplicable4\" role=\" \">role has only whitespace</div>\n"
  },
  {
    "path": "test/integration/rules/aria-roles/aria-roles.json",
    "content": "{\n  \"description\": \"aria-roles tests\",\n  \"rule\": \"aria-roles\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"],\n    [\"#fail12\"],\n    [\"#fail13\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass25\"],\n    [\"#pass26\"],\n    [\"#pass27\"],\n    [\"#pass28\"],\n    [\"#pass29\"],\n    [\"#pass30\"],\n    [\"#pass31\"],\n    [\"#pass32\"],\n    [\"#pass33\"],\n    [\"#pass34\"],\n    [\"#pass35\"],\n    [\"#pass36\"],\n    [\"#pass37\"],\n    [\"#pass38\"],\n    [\"#pass39\"],\n    [\"#pass40\"],\n    [\"#pass41\"],\n    [\"#pass42\"],\n    [\"#pass43\"],\n    [\"#pass44\"],\n    [\"#pass45\"],\n    [\"#pass46\"],\n    [\"#pass47\"],\n    [\"#pass48\"],\n    [\"#pass49\"],\n    [\"#pass50\"],\n    [\"#pass51\"],\n    [\"#pass52\"],\n    [\"#pass53\"],\n    [\"#pass54\"],\n    [\"#pass55\"],\n    [\"#pass56\"],\n    [\"#pass57\"],\n    [\"#pass58\"],\n    [\"#pass59\"],\n    [\"#pass60\"],\n    [\"#pass61\"],\n    [\"#pass62\"],\n    [\"#pass63\"],\n    [\"#pass64\"],\n    [\"#pass65\"],\n    [\"#pass66\"],\n    [\"#pass67\"],\n    [\"#pass69\"],\n    [\"#pass70\"],\n    [\"#pass71\"],\n    [\"#pass72\"],\n    [\"#pass73\"],\n    [\"#pass74\"],\n    [\"#pass76\"],\n    [\"#pass77\"],\n    [\"#pass78\"],\n    [\"#pass79\"],\n    [\"#pass80\"],\n    [\"#pass81\"],\n    [\"#pass82\"],\n    [\"#pass83\"],\n    [\"#pass84\"],\n    [\"#pass85\"],\n    [\"#pass87\"],\n    [\"#pass88\"],\n    [\"#pass89\"],\n    [\"#pass90\"],\n    [\"#pass91\"],\n    [\"#pass92\"],\n    [\"#pass93\"],\n    [\"#pass94\"],\n    [\"#pass95\"],\n    [\"#pass96\"],\n    [\"#pass97\"],\n    [\"#pass98\"],\n    [\"#pass99\"],\n    [\"#pass100\"],\n    [\"#pass101\"],\n    [\"#pass102\"],\n    [\"#pass103\"],\n    [\"#pass104\"],\n    [\"#pass105\"],\n    [\"#pass106\"],\n    [\"#pass107\"],\n    [\"#pass108\"],\n    [\"#pass109\"],\n    [\"#pass110\"],\n    [\"#pass111\"],\n    [\"#pass112\"],\n    [\"#pass113\"],\n    [\"#pass114\"],\n    [\"#pass115\"],\n    [\"#pass116\"],\n    [\"#pass117\"],\n    [\"#pass118\"],\n    [\"#pass119\"],\n    [\"#pass120\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-tab-name/aria-tab-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"tab\">Tab Name</div>\n<div id=\"pass2\" role=\"tab\" title=\"Item\"></div>\n<div id=\"pass3\" role=\"tab\" aria-label=\"Tab Name\"></div>\n<div id=\"pass4\" role=\"tab\" aria-labelledby=\"tab-label\"></div>\n\n<span id=\"tab-label\">Tab Name</span>\n\n<div role=\"tablist\">\n  <span id=\"pass5\" role=\"tab\" tabindex=\"0\">\n    Tab Name\n    <i class=\"fa-solid fa-house\"></i>\n  </span>\n</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"tab\"></div>\n<div id=\"fail2\" role=\"tab\" aria-labelledby=\"item-non-existant\"></div>\n<div id=\"fail3\" role=\"tab\" aria-labelledby=\"item-empty\"></div>\n\n<div id=\"item-empty\"></div>\n\n<div role=\"tablist\">\n  <span id=\"fail4\" role=\"tab\" tabindex=\"0\">\n    <i class=\"fa-solid fa-house\"></i>\n  </span>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-tab-name/aria-tab-name.json",
    "content": "{\n  \"description\": \"aria-tab-name test\",\n  \"rule\": \"aria-tab-name\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-text/aria-text.html",
    "content": "<h1>\n  <span role=\"text\" id=\"pass1\"\n    >Digital accessibility, <br />\n    for everyone.\n  </span>\n</h1>\n<a href=\"#\"\n  ><span role=\"text\" id=\"pass2\">Buy <strong>t-shirts</strong> now</span></a\n>\n<div role=\"text\" id=\"pass3\">Some text<span> and some more text</span></div>\n\n<div role=\"text\" id=\"fail1\">\n  Still an <a href=\"#\">interactive link</a> because of the author error.\n</div>\n<div role=\"text\" id=\"fail2\">\n  <a href=\"#\" role=\"none\">Flattened text</a> because of the explicit role.\n</div>\n<div role=\"text\" id=\"fail3\">\n  <a href=\"#\" tabindex=\"-1\" role=\"none\">Flattened text</a> because of the\n  explicit role.\n</div>\n<p role=\"text\" id=\"fail4\"><button>Hello</button></p>\n<p role=\"text\" id=\"fail5\"><button tabindex=\"-1\">Hello</button></p>\n<p role=\"text\" id=\"pass4\"><span tabindex=\"-1\">Hello</span></p>\n<div role=\"text\" id=\"pass5\">\n  <a tabindex=\"-1\"\n    >passes because no href makes this not have the implicit role of 'link'</a\n  >\n</div>\n<div role=\"text\" id=\"pass6\">\n  <a>passes because no href makes this not have the implicit role of 'link'</a>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-text/aria-text.json",
    "content": "{\n  \"description\": \"aria-text tests\",\n  \"rule\": \"aria-text\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-toggle-field-name/aria-toggle-field-name.html",
    "content": "<!-- PASS -->\n<!-- checkbox -->\n<div id=\"pass1\" role=\"checkbox\">Newspaper</div>\n<!-- menuitemcheckbox -->\n<ul role=\"menu\">\n  <li\n    id=\"pass2\"\n    role=\"menuitemcheckbox\"\n    aria-label=\"Word wrap\"\n    aria-checked=\"true\"\n  ></li>\n</ul>\n<!-- menuitemradio -->\n<p id=\"pass3Label\">Sans-serif</p>\n<ul role=\"menu\">\n  <li\n    id=\"pass3\"\n    role=\"menuitemradio\"\n    aria-labelledby=\"pass3Label\"\n    aria-checked=\"true\"\n  ></li>\n</ul>\n<!-- radio -->\n<div role=\"radiogroup\">\n  <div\n    id=\"pass4\"\n    role=\"radio\"\n    aria-checked=\"false\"\n    tabindex=\"0\"\n    title=\"Regular Crust\"\n  ></div>\n</div>\n<!-- switch -->\n<div\n  id=\"pass5\"\n  role=\"switch\"\n  aria-checked=\"true\"\n  aria-label=\"Toggle blue light:\"\n>\n  <span>off</span>\n  <span>on</span>\n</div>\n<div role=\"listbox\" aria-label=\"Greeting\">\n  <div role=\"option\" id=\"pass6\">Hello world</div>\n</div>\n\n<!-- FAIL -->\n<!-- checkbox -->\n<div id=\"fail1\" role=\"checkbox\" aria-labelledby=\"does-not-exist\"></div>\n<!-- menuitemcheckbox -->\n<ul role=\"menu\">\n  <li id=\"fail2\" role=\"menuitemcheckbox\" aria-checked=\"true\"></li>\n</ul>\n<!-- menuitemradio -->\n<ul role=\"menu\">\n  <li id=\"fail3\" role=\"menuitemradio\" aria-checked=\"true\"></li>\n</ul>\n<!-- radio -->\n<div role=\"radiogroup\">\n  <div id=\"fail4\" role=\"radio\" aria-checked=\"false\" tabindex=\"0\"></div>\n</div>\n<!-- switch -->\n<div id=\"fail5\" role=\"switch\" aria-checked=\"true\">\n  <span></span>\n  <span></span>\n</div>\n<div role=\"listbox\" aria-label=\"Greeting\">\n  <div\n    role=\"option\"\n    id=\"fail6\"\n    title=\"\"\n    aria-label=\"\"\n    aria-labelledby=\"fake\"\n  ></div>\n</div>\n\n<!-- INAPPLICABLE -->\n<input id=\"inapplicable1\" />\n<select id=\"inapplicable2\">\n  <option value=\"volvo\">Volvo</option>\n  <option value=\"saab\">Saab</option>\n  <option value=\"opel\">Opel</option>\n</select>\n<textarea id=\"inapplicable3\" title=\"Label\"></textarea>\n\n<!-- INCOMPLETE -->\n<label>\n  first name\n  <div id=\"canttell1\" role=\"checkbox\" aria-label=\"name\"></div>\n</label>\n<label for=\"canttell2\">first name</label>\n<div role=\"checkbox\" id=\"canttell2\" aria-label=\"name\"></div>\n"
  },
  {
    "path": "test/integration/rules/aria-toggle-field-name/aria-toggle-field-name.json",
    "content": "{\n  \"description\": \"aria-toggle-field-name test\",\n  \"rule\": \"aria-toggle-field-name\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"]\n  ],\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"]\n  ],\n  \"incomplete\": [[\"#canttell1\"], [\"#canttell2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-tooltip-name/aria-tooltip-name.html",
    "content": "<!-- PASS -->\n<div id=\"pass1\" role=\"tooltip\">Copy this content</div>\n<div id=\"pass2\" role=\"tooltip\" title=\"Copy this content\"></div>\n<div id=\"pass3\" role=\"tooltip\" aria-label=\"Copy this content\"></div>\n<div id=\"pass4\" role=\"tooltip\" aria-labelledby=\"copy\"></div>\n\n<div id=\"copy\">Copy this content</div>\n\n<!-- FAIL -->\n<div id=\"fail1\" role=\"tooltip\"></div>\n<div id=\"fail2\" role=\"tooltip\" aria-labelledby=\"copy-non-existant\"></div>\n<div id=\"fail3\" role=\"tooltip\" aria-labelledby=\"copy-empty\"></div>\n\n<div id=\"copy-empty\"></div>\n\n<!-- INAPPLICABLE -->\n<img role=\"tooltip\" alt=\"Label\" id=\"inapplicable1\" />\n<input role=\"tooltip\" title=\"Label\" id=\"inapplicable2\" />\n<button role=\"tooltip\" title=\"Label\" id=\"inapplicable3\"></button>\n<a href=\"#\" role=\"tooltip\" title=\"Label\" id=\"inapplicable4\"></a>\n<select role=\"tooltip\" title=\"Label\" id=\"inapplicable5\">\n  <option value=\"volvo\">Volvo</option>\n  <option value=\"saab\">Saab</option>\n  <option value=\"opel\">Opel</option>\n</select>\n<textarea role=\"tooltip\" id=\"inapplicable6\" title=\"Label\"></textarea>\n"
  },
  {
    "path": "test/integration/rules/aria-tooltip-name/aria-tooltip-name.json",
    "content": "{\n  \"description\": \"aria-tooltip-name test\",\n  \"rule\": \"aria-tooltip-name\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-treeitem-name/aria-treeitem-name.html",
    "content": "<!-- PASS -->\n<div role=\"tree\">\n  <div id=\"pass1\" role=\"treeitem\">Item</div>\n  <div id=\"pass2\" role=\"treeitem\" title=\"Item\"></div>\n  <div id=\"pass3\" role=\"treeitem\" aria-label=\"Item\"></div>\n  <div id=\"pass4\" role=\"treeitem\" aria-labelledby=\"item\"></div>\n</div>\n\n<div id=\"item\">Item</div>\n\n<!-- FAIL -->\n<div role=\"tree\">\n  <div id=\"fail1\" role=\"treeitem\"></div>\n  <div id=\"fail2\" role=\"treeitem\" aria-labelledby=\"item-non-existent\"></div>\n  <div id=\"fail3\" role=\"treeitem\" aria-labelledby=\"item-empty\"></div>\n</div>\n\n<div id=\"item-empty\"></div>\n\n<!-- INAPPLICABLE -->\n<div role=\"tree\">\n  <img role=\"treeitem\" alt=\"Label\" id=\"inapplicable1\" />\n  <input role=\"treeitem\" title=\"Label\" id=\"inapplicable2\" />\n  <button role=\"treeitem\" title=\"Label\" id=\"inapplicable3\"></button>\n  <a href=\"#\" role=\"treeitem\" title=\"Label\" id=\"inapplicable4\"></a>\n  <select role=\"treeitem\" title=\"Label\" id=\"inapplicable5\">\n    <option value=\"volvo\">Volvo</option>\n    <option value=\"saab\">Saab</option>\n    <option value=\"opel\">Opel</option>\n  </select>\n  <textarea role=\"treeitem\" id=\"inapplicable6\" title=\"Label\"></textarea>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-treeitem-name/aria-treeitem-name.json",
    "content": "{\n  \"description\": \"aria-treeitem-name test\",\n  \"rule\": \"aria-treeitem-name\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-valid-attr/aria-valid-attr.html",
    "content": "<h2>Violations</h2>\n<div>\n  <div aria-cat=\"maybe\" id=\"violation1\"></div>\n  <div aria-bat=\"no\" id=\"violation2\"></div>\n  <div aria-fat=\"a lil bit\" id=\"violation3\"></div>\n  <div aria-rat=\"ew\" id=\"violation4\"></div>\n  <div aria-pat=\"ok\" id=\"violation5\"></div>\n  <div aria-hat=\"plz\" id=\"violation6\"></div>\n  <div aria-sat=\"or sun\" id=\"violation7\"></div>\n  <div aria-mat=\"lie\" id=\"violation8\"></div>\n</div>\n<h2>Possible False Positives</h2>\n<div>\n  <div id=\"pass1\" aria-activedescendant=\"stuff\">hi</div>\n  <div id=\"pass2\" aria-atomic=\"stuff\">hi</div>\n  <div id=\"pass3\" aria-autocomplete=\"stuff\">hi</div>\n  <div id=\"pass4\" aria-busy=\"stuff\">hi</div>\n  <div id=\"pass5\" aria-checked=\"stuff\">hi</div>\n  <div id=\"pass6\" aria-controls=\"stuff\">hi</div>\n  <div id=\"pass7\" aria-describedby=\"stuff\">hi</div>\n  <div id=\"pass8\" aria-disabled=\"stuff\">hi</div>\n  <div id=\"pass9\" aria-dropeffect=\"stuff\">hi</div>\n  <div id=\"pass10\" aria-expanded=\"stuff\">hi</div>\n  <div id=\"pass11\" aria-flowto=\"stuff\">hi</div>\n  <div id=\"pass12\" aria-grabbed=\"stuff\">hi</div>\n  <div id=\"pass13\" aria-haspopup=\"stuff\">hi</div>\n  <div id=\"pass14\" aria-hidden=\"stuff\">hi</div>\n  <div id=\"pass15\" aria-invalid=\"stuff\">hi</div>\n  <div id=\"pass16\" aria-keyshortcuts=\"stuff\">hi</div>\n  <div id=\"pass17\" aria-label=\"stuff\">hi</div>\n  <div id=\"pass18\" aria-labelledby=\"stuff\">hi</div>\n  <div id=\"pass19\" aria-level=\"stuff\">hi</div>\n  <div id=\"pass20\" aria-live=\"stuff\">hi</div>\n  <div id=\"pass21\" aria-modal=\"stuff\">hi</div>\n  <div id=\"pass22\" aria-multiline=\"stuff\">hi</div>\n  <div id=\"pass23\" aria-multiselectable=\"stuff\">hi</div>\n  <div id=\"pass24\" aria-placeholder=\"stuff\">hi</div>\n  <div id=\"pass25\" aria-orientation=\"stuff\">hi</div>\n  <div id=\"pass26\" aria-owns=\"stuff\">hi</div>\n  <div id=\"pass27\" aria-posinset=\"stuff\">hi</div>\n  <div id=\"pass28\" aria-pressed=\"stuff\">hi</div>\n  <div id=\"pass29\" aria-readonly=\"stuff\">hi</div>\n  <div id=\"pass30\" aria-relevant=\"stuff\">hi</div>\n  <div id=\"pass31\" aria-required=\"stuff\">hi</div>\n  <div id=\"pass32\" aria-selected=\"stuff\">hi</div>\n  <div id=\"pass33\" aria-setsize=\"stuff\">hi</div>\n  <div id=\"pass34\" aria-sort=\"stuff\">hi</div>\n  <div id=\"pass35\" aria-valuemax=\"stuff\">hi</div>\n  <div id=\"pass36\" aria-valuemin=\"stuff\">hi</div>\n  <div id=\"pass37\" aria-valuenow=\"stuff\">hi</div>\n  <div id=\"pass38\" aria-valuetext=\"stuff\">hi</div>\n\n  <div id=\"pass39\" role=\"alert\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass40\" role=\"application\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass41\" role=\"banner\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass42\" role=\"checkbox\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass43\" role=\"contentinfo\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass44\" role=\"doc-appendix\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass45\" role=\"doc-glossary\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass46\" role=\"group\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass47\" role=\"log\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass48\" role=\"menubar\" aria-errormessage=\"stuff\">hi</div>\n  <div id=\"pass49\" role=\"scrollbar\" aria-errormessage=\"stuff\">hi</div>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-valid-attr/aria-valid-attr.json",
    "content": "{\n  \"description\": \"aria-valid-attr tests\",\n  \"rule\": \"aria-valid-attr\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation8\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass25\"],\n    [\"#pass26\"],\n    [\"#pass27\"],\n    [\"#pass28\"],\n    [\"#pass29\"],\n    [\"#pass30\"],\n    [\"#pass31\"],\n    [\"#pass32\"],\n    [\"#pass33\"],\n    [\"#pass34\"],\n    [\"#pass35\"],\n    [\"#pass36\"],\n    [\"#pass37\"],\n    [\"#pass38\"],\n    [\"#pass39\"],\n    [\"#pass40\"],\n    [\"#pass41\"],\n    [\"#pass42\"],\n    [\"#pass43\"],\n    [\"#pass44\"],\n    [\"#pass45\"],\n    [\"#pass46\"],\n    [\"#pass47\"],\n    [\"#pass48\"],\n    [\"#pass49\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.html",
    "content": "<h2>Violations</h2>\n<div>\n  <div aria-activedescendant=\"Idonotexist\" id=\"violation1\">hi</div>\n  <div aria-activedescendant=\"ref noexist ref2\" id=\"violation2\">hi</div>\n\n  <div aria-atomic=\"blah\" id=\"violation3\">hi</div>\n\n  <div aria-autocomplete=\"stuff\" id=\"violation4\">none</div>\n\n  <div aria-busy=\"blah\" id=\"violation5\">hi</div>\n\n  <div aria-checked=\"stuff\" id=\"violation6\">hi</div>\n  <div aria-controls=\"stuff\" id=\"violation7\">hi</div>\n  <div aria-disabled=\"stuff\" id=\"violation10\">hi</div>\n  <div aria-dropeffect=\"stuff\" id=\"violation11\">hi</div>\n  <div aria-expanded=\"stuff\" id=\"violation12\">hi</div>\n  <div aria-flowto=\"stuff\" id=\"violation13\">hi</div>\n  <div aria-grabbed=\"stuff\" id=\"violation14\">hi</div>\n  <div aria-haspopup=\"stuff\" id=\"violation15\">hi</div>\n  <div aria-hidden=\"stuff\" id=\"violation16\">hi</div>\n  <div aria-invalid=\"stuff\" id=\"violation17\">hi</div>\n  <div aria-level=\"stuff\" id=\"violation19\">hi</div>\n  <div aria-live=\"stuff\" id=\"violation20\">hi</div>\n  <div aria-multiline=\"stuff\" id=\"violation21\">hi</div>\n  <div aria-multiselectable=\"stuff\" id=\"violation22\">hi</div>\n  <div aria-owns=\"stuff\" id=\"violation23\">hi</div>\n  <div aria-posinset=\"stuff\" id=\"violation24\">hi</div>\n  <div aria-pressed=\"stuff\" id=\"violation25\">hi</div>\n  <div aria-readonly=\"stuff\" id=\"violation26\">hi</div>\n  <div aria-relevant=\"stuff\" id=\"violation27\">hi</div>\n  <div aria-required=\"stuff\" id=\"violation28\">hi</div>\n  <div aria-selected=\"stuff\" id=\"violation29\">hi</div>\n  <div aria-setsize=\"stuff\" id=\"violation30\">hi</div>\n  <div aria-sort=\"stuff\" id=\"violation31\">hi</div>\n  <div aria-valuemax=\"stuff\" id=\"violation32\">hi</div>\n  <div aria-valuemin=\"stuff\" id=\"violation33\">hi</div>\n  <div aria-valuenow=\"stuff\" id=\"violation34\">hi</div>\n  <div aria-errormessage=\"violation35-ref\" aria-invalid=\"true\" id=\"violation35\">\n    hi\n  </div>\n  <div id=\"violation35-ref\"></div>\n  <!-- don't allow empty aria-valuetext -->\n\n  <div aria-level=\"0\" id=\"violation36\">hi</div>\n  <div aria-level=\"-1\" id=\"violation37\">hi</div>\n  <div aria-level=\"-22\" id=\"violation38\">hi</div>\n\n  <div aria-posinset=\"0\" id=\"violation39\">hi</div>\n  <div aria-posinset=\"-1\" id=\"violation40\">hi</div>\n  <div aria-posinset=\"-22\" id=\"violation41\">hi</div>\n\n  <div aria-setsize=\"-22\" id=\"violation42\">hi</div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation43\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"violation43-ref\"\n    />\n    <div id=\"violation43-ref\" hidden>Error message 1</div>\n  </div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation44\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"violation44-ref\"\n    />\n    <div id=\"violation44-ref\" style=\"display: none\">Error message 1</div>\n  </div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation45\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"violation45-ref\"\n    />\n    <div id=\"violation45-ref\" style=\"visibility: hidden\">Error message 1</div>\n  </div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation46\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"violation46-ref\"\n    />\n    <div id=\"violation46-ref\" aria-hidden=\"true\">Error message 1</div>\n  </div>\n\n  <!-- Multiple IDs in aria-errormessage are not supported -->\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation47\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"violation47-ref1 violation47-ref2\"\n      aria-describedby=\"violation47-ref1 violation47-ref2\"\n    />\n    <div id=\"violation47-ref1\">Error message 1</div>\n    <div id=\"violation47-ref2\">Error message 2</div>\n  </div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"violation48\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"ref ref2\"\n    />\n  </div>\n</div>\n<h2>Possible False Positives</h2>\n<div>\n  <div aria-activedescendant=\"ref\" id=\"pass1\">hi</div>\n\n  <div aria-atomic=\"true\" id=\"pass2\">hi</div>\n  <div aria-atomic=\"false\" id=\"pass3\">hi</div>\n\n  <div aria-autocomplete=\"inline\" id=\"pass4\">inline</div>\n  <div aria-autocomplete=\"list\" id=\"pass5\">list</div>\n  <div aria-autocomplete=\"both\" id=\"pass6\">both</div>\n  <div aria-autocomplete=\"none\" id=\"pass7\">none</div>\n\n  <div aria-busy=\"true\" id=\"pass8\">hi</div>\n  <div aria-busy=\"false\" id=\"pass9\">hi</div>\n\n  <div aria-checked=\"true\" id=\"pass10\">hi</div>\n  <div aria-checked=\"false\" id=\"pass11\">hi</div>\n  <div aria-checked=\"mixed\" id=\"pass12\">hi</div>\n  <div aria-checked=\"undefined\" id=\"pass13\">hi</div>\n\n  <div aria-current=\"page\" id=\"pass14\">hi</div>\n  <div aria-current=\"step\" id=\"pass15\">hi</div>\n  <div aria-current=\"true\" id=\"pass16\">hi</div>\n  <div aria-current=\"location\" id=\"pass17\">hi</div>\n  <div aria-current=\"date\" id=\"pass18\">hi</div>\n  <div aria-current=\"time\" id=\"pass19\">hi</div>\n\n  <div aria-controls=\"ref\" id=\"pass20\">hi</div>\n  <div aria-controls=\"ref ref2\" id=\"pass21\">hi</div>\n  <div aria-controls=\" ref ref2 \" id=\"pass22\">hi</div>\n\n  <div aria-describedby=\"ref\" id=\"pass23\">hi</div>\n  <div aria-describedby=\"ref ref2 failref\" id=\"pass24\">hi</div>\n  <div aria-describedby=\" ref ref2 \" id=\"pass25\">hi</div>\n\n  <div aria-disabled=\"true\" id=\"pass26\">hi</div>\n  <div aria-disabled=\"false\" id=\"pass27\">hi</div>\n\n  <div aria-dropeffect=\"copy\" id=\"pass28\">hi</div>\n  <div aria-dropeffect=\"move\" id=\"pass29\">hi</div>\n  <div aria-dropeffect=\"link\" id=\"pass30\">hi</div>\n  <div aria-dropeffect=\"execute\" id=\"pass31\">hi</div>\n  <div aria-dropeffect=\"popup\" id=\"pass32\">hi</div>\n  <div aria-dropeffect=\"none\" id=\"pass33\">hi</div>\n\n  <div aria-expanded=\"true\" id=\"pass34\">hi</div>\n  <div aria-expanded=\"false\" id=\"pass35\">hi</div>\n  <div aria-expanded=\"undefined\" id=\"pass36\">hi</div>\n\n  <div aria-flowto=\"ref\" id=\"pass37\">hi</div>\n  <div aria-flowto=\"ref ref2\" id=\"pass38\">hi</div>\n  <div aria-flowto=\" ref ref2 \" id=\"pass39\">hi</div>\n\n  <div aria-grabbed=\"true\" id=\"pass40\">hi</div>\n  <div aria-grabbed=\"false\" id=\"pass41\">hi</div>\n  <div aria-grabbed=\"undefined\" id=\"pass42\">hi</div>\n\n  <div aria-haspopup=\"true\" id=\"pass43\">hi</div>\n  <div aria-haspopup=\"false\" id=\"pass44\">hi</div>\n  <div aria-haspopup=\"menu\" id=\"pass45\">hi</div>\n  <div aria-haspopup=\"listbox\" id=\"pass46\">hi</div>\n  <div aria-haspopup=\"tree\" id=\"pass47\">hi</div>\n  <div aria-haspopup=\"grid\" id=\"pass48\">hi</div>\n  <div aria-haspopup=\"dialog\" id=\"pass49\">hi</div>\n\n  <div aria-hidden=\"false\" id=\"pass50\">hi</div>\n\n  <div aria-invalid=\"true\" id=\"pass51\">hi</div>\n  <div aria-invalid=\"false\" id=\"pass52\">hi</div>\n  <div aria-invalid=\"spelling\" id=\"pass53\">hi</div>\n  <div aria-invalid=\"grammar\" id=\"pass54\">hi</div>\n\n  <div aria-label=\"stuff\" id=\"pass55\">hi</div>\n\n  <div aria-labelledby=\"ref\" id=\"pass56\">hi</div>\n  <div aria-labelledby=\"ref ref2 failedref\" id=\"pass57\">hi</div>\n  <div aria-labelledby=\" ref ref2 \" id=\"pass58\">hi</div>\n\n  <div aria-level=\"1\" id=\"pass59\">hi</div>\n  <div aria-level=\"+1\" id=\"pass61\">hi</div>\n\n  <div aria-live=\"off\" id=\"pass66\">hi</div>\n  <div aria-live=\"polite\" id=\"pass67\">hi</div>\n  <div aria-live=\"assertive\" id=\"pass68\">hi</div>\n\n  <div aria-modal=\"true\" id=\"pass69\">hi</div>\n  <div aria-modal=\"false\" id=\"pass70\">hi</div>\n\n  <div aria-multiline=\"true\" id=\"pass71\">hi</div>\n  <div aria-multiline=\"false\" id=\"pass72\">hi</div>\n\n  <div aria-multiselectable=\"true\" id=\"pass73\">hi</div>\n  <div aria-multiselectable=\"false\" id=\"pass74\">hi</div>\n\n  <div aria-orientation=\"horizontal\" id=\"pass75\">hi</div>\n  <div aria-orientation=\"vertical\" id=\"pass76\">hi</div>\n\n  <div aria-owns=\"ref\" id=\"pass77\">hi</div>\n  <div aria-owns=\"ref ref2 failedref\" id=\"pass78\">hi</div>\n  <div aria-owns=\" ref ref2 \" id=\"pass79\">hi</div>\n\n  <div aria-posinset=\"1\" id=\"pass80\">hi</div>\n  <div aria-posinset=\"22\" id=\"pass81\">hi</div>\n  <div aria-posinset=\"+1\" id=\"pass82\">hi</div>\n  <div aria-posinset=\"+22\" id=\"pass83\">hi</div>\n\n  <div aria-pressed=\"true\" id=\"pass87\">hi</div>\n  <div aria-pressed=\"false\" id=\"pass88\">hi</div>\n  <div aria-pressed=\"mixed\" id=\"pass89\">hi</div>\n  <div aria-pressed=\"undefined\" id=\"pass90\">hi</div>\n\n  <div aria-readonly=\"true\" id=\"pass91\">hi</div>\n  <div aria-readonly=\"false\" id=\"pass92\">hi</div>\n\n  <div aria-relevant=\"additions\" id=\"pass93\">hi</div>\n  <div aria-relevant=\"removals\" id=\"pass94\">hi</div>\n  <div aria-relevant=\"text\" id=\"pass95\">hi</div>\n  <div aria-relevant=\"all\" id=\"pass96\">hi</div>\n\n  <div aria-required=\"true\" id=\"pass97\">hi</div>\n  <div aria-required=\"false\" id=\"pass98\">hi</div>\n\n  <div aria-selected=\"true\" id=\"pass99\">hi</div>\n  <div aria-selected=\"false\" id=\"pass100\">hi</div>\n  <div aria-selected=\"undefined\" id=\"pass101\">hi</div>\n\n  <div aria-setsize=\"0\" id=\"pass102\">hi</div>\n  <div aria-setsize=\"1\" id=\"pass103\">hi</div>\n  <div aria-setsize=\"22\" id=\"pass104\">hi</div>\n  <div aria-setsize=\"-1\" id=\"pass105\">hi</div>\n  <div aria-setsize=\"+1\" id=\"pass106\">hi</div>\n  <div aria-setsize=\"+22\" id=\"pass107\">hi</div>\n\n  <div aria-sort=\"ascending\" id=\"pass109\">hi</div>\n  <div aria-sort=\"descending\" id=\"pass110\">hi</div>\n  <div aria-sort=\"other\" id=\"pass111\">hi</div>\n  <div aria-sort=\"none\" id=\"pass112\">hi</div>\n\n  <div aria-valuemax=\"0.1\" id=\"pass113\">hi</div>\n  <div aria-valuemax=\"1.1\" id=\"pass114\">hi</div>\n  <div aria-valuemax=\"12.22\" id=\"pass115\">hi</div>\n  <div aria-valuemax=\"1.\" id=\"pass116\">hi</div>\n  <div aria-valuemax=\".1\" id=\"pass117\">hi</div>\n  <div aria-valuemax=\"412\" id=\"pass118\">hi</div>\n  <div aria-valuemax=\"+0.1\" id=\"pass119\">hi</div>\n  <div aria-valuemax=\"+1.1\" id=\"pass120\">hi</div>\n  <div aria-valuemax=\"+12.22\" id=\"pass121\">hi</div>\n  <div aria-valuemax=\"+1.\" id=\"pass122\">hi</div>\n  <div aria-valuemax=\"+.1\" id=\"pass123\">hi</div>\n  <div aria-valuemax=\"+412\" id=\"pass124\">hi</div>\n  <div aria-valuemax=\"-0.1\" id=\"pass125\">hi</div>\n  <div aria-valuemax=\"-1.1\" id=\"pass126\">hi</div>\n  <div aria-valuemax=\"-12.22\" id=\"pass127\">hi</div>\n  <div aria-valuemax=\"-1.\" id=\"pass128\">hi</div>\n  <div aria-valuemax=\"-.1\" id=\"pass129\">hi</div>\n  <div aria-valuemax=\"-412\" id=\"pass130\">hi</div>\n\n  <div aria-valuemin=\"0.1\" id=\"pass131\">hi</div>\n  <div aria-valuemin=\"1.1\" id=\"pass132\">hi</div>\n  <div aria-valuemin=\"12.22\" id=\"pass133\">hi</div>\n  <div aria-valuemin=\"1.\" id=\"pass134\">hi</div>\n  <div aria-valuemin=\".1\" id=\"pass135\">hi</div>\n  <div aria-valuemin=\"412\" id=\"pass136\">hi</div>\n  <div aria-valuemin=\"+0.1\" id=\"pass137\">hi</div>\n  <div aria-valuemin=\"+1.1\" id=\"pass138\">hi</div>\n  <div aria-valuemin=\"+12.22\" id=\"pass139\">hi</div>\n  <div aria-valuemin=\"+1.\" id=\"pass140\">hi</div>\n  <div aria-valuemin=\"+.1\" id=\"pass141\">hi</div>\n  <div aria-valuemin=\"+412\" id=\"pass142\">hi</div>\n  <div aria-valuemin=\"-0.1\" id=\"pass143\">hi</div>\n  <div aria-valuemin=\"-1.1\" id=\"pass144\">hi</div>\n  <div aria-valuemin=\"-12.22\" id=\"pass145\">hi</div>\n  <div aria-valuemin=\"-1.\" id=\"pass146\">hi</div>\n  <div aria-valuemin=\"-.1\" id=\"pass147\">hi</div>\n  <div aria-valuemin=\"-412\" id=\"pass148\">hi</div>\n  <!-- VALUENOW -->\n  <div aria-valuenow=\"0.1\" id=\"pass149\">hi</div>\n  <div aria-valuenow=\"1.1\" id=\"pass150\">hi</div>\n  <div aria-valuenow=\"12.22\" id=\"pass151\">hi</div>\n  <div aria-valuenow=\"1.\" id=\"pass152\">hi</div>\n  <div aria-valuenow=\".1\" id=\"pass153\">hi</div>\n  <div aria-valuenow=\"412\" id=\"pass154\">hi</div>\n  <div aria-valuenow=\"+0.1\" id=\"pass155\">hi</div>\n  <div aria-valuenow=\"+1.1\" id=\"pass156\">hi</div>\n  <div aria-valuenow=\"+12.22\" id=\"pass157\">hi</div>\n  <div aria-valuenow=\"+1.\" id=\"pass158\">hi</div>\n  <div aria-valuenow=\"+.1\" id=\"pass159\">hi</div>\n  <div aria-valuenow=\"+412\" id=\"pass160\">hi</div>\n  <div aria-valuenow=\"-0.1\" id=\"pass161\">hi</div>\n  <div aria-valuenow=\"-1.1\" id=\"pass162\">hi</div>\n  <div aria-valuenow=\"-12.22\" id=\"pass163\">hi</div>\n  <div aria-valuenow=\"-1.\" id=\"pass164\">hi</div>\n  <div aria-valuenow=\"-.1\" id=\"pass165\">hi</div>\n  <div aria-valuenow=\"-412\" id=\"pass166\">hi</div>\n  <div aria-valuetext=\"stuff\" id=\"pass167\">hi</div>\n  <div aria-errormessage=\"pass168-ref\" id=\"pass168\" aria-invalid=\"true\">hi</div>\n  <div id=\"pass168-ref\" role=\"alert\"></div>\n  <div aria-errormessage=\"pass68\" id=\"pass169\" aria-invalid=\"true\">hi</div>\n  <div\n    aria-errormessage=\"ref\"\n    aria-describedby=\"ref\"\n    aria-invalid=\"true\"\n    id=\"pass170\"\n  >\n    hi\n  </div>\n\n  <!-- Allowed empty -->\n  <div aria-activedescendant=\"\" id=\"pass171\">hi</div>\n  <div aria-current=\"\" id=\"pass172\">hi</div>\n  <div aria-controls=\"\" id=\"pass174\">hi</div>\n  <div aria-describedby=\"\" id=\"pass175\">hi</div>\n  <div aria-errormessage=\"\" id=\"pass176\" aria-invalid=\"true\">hi</div>\n  <div aria-flowto=\"\" id=\"pass177\">hi</div>\n  <div aria-haspopup=\"\" id=\"pass178\">hi</div>\n  <div aria-keyshortcuts=\"\" id=\"pass180\">hi</div>\n  <div aria-label=\"\" id=\"pass181\">hi</div>\n  <div aria-labelledby=\"\" id=\"pass182\">hi</div>\n  <div aria-owns=\"\" id=\"pass183\">hi</div>\n  <div aria-placeholder=\"\" id=\"pass184\">hi</div>\n  <div aria-errormessage=\"invalidId\" id=\"pass185\">hi</div>\n  <div aria-errormessage=\"invalidId\" aria-invalid=\"false\" id=\"pass186\">hi</div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"pass187\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"pass68\"\n    />\n  </div>\n\n  <div aria-live=\"assertive\" aria-hidden=\"false\" id=\"pass188\">hi</div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"pass189\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"pass188\"\n    />\n  </div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"pass190\"\n      aria-invalid=\"false\"\n      aria-errormessage=\"violation44-ref\"\n    />\n  </div>\n  <div\n    id=\"pass191\"\n    aria-braillelabel=\"\"\n    aria-brailleroledescription=\"\"\n    aria-valuetext=\"\"\n  ></div>\n\n  <div>\n    <input\n      type=\"text\"\n      id=\"pass192\"\n      aria-invalid=\"true\"\n      aria-errormessage=\"pass192-ref1\"\n      aria-describedby=\"pass192-ref1 pass192-ref2\"\n    />\n    <div id=\"pass192-ref1\">Error message 1</div>\n    <div id=\"pass192-ref2\">Error message 2</div>\n  </div>\n\n  <div id=\"ref\">Hi</div>\n  <div id=\"ref2\">Hi2</div>\n\n  <div aria-describedby=\"stuff\" id=\"incomplete1\">hi</div>\n  <div aria-current=\"stage\" id=\"incomplete2\">hi</div>\n  <div aria-labelledby=\"stuff\" id=\"incomplete3\">hi</div>\n  <div aria-level=\"22\" id=\"incomplete4\">hi</div>\n  <div aria-level=\"+22\" id=\"incomplete5\">hi</div>\n  <div role=\"checkbox\" id=\"incomplete6\" aria-checked>I'm not checked</div>\n  <div role=\"textbox\" id=\"incomplete7\" aria-invalid=\"\">I'm actually valid</div>\n  <div role=\"textbox\" id=\"incomplete8\" aria-hidden>I'm not really gone</div>\n  <div id=\"incomplete9\" aria-controls=\"stuff\" aria-haspopup=\"true\">\n    May have injected html to control dynamically later, who knows!\n  </div>\n</div>\n"
  },
  {
    "path": "test/integration/rules/aria-valid-attr-value/aria-valid-attr-value.json",
    "content": "{\n  \"description\": \"aria-valid-attr-value tests\",\n  \"rule\": \"aria-valid-attr-value\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation10\"],\n    [\"#violation11\"],\n    [\"#violation12\"],\n    [\"#violation13\"],\n    [\"#violation14\"],\n    [\"#violation15\"],\n    [\"#violation16\"],\n    [\"#violation17\"],\n    [\"#violation19\"],\n    [\"#violation20\"],\n    [\"#violation21\"],\n    [\"#violation22\"],\n    [\"#violation23\"],\n    [\"#violation24\"],\n    [\"#violation25\"],\n    [\"#violation26\"],\n    [\"#violation27\"],\n    [\"#violation28\"],\n    [\"#violation29\"],\n    [\"#violation30\"],\n    [\"#violation31\"],\n    [\"#violation32\"],\n    [\"#violation33\"],\n    [\"#violation34\"],\n    [\"#violation35\"],\n    [\"#violation36\"],\n    [\"#violation37\"],\n    [\"#violation38\"],\n    [\"#violation39\"],\n    [\"#violation40\"],\n    [\"#violation41\"],\n    [\"#violation42\"],\n    [\"#violation43\"],\n    [\"#violation44\"],\n    [\"#violation45\"],\n    [\"#violation46\"],\n    [\"#violation47\"],\n    [\"#violation48\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass25\"],\n    [\"#pass26\"],\n    [\"#pass27\"],\n    [\"#pass28\"],\n    [\"#pass29\"],\n    [\"#pass30\"],\n    [\"#pass31\"],\n    [\"#pass32\"],\n    [\"#pass33\"],\n    [\"#pass34\"],\n    [\"#pass35\"],\n    [\"#pass36\"],\n    [\"#pass37\"],\n    [\"#pass38\"],\n    [\"#pass39\"],\n    [\"#pass40\"],\n    [\"#pass41\"],\n    [\"#pass42\"],\n    [\"#pass43\"],\n    [\"#pass44\"],\n    [\"#pass45\"],\n    [\"#pass46\"],\n    [\"#pass47\"],\n    [\"#pass48\"],\n    [\"#pass49\"],\n    [\"#pass50\"],\n    [\"#pass51\"],\n    [\"#pass52\"],\n    [\"#pass53\"],\n    [\"#pass54\"],\n    [\"#pass55\"],\n    [\"#pass56\"],\n    [\"#pass57\"],\n    [\"#pass58\"],\n    [\"#pass59\"],\n    [\"#pass61\"],\n    [\"#pass66\"],\n    [\"#pass67\"],\n    [\"#pass68\"],\n    [\"#pass69\"],\n    [\"#pass70\"],\n    [\"#pass71\"],\n    [\"#pass72\"],\n    [\"#pass73\"],\n    [\"#pass74\"],\n    [\"#pass75\"],\n    [\"#pass76\"],\n    [\"#pass77\"],\n    [\"#pass78\"],\n    [\"#pass79\"],\n    [\"#pass80\"],\n    [\"#pass81\"],\n    [\"#pass82\"],\n    [\"#pass83\"],\n    [\"#pass87\"],\n    [\"#pass88\"],\n    [\"#pass89\"],\n    [\"#pass90\"],\n    [\"#pass91\"],\n    [\"#pass92\"],\n    [\"#pass93\"],\n    [\"#pass94\"],\n    [\"#pass95\"],\n    [\"#pass96\"],\n    [\"#pass97\"],\n    [\"#pass98\"],\n    [\"#pass99\"],\n    [\"#pass100\"],\n    [\"#pass101\"],\n    [\"#pass102\"],\n    [\"#pass103\"],\n    [\"#pass104\"],\n    [\"#pass105\"],\n    [\"#pass106\"],\n    [\"#pass107\"],\n    [\"#pass109\"],\n    [\"#pass110\"],\n    [\"#pass111\"],\n    [\"#pass112\"],\n    [\"#pass113\"],\n    [\"#pass114\"],\n    [\"#pass115\"],\n    [\"#pass116\"],\n    [\"#pass117\"],\n    [\"#pass118\"],\n    [\"#pass119\"],\n    [\"#pass120\"],\n    [\"#pass121\"],\n    [\"#pass122\"],\n    [\"#pass123\"],\n    [\"#pass124\"],\n    [\"#pass125\"],\n    [\"#pass126\"],\n    [\"#pass127\"],\n    [\"#pass128\"],\n    [\"#pass129\"],\n    [\"#pass130\"],\n    [\"#pass131\"],\n    [\"#pass132\"],\n    [\"#pass133\"],\n    [\"#pass134\"],\n    [\"#pass135\"],\n    [\"#pass136\"],\n    [\"#pass137\"],\n    [\"#pass138\"],\n    [\"#pass139\"],\n    [\"#pass140\"],\n    [\"#pass141\"],\n    [\"#pass142\"],\n    [\"#pass143\"],\n    [\"#pass144\"],\n    [\"#pass145\"],\n    [\"#pass146\"],\n    [\"#pass147\"],\n    [\"#pass148\"],\n    [\"#pass149\"],\n    [\"#pass150\"],\n    [\"#pass151\"],\n    [\"#pass152\"],\n    [\"#pass153\"],\n    [\"#pass154\"],\n    [\"#pass155\"],\n    [\"#pass156\"],\n    [\"#pass157\"],\n    [\"#pass158\"],\n    [\"#pass159\"],\n    [\"#pass160\"],\n    [\"#pass161\"],\n    [\"#pass162\"],\n    [\"#pass163\"],\n    [\"#pass164\"],\n    [\"#pass165\"],\n    [\"#pass166\"],\n    [\"#pass167\"],\n    [\"#pass168\"],\n    [\"#pass169\"],\n    [\"#pass170\"],\n    [\"#pass171\"],\n    [\"#pass172\"],\n    [\"#pass174\"],\n    [\"#pass175\"],\n    [\"#pass176\"],\n    [\"#pass177\"],\n    [\"#pass178\"],\n    [\"#pass180\"],\n    [\"#pass181\"],\n    [\"#pass182\"],\n    [\"#pass183\"],\n    [\"#pass184\"],\n    [\"#pass185\"],\n    [\"#pass186\"],\n    [\"#pass187\"],\n    [\"#pass188\"],\n    [\"#pass189\"],\n    [\"#pass190\"],\n    [\"#pass191\"],\n    [\"#pass192\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"],\n    [\"#incomplete9\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/audio-caption/audio-caption.html",
    "content": "<audio id=\"empty\"></audio>\n<audio id=\"caption\"><track kind=\"captions\" /></audio>\n"
  },
  {
    "path": "test/integration/rules/audio-caption/audio-caption.json",
    "content": "{\n  \"description\": \"audio-caption test\",\n  \"rule\": \"audio-caption\",\n  \"incomplete\": [[\"#empty\"]],\n  \"passes\": [[\"#caption\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/autocomplete-valid/autocomplete-valid.html",
    "content": "<input autocomplete=\"username\" id=\"pass1\" />\n\n<select autocomplete=\"bday-month\" id=\"pass2\">\n  <option>January</option>\n  <option>...</option>\n</select>\n\n<textarea autocomplete=\"Street-Address\" id=\"pass3\"></textarea>\n\n<input autocomplete=\"Work EMail\" id=\"pass4\" />\n\n<input autocomplete=\"section-partner email\" id=\"pass5\" />\n\n<input autocomplete=\"section-primary shipping work email\" id=\"pass6\" />\n\n<!-- Unknown autocomplete term -->\n<input autocomplete=\"badterm\" id=\"fail1\" />\n\n<!-- \"work\" not allowed before \"photo\" -->\n<input autocomplete=\"work photo\" id=\"fail2\" />\n\n<!-- invalid order of terms -->\n<input autocomplete=\"work shipping email\" id=\"fail3\" />\n\n<!-- comma seperated rather than space separated list -->\n<input autocomplete=\"work,email\" id=\"fail4\" />\n\n<!-- webauthn goes at the end -->\n<input autocomplete=\"webauthn email\" id=\"fail5\" />\n\n<!-- Incorrect element -->\n<button autocomplete=\"username\" id=\"inapplicable1\"></button>\n\n<!-- Empty attribute -->\n<input autocomplete=\"\" id=\"inapplicable2\" />\n\n<!-- Hidden through display:none -->\n<input autocomplete=\"username\" style=\"display: none\" id=\"inapplicable3\" />\n\n<!-- Off screen and not focusable -->\n<input\n  autocomplete=\"username\"\n  tabindex=\"-1\"\n  aria-hidden=\"true\"\n  style=\"position: absolute; top: -9999em\"\n  id=\"inapplicable4\"\n/>\n\n<!-- input button -->\n<input type=\"button\" autocomplete=\"username\" id=\"inapplicable5\" />\n\n<!-- hidden -->\n<input type=\"hidden\" autocomplete=\"username\" id=\"inapplicable6\" />\n\n<!-- native disabled -->\n<input autocomplete=\"username\" disabled id=\"inapplicable7\" />\n\n<!-- ARIA disabled -->\n<input autocomplete=\"username\" aria-disabled=\"true\" id=\"inapplicable8\" />\n\n<!-- non-widget element -->\n<input\n  type=\"button\"\n  role=\"none\"\n  tabindex=\"-1\"\n  autocomplete=\"username\"\n  id=\"inapplicable9\"\n/>\n<input autocomplete=\"  \" id=\"inapplicable10\" />\n\n<input autocomplete=\"on\" id=\"pass7\" />\n<input autocomplete=\" on \" id=\"pass8\" />\n<input autocomplete=\" OFF \" id=\"pass9\" />\n<input autocomplete=\"   name   \" id=\"pass10\" />\n<input autocomplete=\"honorific-prefix\" id=\"pass11\" />\n<input autocomplete=\"GIVEN-NAME\" id=\"pass12\" />\n<input autocomplete=\"additional-name\" id=\"pass13\" />\n<input autocomplete=\"section-foo billing family-name\" id=\"pass14\" />\n<input autocomplete=\"honorific-suffix\" id=\"pass15\" />\n<input autocomplete=\"nickname\" id=\"pass16\" />\n<input autocomplete=\"USERNAME\" id=\"pass17\" />\n<input autocomplete=\"section-foo new-password\" id=\"pass18\" />\n<input autocomplete=\"shipping current-password\" id=\"pass19\" />\n<input autocomplete=\"organization-title\" id=\"pass20\" />\n<input autocomplete=\"organization\" id=\"pass21\" />\n<textarea autocomplete=\"section-foo street-address\" id=\"pass22\"></textarea>\n<input autocomplete=\"address-line1\" id=\"pass23\" />\n<input autocomplete=\"billing address-line2\" id=\"pass24\" />\n<input autocomplete=\"address-line3\" id=\"pass25\" />\n<input autocomplete=\"section-foo address-level4\" id=\"pass26\" />\n<input autocomplete=\"address-level3\" id=\"pass27\" />\n<input autocomplete=\"address-level2\" id=\"pass28\" />\n<input autocomplete=\"SHIPPING address-level1\" id=\"pass29\" />\n<input autocomplete=\"section-foo country\" id=\"pass30\" />\n<input autocomplete=\"country-name\" id=\"pass31\" />\n<input autocomplete=\"postal-code\" id=\"pass32\" />\n<input autocomplete=\"CC-name\" id=\"pass33\" />\n<input autocomplete=\"section-foo billing cc-given-name\" id=\"pass34\" />\n<input autocomplete=\"cc-additional-name\" id=\"pass35\" />\n<input autocomplete=\"cc-family-name\" id=\"pass36\" />\n<input autocomplete=\"cc-number\" id=\"pass37\" />\n<input autocomplete=\"section-foo cc-exp\" id=\"pass38\" />\n<input autocomplete=\"shipping CC-Exp-Month\" id=\"pass39\" />\n<input autocomplete=\"cc-exp-year\" id=\"pass40\" />\n<input autocomplete=\"cc-csc\" id=\"pass41\" />\n<input autocomplete=\"section-foo cc-type\" id=\"pass42\" />\n<input autocomplete=\"transaction-currency\" id=\"pass43\" />\n<input autocomplete=\"billing transaction-amount\" id=\"pass44\" />\n<input autocomplete=\"language\" id=\"pass45\" />\n<input autocomplete=\"section-foo bday\" id=\"pass46\" />\n<input autocomplete=\"bday-day\" id=\"pass47\" />\n<input autocomplete=\"bday-month\" id=\"pass48\" />\n<input autocomplete=\"section-foo shipping bday-year\" id=\"pass49\" />\n<input autocomplete=\"sex\" id=\"pass50\" />\n<input autocomplete=\"url\" id=\"pass51\" />\n<input autocomplete=\"photo\" id=\"pass52\" />\n<input autocomplete=\"section-foo photo\" id=\"pass53\" />\n<input autocomplete=\"HOME TEL\" id=\"pass54\" />\n<input autocomplete=\"section-foo shipping work tel-country-code\" id=\"pass55\" />\n<input autocomplete=\"mobile tel-national\" id=\"pass56\" />\n<input autocomplete=\"fax tel-area-code\" id=\"pass57\" />\n<input autocomplete=\"pager tel-local\" id=\"pass58\" />\n<input autocomplete=\"home tel-local-prefix\" id=\"pass59\" />\n<input autocomplete=\"work tel-local-suffix\" id=\"pass60\" />\n<input autocomplete=\"SECTION-foo mobile tel-extension\" id=\"pass61\" />\n<input autocomplete=\"fax email\" id=\"pass62\" />\n<input type=\"username\" autocomplete=\"email\" id=\"pass83\" />\n<input autocomplete=\"pager impp\" id=\"pass63\" />\n<input\n  autocomplete=\"off\"\n  id=\"pass64\"\n  name=\"input_1\"\n  placeholder=\"Numeric Input Field\"\n  value=\"42\"\n  type=\"tel\"\n/>\n<input autocomplete=\"on\" id=\"pass65\" value=\"\" type=\"url\" />\n<input autocomplete=\"off\" id=\"pass66\" value=\"42\" type=\"datetime-local\" />\n<input autocomplete=\"street-address\" id=\"pass67\" type=\"text\" />\n<input autocomplete=\"on\" id=\"pass68\" value=\"\" type=\"url     \" />\n<input autocomplete=\"off\" id=\"pass69\" value=\"42\" type=\"   DateTime-Local\" />\n\n<input autocomplete=\"tel-country-code\" id=\"pass70\" type=\"tel\" />\n<input autocomplete=\"tel-national\" id=\"pass71\" type=\"tel\" />\n<input autocomplete=\"tel-area-code\" id=\"pass72\" type=\"tel\" />\n<input autocomplete=\"tel-local\" id=\"pass73\" type=\"tel\" />\n<input autocomplete=\"tel-local-prefix\" id=\"pass74\" type=\"tel\" />\n<input autocomplete=\"tel-local-suffix\" id=\"pass75\" type=\"tel\" />\n<input autocomplete=\"tel-extension\" id=\"pass76\" type=\"tel\" />\n\n<input autocomplete=\"one-time-code\" id=\"pass77\" />\n\n<input autocomplete=\"cc-number\" type=\"tel\" id=\"pass78\" />\n<input autocomplete=\"cc-exp\" type=\"tel\" id=\"pass79\" />\n<input autocomplete=\"cc-exp-month\" type=\"tel\" id=\"pass80\" />\n<input autocomplete=\"cc-exp-year\" type=\"tel\" id=\"pass81\" />\n<input autocomplete=\"cc-csc\" type=\"tel\" id=\"pass82\" />\n\n<!-- Attribute values that contains synonyms of on/off -->\n<input autocomplete=\"none\" id=\"pass84\" />\n<input autocomplete=\"false\" id=\"pass85\" />\n<input autocomplete=\"true\" id=\"pass86\" />\n<input autocomplete=\"disabled\" id=\"pass87\" />\n<input autocomplete=\"enabled\" id=\"pass88\" />\n<input autocomplete=\"undefined\" id=\"pass89\" />\n<input autocomplete=\"null\" id=\"pass90\" />\n<input autocomplete=\"xon\" id=\"pass91\" />\n<input autocomplete=\"xoff\" id=\"pass92\" />\n\n<!-- Webauthn tokens -->\n<input autocomplete=\"email webauthn\" id=\"pass93\" />\n<input autocomplete=\"section-foo email webauthn\" id=\"pass94\" />\n<input autocomplete=\"section-foo work email webauthn\" id=\"pass95\" />\n<input autocomplete=\"section-foo billing work email webauthn\" id=\"pass96\" />\n\n<!-- Incomplete, ignored values -->\n<input autocomplete=\"text\" id=\"incomplete1\" />\n<input autocomplete=\"pronouns\" id=\"incomplete2\" />\n<input autocomplete=\"gender\" id=\"incomplete3\" />\n<input autocomplete=\"message\" id=\"incomplete4\" />\n<input autocomplete=\"content\" id=\"incomplete5\" />\n\n<!-- When readonly attributes placed, should be ignored -->\n<input autocomplete=\"badterm\" readonly id=\"inapplicable11\" />\n<input autocomplete=\"badterm\" aria-readonly=\"true\" id=\"inapplicable12\" />\n"
  },
  {
    "path": "test/integration/rules/autocomplete-valid/autocomplete-valid.json",
    "content": "{\n  \"description\": \"autocomplete-valid tests\",\n  \"rule\": \"autocomplete-valid\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass25\"],\n    [\"#pass26\"],\n    [\"#pass27\"],\n    [\"#pass28\"],\n    [\"#pass29\"],\n    [\"#pass30\"],\n    [\"#pass31\"],\n    [\"#pass32\"],\n    [\"#pass33\"],\n    [\"#pass34\"],\n    [\"#pass35\"],\n    [\"#pass36\"],\n    [\"#pass37\"],\n    [\"#pass38\"],\n    [\"#pass39\"],\n    [\"#pass40\"],\n    [\"#pass41\"],\n    [\"#pass42\"],\n    [\"#pass43\"],\n    [\"#pass44\"],\n    [\"#pass45\"],\n    [\"#pass46\"],\n    [\"#pass47\"],\n    [\"#pass48\"],\n    [\"#pass49\"],\n    [\"#pass50\"],\n    [\"#pass51\"],\n    [\"#pass52\"],\n    [\"#pass53\"],\n    [\"#pass54\"],\n    [\"#pass55\"],\n    [\"#pass56\"],\n    [\"#pass57\"],\n    [\"#pass58\"],\n    [\"#pass59\"],\n    [\"#pass60\"],\n    [\"#pass61\"],\n    [\"#pass62\"],\n    [\"#pass63\"],\n    [\"#pass64\"],\n    [\"#pass65\"],\n    [\"#pass66\"],\n    [\"#pass67\"],\n    [\"#pass68\"],\n    [\"#pass69\"],\n    [\"#pass70\"],\n    [\"#pass71\"],\n    [\"#pass72\"],\n    [\"#pass73\"],\n    [\"#pass74\"],\n    [\"#pass75\"],\n    [\"#pass76\"],\n    [\"#pass77\"],\n    [\"#pass78\"],\n    [\"#pass79\"],\n    [\"#pass80\"],\n    [\"#pass81\"],\n    [\"#pass82\"],\n    [\"#pass83\"],\n    [\"#pass84\"],\n    [\"#pass85\"],\n    [\"#pass86\"],\n    [\"#pass87\"],\n    [\"#pass88\"],\n    [\"#pass89\"],\n    [\"#pass90\"],\n    [\"#pass91\"],\n    [\"#pass92\"],\n    [\"#pass93\"],\n    [\"#pass94\"],\n    [\"#pass95\"],\n    [\"#pass96\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/avoid-inline-spacing/avoid-inline-spacing.html",
    "content": "<!-- Pass -->\n<p id=\"pass1\" style=\"font-size: 200%\">\n  I stepped on a Corn Flake, now I'm a Cereal Killer\n</p>\n<p id=\"pass2\" style=\"line-height: 1.5\">\n  On a scale from one to ten what is your favourite colour of the alphabet.\n</p>\n<p id=\"pass3\" style=\"letter-spacing: 50px\">\n  The quick brown fox jumped over the lazy dog\n</p>\n<p id=\"pass4\" style=\"word-spacing: 10px\">\n  A group of 24 Caterpillars have 694 legs\n</p>\n<p id=\"pass5\" style=\"word-spacing: 20ch; letter-spacing: 50rem; line-height: 3\">\n  Look, a distraction!\n</p>\n<p id=\"pass6\" style=\"line-height: 1.5em !important\">Banana<br />pass</p>\n<!-- one-liner   -->\n<p id=\"pass7\" style=\"line-height: 1.4em !important\">Banana</p>\n<p id=\"pass8\" style=\"letter-spacing: 0.12em !important\">\n  We need more cheeeeeeessseeeee!!!\n</p>\n<p id=\"pass9\" style=\"word-spacing: 0.16em !important\">\n  The cheese grater is in the way!\n</p>\n\n<!-- CSS keywords -->\n<p id=\"pass10\" style=\"letter-spacing: unset !important\">Banana</p>\n<p id=\"pass11\" style=\"word-spacing: inherit !important\">Banana</p>\n<p id=\"pass12\" style=\"line-height: revert !important\">Banana<br />pass</p>\n<p id=\"pass13\" style=\"line-height: revert-layer !important\">Banana<br />pass</p>\n\n<!-- Fail -->\n<p id=\"fail1\" style=\"line-height: 1.4em !important\">Banana<br />error</p>\n<p id=\"fail2\" style=\"letter-spacing: 0.1em !important\">\n  We need more cheeeeeeessseeeee!!!\n</p>\n<p id=\"fail3\" style=\"word-spacing: 0.15em !important\">\n  The cheese grater is in the way!\n</p>\n<p\n  id=\"fail4\"\n  style=\"word-spacing: 200%; letter-spacing: 1px !important; line-height: 3\"\n>\n  Yo Darth Vader\n</p>\n\n<!-- Inapplicable -->\n<p id=\"inapplicable1\">I am so blue I'm greener than purple.</p>\n<p id=\"inapplicable2\" style=\"word-spacing: 0.15em; opacity: 0\">\n  Some hidden text\n</p>\n"
  },
  {
    "path": "test/integration/rules/avoid-inline-spacing/avoid-inline-spacing.json",
    "content": "{\n  \"description\": \"avoid-inline-spacing tests\",\n  \"rule\": \"avoid-inline-spacing\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/blink/blink.html",
    "content": "<p style=\"display: none\"><blink id=\"pass1\">text</blink></p>\n<p><blink id=\"fail1\">text</blink></p>\n<p aria-hidden=\"true\"><blink id=\"fail2\">text</blink></p>\n"
  },
  {
    "path": "test/integration/rules/blink/blink.json",
    "content": "{\n  \"description\": \"blink test\",\n  \"rule\": \"blink\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"]],\n  \"passes\": [[\"#pass1\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/button-name/button-name.html",
    "content": "<button id=\"empty\"></button>\n<button id=\"text\">Name</button>\n<button id=\"val\" value=\"Button Name\"></button>\n<button id=\"al\" aria-label=\"Name\"></button>\n<button id=\"alempty\" aria-label=\"\"></button>\n<button id=\"alb\" aria-labelledby=\"labeldiv\"></button>\n<button id=\"albmissing\" aria-labelledby=\"nonexistent\"></button>\n<button id=\"albempty\" aria-labelledby=\"emptydiv\"></button>\n<div id=\"labeldiv\">Button label</div>\n<div id=\"emptydiv\"></div>\n<button id=\"combo\" aria-label=\"Aria Name\">Name</button>\n<button id=\"buttonTitle\" title=\"Title\"></button>\n<button id=\"buttonvalue\" value=\"foo\" tabindex=\"-1\"></button>\n\n<button id=\"fail1\" role=\"presentation\"></button>\n<button id=\"fail2\" role=\"none\"></button>\n<button id=\"fail3\" role=\"img\" disabled></button>\n<button id=\"fail4\" role=\"gridcell\"></button>\n\n<button id=\"pass1\" role=\"presentation\" disabled></button>\n<button id=\"pass2\" role=\"none\" disabled></button>\n\n<label>Name<button id=\"pass3\"></button></label>\n<label for=\"pass4\">Name</label>\n<button id=\"pass4\"></button>\n\n<span id=\"inapplicable1\" role=\"button\">Does not apply</span>\n<input type=\"submit\" value=\"submit\" role=\"button\" id=\"inapplicable2\" />\n<button id=\"inapplicable2\" role=\"gridcell\" disabled></button>\n"
  },
  {
    "path": "test/integration/rules/button-name/button-name.json",
    "content": "{\n  \"description\": \"button-name test\",\n  \"rule\": \"button-name\",\n  \"violations\": [\n    [\"#empty\"],\n    [\"#val\"],\n    [\"#alempty\"],\n    [\"#albmissing\"],\n    [\"#albempty\"],\n    [\"#buttonvalue\"],\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"]\n  ],\n  \"passes\": [\n    [\"#text\"],\n    [\"#al\"],\n    [\"#alb\"],\n    [\"#combo\"],\n    [\"#buttonTitle\"],\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/color-contrast/color-contrast.html",
    "content": "<div id=\"pass1\" style=\"background-color: white; color: black\">\n  This is a pass.\n</div>\n<div id=\"pass2\" style=\"background-color: gray; color: white; font-size: 40px\">\n  This is a pass.\n</div>\n<div\n  id=\"pass3\"\n  style=\"\n    background-color: gray;\n    color: white;\n    font-size: 20px;\n    font-weight: bold;\n  \"\n>\n  This is a pass.\n</div>\n\n<div style=\"background-color: black\">\n  <div\n    style=\"color: transparent; text-shadow: 0 0 0 white\"\n    id=\"text-shadow-fg-pass\"\n  >\n    Pass.\n  </div>\n</div>\n\n<div id=\"fail1\" style=\"background-color: gray; color: white; font-size: 20px\">\n  This is a fail.<b id=\"pass4\">But this is a pass.</b>\n</div>\n\n<div style=\"background-color: black\">\n  <div\n    style=\"background-color: rgba(255, 255, 255, 0.1); color: white\"\n    id=\"pass5\"\n  >\n    Pass.\n  </div>\n</div>\n\n<div style=\"position: relative; height: 40px\">\n  <label style=\"background-color: black; color: white\" id=\"pass7\">\n    Label\n    <input style=\"position: absolute; top: 20px\" />\n  </label>\n</div>\n\n<div\n  id=\"pass8\"\n  style=\"color: #000; background: #737373; text-shadow: white 0 0 3px\"\n>\n  Hello world\n</div>\n\n<div id=\"fail2\" style=\"background-color: gray; color: white; font-size: 8px\">\n  This is a fail.\n</div>\n<div id=\"fail3\" style=\"background-color: yellow; color: white; font-size: 40px\">\n  This is a fail.\n</div>\n\n<div style=\"background-color: white\">\n  <div id=\"fail6\" style=\"opacity: 0.1; background-color: white; color: black\">\n    This is a fail.\n  </div>\n</div>\n\n<div\n  id=\"fail7\"\n  style=\"color: #000; background: #777; text-shadow: black 0 0 0.2em\"\n>\n  Hello world\n</div>\n\n<!-- shouldnt run -->\n<input\n  id=\"ignore0\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"hidden\"\n/>\n<input\n  id=\"ignore1\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"radio\"\n/>\n<input\n  id=\"ignore2\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"checkbox\"\n/>\n<input\n  id=\"ignore4\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"hidden\"\n/>\n<select id=\"ignore5\" style=\"background: #000; color: #000\"></select>\n\n<div\n  id=\"ignore6\"\n  style=\"\n    color: yellow;\n    width: 100px;\n    overflow: hidden;\n    text-indent: 200px;\n    background-color: white;\n  \"\n>\n  text\n</div>\n\n<label for=\"disabled1\">Hello</label>\n<input id=\"disabled1\" disabled />\n<div id=\"disabled2\">Hello</div>\n<input aria-labelledby=\"disabled2\" disabled />\n<div\n  id=\"disabled3\"\n  style=\"background: #000; color: #000\"\n  role=\"alert\"\n  aria-disabled=\"true\"\n>\n  hello\n</div>\n<label style=\"color: #fff\" id=\"disabled4\">\n  Test\n  <input type=\"text\" disabled />\n</label>\n\n<div\n  style=\"\n    background-image: url(data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7);\n  \"\n>\n  <div style=\"color: white\" id=\"canttell1\">Background image</div>\n</div>\n\n<div style=\"height: 40px; position: relative\">\n  <div\n    style=\"\n      background-color: white;\n      height: 60px;\n      width: 40px;\n      border: 1px solid;\n    \"\n  >\n    <div\n      id=\"canttell2\"\n      style=\"color: white; height: 40px; width: 60px; border: 1px solid red\"\n    >\n      Outside parent\n    </div>\n  </div>\n</div>\n\n<div style=\"background-color: white; height: 60px; width: 40px\">\n  <div id=\"canttell3\" style=\"color: white; height: 40px; width: 60px\">\n    Background overlap\n  </div>\n</div>\n\n<div\n  style=\"\n    background-color: white;\n    height: 60px;\n    width: 80px;\n    border: 1px solid;\n    position: relative;\n  \"\n>\n  <div\n    id=\"canttell4\"\n    style=\"color: white; height: 40px; width: 60px; border: 1px solid red\"\n  >\n    Element overlap\n  </div>\n  <div\n    style=\"\n      position: absolute;\n      top: 0;\n      width: 60px;\n      height: 40px;\n      background-color: #000;\n    \"\n  ></div>\n</div>\n<div style=\"background-color: white\">\n  <div\n    style=\"background-color: rgba(255, 255, 255, 0.1); color: white\"\n    id=\"canttell5\"\n  >\n    1:1 ratio with transparency\n  </div>\n</div>\n\n<input\n  id=\"canttell6\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"text\"\n/>\n<input\n  id=\"canttell7\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"button\"\n/>\n<input\n  id=\"canttell8\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"submit\"\n/>\n<input\n  id=\"canttell9\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n  type=\"reset\"\n/>\n<select id=\"canttell10\" style=\"background: #000; color: #000\">\n  <option>Hello</option>\n</select>\n<textarea id=\"canttell11\" style=\"background: #000; color: #000\">hello</textarea>\n<input\n  id=\"canttell12\"\n  type=\"button\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n/>\n<input\n  id=\"canttell13\"\n  type=\"reset\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n/>\n<input\n  id=\"canttell14\"\n  type=\"submit\"\n  style=\"background: #000; color: #000\"\n  value=\"hello\"\n/>\n<input id=\"canttell15\" style=\"background: #000; color: #000\" value=\"hello\" />\n<div\n  id=\"canttell16\"\n  style=\"background: #000; color: #000\"\n  role=\"alert\"\n  aria-disabled=\"false\"\n>\n  hello\n</div>\n\n<div id=\"canttell17\" style=\"background: #000; color: #000\" aria-hidden=\"true\">\n  text\n</div>\n\n<div style=\"background-color: white; height: 60px; width: 80px\">\n  <div id=\"canttell18\" style=\"color: white; height: 40px; width: 60px\">Hi</div>\n</div>\n\n<div\n  style=\"background-color: white; height: 60px; width: 80px; overflow: scroll\"\n>\n  <div\n    id=\"canttell19\"\n    style=\"margin-left: 80px; color: white; height: 80px; width: 90px\"\n  >\n    Hi\n  </div>\n</div>\n\n<div style=\"background-color: #fff\">\n  <div id=\"canttell20\" style=\"color: #ddd\">\n    &#x20A0; &#x20A1; &#x20A2; &#x20A3;\n  </div>\n</div>\n\n<div style=\"width: 0; height: 50px; overflow: scroll\">\n  <div id=\"ignore7\" style=\"width: 200px; height: 200px; background-color: blue\">\n    Hello World\n  </div>\n</div>\n\n<div\n  style=\"\n    color: oklch(0.961073 0.000047911 none / 0.2);\n    background-color: black;\n    font-size: 14pt;\n    font-weight: 900;\n  \"\n>\n  <span id=\"canttell21\" style=\"font-weight: lighter\"\n    >This is a foreground colorParse fail!</span\n  >\n</div>\n\n<div\n  style=\"\n    color: gray;\n    background-color: oklch(0.961073 0.000047911 none / 0.2);\n    font-size: 14pt;\n    font-weight: 900;\n  \"\n>\n  <span id=\"canttell22\" style=\"font-weight: lighter\"\n    >This is a background colorParse fail!</span\n  >\n</div>\n\n<div\n  style=\"\n    background-color: #fff;\n    color: #000;\n    text-shadow: 1px 1px oklch(0.961073 0.000047911 none / 0.2);\n  \"\n>\n  <span id=\"canttell23\" style=\"font-weight: lighter\"\n    >This is a text-shadow colorParse fail!</span\n  >\n</div>\n\n<p\n  style=\"\n    text-overflow: ellipsis;\n    overflow: hidden;\n    white-space: nowrap;\n    max-width: 200px;\n    background-color: #fff;\n  \"\n>\n  <span id=\"pass9\" style=\"color: #000\">\n    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin\n    quam. Fusce mi odio, egestas pulvinar erat eget, vehicula tempus est. Proin\n    vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu\n    facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim\n    nulla, sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque\n    imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna,\n    mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh,\n    quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla\n    ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim.\n    Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris\n    dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor\n    felis ante non libero.\n  </span>\n</p>\n\n<div\n  id=\"text-container\"\n  style=\"\n    overflow: hidden;\n    background-color: teal;\n    height: 30px;\n    width: 200px;\n    margin-bottom: 60px;\n  \"\n>\n  <span id=\"fail8\" style=\"color: grey\"\n    >this is a span with some text inside of it</span\n  >\n  <span id=\"fail9\" style=\"color: grey\"\n    >this is a span with some text inside of it</span\n  >\n  <span id=\"ignore8\" style=\"color: grey\"\n    >this is a span with some text inside of it</span\n  >\n  <span id=\"ignore9\" style=\"color: grey\"\n    >this is a span with some text inside of it</span\n  >\n</div>\n\n<details>\n  <summary id=\"pass10\">Summary</summary>\n  <span id=\"ignore10\">Some text</span>\n</details>\n\n<details open>\n  <summary id=\"pass11\">Summary</summary>\n  <span id=\"pass12\">Some text</span>\n</details>\n\n<div style=\"background: #222\">\n  <div\n    style=\"\n      background-color: white;\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      max-width: 300px;\n    \"\n  >\n    <span id=\"pass13\"\n      >Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim\n      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\n      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate\n      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat\n      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id\n      est laborum.</span\n    >\n  </div>\n</div>\n\n<div style=\"background-color: #222; height: 40px; width: 100px\"></div>\n<div style=\"margin: -40px 0 40px\" id=\"fail10\">Hello world</div>\n\n<div\n  style=\"\n    overflow: hidden;\n    width: 300px;\n    height: 200px;\n    background: #222;\n    margin-bottom: 150px;\n  \"\n>\n  <div style=\"overflow: hidden; position: relative; left: 100px\">\n    <div id=\"fail11\" style=\"position: relative; top: 100px\">\n      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim\n      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\n      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate\n      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat\n      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id\n      est laborum.\n    </div>\n  </div>\n</div>\n\n<ul>\n  <li id=\"pass14\">\n    Lorem\n    <ul>\n      <li id=\"pass15\">Ipsum</li>\n    </ul>\n  </li>\n</ul>\n\n<ul style=\"font-size: 14px; line-height: 1.6em\">\n  <li id=\"pass16\">\n    Test\n    <ul>\n      <li></li>\n    </ul>\n  </li>\n\n  <li id=\"pass17\">\n    Lorem <strong id=\"pass18\">ipsum</strong>.\n    <ul>\n      <li></li>\n    </ul>\n  </li>\n</ul>\n\n<label id=\"pass19\"\n  >Do you like peas?\n  <input id=\"pass20\" name=\"peas\" style=\"width: 100%\" />\n</label>\n\n<p style=\"color: #ccc; -webkit-text-stroke: 0.03em #444\" id=\"pass21\">\n  Text-stroke 0.03em\n</p>\n<p style=\"color: #ccc; -webkit-text-stroke: 0.02em #444\" id=\"fail12\">\n  Text-stroke 0.02em\n</p>\n\n<div\n  id=\"backdrop\"\n  style=\"\n    position: absolute;\n    z-index: 1;\n    height: 40px;\n    background: #333;\n    width: 100%;\n  \"\n></div>\n<div style=\"display: flex\">\n  <div style=\"background: white; z-index: 1\">\n    <span id=\"pass22\">This div will be on top of the stack</span>\n  </div>\n</div>\n\n<div id=\"ignore11\" style=\"background: #000; color: #333\" inert>hello</div>\n\n<div inert>\n  <div>\n    <div id=\"ignore12\" style=\"background: #000; color: #333\">hello</div>\n  </div>\n</div>\n\n<div\n  id=\"pass23\"\n  style=\"\n    color: #000;\n    background: #737373;\n    text-shadow: color(display-p3 1 1 1 / 1) 0 0 3px;\n  \"\n>\n  Hello world\n</div>\n\n<div style=\"overflow: hidden; width: 50px\">\n  <div style=\"overflow: hidden; width: 25px\">\n    <div id=\"ignore13\" style=\"padding-left: 65px\">Hello World</div>\n  </div>\n</div>\n\n<div\n  id=\"ignore13\"\n  style=\"background-color: gray; color: white; font-size: 0; font-weight: bold\"\n>\n  Ignore.\n</div>\n\n<textarea id=\"pass24\" style=\"background-color: white; color: black\">\nHello world</textarea\n>\n"
  },
  {
    "path": "test/integration/rules/color-contrast/color-contrast.json",
    "content": "{\n  \"description\": \"color-contrast test\",\n  \"rule\": \"color-contrast\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"],\n    [\"#fail12\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass7\"],\n    [\"#pass7 > input\"],\n    [\"#pass8\"],\n    [\"#text-shadow-fg-pass\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"]\n  ],\n  \"incomplete\": [\n    [\"#canttell1\"],\n    [\"#canttell2\"],\n    [\"#canttell3\"],\n    [\"#canttell4\"],\n    [\"#canttell5\"],\n    [\"#canttell6\"],\n    [\"#canttell7\"],\n    [\"#canttell8\"],\n    [\"#canttell9\"],\n    [\"#canttell10\"],\n    [\"#canttell11\"],\n    [\"#canttell12\"],\n    [\"#canttell13\"],\n    [\"#canttell14\"],\n    [\"#canttell15\"],\n    [\"#canttell16\"],\n    [\"#canttell17\"],\n    [\"#canttell18\"],\n    [\"#canttell19\"],\n    [\"#canttell20\"],\n    [\"#canttell21\"],\n    [\"#canttell22\"],\n    [\"#canttell23\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/color-contrast/text-shadows.html",
    "content": "<style>\n  section {\n    display: inline-block;\n    padding: 18px;\n    font-size: 32px;\n  }\n  section > div {\n    display: flex;\n  }\n  i {\n    display: inline-block;\n    padding: 3px 12px;\n    font-style: normal;\n    min-width: 135px;\n    position: relative;\n  }\n\n  .incomplete:after,\n  .violations:after,\n  .passes:after {\n    position: absolute;\n    top: 3px;\n    right: 6px;\n    font-size: 12px;\n    text-shadow: none;\n    background: rgba(0, 0, 0, 0.7);\n    border-radius: 4px;\n    width: 14px;\n    text-align: center;\n    font-weight: bold;\n  }\n\n  section .violations:after {\n    content: '×';\n    color: salmon;\n  }\n  section .incomplete:after {\n    content: '?';\n    color: khaki;\n  }\n  section .passes:after {\n    content: '✓';\n    color: lightgreen;\n  }\n\n  .s1 {\n    color: #666;\n    background-color: #aaa;\n  }\n  .s2 {\n    color: #aaa;\n    background-color: #666;\n  }\n  .s3 {\n    color: #666;\n    background-color: #666;\n  }\n  .s4 {\n    color: #aaa;\n    background-color: #aaa;\n  }\n  .s5 {\n    color: #aaa;\n    background-color: #333;\n  }\n  .s6 {\n    color: #444;\n    background-color: #ccc;\n  }\n  .s7 {\n    color: #838383;\n    background-color: #bbb;\n  }\n\n  .f1 {\n    font-size: 12px;\n  }\n  .f2 {\n    font-size: 16px;\n  }\n  .f3 {\n    font-size: 20px;\n  }\n  .f4 {\n    font-size: 24px;\n  }\n  .f5 {\n    font-size: 28px;\n  }\n\n  .r1 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #000,\n      0 0 0.03em #000,\n      0 0 0.03em #000;\n  }\n  .r1 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #000,\n      0 0 0.06em #000,\n      0 0 0.06em #000;\n  }\n  .r1 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #000,\n      0 0 0.12em #000,\n      0 0 0.12em #000;\n  }\n  .r1 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #000,\n      0 0 0.25em #000,\n      0 0 0.25em #000;\n  }\n\n  .r2 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #444,\n      0 0 0.03em #444,\n      0 0 0.03em #444;\n  }\n  .r2 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #444,\n      0 0 0.06em #444,\n      0 0 0.06em #444;\n  }\n  .r2 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #444,\n      0 0 0.12em #444,\n      0 0 0.12em #444;\n  }\n  .r2 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #444,\n      0 0 0.25em #444,\n      0 0 0.25em #444;\n  }\n\n  .r3 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #888,\n      0 0 0.03em #888,\n      0 0 0.03em #888;\n  }\n  .r3 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #888,\n      0 0 0.06em #888,\n      0 0 0.06em #888;\n  }\n  .r3 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #888,\n      0 0 0.12em #888,\n      0 0 0.12em #888;\n  }\n  .r3 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #888,\n      0 0 0.25em #888,\n      0 0 0.25em #888;\n  }\n\n  .r4 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #ccc,\n      0 0 0.03em #ccc,\n      0 0 0.03em #ccc;\n  }\n  .r4 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #ccc,\n      0 0 0.06em #ccc,\n      0 0 0.06em #ccc;\n  }\n  .r4 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #ccc,\n      0 0 0.12em #ccc,\n      0 0 0.12em #ccc;\n  }\n  .r4 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #ccc,\n      0 0 0.25em #ccc,\n      0 0 0.25em #ccc;\n  }\n\n  .r5 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.03em #fff;\n  }\n  .r5 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.06em #fff;\n  }\n  .r5 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.12em #fff;\n  }\n  .r5 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.25em #fff;\n  }\n\n  .r6 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #000,\n      0 0 0.03em #000,\n      0 0 0.03em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r6 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #000,\n      0 0 0.06em #000,\n      0 0 0.06em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r6 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #000,\n      0 0 0.12em #000,\n      0 0 0.12em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r6 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #000,\n      0 0 0.25em #000,\n      0 0 0.25em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n\n  .r7 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #444,\n      0 0 0.03em #444,\n      0 0 0.03em #444,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r7 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #444,\n      0 0 0.06em #444,\n      0 0 0.06em #444,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r7 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #444,\n      0 0 0.12em #444,\n      0 0 0.12em #444,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r7 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #444,\n      0 0 0.25em #444,\n      0 0 0.25em #444,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n\n  .r8 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #888,\n      0 0 0.03em #888,\n      0 0 0.03em #888,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r8 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #888,\n      0 0 0.06em #888,\n      0 0 0.06em #888,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r8 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #888,\n      0 0 0.12em #888,\n      0 0 0.12em #888,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r8 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #888,\n      0 0 0.25em #888,\n      0 0 0.25em #888,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n\n  .r9 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #ccc,\n      0 0 0.03em #ccc,\n      0 0 0.03em #ccc,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r9 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #ccc,\n      0 0 0.06em #ccc,\n      0 0 0.06em #ccc,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r9 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #ccc,\n      0 0 0.12em #ccc,\n      0 0 0.12em #ccc,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r9 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #ccc,\n      0 0 0.25em #ccc,\n      0 0 0.25em #ccc,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n\n  .r10 i:nth-child(1) {\n    text-shadow:\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.5em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r10 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.5em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r10 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.5em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n  .r10 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.5em #000,\n      0 0 0.5em #000,\n      0 0 0.5em #000;\n  }\n\n  .r11 i:nth-child(1) {\n    text-shadow: 0 0 0.14em #000;\n  }\n  .r11 i:nth-child(2) {\n    text-shadow: 0 0 0.18em #000;\n  }\n  .r11 i:nth-child(3) {\n    text-shadow: 0 0 0.22em #000;\n  }\n  .r11 i:nth-child(4) {\n    text-shadow: 0 0 0.26em #000;\n  }\n\n  .l1 i:nth-child(1) {\n    text-shadow: 0 0 0.03em #fff;\n  }\n  .l1 i:nth-child(2) {\n    text-shadow:\n      0 0 0.03em #fff,\n      0 0 0.03em #fff;\n  }\n  .l1 i:nth-child(3) {\n    text-shadow:\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.03em #fff;\n  }\n  .l1 i:nth-child(4) {\n    text-shadow:\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.03em #fff,\n      0 0 0.03em #fff;\n  }\n\n  .l2 i:nth-child(1) {\n    text-shadow: 0 0 0.06em #fff;\n  }\n  .l2 i:nth-child(2) {\n    text-shadow:\n      0 0 0.06em #fff,\n      0 0 0.06em #fff;\n  }\n  .l2 i:nth-child(3) {\n    text-shadow:\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.06em #fff;\n  }\n  .l2 i:nth-child(4) {\n    text-shadow:\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.06em #fff,\n      0 0 0.06em #fff;\n  }\n\n  .l3 i:nth-child(1) {\n    text-shadow: 0 0 0.12em #fff;\n  }\n  .l3 i:nth-child(2) {\n    text-shadow:\n      0 0 0.12em #fff,\n      0 0 0.12em #fff;\n  }\n  .l3 i:nth-child(3) {\n    text-shadow:\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.12em #fff;\n  }\n  .l3 i:nth-child(4) {\n    text-shadow:\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.12em #fff,\n      0 0 0.12em #fff;\n  }\n\n  .l4 i:nth-child(1) {\n    text-shadow: 0 0 0.25em #fff;\n  }\n  .l4 i:nth-child(2) {\n    text-shadow:\n      0 0 0.25em #fff,\n      0 0 0.25em #fff;\n  }\n  .l4 i:nth-child(3) {\n    text-shadow:\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.25em #fff;\n  }\n  .l4 i:nth-child(4) {\n    text-shadow:\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.25em #fff,\n      0 0 0.25em #fff;\n  }\n\n  .l5 i:nth-child(1) {\n    text-shadow: 0 0 0.5em #fff;\n  }\n  .l5 i:nth-child(2) {\n    text-shadow:\n      0 0 0.5em #fff,\n      0 0 0.5em #fff;\n  }\n  .l5 i:nth-child(3) {\n    text-shadow:\n      0 0 0.5em #fff,\n      0 0 0.5em #fff,\n      0 0 0.5em #fff;\n  }\n  .l5 i:nth-child(4) {\n    text-shadow:\n      0 0 0.5em #fff,\n      0 0 0.5em #fff,\n      0 0 0.5em #fff,\n      0 0 0.5em #fff;\n  }\n\n  .x1 i:nth-child(1) {\n    text-shadow:\n      0 -1px 0 #000,\n      1px 0 0 #000,\n      0 1px 0 #000;\n  }\n  .x1 i:nth-child(2) {\n    text-shadow:\n      0 -1px 0 #000,\n      1px 0 0 #000,\n      0 1px 0 #000,\n      -1px 0 0 #000;\n  }\n  .x1 i:nth-child(3) {\n    text-shadow:\n      -1px -1px 0 #000,\n      1px -1px 0 #000,\n      1px 1px 0 #000,\n      -1px 1px 0 #000;\n  }\n  .x1 i:nth-child(4) {\n    text-shadow:\n      -1px -1px 0 #000,\n      0 -1px 0 #000,\n      1px -1px 0 #000,\n      1px 0 0 #000,\n      1px 1px 0 #000,\n      0 1px 0 #000,\n      -1px 0 0 #000;\n  }\n  .x2 i:nth-child(1) {\n    text-shadow:\n      0 -1px 0 #444,\n      1px 0 0 #444,\n      0 1px 0 #444;\n  }\n  .x2 i:nth-child(2) {\n    text-shadow:\n      0 -1px 0 #444,\n      1px 0 0 #444,\n      0 1px 0 #444,\n      -1px 0 0 #444;\n  }\n  .x2 i:nth-child(3) {\n    text-shadow:\n      -1px -1px 0 #444,\n      1px -1px 0 #444,\n      1px 1px 0 #444,\n      -1px 1px 0 #444;\n  }\n  .x2 i:nth-child(4) {\n    text-shadow:\n      -1px -1px 0 #444,\n      0 -1px 0 #444,\n      1px -1px 0 #444,\n      1px 0 0 #444,\n      1px 1px 0 #444,\n      0 1px 0 #444,\n      -1px 0 0 #444;\n  }\n  .x3 i:nth-child(1) {\n    text-shadow:\n      0 -1px 0 #888,\n      1px 0 0 #888,\n      0 1px 0 #888;\n  }\n  .x3 i:nth-child(2) {\n    text-shadow:\n      0 -1px 0 #888,\n      1px 0 0 #888,\n      0 1px 0 #888,\n      -1px 0 0 #888;\n  }\n  .x3 i:nth-child(3) {\n    text-shadow:\n      -1px -1px 0 #888,\n      1px -1px 0 #888,\n      1px 1px 0 #888,\n      -1px 1px 0 #888;\n  }\n  .x3 i:nth-child(4) {\n    text-shadow:\n      -1px -1px 0 #888,\n      0 -1px 0 #888,\n      1px -1px 0 #888,\n      1px 0 0 #888,\n      1px 1px 0 #888,\n      0 1px 0 #888,\n      -1px 0 0 #888;\n  }\n  .x4 i:nth-child(1) {\n    text-shadow:\n      0 -1px 0 #aaa,\n      1px 0 0 #aaa,\n      0 1px 0 #aaa;\n  }\n  .x4 i:nth-child(2) {\n    text-shadow:\n      0 -1px 0 #aaa,\n      1px 0 0 #aaa,\n      0 1px 0 #aaa,\n      -1px 0 0 #aaa;\n  }\n  .x4 i:nth-child(3) {\n    text-shadow:\n      -1px -1px 0 #aaa,\n      1px -1px 0 #aaa,\n      1px 1px 0 #aaa,\n      -1px 1px 0 #aaa;\n  }\n  .x4 i:nth-child(4) {\n    text-shadow:\n      -1px -1px 0 #aaa,\n      0 -1px 0 #aaa,\n      1px -1px 0 #aaa,\n      1px 0 0 #aaa,\n      1px 1px 0 #aaa,\n      0 1px 0 #aaa,\n      -1px 0 0 #aaa;\n  }\n  .x5 i:nth-child(1) {\n    text-shadow:\n      0 -1px 0 #fff,\n      1px 0 0 #fff,\n      0 1px 0 #fff;\n  }\n  .x5 i:nth-child(2) {\n    text-shadow:\n      0 -1px 0 #fff,\n      1px 0 0 #fff,\n      0 1px 0 #fff,\n      -1px 0 0 #fff;\n  }\n  .x5 i:nth-child(3) {\n    text-shadow:\n      -1px -1px 0 #fff,\n      1px -1px 0 #fff,\n      1px 1px 0 #fff,\n      -1px 1px 0 #fff;\n  }\n  .x5 i:nth-child(4) {\n    text-shadow:\n      -1px -1px 0 #fff,\n      0 -1px 0 #fff,\n      1px -1px 0 #fff,\n      1px 0 0 #fff,\n      1px 1px 0 #fff,\n      0 1px 0 #fff,\n      -1px 0 0 #fff;\n  }\n</style>\n\n<section class=\"s1\" id=\"b1\">\n  <div class=\"r0\"><i id=\"sample1\">Sample</i></div>\n  <div class=\"r1\">\n    <i id=\"b1-r1-i1\">b1-r1-i1</i>\n    <i id=\"b1-r1-i2\">b1-r1-i2</i>\n    <i id=\"b1-r1-i3\">b1-r1-i3</i>\n    <i id=\"b1-r1-i4\">b1-r1-i4</i>\n  </div>\n  <div class=\"r2\">\n    <i id=\"b1-r2-i1\">b1-r2-i1</i>\n    <i id=\"b1-r2-i2\">b1-r2-i2</i>\n    <i id=\"b1-r2-i3\">b1-r2-i3</i>\n    <i id=\"b1-r2-i4\">b1-r2-i4</i>\n  </div>\n  <div class=\"r3\">\n    <i id=\"b1-r3-i1\">b1-r3-i1</i>\n    <i id=\"b1-r3-i2\">b1-r3-i2</i>\n    <i id=\"b1-r3-i3\">b1-r3-i3</i>\n    <i id=\"b1-r3-i4\">b1-r3-i4</i>\n  </div>\n  <div class=\"r4\">\n    <i id=\"b1-r4-i1\">b1-r4-i1</i>\n    <i id=\"b1-r4-i2\">b1-r4-i2</i>\n    <i id=\"b1-r4-i3\">b1-r4-i3</i>\n    <i id=\"b1-r4-i4\">b1-r4-i4</i>\n  </div>\n  <div class=\"r5\">\n    <i id=\"b1-r5-i1\">b1-r5-i1</i>\n    <i id=\"b1-r5-i2\">b1-r5-i2</i>\n    <i id=\"b1-r5-i3\">b1-r5-i3</i>\n    <i id=\"b1-r5-i4\">b1-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s2\" id=\"b2\">\n  <div class=\"r0\"><i id=\"sample2\">Sample</i></div>\n  <div class=\"r1\">\n    <i id=\"b2-r1-i1\">b2-r1-i1</i>\n    <i id=\"b2-r1-i2\">b2-r1-i2</i>\n    <i id=\"b2-r1-i3\">b2-r1-i3</i>\n    <i id=\"b2-r1-i4\">b2-r1-i4</i>\n  </div>\n  <div class=\"r2\">\n    <i id=\"b2-r2-i1\">b2-r2-i1</i>\n    <i id=\"b2-r2-i2\">b2-r2-i2</i>\n    <i id=\"b2-r2-i3\">b2-r2-i3</i>\n    <i id=\"b2-r2-i4\">b2-r2-i4</i>\n  </div>\n  <div class=\"r3\">\n    <i id=\"b2-r3-i1\">b2-r3-i1</i>\n    <i id=\"b2-r3-i2\">b2-r3-i2</i>\n    <i id=\"b2-r3-i3\">b2-r3-i3</i>\n    <i id=\"b2-r3-i4\">b2-r3-i4</i>\n  </div>\n  <div class=\"r4\">\n    <i id=\"b2-r4-i1\">b2-r4-i1</i>\n    <i id=\"b2-r4-i2\">b2-r4-i2</i>\n    <i id=\"b2-r4-i3\">b2-r4-i3</i>\n    <i id=\"b2-r4-i4\">b2-r4-i4</i>\n  </div>\n  <div class=\"r5\">\n    <i id=\"b2-r5-i1\">b2-r5-i1</i>\n    <i id=\"b2-r5-i2\">b2-r5-i2</i>\n    <i id=\"b2-r5-i3\">b2-r5-i3</i>\n    <i id=\"b2-r5-i4\">b2-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s3\" id=\"b3\">\n  <div class=\"r1\">\n    <i id=\"b3-r1-i1\">b3-r1-i1</i>\n    <i id=\"b3-r1-i2\">b3-r1-i2</i>\n    <i id=\"b3-r1-i3\">b3-r1-i3</i>\n    <i id=\"b3-r1-i4\">b3-r1-i4</i>\n  </div>\n  <div class=\"r2\">\n    <i id=\"b3-r2-i1\">b3-r2-i1</i>\n    <i id=\"b3-r2-i2\">b3-r2-i2</i>\n    <i id=\"b3-r2-i3\">b3-r2-i3</i>\n    <i id=\"b3-r2-i4\">b3-r2-i4</i>\n  </div>\n  <div class=\"r3\">\n    <i id=\"b3-r3-i1\">b3-r3-i1</i>\n    <i id=\"b3-r3-i2\">b3-r3-i2</i>\n    <i id=\"b3-r3-i3\">b3-r3-i3</i>\n    <i id=\"b3-r3-i4\">b3-r3-i4</i>\n  </div>\n  <div class=\"r4\">\n    <i id=\"b3-r4-i1\">b3-r4-i1</i>\n    <i id=\"b3-r4-i2\">b3-r4-i2</i>\n    <i id=\"b3-r4-i3\">b3-r4-i3</i>\n    <i id=\"b3-r4-i4\">b3-r4-i4</i>\n  </div>\n  <div class=\"r5\">\n    <i id=\"b3-r5-i1\">b3-r5-i1</i>\n    <i id=\"b3-r5-i2\">b3-r5-i2</i>\n    <i id=\"b3-r5-i3\">b3-r5-i3</i>\n    <i id=\"b3-r5-i4\">b3-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s4\" id=\"b4\">\n  <div class=\"r1\">\n    <i id=\"b4-r1-i1\">b4-r1-i1</i>\n    <i id=\"b4-r1-i2\">b4-r1-i2</i>\n    <i id=\"b4-r1-i3\">b4-r1-i3</i>\n    <i id=\"b4-r1-i4\">b4-r1-i4</i>\n  </div>\n  <div class=\"r2\">\n    <i id=\"b4-r2-i1\">b4-r2-i1</i>\n    <i id=\"b4-r2-i2\">b4-r2-i2</i>\n    <i id=\"b4-r2-i3\">b4-r2-i3</i>\n    <i id=\"b4-r2-i4\">b4-r2-i4</i>\n  </div>\n  <div class=\"r3\">\n    <i id=\"b4-r3-i1\">b4-r3-i1</i>\n    <i id=\"b4-r3-i2\">b4-r3-i2</i>\n    <i id=\"b4-r3-i3\">b4-r3-i3</i>\n    <i id=\"b4-r3-i4\">b4-r3-i4</i>\n  </div>\n  <div class=\"r4\">\n    <i id=\"b4-r4-i1\">b4-r4-i1</i>\n    <i id=\"b4-r4-i2\">b4-r4-i2</i>\n    <i id=\"b4-r4-i3\">b4-r4-i3</i>\n    <i id=\"b4-r4-i4\">b4-r4-i4</i>\n  </div>\n  <div class=\"r5\">\n    <i id=\"b4-r5-i1\">b4-r5-i1</i>\n    <i id=\"b4-r5-i2\">b4-r5-i2</i>\n    <i id=\"b4-r5-i3\">b4-r5-i3</i>\n    <i id=\"b4-r5-i4\">b4-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s5\" id=\"b5\">\n  <div class=\"r1\">\n    <i id=\"b5-r1-i1\">b5-r1-i1</i>\n    <i id=\"b5-r1-i2\">b5-r1-i2</i>\n    <i id=\"b5-r1-i3\">b5-r1-i3</i>\n    <i id=\"b5-r1-i4\">b5-r1-i4</i>\n  </div>\n  <div class=\"r2\">\n    <i id=\"b5-r2-i1\">b5-r2-i1</i>\n    <i id=\"b5-r2-i2\">b5-r2-i2</i>\n    <i id=\"b5-r2-i3\">b5-r2-i3</i>\n    <i id=\"b5-r2-i4\">b5-r2-i4</i>\n  </div>\n  <div class=\"r3\">\n    <i id=\"b5-r3-i1\">b5-r3-i1</i>\n    <i id=\"b5-r3-i2\">b5-r3-i2</i>\n    <i id=\"b5-r3-i3\">b5-r3-i3</i>\n    <i id=\"b5-r3-i4\">b5-r3-i4</i>\n  </div>\n  <div class=\"r4\">\n    <i id=\"b5-r4-i1\">b5-r4-i1</i>\n    <i id=\"b5-r4-i2\">b5-r4-i2</i>\n    <i id=\"b5-r4-i3\">b5-r4-i3</i>\n    <i id=\"b5-r4-i4\">b5-r4-i4</i>\n  </div>\n  <div class=\"r5\">\n    <i id=\"b5-r5-i1\">b5-r5-i1</i>\n    <i id=\"b5-r5-i2\">b5-r5-i2</i>\n    <i id=\"b5-r5-i3\">b5-r5-i3</i>\n    <i id=\"b5-r5-i4\">b5-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s4\" id=\"b6\">\n  <!-- false negative: \"#b6 > .c8 > i:nth-child(4)\" -->\n  <div class=\"r6\">\n    <i id=\"b6-r1-i1\">b6-r1-i1</i>\n    <i id=\"b6-r1-i2\">b6-r1-i2</i>\n    <i id=\"b6-r1-i3\">b6-r1-i3</i>\n    <i id=\"b6-r1-i4\">b6-r1-i4</i>\n  </div>\n  <div class=\"r7\">\n    <i id=\"b6-r2-i1\">b6-r2-i1</i>\n    <i id=\"b6-r2-i2\">b6-r2-i2</i>\n    <i id=\"b6-r2-i3\">b6-r2-i3</i>\n    <i id=\"b6-r2-i4\">b6-r2-i4</i>\n  </div>\n  <div class=\"r8\">\n    <i id=\"b6-r3-i1\">b6-r3-i1</i>\n    <i id=\"b6-r3-i2\">b6-r3-i2</i>\n    <i id=\"b6-r3-i3\">b6-r3-i3</i>\n    <i id=\"b6-r3-i4\">b6-r3-i4</i>\n  </div>\n  <div class=\"r9\">\n    <i id=\"b6-r4-i1\">b6-r4-i1</i>\n    <i id=\"b6-r4-i2\">b6-r4-i2</i>\n    <i id=\"b6-r4-i3\">b6-r4-i3</i>\n    <i id=\"b6-r4-i4\">b6-r4-i4</i>\n  </div>\n  <div class=\"r10\">\n    <i id=\"b6-r5-i1\">b6-r5-i1</i>\n    <i id=\"b6-r5-i2\">b6-r5-i2</i>\n    <i id=\"b6-r5-i3\">b6-r5-i3</i>\n    <i id=\"b6-r5-i4\">b6-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s2\" id=\"b7\">\n  <!-- false negative: \"#b7 > .c10 > i:nth-child(4)\"-->\n  <div class=\"r6\">\n    <i id=\"b7-r1-i1\">b7-r1-i1</i>\n    <i id=\"b7-r1-i2\">b7-r1-i2</i>\n    <i id=\"b7-r1-i3\">b7-r1-i3</i>\n    <i id=\"b7-r1-i4\">b7-r1-i4</i>\n  </div>\n  <div class=\"r7\">\n    <i id=\"b7-r2-i1\">b7-r2-i1</i>\n    <i id=\"b7-r2-i2\">b7-r2-i2</i>\n    <i id=\"b7-r2-i3\">b7-r2-i3</i>\n    <i id=\"b7-r2-i4\">b7-r2-i4</i>\n  </div>\n  <div class=\"r8\">\n    <i id=\"b7-r3-i1\">b7-r3-i1</i>\n    <i id=\"b7-r3-i2\">b7-r3-i2</i>\n    <i id=\"b7-r3-i3\">b7-r3-i3</i>\n    <i id=\"b7-r3-i4\">b7-r3-i4</i>\n  </div>\n  <div class=\"r9\">\n    <i id=\"b7-r4-i1\">b7-r4-i1</i>\n    <i id=\"b7-r4-i2\">b7-r4-i2</i>\n    <i id=\"b7-r4-i3\">b7-r4-i3</i>\n    <i id=\"b7-r4-i4\">b7-r4-i4</i>\n  </div>\n  <div class=\"r10\">\n    <i id=\"b7-r5-i1\">b7-r5-i1</i>\n    <i id=\"b7-r5-i2\">b7-r5-i2</i>\n    <i id=\"b7-r5-i3\">b7-r5-i3</i>\n    <i id=\"b7-r5-i4\">b7-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s1\" id=\"b8\">\n  <div class=\"r6\">\n    <i id=\"b8-r1-i1\">b8-r1-i1</i>\n    <i id=\"b8-r1-i2\">b8-r1-i2</i>\n    <i id=\"b8-r1-i3\">b8-r1-i3</i>\n    <i id=\"b8-r1-i4\">b8-r1-i4</i>\n  </div>\n  <div class=\"r7\">\n    <i id=\"b8-r2-i1\">b8-r2-i1</i>\n    <i id=\"b8-r2-i2\">b8-r2-i2</i>\n    <i id=\"b8-r2-i3\">b8-r2-i3</i>\n    <i id=\"b8-r2-i4\">b8-r2-i4</i>\n  </div>\n  <div class=\"r8\">\n    <i id=\"b8-r3-i1\">b8-r3-i1</i>\n    <i id=\"b8-r3-i2\">b8-r3-i2</i>\n    <i id=\"b8-r3-i3\">b8-r3-i3</i>\n    <i id=\"b8-r3-i4\">b8-r3-i4</i>\n  </div>\n  <div class=\"r9\">\n    <i id=\"b8-r4-i1\">b8-r4-i1</i>\n    <i id=\"b8-r4-i2\">b8-r4-i2</i>\n    <i id=\"b8-r4-i3\">b8-r4-i3</i>\n    <i id=\"b8-r4-i4\">b8-r4-i4</i>\n  </div>\n  <div class=\"r10\">\n    <i id=\"b8-r5-i1\">b8-r5-i1</i>\n    <i id=\"b8-r5-i2\">b8-r5-i2</i>\n    <i id=\"b8-r5-i3\">b8-r5-i3</i>\n    <i id=\"b8-r5-i4\">b8-r5-i4</i>\n  </div>\n</section>\n\n<section class=\"s2 r2\" id=\"b9\">\n  <div class=\"f1\">\n    <i id=\"b9-f1-i1\">b9-f1-i1</i>\n    <i id=\"b9-f1-i2\">b9-f1-i2</i>\n    <i id=\"b9-f1-i3\">b9-f1-i3</i>\n    <i id=\"b9-f1-i4\">b9-f1-i4</i>\n  </div>\n  <div class=\"f2\">\n    <i id=\"b9-f2-i1\">b9-f2-i1</i>\n    <i id=\"b9-f2-i2\">b9-f2-i2</i>\n    <i id=\"b9-f2-i3\">b9-f2-i3</i>\n    <i id=\"b9-f2-i4\">b9-f2-i4</i>\n  </div>\n  <div class=\"f3\">\n    <i id=\"b9-f3-i1\">b9-f3-i1</i>\n    <i id=\"b9-f3-i2\">b9-f3-i2</i>\n    <i id=\"b9-f3-i3\">b9-f3-i3</i>\n    <i id=\"b9-f3-i4\">b9-f3-i4</i>\n  </div>\n  <div class=\"f4\">\n    <i id=\"b9-f4-i1\">b9-f4-i1</i>\n    <i id=\"b9-f4-i2\">b9-f4-i2</i>\n    <i id=\"b9-f4-i3\">b9-f4-i3</i>\n    <i id=\"b9-f4-i4\">b9-f4-i4</i>\n  </div>\n  <div class=\"f5\">\n    <i id=\"b9-f5-i1\">b9-f5-i1</i>\n    <i id=\"b9-f5-i2\">b9-f5-i2</i>\n    <i id=\"b9-f5-i3\">b9-f5-i3</i>\n    <i id=\"b9-f5-i4\">b9-f5-i4</i>\n  </div>\n</section>\n\n<section class=\"s1 r1\" id=\"b10\">\n  <div class=\"f1\">\n    <i id=\"b10-f1-i1\">b10-f1-i1</i>\n    <i id=\"b10-f1-i2\">b10-f1-i2</i>\n    <i id=\"b10-f1-i3\">b10-f1-i3</i>\n    <i id=\"b10-f1-i4\">b10-f1-i4</i>\n  </div>\n  <div class=\"f2\">\n    <i id=\"b10-f2-i1\">b10-f2-i1</i>\n    <i id=\"b10-f2-i2\">b10-f2-i2</i>\n    <i id=\"b10-f2-i3\">b10-f2-i3</i>\n    <i id=\"b10-f2-i4\">b10-f2-i4</i>\n  </div>\n  <div class=\"f3\">\n    <i id=\"b10-f3-i1\">b10-f3-i1</i>\n    <i id=\"b10-f3-i2\">b10-f3-i2</i>\n    <i id=\"b10-f3-i3\">b10-f3-i3</i>\n    <i id=\"b10-f3-i4\">b10-f3-i4</i>\n  </div>\n  <div class=\"f4\">\n    <i id=\"b10-f4-i1\">b10-f4-i1</i>\n    <i id=\"b10-f4-i2\">b10-f4-i2</i>\n    <i id=\"b10-f4-i3\">b10-f4-i3</i>\n    <i id=\"b10-f4-i4\">b10-f4-i4</i>\n  </div>\n  <div class=\"f5\">\n    <i id=\"b10-f5-i1\">b10-f5-i1</i>\n    <i id=\"b10-f5-i2\">b10-f5-i2</i>\n    <i id=\"b10-f5-i3\">b10-f5-i3</i>\n    <i id=\"b10-f5-i4\">b10-f5-i4</i>\n  </div>\n</section>\n\n<section class=\"s4 r7\" id=\"b11\">\n  <div class=\"f1\">\n    <i id=\"b11-f1-i1\">b11-f1-i1</i>\n    <i id=\"b11-f1-i2\">b11-f1-i2</i>\n    <i id=\"b11-f1-i3\">b11-f1-i3</i>\n    <i id=\"b11-f1-i4\">b11-f1-i4</i>\n  </div>\n  <div class=\"f2\">\n    <i id=\"b11-f2-i1\">b11-f2-i1</i>\n    <i id=\"b11-f2-i2\">b11-f2-i2</i>\n    <i id=\"b11-f2-i3\">b11-f2-i3</i>\n    <i id=\"b11-f2-i4\">b11-f2-i4</i>\n  </div>\n  <div class=\"f3\">\n    <i id=\"b11-f3-i1\">b11-f3-i1</i>\n    <i id=\"b11-f3-i2\">b11-f3-i2</i>\n    <i id=\"b11-f3-i3\">b11-f3-i3</i>\n    <i id=\"b11-f3-i4\">b11-f3-i4</i>\n  </div>\n  <div class=\"f4\">\n    <i id=\"b11-f4-i1\">b11-f4-i1</i>\n    <i id=\"b11-f4-i2\">b11-f4-i2</i>\n    <i id=\"b11-f4-i3\">b11-f4-i3</i>\n    <i id=\"b11-f4-i4\">b11-f4-i4</i>\n  </div>\n  <div class=\"f5\">\n    <i id=\"b11-f5-i1\">b11-f5-i1</i>\n    <i id=\"b11-f5-i2\">b11-f5-i2</i>\n    <i id=\"b11-f5-i3\">b11-f5-i3</i>\n    <i id=\"b11-f5-i4\">b11-f5-i4</i>\n  </div>\n</section>\n\n<section class=\"s3\" id=\"b12\">\n  <div class=\"l1\">\n    <i id=\"b12-l1-i1\">b12-l1-i1</i>\n    <i id=\"b12-l1-i2\">b12-l1-i2</i>\n    <i id=\"b12-l1-i3\">b12-l1-i3</i>\n    <i id=\"b12-l1-i4\">b12-l1-i4</i>\n  </div>\n  <div class=\"l2\">\n    <i id=\"b12-l2-i1\">b12-l2-i1</i>\n    <i id=\"b12-l2-i2\">b12-l2-i2</i>\n    <i id=\"b12-l2-i3\">b12-l2-i3</i>\n    <i id=\"b12-l2-i4\">b12-l2-i4</i>\n  </div>\n  <div class=\"l3\">\n    <i id=\"b12-l3-i1\">b12-l3-i1</i>\n    <i id=\"b12-l3-i2\">b12-l3-i2</i>\n    <i id=\"b12-l3-i3\">b12-l3-i3</i>\n    <i id=\"b12-l3-i4\">b12-l3-i4</i>\n  </div>\n  <div class=\"l4\">\n    <i id=\"b12-l4-i1\">b12-l4-i1</i>\n    <i id=\"b12-l4-i2\">b12-l4-i2</i>\n    <i id=\"b12-l4-i3\">b12-l4-i3</i>\n    <i id=\"b12-l4-i4\">b12-l4-i4</i>\n  </div>\n  <div class=\"l5\">\n    <i id=\"b12-l5-i1\">b12-l5-i1</i>\n    <i id=\"b12-l5-i2\">b12-l5-i2</i>\n    <i id=\"b12-l5-i3\">b12-l5-i3</i>\n    <i id=\"b12-l5-i4\">b12-l5-i4</i>\n  </div>\n</section>\n\n<section class=\"s6\" id=\"b13\">\n  <div class=\"f1 r11\">\n    <i id=\"b13-f1-i1\">b13-f1-i1</i>\n    <i id=\"b13-f1-i2\">b13-f1-i2</i>\n    <i id=\"b13-f1-i3\">b13-f1-i3</i>\n    <i id=\"b13-f1-i4\">b13-f1-i4</i>\n  </div>\n  <div class=\"f2 r11\">\n    <i id=\"b13-f2-i1\">b13-f2-i1</i>\n    <i id=\"b13-f2-i2\">b13-f2-i2</i>\n    <i id=\"b13-f2-i3\">b13-f2-i3</i>\n    <i id=\"b13-f2-i4\">b13-f2-i4</i>\n  </div>\n  <div class=\"f3 r11\">\n    <i id=\"b13-f3-i1\">b13-f3-i1</i>\n    <i id=\"b13-f3-i2\">b13-f3-i2</i>\n    <i id=\"b13-f3-i3\">b13-f3-i3</i>\n    <i id=\"b13-f3-i4\">b13-f3-i4</i>\n  </div>\n  <div class=\"f4 r11\">\n    <i id=\"b13-f4-i1\">b13-f4-i1</i>\n    <i id=\"b13-f4-i2\">b13-f4-i2</i>\n    <i id=\"b13-f4-i3\">b13-f4-i3</i>\n    <i id=\"b13-f4-i4\">b13-f4-i4</i>\n  </div>\n  <div class=\"f5 r11\">\n    <i id=\"b13-f5-i1\">b13-f5-i1</i>\n    <i id=\"b13-f5-i2\">b13-f5-i2</i>\n    <i id=\"b13-f5-i3\">b13-f5-i3</i>\n    <i id=\"b13-f5-i4\">b13-f5-i4</i>\n  </div>\n</section>\n\n<section class=\"s4\" id=\"b14\">\n  <div class=\"f1 x2\">\n    <i id=\"b14-f1-i1\">b14-f1-i1</i>\n    <i id=\"b14-f1-i2\">b14-f1-i2</i>\n    <i id=\"b14-f1-i3\">b14-f1-i3</i>\n    <i id=\"b14-f1-i4\">b14-f1-i4</i>\n  </div>\n  <div class=\"f2 x2\">\n    <i id=\"b14-f2-i1\">b14-f2-i1</i>\n    <i id=\"b14-f2-i2\">b14-f2-i2</i>\n    <i id=\"b14-f2-i3\">b14-f2-i3</i>\n    <i id=\"b14-f2-i4\">b14-f2-i4</i>\n  </div>\n  <div class=\"f3 x2\">\n    <i id=\"b14-f3-i1\">b14-f3-i1</i>\n    <i id=\"b14-f3-i2\">b14-f3-i2</i>\n    <i id=\"b14-f3-i3\">b14-f3-i3</i>\n    <i id=\"b14-f3-i4\">b14-f3-i4</i>\n  </div>\n  <div class=\"f4 x2\">\n    <i id=\"b14-f4-i1\">b14-f4-i1</i>\n    <i id=\"b14-f4-i2\">b14-f4-i2</i>\n    <i id=\"b14-f4-i3\">b14-f4-i3</i>\n    <i id=\"b14-f4-i4\">b14-f4-i4</i>\n  </div>\n  <div class=\"f5 x2\">\n    <i id=\"b14-f5-i1\">b14-f5-i1</i>\n    <i id=\"b14-f5-i2\">b14-f5-i2</i>\n    <i id=\"b14-f5-i3\">b14-f5-i3</i>\n    <i id=\"b14-f5-i4\">b14-f5-i4</i>\n  </div>\n</section>\n\n<section class=\"s7\" id=\"b15\">\n  <div class=\"x1\">\n    <i id=\"b15-x1-i1\">b15-x1-i1</i>\n    <i id=\"b15-x1-i2\">b15-x1-i2</i>\n    <i id=\"b15-x1-i3\">b15-x1-i3</i>\n    <i id=\"b15-x1-i4\">b15-x1-i4</i>\n  </div>\n  <div class=\"x2\">\n    <i id=\"b15-x2-i1\">b15-x2-i1</i>\n    <i id=\"b15-x2-i2\">b15-x2-i2</i>\n    <i id=\"b15-x2-i3\">b15-x2-i3</i>\n    <i id=\"b15-x2-i4\">b15-x2-i4</i>\n  </div>\n  <div class=\"x3\">\n    <i id=\"b15-x3-i1\">b15-x3-i1</i>\n    <i id=\"b15-x3-i2\">b15-x3-i2</i>\n    <i id=\"b15-x3-i3\">b15-x3-i3</i>\n    <i id=\"b15-x3-i4\">b15-x3-i4</i>\n  </div>\n  <div class=\"x4\">\n    <i id=\"b15-x4-i1\">b15-x4-i1</i>\n    <i id=\"b15-x4-i2\">b15-x4-i2</i>\n    <i id=\"b15-x4-i3\">b15-x4-i3</i>\n    <i id=\"b15-x4-i4\">b15-x4-i4</i>\n  </div>\n  <div class=\"x5\">\n    <i id=\"b15-x5-i1\">b15-x5-i1</i>\n    <i id=\"b15-x5-i2\">b15-x5-i2</i>\n    <i id=\"b15-x5-i3\">b15-x5-i3</i>\n    <i id=\"b15-x5-i4\">b15-x5-i4</i>\n  </div>\n</section>\n\n<script src=\"/axe.js\"></script>\n<script>\n  // Script added for testing purposes, does not run in automated tests\n  if (window.top == window.self) {\n    axe.run(\n      { runOnly: ['color-contrast'], elementRef: true },\n      function (err, res) {\n        console.log(res);\n        /*\n\t\t\tAll these test cases are on the edge of pass and fail. If we ever revisit them, these are the ones could be improved on:\n\t\t\t- False negatives:\n\t\t\t\t- b3-r1-i1\n\t\t\t\t- b4-r1-i1, b4-r2-i1\n\t\t\t\t- b8-r1-i1, b8-r5-i1\n\t\t\t*/\n        ['violations', 'incomplete', 'passes'].forEach(key => {\n          if (res[key][0]) {\n            res[key][0].nodes.forEach(({ element }) => {\n              element.classList.add(key);\n            });\n          }\n        });\n      }\n    );\n  }\n</script>\n"
  },
  {
    "path": "test/integration/rules/color-contrast/text-shadows.json",
    "content": "{\n  \"description\": \"text-shadow test\",\n  \"rule\": \"color-contrast\",\n  \"violations\": [\n    [\"#sample1\"],\n    [\"#sample2\"],\n\n    [\"#b1-r1-i4\"],\n    [\"#b1-r2-i2\"],\n    [\"#b1-r2-i3\"],\n    [\"#b1-r2-i4\"],\n    [\"#b1-r3-i1\"],\n    [\"#b1-r3-i2\"],\n    [\"#b1-r3-i3\"],\n    [\"#b1-r3-i4\"],\n\n    [\"#b2-r3-i1\"],\n    [\"#b2-r3-i2\"],\n    [\"#b2-r3-i3\"],\n    [\"#b2-r3-i4\"],\n    [\"#b2-r4-i4\"],\n    [\"#b2-r4-i1\"],\n    [\"#b2-r4-i2\"],\n    [\"#b2-r4-i3\"],\n    [\"#b2-r5-i4\"],\n\n    [\"#b3-r1-i3\"],\n    [\"#b3-r1-i4\"],\n    [\"#b3-r2-i1\"],\n    [\"#b3-r2-i2\"],\n    [\"#b3-r2-i3\"],\n    [\"#b3-r2-i4\"],\n    [\"#b3-r3-i1\"],\n    [\"#b3-r3-i2\"],\n    [\"#b3-r3-i3\"],\n    [\"#b3-r3-i4\"],\n    [\"#b3-r4-i3\"],\n    [\"#b3-r4-i4\"],\n\n    [\"#b4-r2-i2\"],\n    [\"#b4-r2-i3\"],\n    [\"#b4-r2-i4\"],\n    [\"#b4-r3-i1\"],\n    [\"#b4-r3-i2\"],\n    [\"#b4-r3-i3\"],\n    [\"#b4-r3-i4\"],\n    [\"#b4-r4-i1\"],\n    [\"#b4-r4-i2\"],\n    [\"#b4-r4-i3\"],\n    [\"#b4-r4-i4\"],\n    [\"#b4-r5-i1\"],\n    [\"#b4-r5-i2\"],\n    [\"#b4-r5-i3\"],\n    [\"#b4-r5-i4\"],\n\n    [\"#b5-r3-i4\"],\n    [\"#b5-r4-i4\"],\n    [\"#b3-r4-i1\"],\n    [\"#b3-r4-i2\"],\n    [\"#b5-r5-i4\"],\n\n    [\"#b6-r3-i1\"],\n    [\"#b6-r3-i2\"],\n    [\"#b6-r3-i3\"],\n    [\"#b6-r4-i1\"],\n    [\"#b6-r4-i2\"],\n    [\"#b6-r4-i3\"],\n    [\"#b6-r4-i4\"],\n    [\"#b6-r5-i4\"],\n\n    [\"#b7-r4-i4\"],\n    [\"#b7-r5-i4\"],\n\n    [\"#b8-r1-i4\"],\n    [\"#b8-r2-i1\"],\n    [\"#b8-r2-i2\"],\n    [\"#b8-r2-i3\"],\n    [\"#b8-r2-i4\"],\n    [\"#b8-r3-i1\"],\n    [\"#b8-r3-i2\"],\n    [\"#b8-r3-i3\"],\n    [\"#b8-r3-i4\"],\n    [\"#b8-r4-i1\"],\n    [\"#b8-r4-i2\"],\n    [\"#b8-r4-i3\"],\n    [\"#b8-r4-i4\"],\n    [\"#b8-r5-i4\"],\n\n    [\"#b9-f1-i1\"],\n    [\"#b9-f1-i2\"],\n    [\"#b9-f1-i3\"],\n    [\"#b9-f1-i4\"],\n    [\"#b9-f2-i1\"],\n    [\"#b9-f2-i2\"],\n    [\"#b9-f2-i3\"],\n    [\"#b9-f2-i4\"],\n    [\"#b9-f3-i1\"],\n    [\"#b9-f3-i2\"],\n    [\"#b9-f3-i3\"],\n    [\"#b9-f3-i4\"],\n\n    [\"#b10-f1-i4\"],\n    [\"#b10-f2-i4\"],\n    [\"#b10-f3-i4\"],\n    [\"#b10-f4-i4\"],\n    [\"#b10-f5-i4\"],\n\n    [\"#b11-f1-i1\"],\n    [\"#b11-f1-i2\"],\n    [\"#b11-f1-i3\"],\n    [\"#b11-f2-i1\"],\n    [\"#b11-f2-i2\"],\n    [\"#b11-f2-i3\"],\n    [\"#b11-f3-i1\"],\n    [\"#b11-f3-i2\"],\n    [\"#b11-f3-i3\"],\n\n    [\"#b12-l1-i1\"],\n    [\"#b12-l2-i1\"],\n    [\"#b12-l3-i1\"],\n    [\"#b12-l4-i1\"],\n    [\"#b12-l5-i1\"],\n    [\"#b12-l4-i2\"],\n    [\"#b12-l5-i2\"],\n    [\"#b12-l5-i3\"],\n\n    [\"#b13-f1-i3\"],\n    [\"#b13-f1-i4\"],\n    [\"#b13-f2-i3\"],\n    [\"#b13-f2-i4\"],\n    [\"#b13-f3-i3\"],\n    [\"#b13-f3-i4\"],\n\n    [\"#b14-f1-i2\"],\n    [\"#b14-f1-i3\"],\n    [\"#b14-f1-i4\"],\n    [\"#b14-f2-i2\"],\n    [\"#b14-f2-i3\"],\n    [\"#b14-f2-i4\"],\n    [\"#b14-f3-i2\"],\n    [\"#b14-f3-i3\"],\n    [\"#b14-f3-i4\"],\n    [\"#b14-f4-i2\"],\n    [\"#b14-f5-i2\"],\n\n    [\"#b15-x2-i2\"],\n    [\"#b15-x2-i3\"],\n    [\"#b15-x2-i4\"],\n    [\"#b15-x3-i2\"],\n    [\"#b15-x3-i3\"],\n    [\"#b15-x3-i4\"],\n    [\"#b15-x4-i2\"],\n    [\"#b15-x4-i3\"],\n    [\"#b15-x4-i4\"]\n  ],\n  \"passes\": [\n    [\"#b1-r1-i1\"],\n    [\"#b1-r1-i2\"],\n    [\"#b1-r2-i1\"],\n    [\"#b1-r4-i1\"],\n    [\"#b1-r4-i2\"],\n    [\"#b1-r4-i3\"],\n    [\"#b1-r4-i4\"],\n    [\"#b1-r5-i1\"],\n    [\"#b1-r5-i2\"],\n    [\"#b1-r5-i3\"],\n    [\"#b1-r5-i4\"],\n\n    [\"#b2-r1-i1\"],\n    [\"#b2-r1-i2\"],\n    [\"#b2-r1-i3\"],\n    [\"#b2-r1-i4\"],\n    [\"#b2-r2-i1\"],\n    [\"#b2-r2-i2\"],\n    [\"#b2-r2-i3\"],\n    [\"#b2-r2-i4\"],\n    [\"#b2-r5-i1\"],\n    [\"#b2-r5-i2\"],\n    [\"#b2-r5-i3\"],\n\n    [\"#b3-r1-i1\"],\n    [\"#b3-r1-i2\"],\n    [\"#b1-r1-i3\"],\n    [\"#b3-r5-i1\"],\n    [\"#b3-r5-i2\"],\n    [\"#b3-r5-i3\"],\n    [\"#b3-r5-i4\"],\n\n    [\"#b4-r1-i1\"],\n    [\"#b4-r1-i2\"],\n    [\"#b4-r1-i3\"],\n    [\"#b4-r1-i4\"],\n    [\"#b4-r2-i1\"],\n\n    [\"#b5-r1-i1\"],\n    [\"#b5-r1-i2\"],\n    [\"#b5-r1-i3\"],\n    [\"#b5-r1-i4\"],\n    [\"#b5-r2-i1\"],\n    [\"#b5-r2-i2\"],\n    [\"#b5-r2-i3\"],\n    [\"#b5-r2-i4\"],\n    [\"#b5-r3-i1\"],\n    [\"#b5-r3-i2\"],\n    [\"#b5-r3-i3\"],\n    [\"#b5-r4-i1\"],\n    [\"#b5-r4-i2\"],\n    [\"#b5-r4-i3\"],\n    [\"#b5-r5-i1\"],\n    [\"#b5-r5-i2\"],\n    [\"#b5-r5-i3\"],\n\n    [\"#b6-r1-i1\"],\n    [\"#b6-r1-i2\"],\n    [\"#b6-r1-i3\"],\n    [\"#b6-r1-i4\"],\n    [\"#b6-r2-i1\"],\n    [\"#b6-r2-i2\"],\n    [\"#b6-r2-i3\"],\n    [\"#b6-r2-i4\"],\n    [\"#b6-r3-i4\"],\n    [\"#b6-r5-i1\"],\n    [\"#b6-r5-i2\"],\n    [\"#b6-r5-i3\"],\n\n    [\"#b7-r1-i1\"],\n    [\"#b7-r1-i2\"],\n    [\"#b7-r1-i3\"],\n    [\"#b7-r1-i4\"],\n    [\"#b7-r2-i1\"],\n    [\"#b7-r2-i2\"],\n    [\"#b7-r2-i3\"],\n    [\"#b7-r2-i4\"],\n    [\"#b7-r3-i1\"],\n    [\"#b7-r3-i2\"],\n    [\"#b7-r3-i3\"],\n    [\"#b7-r3-i4\"],\n    [\"#b7-r4-i1\"],\n    [\"#b7-r4-i2\"],\n    [\"#b7-r4-i3\"],\n    [\"#b7-r5-i1\"],\n    [\"#b7-r5-i2\"],\n    [\"#b7-r5-i3\"],\n\n    [\"#b8-r1-i1\"],\n    [\"#b8-r1-i2\"],\n    [\"#b8-r1-i3\"],\n    [\"#b8-r5-i1\"],\n    [\"#b8-r5-i2\"],\n    [\"#b8-r5-i3\"],\n\n    [\"#b9-f4-i1\"],\n    [\"#b9-f4-i2\"],\n    [\"#b9-f4-i3\"],\n    [\"#b9-f4-i4\"],\n    [\"#b9-f5-i1\"],\n    [\"#b9-f5-i2\"],\n    [\"#b9-f5-i3\"],\n    [\"#b9-f5-i4\"],\n\n    [\"#b10-f1-i1\"],\n    [\"#b10-f1-i2\"],\n    [\"#b10-f1-i3\"],\n    [\"#b10-f2-i1\"],\n    [\"#b10-f2-i2\"],\n    [\"#b10-f2-i3\"],\n    [\"#b10-f3-i1\"],\n    [\"#b10-f3-i2\"],\n    [\"#b10-f3-i3\"],\n    [\"#b10-f4-i1\"],\n    [\"#b10-f4-i2\"],\n    [\"#b10-f4-i3\"],\n    [\"#b10-f5-i1\"],\n    [\"#b10-f5-i2\"],\n    [\"#b10-f5-i3\"],\n\n    [\"#b11-f1-i4\"],\n    [\"#b11-f2-i4\"],\n    [\"#b11-f3-i4\"],\n    [\"#b11-f4-i3\"],\n    [\"#b11-f4-i4\"],\n    [\"#b11-f4-i1\"],\n    [\"#b11-f4-i2\"],\n    [\"#b11-f5-i3\"],\n    [\"#b11-f5-i4\"],\n    [\"#b11-f5-i1\"],\n    [\"#b11-f5-i2\"],\n\n    [\"#b12-l1-i2\"],\n    [\"#b12-l1-i3\"],\n    [\"#b12-l1-i4\"],\n    [\"#b12-l2-i2\"],\n    [\"#b12-l2-i3\"],\n    [\"#b12-l2-i4\"],\n    [\"#b12-l3-i2\"],\n    [\"#b12-l3-i3\"],\n    [\"#b12-l3-i4\"],\n    [\"#b12-l4-i3\"],\n    [\"#b12-l4-i4\"],\n    [\"#b12-l5-i4\"],\n\n    [\"#b13-f1-i1\"],\n    [\"#b13-f1-i2\"],\n    [\"#b13-f2-i1\"],\n    [\"#b13-f2-i2\"],\n    [\"#b13-f3-i1\"],\n    [\"#b13-f3-i2\"],\n    [\"#b13-f4-i1\"],\n    [\"#b13-f4-i2\"],\n    [\"#b13-f4-i3\"],\n    [\"#b13-f4-i4\"],\n    [\"#b13-f5-i1\"],\n    [\"#b13-f5-i2\"],\n    [\"#b13-f5-i3\"],\n    [\"#b13-f5-i4\"],\n\n    [\"#b14-f4-i3\"],\n    [\"#b14-f4-i4\"],\n    [\"#b14-f5-i3\"],\n    [\"#b14-f5-i4\"],\n\n    [\"#b15-x1-i2\"],\n    [\"#b15-x1-i3\"],\n    [\"#b15-x1-i4\"],\n    [\"#b15-x5-i2\"],\n    [\"#b15-x5-i3\"],\n    [\"#b15-x5-i4\"]\n  ],\n  \"incomplete\": [\n    [\"#b14-f1-i1\"],\n    [\"#b14-f2-i1\"],\n    [\"#b14-f3-i1\"],\n    [\"#b14-f4-i1\"],\n    [\"#b14-f5-i1\"],\n    [\"#b15-x1-i1\"],\n    [\"#b15-x2-i1\"],\n    [\"#b15-x3-i1\"],\n    [\"#b15-x4-i1\"],\n    [\"#b15-x5-i1\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/color-contrast-enhanced/color-contrast-enhanced.html",
    "content": "<div id=\"pass1\" style=\"background-color: white; color: black\">\n  This is a pass.\n</div>\n\n<div id=\"fail1\" style=\"background-color: #666; color: white; font-size: 20px\">\n  This is a fail. <b id=\"pass2\">But this is a pass.</b>\n</div>\n\n<p style=\"color: #666; -webkit-text-stroke: 0.03em #444\" id=\"pass3\">\n  Text-stroke 0.03em\n</p>\n<p style=\"color: #666; -webkit-text-stroke: 0.02em #444\" id=\"fail2\">\n  Text-stroke 0.02em\n</p>\n"
  },
  {
    "path": "test/integration/rules/color-contrast-enhanced/color-contrast-enhanced.json",
    "content": "{\n  \"description\": \"color-contrast test\",\n  \"rule\": \"color-contrast-enhanced\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/definition-list/definition-list.html",
    "content": "<dl id=\"emptydl\"></dl>\n<dl id=\"divdl\">\n  <div>This shouldn't be here.</div>\n</dl>\n<dl id=\"mixeddl\">\n  <dt>Correct.</dt>\n  <dd>Correct.</dd>\n  <div>Wrong.</div>\n</dl>\n<dl id=\"repeatdl\">\n  <dt>Thing 1</dt>\n  <dd>Thingy 1</dd>\n  <dt>Thing</dt>\n  <dd>Thingy</dd>\n  <dd>Thingy 2</dd>\n</dl>\n<dl id=\"properdl\">\n  <dt>Thing</dt>\n  <dd>Thingy</dd>\n</dl>\n<dl id=\"unordereddl\">\n  <dd>Thingy</dd>\n  <dt>Thing</dt>\n</dl>\n<dl id=\"scriptdl\">\n  <script></script>\n  <template> </template>\n  <dt>thang</dt>\n  <dd>thangy</dd>\n</dl>\n<dl id=\"emptyscriptdl\">\n  <script></script>\n  <template> </template>\n</dl>\n<dl id=\"mixedscriptdl\">\n  <script></script>\n  <template> </template>\n  <p>para</p>\n</dl>\n<dl id=\"ddrolechanged\">\n  <dd role=\"menuitem\">Thingy</dd>\n  <dt>Thing</dt>\n</dl>\n<dl id=\"dtrolechanged\">\n  <dd>Thingy</dd>\n  <dt role=\"menuitem\">Thing</dt>\n</dl>\n"
  },
  {
    "path": "test/integration/rules/definition-list/definition-list.json",
    "content": "{\n  \"description\": \"definition-list test\",\n  \"rule\": \"definition-list\",\n  \"violations\": [\n    [\"#divdl\"],\n    [\"#mixeddl\"],\n    [\"#unordereddl\"],\n    [\"#mixedscriptdl\"],\n    [\"#ddrolechanged\"],\n    [\"#dtrolechanged\"]\n  ],\n  \"passes\": [\n    [\"#emptydl\"],\n    [\"#repeatdl\"],\n    [\"#properdl\"],\n    [\"#scriptdl\"],\n    [\"#emptyscriptdl\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/dlitem/dlitem.html",
    "content": "<dd id=\"uncontained\">Should belong to a list.</dd>\n<dt id=\"alsouncontained\">Should belong to a list.</dt>\n<dl>\n  <dt id=\"contained\">Does belong to a list.</dt>\n  <dd id=\"alsocontained\">Also belongs to a list.</dd>\n  <div>\n    <dt id=\"grouped\">Grouped in a div.</dt>\n    <dd id=\"alsogrouped\">Also grouped in a div.</dd>\n  </div>\n  <div role=\"main\">\n    <dt id=\"incorrectlygrouped\">Grouped in a div with role.</dt>\n    <dd id=\"alsoincorrectlygrouped\">Also grouped in a div with role.</dd>\n  </div>\n</dl>\n<dl role=\"menubar\">\n  <dt id=\"uncontainedbyrole\">Not part of a list.</dt>\n  <dd id=\"alsouncontainedbyrole\">Also not part of a list.</dd>\n</dl>\n"
  },
  {
    "path": "test/integration/rules/dlitem/dlitem.json",
    "content": "{\n  \"description\": \"dlitem test\",\n  \"rule\": \"dlitem\",\n  \"violations\": [\n    [\"#uncontained\"],\n    [\"#alsouncontained\"],\n    [\"#uncontainedbyrole\"],\n    [\"#alsouncontainedbyrole\"],\n    [\"#incorrectlygrouped\"],\n    [\"#alsoincorrectlygrouped\"]\n  ],\n  \"passes\": [[\"#contained\"], [\"#alsocontained\"], [\"#grouped\"], [\"#alsogrouped\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/duplicate-id/duplicate-id.html",
    "content": "<p id=\"fail1\" class=\"fail1\">This is my first paragraph with this ID.</p>\n<div style=\"display: none\">\n  <p id=\"fail1\">This is my second paragraph with this ID.</p>\n</div>\n\n<input id=\"ignored1\" />\n<textarea id=\"ignored2\"></textarea>\n<select id=\"ignored3\"></select>\n<div tabindex=\"0\" id=\"ignored4\"></div>\n\n<span id=\"ignored5\"></span>\n<button id=\"ignored6\"></button>\n<div aria-labelledby=\"ignored5 ignored6\"></div>\n\n<input id=\"pass1\" type=\"hidden\" />\n<button id=\"pass2\" disabled></button>\n<button id=\"pass3\" style=\"display: none\"></button>\n"
  },
  {
    "path": "test/integration/rules/duplicate-id/duplicate-id.json",
    "content": "{\n  \"description\": \"duplicate-id test\",\n  \"rule\": \"duplicate-id\",\n  \"violations\": [[\".fail1\"]],\n  \"passes\": [[\"#fixture\"], [\"#pass1\"], [\"#pass2\"], [\"#pass3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/duplicate-id-active/duplicate-id-active.html",
    "content": "<p id=\"ignore1\">This is my first paragraph with this ID.</p>\n<div style=\"display: none\">\n  <p id=\"ignore1\">This is my second paragraph with this ID.</p>\n</div>\n\n<button id=\"fail1\" class=\"fail1\"></button>\n<button id=\"fail1\"></button>\n<input id=\"fail2\" class=\"fail2\" />\n<input id=\"fail2\" />\n<textarea id=\"fail3\" class=\"fail3\"></textarea>\n<textarea id=\"fail3\"></textarea>\n<select id=\"fail4\" class=\"fail4\"></select>\n<select id=\"fail4\"></select>\n<div tabindex=\"0\" id=\"fail5\" class=\"fail5\"></div>\n<div tabindex=\"0\" id=\"fail5\"></div>\n\n<input id=\"pass1\" />\n<textarea id=\"pass2\"></textarea>\n<select id=\"pass3\"></select>\n<div tabindex=\"0\" id=\"pass4\"></div>\n\n<span id=\"ignored1\"></span>\n<button id=\"ignored2\"></button>\n<div aria-labelledby=\"ignored1 ignored2\"></div>\n<input id=\"ignore3\" type=\"hidden\" />\n<button id=\"ignore4\" disabled></button>\n<button id=\"ignore5\" style=\"display: none\"></button>\n"
  },
  {
    "path": "test/integration/rules/duplicate-id-active/duplicate-id-active.json",
    "content": "{\n  \"description\": \"duplicate-id-active test\",\n  \"rule\": \"duplicate-id-active\",\n  \"violations\": [[\".fail1\"], [\".fail2\"], [\".fail3\"], [\".fail4\"], [\".fail5\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/duplicate-id-aria/duplicate-id-aria.html",
    "content": "<p id=\"ignore1\">This is my first paragraph with this ID.</p>\n<div style=\"display: none\">\n  <p id=\"ignore1\">This is my second paragraph with this ID.</p>\n</div>\n\n<input id=\"ignore2\" />\n<textarea id=\"ignore3\"></textarea>\n<select id=\"ignore4\"></select>\n<div tabindex=\"0\" id=\"ignore5\"></div>\n\n<span id=\"incomplete1\" class=\"incomplete1\"></span>\n<button id=\"incomplete1\"></button>\n<span id=\"pass1\"></span>\n<button id=\"pass2\"></button>\n<div aria-labelledby=\"incomplete1 pass1 pass2\"></div>\n\n<input id=\"ignore6\" type=\"hidden\" />\n<button id=\"ignore7\" disabled></button>\n<button id=\"ignore8\" style=\"display: none\"></button>\n"
  },
  {
    "path": "test/integration/rules/duplicate-id-aria/duplicate-id-aria.json",
    "content": "{\n  \"description\": \"duplicate-id-aria test\",\n  \"rule\": \"duplicate-id-aria\",\n  \"violations\": [],\n  \"incomplete\": [[\".incomplete1\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/empty-heading/empty-heading.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>empty-heading test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <h1 id=\"ignore1\" role=\"banner\">Meh</h1>\n\n    <h1 id=\"pass1\">Ok</h1>\n    <h2 id=\"pass2\">Ok</h2>\n    <h3 id=\"pass3\">Ok</h3>\n    <h4 id=\"pass4\">Ok</h4>\n    <h5 id=\"pass5\">Ok</h5>\n    <h6 id=\"pass6\">Ok</h6>\n    <div id=\"pass7\" role=\"heading\">Ok</div>\n    <h1 id=\"fail1\"></h1>\n    <h1 id=\"fail2\"><div style=\"display: none\">Not Ok</div></h1>\n    <h1 id=\"fail3\"></h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/empty-heading/empty-heading.json",
    "content": "{\n  \"description\": \"empty-heading test\",\n  \"rule\": \"empty-heading\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/empty-table-header/empty-table-header.html",
    "content": "<html lang=\"en\">\n  <head>\n    <title>empty-table-header test</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n\n  <body>\n    <table>\n      <tr>\n        <th id=\"pass1\">Ok</th>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <td id=\"pass2\" role=\"rowheader\">rowheader with name</td>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <td id=\"pass3\" role=\"columnheader\">columnheader with name</td>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <th id=\"fail1\"></th>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <td id=\"fail2\" role=\"rowheader\">\n          <div style=\"display: none\">Not Ok</div>\n        </td>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <td id=\"fail3\" role=\"columnheader\">\n          <div style=\"display: none\">Not Ok</div>\n        </td>\n      </tr>\n    </table>\n\n    <table>\n      <tr>\n        <th id=\"ignore1\" role=\"spinbutton\">rowheader with a role</th>\n      </tr>\n    </table>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/empty-table-header/empty-table-header.json",
    "content": "{\n  \"description\": \"empty-table-header tests\",\n  \"rule\": \"empty-table-header\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/focus-order-semantics/focus-order-semantics.html",
    "content": "<h2>Elements placed in the tab order with tabindex</h2>\n<div>\n  <h3>Non-interactive elements</h3>\n  <div>\n    <span id=\"violation1\" tabindex=\"0\">Shouldn't be in the focus order</span>\n    <div id=\"violation2\" tabindex=\"1\">Shouldn't be in the focus order</div>\n  </div>\n\n  <h3>Elements placed in focus order and given valid interactive roles</h3>\n  <div>\n    <div id=\"pass1\" role=\"listbox\" tabindex=\"0\"></div>\n    <ol role=\"radiogroup\" tabindex=\"0\"></ol>\n    <a role=\"button\" tabindex=\"0\">Click me, I'm a button!</a>\n  </div>\n\n  <h3>Scrollable elements</h3>\n  <div>\n    <h4>Invalid landmark roles for scrollable containers</h4>\n    <div>\n      <div id=\"violation3\" role=\"banner\" tabindex=\"0\"></div>\n      <div id=\"violation4\" role=\"search\" tabindex=\"0\"></div>\n      <div id=\"violation5\" role=\"marquee\" tabindex=\"0\"></div>\n      <div id=\"violation6\" role=\"timer\" tabindex=\"0\"></div>\n    </div>\n    <h4>Valid landmark roles for scrollable containers</h4>\n    <div>\n      <div id=\"pass2\" role=\"form\" tabindex=\"0\"></div>\n      <div id=\"pass3\" role=\"navigation\" tabindex=\"0\"></div>\n      <div id=\"pass4\" role=\"complementary\" tabindex=\"0\"></div>\n      <div id=\"pass5\" role=\"contentinfo\" tabindex=\"0\"></div>\n      <div id=\"pass6\" role=\"main\" tabindex=\"0\"></div>\n      <div id=\"pass7\" role=\"region\" tabindex=\"0\"></div>\n      <div id=\"pass8\" role=\"application\" tabindex=\"0\"></div>\n      <div id=\"pass9\" role=\"tooltip\" tabindex=\"0\"></div>\n      <div id=\"pass10\" role=\"article\" tabindex=\"0\"></div>\n      <div id=\"pass11\" role=\"alert\" tabindex=\"0\"></div>\n      <div id=\"pass12\" role=\"log\" tabindex=\"0\"></div>\n      <div id=\"pass13\" role=\"status\" tabindex=\"0\"></div>\n      <div id=\"pass14\" role=\"tabpanel\" tabindex=\"0\"></div>\n    </div>\n    <h4>Valid window roles for scrollable containers</h4>\n    <div>\n      <div id=\"pass15\" role=\"alertdialog\" tabindex=\"0\"></div>\n      <div id=\"pass16\" role=\"dialog\" tabindex=\"0\"></div>\n    </div>\n    <h4>\n      Valid scrollable HTML tags for scrollable regions, not selected by this\n      rule\n    </h4>\n    <div>\n      <nav tabindex=\"0\"></nav>\n      <section tabindex=\"0\"></section>\n      <article tabindex=\"0\"></article>\n      <aside tabindex=\"0\"></aside>\n    </div>\n  </div>\n</div>\n"
  },
  {
    "path": "test/integration/rules/focus-order-semantics/focus-order-semantics.json",
    "content": "{\n  \"rule\": \"focus-order-semantics\",\n  \"description\": \"focus-order-semantics\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"]\n  ],\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/form-field-multiple-labels/form-field-multiple-labels.html",
    "content": "<!-- incomplete -->\n<label for=\"incomplete1\">Hi</label>\n<label for=\"incomplete1\">Foo</label>\n<input type=\"text\" id=\"incomplete1\" />\n\n<label for=\"incomplete2\">label one</label>\n<label for=\"incomplete2\">label two</label>\n<input type=\"checkbox\" id=\"incomplete2\" />\n\n<label for=\"incomplete3\" id=\"l1\">label one</label>\n<label for=\"incomplete3\">label two</label>\n<input type=\"checkbox\" id=\"incomplete3\" aria-labelledby=\"l1\" />\n\n<label for=\"incomplete4\">First Name:</label>\n<label\n  >First Name:\n  <input type=\"text\" id=\"incomplete4\" />\n</label>\n\n<label for=\"incomplete5\">Choose an option:</label>\n<label>\n  Choose an option:\n  <select id=\"incomplete5\">\n    <option selected=\"selected\">Chosen</option>\n    <option>Not Selected</option>\n  </select>\n</label>\n\n<label for=\"incomplete6\">Enter your comments:</label>\n<label>\n  Enter your comments:\n  <textarea id=\"incomplete6\"></textarea>\n</label>\n\n<label>\n  Enter your comments:\n  <label>\n    Enter your comments:\n    <textarea id=\"incomplete7\"></textarea>\n  </label>\n</label>\n\n<label>\n  Enter your comments:\n  <label>\n    Enter your comments:\n    <label>\n      Enter your comments:\n      <textarea id=\"incomplete8\"></textarea>\n    </label>\n  </label>\n</label>\n\n<label for=\"incomplete9\">Enter your comments:</label>\n<label>\n  Enter your comments:\n  <label>\n    Enter your comments:\n    <label>\n      Enter your comments:\n      <textarea id=\"incomplete9\"></textarea>\n    </label>\n  </label>\n</label>\n\n<!-- Pass -->\n<label for=\"pass1\">Label</label>\n<input type=\"text\" id=\"pass1\" />\n\n<textarea id=\"pass2\" title=\"Label\"></textarea>\n\n<label\n  >First Name:\n  <input type=\"text\" id=\"pass3\" />\n</label>\n\n<label>\n  Choose an option:\n  <select id=\"pass4\">\n    <option selected=\"selected\">Chosen</option>\n    <option>Not Selected</option>\n  </select>\n</label>\n\n<label>\n  Enter your comments:\n  <textarea id=\"pass5\"></textarea>\n</label>\n"
  },
  {
    "path": "test/integration/rules/form-field-multiple-labels/form-field-multiple-labels.json",
    "content": "{\n  \"description\": \"form-field-multiple-labels label test\",\n  \"rule\": \"form-field-multiple-labels\",\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"],\n    [\"#incomplete9\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/frame-focusable-content/frame-focusable-content.html",
    "content": "<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/not-focusable.html\"\n  tabindex=\"-1\"\n  id=\"pass1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable-negative-tabindex.html\"\n  tabindex=\"-1\"\n  id=\"pass2\"\n></iframe>\n\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  tabindex=\"-1\"\n  id=\"fail1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  tabindex=\"-1\"\n  width=\"2\"\n  height=\"1\"\n  id=\"fail2\"\n></iframe>\n\n<!-- inapplicable -->\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  id=\"inapplicable-1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  tabindex=\"0\"\n  id=\"inapplicable-2\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  tabindex=\"-1\"\n  width=\"1\"\n  height=\"1\"\n  id=\"inapplicable-3\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  tabindex=\"-1\"\n  width=\"0\"\n  height=\"0\"\n  id=\"inapplicable-3\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-focusable-content/frames/focusable.html\"\n  inert\n  id=\"inapplicable-4\"\n></iframe>\n"
  },
  {
    "path": "test/integration/rules/frame-focusable-content/frame-focusable-content.json",
    "content": "{\n  \"description\": \"frame-focusable-content tests\",\n  \"rule\": \"frame-focusable-content\",\n  \"violations\": [\n    [\"#fail1\", \"#focusable\"],\n    [\"#fail2\", \"#focusable\"]\n  ],\n  \"passes\": [\n    [\"#pass1\", \"#not-focusable\"],\n    [\"#pass2\", \"#focusable\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/frame-focusable-content/frames/focusable-negative-tabindex.html",
    "content": "<!doctype html>\n<html id=\"focusable\">\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <button tabindex=\"-1\">Click</button>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-focusable-content/frames/focusable.html",
    "content": "<!doctype html>\n<html id=\"focusable\">\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <button>Click</button>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-focusable-content/frames/not-focusable.html",
    "content": "<!doctype html>\n<html id=\"not-focusable\">\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>Hello</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title/frame-title.html",
    "content": "<iframe\n  src=\"/integration/rules/frame-title/frames/level1.html\"\n  id=\"violation1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  title=\"Duplicated\"\n  id=\"pass2\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  title=\"Duplicated\"\n  id=\"pass3\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  aria-label=\"some label\"\n  id=\"pass4\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  aria-label=\"\"\n  id=\"violation3\"\n></iframe>\n\n<div id=\"mylabel\">label!</div>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  aria-labelledby=\"mylabel\"\n  id=\"pass5\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"pass6\"\n  role=\"presentation\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"pass7\"\n  role=\"none\"\n></iframe>\n\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation4\"\n  role=\"none\"\n  aria-live=\"assertive\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation5\"\n  role=\"presentation\"\n  aria-live=\"assertive\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation6\"\n  role=\"none\"\n  tabindex=\"0\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation7\"\n  role=\"presentation\"\n  tabindex=\"0\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation8\"\n  role=\"none\"\n  title=\" \"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"violation9\"\n  role=\"presentation\"\n  title=\"\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title/frames/level1a.html\"\n  id=\"inapplicable1\"\n  tabindex=\"-1\"\n></iframe>\n"
  },
  {
    "path": "test/integration/rules/frame-title/frame-title.json",
    "content": "{\n  \"description\": \"frame-title test\",\n  \"rule\": \"frame-title\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation1\", \"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation8\"],\n    [\"#violation9\"]\n  ],\n  \"passes\": [\n    [\"#violation1\", \"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/frame-title/frames/level1.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"level2.html\" title=\"\" id=\"violation2\"></iframe>\n    <iframe src=\"level2-a.html\" title=\"hello\" id=\"pass1\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title/frames/level1a.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Duplicated</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>Hi</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title/frames/level2-a.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title/frames/level2.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frame-title-unique.html",
    "content": "<iframe\n  src=\"/integration/rules/frame-title-unique/frames/level1.html\"\n  id=\"ignore1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title-unique/frames/level1a.html\"\n  title=\"Duplicated\"\n  id=\"incomplete1\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title-unique/frames/level1a.html\"\n  title=\"Duplicated\"\n  id=\"incomplete2\"\n></iframe>\n<iframe\n  src=\"/integration/rules/frame-title-unique/frames/level1a.html\"\n  title=\"I'm an individual\"\n  id=\"pass2\"\n></iframe>\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frame-title-unique.json",
    "content": "{\n  \"description\": \"frame-title-unique test\",\n  \"rule\": \"frame-title-unique\",\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]],\n  \"passes\": [[\"#ignore1\", \"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frames/level1.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <iframe src=\"level2.html\" title=\"  \" id=\"ignore2\"></iframe>\n    <iframe src=\"level2-a.html\" title=\"hello\" id=\"pass1\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frames/level1a.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Duplicated</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <p>Hi</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frames/level2-a.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Hello</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/frame-title-unique/frames/level2.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body></body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/heading-order/heading-order.html",
    "content": "<h1 id=\"heading1\">Header</h1>\n<h1 id=\"heading2\">Header</h1>\n<h2 id=\"heading3\">Header</h2>\n<h3 id=\"heading4\">Header</h3>\n<h1 id=\"heading5\">Header</h1>\n<h3 id=\"heading6\">Header</h3>\n<h3 id=\"heading7\">Header</h3>\n\n<h6 role=\"banner\">Ignore</h6>\n"
  },
  {
    "path": "test/integration/rules/heading-order/heading-order.json",
    "content": "{\n  \"description\": \"heading-order test\",\n  \"rule\": \"heading-order\",\n  \"violations\": [[\"#heading6\"]],\n  \"passes\": [\n    [\"#heading1\"],\n    [\"#heading2\"],\n    [\"#heading3\"],\n    [\"#heading4\"],\n    [\"#heading5\"],\n    [\"#heading7\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/hidden-content/hidden-content.html",
    "content": "<div id=\"canttell1\" style=\"display: none\">\n  <p id=\"pass1\">Some paragraph text.</p>\n</div>\n<div id=\"canttell2\" style=\"visibility: hidden\">\n  <p id=\"pass2\">Some paragraph text.</p>\n</div>\n<span id=\"pass3\" aria-hidden=\"true\"></span>\n<template id=\"pass4\">Hello, world.</template>\n<script id=\"pass5\" src=\"some.js\">\n  var noop = function () { return null; }\n</script>\n"
  },
  {
    "path": "test/integration/rules/hidden-content/hidden-content.json",
    "content": "{\n  \"description\": \"hidden-content test\",\n  \"rule\": \"hidden-content\",\n  \"incomplete\": [[\"#canttell1\"], [\"#canttell2\"]],\n  \"passes\": [\n    [\"#fixture\"],\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/identical-links-same-purpose/identical-links-same-purpose.html",
    "content": "<!-- pass -->\n<!-- native links with identical purposes -->\n<a id=\"pass1\" href=\"/home/#/foo\">Pass 1</a>\n<a id=\"pass1-identical1\" href=\"/home/#/foo\">Pass 1</a>\n\n<!-- native links with same (different order) query params -->\n<a id=\"pass2\" href=\"/search/?a=foo&b=123\">Pass 2</a>\n<a id=\"pass2-identical1\" href=\"/search/?b=123&a=foo\">Pass 2</a>\n\n<!-- native links, normal inline link, don't worry about it -->\n<a id=\"pass3\" href=\"/home/#foo\">Pass 3</a>\n<a id=\"pass3-identical1\" href=\"/home/#\">Pass 3</a>\n\n<!-- native links, with identical hash -->\n<a id=\"pass4\" href=\"/home/#!foo\">Pass 4</a>\n<a id=\"pass4-identical1\" href=\"/home/#!foo\">Pass 4</a>\n\n<!-- native links, hash-bange URL is the same -->\n<a id=\"pass5\" href=\"/home/#!/foo\">Pass 5</a>\n<a id=\"pass5-identical1\" href=\"/home/#!/foo\">Pass 5</a>\n\n<!-- native links, same query -->\n<a id=\"pass6\" href=\"/search/?q=foo#bar\">Pass 6</a>\n<a id=\"pass6-identical1\" href=\"/search/?q=foo#foo\">Pass 6</a>\n\n<!-- native links, ignore trailing slashes -->\n<a id=\"pass7\" href=\"/home\">Pass 7</a>\n<a id=\"pass7-identical1\" href=\"/home/\">Pass 7</a>\n\n<!-- native links, special-case for an index.html file -->\n<a id=\"pass8\" href=\"/home/\">Pass 8</a>\n<a id=\"pass8-identical1\" href=\"/home/index.html\">Pass 8</a>\n\n<!-- native links, index.*** file, should exclude any index.[php,html,etc] file -->\n<a id=\"pass9\" href=\"/home/\">Pass 9</a>\n<a id=\"pass9-identical1\" href=\"/home/index.foo\">Pass 9</a>\n\n<!-- native links, same resource, different hash within same page -->\n<a id=\"pass10\" href=\"/home\">Pass 10</a>\n<a id=\"pass10-identical1\" href=\"/home#foo\">Pass 10</a>\n\n<!-- native links, with different accessible name -->\n<a id=\"pass11\" href=\"/home\">Pass 11</a>\n<a id=\"pass11-identical-resource-but-different-name\" href=\"/home\"\n  >Pass 11, but different accessible name</a\n>\n\n<!-- ARIA links, with different accessible name -->\n<button id=\"pass12\" role=\"link\">Pass 12</button>\n<button id=\"pass12-identical-resource-but-different-name\" role=\"link\">\n  Pass 12, but different accessible name\n</button>\n\n<!-- area[href], with parent map, which is referred by img[usemap], with different accessible name -->\n<map name=\"shapes-map\">\n  <area\n    id=\"pass13\"\n    shape=\"rect\"\n    aria-label=\"Pass 13\"\n    coords=\"19,28,222,228\"\n    href=\"foo\"\n  />\n  <area\n    id=\"pass13-identical-resource-but-different-name\"\n    shape=\"circle\"\n    aria-label=\"Pass 13, but different accessible name\"\n    coords=\"361,132,96\"\n    href=\"foo\"\n  />\n</map>\n<img src=\"shape.png\" usemap=\"#shapes-map\" alt=\"\" />\n\n<!-- AREA with same accessible name, and serve same purpose -->\n<map name=\"infographic-pass\">\n  <area\n    id=\"pass14\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,254,219,130,228\"\n    href=\"https://developer.mozilla.org/docs/Web/HTML\"\n    title=\"HTML Logo\"\n  />\n  <area\n    id=\"pass14-identical1\"\n    shape=\"poly\"\n    coords=\"130,147,130,228,6,219,59,107\"\n    href=\"https://developer.mozilla.org/docs/Web/HTML\"\n    target=\"_blank\"\n    aria-label=\"HTML Logo\"\n  />\n</map>\n<img\n  usemap=\"#infographic-pass\"\n  src=\"https://interactive-examples.mdn.mozilla.net/media/examples/mdn-info2.png\"\n  alt=\"MDN infographic\"\n/>\n\n<!-- native links, same resource (one specified as relative and another as absolute) -->\n<!-- Note: using the hostname and protocol here as need to represent relative and absolute url -->\n<a id=\"pass15\" href=\"/home\">Pass 15</a>\n<a id=\"pass15-identical1\" href=\"http://localhost:9876/home\">Pass 15</a>\n\n<!-- ARIA links, with same resource, where one inside svg document -->\n<a id=\"pass16\" href=\"http://deque.com\">Pass 16</a>\n<svg\n  viewBox=\"0 0 100 100\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n>\n  <a id=\"pass16-identical1\" href=\"http://deque.com\" aria-label=\"Pass 16\">\n    <circle cx=\"50\" cy=\"40\" r=\"35\" />\n  </a>\n</svg>\n\n<!-- ARIA links, with same resource, both are inside svg document -->\n<svg\n  viewBox=\"0 0 100 100\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n>\n  <a href=\"http://deque.com\" aria-label=\"Pass 17\" id=\"pass17\">\n    <circle cx=\"50\" cy=\"40\" r=\"35\" />\n  </a>\n  <a href=\"http://deque.com\" id=\"pass17-identical1\">\n    <text x=\"50\" y=\"90\" text-anchor=\"middle\">Pass 17</text>\n  </a>\n</svg>\n\n<!-- native links with identical name and empty resource (href) -->\n<a id=\"pass18\" href=\"\">Pass 18</a>\n<a id=\"pass18-identical1\" href=\"\">Pass 18</a>\n\n<!-- incomplete -->\n<!-- Note:identical incomplete nodes are appended as relatedNodes -->\n<!-- native links with different purpose -->\n<a id=\"incomplete1\" href=\"/home/#/bar\">Incomplete 1</a>\n<a id=\"incomplete1-identical1\" href=\"/home/#/\">Incomplete 1</a>\n\n<!-- native links with different query params -->\n<a id=\"incomplete2\" href=\"/search/?q=foo\">Incomplete 2</a>\n<a id=\"incomplete2-identical1\" href=\"/search/?q=bar\">Incomplete 2</a>\n\n<!-- native links with different `shebang` -->\n<a id=\"incomplete3\" href=\"/search/?q=foo#!/foo\">Incomplete 3</a>\n<a id=\"incomplete3-identical1\" href=\"/search/?q=foo#!/bar\">Incomplete 3</a>\n\n<!-- ARIA links with no href but same accessible name -->\n<button id=\"incomplete4\" role=\"link\">Incomplete 4</button>\n<div id=\"incomplete4-identical1\" role=\"link\">Incomplete 4</div>\n\n<!-- native and aria links with different resource -->\n<button id=\"incomplete5\" role=\"link\">Incomplete 6</button>\n<a id=\"incomplete5-identical1\" href=\"foo.html\">Incomplete 6</a>\n\n<!-- native links same accesisble names not serving same purpose (casing is normalised) -->\n<a id=\"incomplete6\" href=\"/home/#/foo\">INCOMPLETE 7</a>\n<a id=\"incomplete6-identical1\" href=\"/home/#/bar\">Incomplete 7</a>\n\n<!-- native links same accessible name not serving same purpose (whitespace is normalised) -->\n<a id=\"incomplete7\" href=\"/home/#/foo\"> Incomplete 8 </a>\n<a id=\"incomplete7-identical1\" href=\"/home/#/bar\">Incomplete 8</a>\n\n<!-- native links with same accessible name and not serving same purpose (punctuations is normalised) -->\n<a id=\"incomplete8\" href=\"/foo\">Incomplete 9 >>></a>\n<a id=\"incomplete8-identical1\" href=\"/bar\">Incomplete 9</a>\n\n<!-- native links with same accessible name and not serving same purpose (punctuations is normalised) -->\n<a id=\"incomplete9\" href=\"/foo\">Incomplete 10!</a>\n<a id=\"incomplete9-identical1\" href=\"/bar\">Incomplete 10</a>\n\n<!-- native links with same accessible name and not serving same purpose (emoji is normalised) -->\n<a id=\"incomplete10\" href=\"/foo\">Incomplete 11!</a>\n<a id=\"incomplete10-identical1\" href=\"/bar\">🤘 Incomplete 11 🤘</a>\n\n<!-- native links with same accessible name but different resource  -->\n<a id=\"incomplete11\" href=\"/search/?q=foo#/foo\">Incomplete 12</a>\n<a id=\"incomplete11-identical1\" href=\"/search/?q=foo#/bar\">Incomplete 12</a>\n\n<!-- AREA with same accessible name, but different resource -->\n<map name=\"infographic-incomplete\">\n  <area\n    id=\"incomplete12\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,254,219,130,228\"\n    href=\"https://developer.mozilla.org/docs/Web/HTML\"\n    title=\"HTML\"\n  />\n  <area\n    id=\"incomplete12-identical1\"\n    shape=\"poly\"\n    coords=\"130,147,130,228,6,219,59,107\"\n    href=\"https://developer.mozilla.org/docs/Web/CSS\"\n    target=\"_blank\"\n    title=\"HTML\"\n  />\n  <area\n    id=\"incomplete12-identical2\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,130,4,59,107\"\n    href=\"https://developer.mozilla.org/docs/Web/JavaScript\"\n    target=\"_blank\"\n    aria-label=\"HTML\"\n  />\n</map>\n<img\n  usemap=\"#infographic-incomplete\"\n  src=\"https://interactive-examples.mdn.mozilla.net/media/examples/mdn-info2.png\"\n  alt=\"MDN infographic\"\n/>\n\n<!-- native links with different purpose -->\n<a id=\"incomplete13\" href=\"https://facebook.com\">follow us</a>\n<a id=\"incomplete13-identical1\" href=\"https://instagram.com\">follow us</a>\n\n<!-- multiple native links with differnt URLS with same filename but different pathname -->\n<a id=\"incomplete14\" href=\"/test/integration/full/directory-one/foo.html\"\n  >Incomplete 14</a\n>\n<a id=\"incomplete14-identical1\" href=\"foo.html\">Incomplete 14</a>\n<a id=\"incomplete14-identical2\" href=\"../directory-two/foo.html\"\n  >Incomplete 14</a\n>\n\n<!-- inapplicable -->\n<!-- area element has no parent map -->\n<area\n  id=\"inapplicable1\"\n  shape=\"poly\"\n  coords=\"130,147,200,107,254,219,130,228\"\n  href=\"https://developer.mozilla.org/docs/Web/HTML\"\n  title=\"Inapplicable AREA\"\n/>\n\n<!-- area has map parent, which has no name -->\n<map>\n  <area\n    id=\"inapplicable2\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,254,219,130,228\"\n    href=\"https://developer.mozilla.org/docs/Web/HTML\"\n    title=\"Inapplicable AREA\"\n  />\n</map>\n\n<!-- area has map parent, which has name, but not referred by img[usemap] -->\n<map name=\"iam-not-used-anywhere\">\n  <area\n    id=\"inapplicable3\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,254,219,130,228\"\n    href=\"https://developer.mozilla.org/docs/Web/HTML\"\n    title=\"Inapplicable AREA\"\n  />\n</map>\n\n<!-- native link with no href attribute -->\n<a id=\"inapplicable4\">Inapplicable 4</a>\n\n<!-- AREA with map, referred by img, has no href-->\n<map name=\"infographic-inapplicable5\">\n  <area\n    id=\"inapplicable5\"\n    shape=\"poly\"\n    coords=\"130,147,200,107,254,219,130,228\"\n    title=\"Inapplicable HTML Logo\"\n  />\n</map>\n<img usemap=\"#infographic-inapplicable5\" alt=\"MDN infographic\" />\n"
  },
  {
    "path": "test/integration/rules/identical-links-same-purpose/identical-links-same-purpose.json",
    "content": "{\n  \"description\": \"identical-links-same-purpose tests\",\n  \"rule\": \"identical-links-same-purpose\",\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass11-identical-resource-but-different-name\"],\n    [\"#pass12\"],\n    [\"#pass12-identical-resource-but-different-name\"],\n    [\"#pass13\"],\n    [\"#pass13-identical-resource-but-different-name\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"],\n    [\"#incomplete9\"],\n    [\"#incomplete10\"],\n    [\"#incomplete11\"],\n    [\"#incomplete12\"],\n    [\"#incomplete13\"],\n    [\"#incomplete14\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/image-alt/image-alt.html",
    "content": "<div id=\"monkeys\">Bananas</div>\n<div id=\"ninjamonkeys\" style=\"display: none\">Banana bombs</div>\n<img src=\"img.jpg\" id=\"violation1\" />\n<img src=\"img.jpg\" id=\"violation2\" aria-label=\"\" />\n<img src=\"img.jpg\" id=\"violation3\" aria-labelledby=\"nomatchy\" />\n<img src=\"img.jpg\" id=\"violation4\" aria-labelledby=\"\" />\n<img src=\"img.jpg\" id=\"pass1\" alt=\"monkeys\" />\n<img src=\"img.jpg\" id=\"pass2\" aria-label=\"monkeys\" />\n<img src=\"img.jpg\" id=\"pass3\" aria-labelledby=\"monkeys\" />\n<img src=\"img.jpg\" id=\"pass4\" alt=\"\" />\n<img src=\"img.jpg\" id=\"violation5\" title=\"\" />\n<img src=\"img.jpg\" id=\"pass5\" title=\"monkeys\" />\n<img src=\"img.jpg\" id=\"pass6\" role=\"presentation\" />\n<img src=\"img.jpg\" id=\"pass7\" role=\"none\" />\n<img src=\"img.jpg\" id=\"pass8\" aria-labelledby=\"ninjamonkeys\" />\n<img src=\"img.jpg\" id=\"violation6\" alt=\" \" />\n\n<img src=\"img.jpg\" id=\"violation7\" role=\"presentation\" aria-live=\"assertive\" />\n<img src=\"img.jpg\" id=\"violation8\" role=\"none\" aria-live=\"assertive\" />\n<img src=\"img.jpg\" id=\"violation9\" role=\"presentation\" tabindex=\"0\" />\n<img src=\"img.jpg\" id=\"violation10\" role=\"none\" tabindex=\"0\" />\n\n<img src=\"img.jpg\" id=\"violation11\" role=\"button\" />\n<img src=\"img.jpg\" id=\"violation12\" role=\"separator\" tabindex=\"0\" />\n<img src=\"img.jpg\" id=\"inapplicable1\" role=\"separator\" />\n\n<img src=\"img.jpg\" id=\"pass9\" role=\"button\" aria-label=\"foo\" />\n<img src=\"img.jpg\" id=\"pass10\" role=\"checkbox\" title=\"bar\" />\n<img src=\"img.jpg\" id=\"pass11\" role=\"menuitemradio\" aria-labelledby=\"monkeys\" />\n\n<img src=\"img.jpg\" id=\"violation13\" role=\"option\" aria-label=\"\" />\n<img src=\"img.jpg\" id=\"violation14\" role=\"progressbar\" title=\"\" />\n<img src=\"img.jpg\" id=\"violation15\" role=\"treeitem\" aria-labelledby=\"\" />\n\n<img role=\"button\" id=\"violation16\" />\n"
  },
  {
    "path": "test/integration/rules/image-alt/image-alt.json",
    "content": "{\n  \"description\": \"image-alt tests\",\n  \"rule\": \"image-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation8\"],\n    [\"#violation9\"],\n    [\"#violation10\"],\n    [\"#violation11\"],\n    [\"#violation12\"],\n    [\"#violation13\"],\n    [\"#violation14\"],\n    [\"#violation15\"],\n    [\"#violation16\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/image-redundant-alt/image-redundant-alt.html",
    "content": "<button><img id=\"pass1\" alt=\"Img alt text\" />Link text</button>\n<a href=\"#\"><img id=\"pass2\" alt=\"Img alt text\" />Link text</a>\n<a href=\"#\"><img id=\"pass3\" alt=\"Link text\" role=\"presentation\" />Link text</a>\n\n<button><img id=\"fail1\" alt=\"button text\" />Button text</button>\n<a href=\"#\"><img id=\"fail2\" alt=\"Link text\" />Link text</a>\n<p><img id=\"fail3\" alt=\"paragraph text\" />paragraph text</p>\n<table>\n  <tr>\n    <th><img id=\"fail4\" alt=\"header cell text\" />header cell text</th>\n    <td><img id=\"fail5\" alt=\"data cell text\" />data cell text</td>\n  </tr>\n</table>\n\n<ul>\n  <li><img id=\"fail6\" alt=\"list text\" />list text</li>\n</ul>\n\n<!-- ignore -->\n<a href=\"#\"><img alt=\"Link text\" aria-hidden=\"true\" />Link text</a>\n"
  },
  {
    "path": "test/integration/rules/image-redundant-alt/image-redundant-alt.json",
    "content": "{\n  \"description\": \"image-redundant-alt test\",\n  \"rule\": \"image-redundant-alt\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/input-button-name/input-button-name.html",
    "content": "<form action=\"#\">\n  <input type=\"button\" id=\"fail1\" />\n  <input type=\"button\" id=\"pass1\" value=\"Button Name\" />\n  <input type=\"button\" id=\"pass2\" aria-label=\"Name\" />\n  <input type=\"button\" id=\"fail2\" aria-label=\"\" />\n  <input type=\"button\" id=\"pass3\" aria-labelledby=\"labeldiv\" />\n  <input type=\"button\" id=\"fail3\" aria-labelledby=\"nonexistent\" />\n  <input type=\"button\" id=\"fail4\" aria-labelledby=\"emptydiv\" />\n  <div id=\"labeldiv\">Button label</div>\n  <div id=\"emptydiv\"></div>\n  <input type=\"button\" id=\"pass4\" value=\"Name\" aria-label=\"Aria Name\" />\n  <input type=\"submit\" id=\"pass5\" />\n  <input type=\"submit\" value=\"\" id=\"fail5\" />\n  <input type=\"submit\" value=\"Something\" id=\"pass6\" />\n  <input type=\"reset\" id=\"pass7\" />\n  <input type=\"reset\" value=\"\" id=\"fail6\" />\n  <input type=\"reset\" value=\"Something\" id=\"pass8\" />\n  <input type=\"button\" title=\"Something\" id=\"pass9\" />\n  <input type=\"submit\" title=\"Something\" id=\"pass10\" />\n  <input type=\"reset\" title=\"Something\" id=\"pass11\" />\n\n  <input type=\"button\" role=\"none\" id=\"fail7\" />\n  <input type=\"button\" role=\"presentation\" id=\"fail8\" />\n  <input type=\"button\" role=\"none\" id=\"pass12\" disabled />\n  <input type=\"button\" role=\"presentation\" id=\"pass13\" disabled />\n\n  <label>Name<input type=\"button\" id=\"pass14\" /></label>\n  <label for=\"pass15\">Name</label>\n  <input type=\"button\" id=\"pass15\" />\n\n  <input type=\"button\" id=\"fail9\" role=\"img\" disabled />\n  <input type=\"button\" id=\"fail10\" role=\"gridcell\" />\n  <input type=\"button\" id=\"inapplicable1\" role=\"gridcell\" disabled />\n</form>\n"
  },
  {
    "path": "test/integration/rules/input-button-name/input-button-name.json",
    "content": "{\n  \"description\": \"input-button-name test\",\n  \"rule\": \"input-button-name\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/input-image-alt/input-image-alt.html",
    "content": "<div id=\"monkeys\">Bananas</div>\n<form action=\"#\">\n  <input type=\"image\" src=\"img.jpg\" id=\"violation1\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"violation2\" aria-label=\"\" />\n  <input\n    type=\"image\"\n    src=\"img.jpg\"\n    id=\"violation3\"\n    aria-labelledby=\"nomatchy\"\n  />\n  <input type=\"image\" src=\"img.jpg\" id=\"violation4\" aria-labelledby=\"\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"pass1\" alt=\"monkeys\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"pass2\" aria-label=\"monkeys\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"pass3\" aria-labelledby=\"monkeys\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"pass4\" title=\"monkeys\" />\n  <input type=\"image\" src=\"img.jpg\" id=\"violation5\" alt=\"\" />\n\n  <label>Name<input type=\"image\" src=\"img.jpg\" id=\"pass5\" /></label>\n  <label for=\"pass6\">Name</label>\n  <input type=\"image\" src=\"img.jpg\" id=\"pass6\" />\n\n  <input type=\"image\" src=\"img.jpg\" id=\"violation6\" role=\"img\" disabled />\n  <input type=\"image\" src=\"img.jpg\" id=\"violation7\" role=\"gridcell\" />\n  <input\n    type=\"image\"\n    src=\"img.jpg\"\n    id=\"inapplicable1\"\n    role=\"gridcell\"\n    disabled\n  />\n</form>\n"
  },
  {
    "path": "test/integration/rules/input-image-alt/input-image-alt.json",
    "content": "{\n  \"description\": \"input-image-alt tests\",\n  \"rule\": \"input-image-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/label/label.html",
    "content": "<form action=\"#\">\n  <input type=\"hidden\" id=\"na1\" />\n  <input type=\"image\" id=\"na2\" />\n  <input type=\"button\" id=\"na3\" />\n  <input type=\"submit\" id=\"na4\" />\n  <input type=\"reset\" id=\"na5\" />\n  <input type=\"text\" id=\"fail1\" />\n  <textarea id=\"fail2\"></textarea>\n  <input type=\"text\" aria-label=\"label\" id=\"pass1\" />\n  <textarea aria-label=\"label\" id=\"pass2\"></textarea>\n  <input type=\"text\" aria-labelledby=\"label\" id=\"pass3\" />\n  <textarea aria-labelledby=\"label\" id=\"pass4\"></textarea>\n  <div id=\"label\">Label</div>\n  <label>Label <input type=\"text\" id=\"pass5\" /></label>\n  <label>Label <textarea id=\"pass6\"></textarea></label>\n\n  <label><input type=\"text\" id=\"fail3\" /></label>\n  <label><textarea id=\"fail4\"></textarea></label>\n  <label for=\"fail5\"></label><input type=\"text\" id=\"fail5\" />\n  <label for=\"fail6\"></label><textarea id=\"fail6\"></textarea>\n\n  <label for=\"fail7\" style=\"display: none\">Text</label\n  ><input type=\"text\" id=\"fail7\" /> <label for=\"pass7\">Label</label\n  ><input type=\"text\" id=\"pass7\" /><label for=\"pass8\">Label</label\n  ><textarea id=\"pass8\"></textarea>\n\n  <input id=\"pass9\" title=\"Label\" />\n  <textarea id=\"pass10\" title=\"Label\"></textarea>\n\n  <label\n    ><label><input type=\"text\" id=\"fail8\" /></label\n  ></label>\n\n  <div>\n    <select id=\"pass11-label\">\n      <option selected=\"selected\">Chosen</option>\n      <option>Not Selected</option>\n    </select>\n    <input aria-labelledby=\"pass11-label\" type=\"text\" id=\"pass11\" />\n  </div>\n\n  <div>\n    <label for=\"pass12\">\n      <select>\n        <option selected=\"selected\">Chosen</option>\n        <option>Not Selected</option>\n      </select>\n    </label>\n    <input type=\"text\" id=\"pass12\" />\n  </div>\n\n  <div>\n    <label for=\"pass13\" style=\"display: none\"> Hello world </label>\n    <input id=\"pass13\" title=\"Hi\" />\n  </div>\n\n  <input id=\"pass14\" role=\"none\" disabled />\n  <input id=\"pass15\" role=\"presentation\" disabled />\n\n  <div id=\"hiddenlabel\" aria-hidden=\"true\">Hidden label</div>\n  <input type=\"text\" id=\"pass16\" aria-labelledby=\"hiddenlabel\" />\n\n  <label> <input id=\"fail9\" value=\"val\" /></label>\n  <label for=\"fail10\"> <input id=\"fail10\" value=\"val\" /></label>\n  <div id=\"lbl-f13\">\n    <input id=\"fail11\" value=\"val\" aria-labelledby=\"lbl-f13\" />\n  </div>\n  <label><textarea id=\"fail12\"> value</textarea></label>\n\n  <table>\n    <tr>\n      <th id=\"pass17-label\">Named from author elm</th>\n      <td><input id=\"pass17\" aria-labelledby=\"pass17-label\" /></td>\n    </tr>\n  </table>\n\n  <div id=\"fail13-label\">\n    <span role=\"progressbar\" aria-valuenow=\"23\">23%</span>\n  </div>\n  <input type=\"text\" id=\"fail13\" aria-labelledby=\"fail13-label\" />\n\n  <div id=\"fail14-label\">\n    <select>\n      <option selected></option>\n      <option>Option 2</option>\n    </select>\n  </div>\n  <input type=\"text\" id=\"fail14\" aria-labelledby=\"fail14-label\" />\n</form>\n"
  },
  {
    "path": "test/integration/rules/label/label.json",
    "content": "{\n  \"description\": \"label test\",\n  \"rule\": \"label\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"],\n    [\"#fail10\"],\n    [\"#fail11\"],\n    [\"#fail12\"],\n    [\"#fail13\"],\n    [\"#fail14\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.html",
    "content": "<!-- Pass -->\n<div id=\"pass1\" role=\"link\" aria-label=\"next page \">next page</div>\n<div id=\"pass2\" role=\"link\" aria-label=\"Next Page\">next page</div>\n<button id=\"pass3\" name=\"link\" aria-label=\"Next Page in the list\">\n  Next Page\n</button>\n<div id=\"pass4\" role=\"link\" aria-label=\"next page &nbsp \">next page</div>\n<div id=\"pass5\" role=\"link\" aria-label=\"Next Page\">next pAge</div>\n<div id=\"labelForPass6\">uNtIl the very end &nbsp</div>\n<div id=\"pass6\" role=\"button\" aria-labelledby=\"labelForPass6\">\n  UNTIL THE VeRy EnD\n</div>\n\n<button id=\"pass7\" name=\"link\" aria-label=\"Next Page in the list\">\n  Next Page\n</button>\n\n<!-- Fail -->\n<div id=\"fail1\" role=\"link\" aria-label=\"OK\">Next</div>\n<button id=\"fail2\" name=\"link\" aria-label=\"the full\">The full label</button>\n<div id=\"fail3\" role=\"link\" aria-label=\"OK\">Next</div>\n<button id=\"fail4\" name=\"link\" aria-label=\"the full\">The full label</button>\n<div id=\"labelForFail5\">123</div>\n<div role=\"button\" id=\"fail5\" aria-labelledby=\"labelForFail5\">some content</div>\n\n<!-- incomplete -->\n<button id=\"incomplete1\" aria-label=\"comet\">☄️</button>\n<button id=\"incomplete2\" aria-label=\"☄️\">shooting star</button>\n<button id=\"incomplete3\" aria-label=\"help\">?</button>\n<button id=\"incomplete4\" aria-label=\"&#x1F354;\">&#x1F354;</button>\n<button id=\"incomplete5\" aria-label=\"close\">&#10060;</button>\n<button id=\"incomplete6\" aria-label=\"wink\">;)</button>\n<button id=\"incomplete7\" aria-label=\"close\">X</button>\n<button id=\"incomplete8\" aria-label=\"comet\">☄️</button>\n<button id=\"incomplete9\" aria-label=\"help\">?</button>\n<button id=\"incomplete10\" aria-label=\"&#x1F354;\">&#x1F354;</button>\n<button id=\"incomplete11\" aria-label=\"close\">&#10060;</button>\n<button id=\"incomplete12\" aria-label=\"close\">×</button>\n<button id=\"incomplete13\" aria-label=\"italic\">i</button>\n<button id=\"incomplete14\" aria-label=\"bold\">B</button>\n<button id=\"incomplete15\" aria-label=\"alphabetical sort\">ABC</button>\n<button id=\"incomplete16\" aria-label=\"toggle capitalization\">aA</button>\n<button id=\"incomplete17\" aria-label=\"CJK character\"></button>\n\n<!-- inapplicable -->\n<a id=\"inapplicable1\" aria-label=\"OK\">Next</a>\n<input id=\"inapplicable2\" type=\"email\" aria-label=\"E-mail\" value=\"Contact\" />\n<div id=\"inapplicable3\" role=\"tooltip\" aria-label=\"OK\">Ok</div>\n<div id=\"inapplicable4\" role=\"feed\" aria-label=\"RSS Feed\">RSS Feed</div>\n<div id=\"inapplicable5\" role=\"marquee\" aria-label=\"Foo\">Foo Restaurant</div>\n"
  },
  {
    "path": "test/integration/rules/label-content-name-mismatch/label-content-name-mismatch.json",
    "content": "{\n  \"description\": \"label-content-name-mismatch tests\",\n  \"rule\": \"label-content-name-mismatch\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"],\n    [\"#incomplete9\"],\n    [\"#incomplete10\"],\n    [\"#incomplete11\"],\n    [\"#incomplete12\"],\n    [\"#incomplete13\"],\n    [\"#incomplete14\"],\n    [\"#incomplete15\"],\n    [\"#incomplete16\"],\n    [\"#incomplete17\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/label-title-only/label-title-only.html",
    "content": "<form action=\"#\">\n  <input type=\"text\" id=\"fail1\" title=\"Label\" />\n  <select id=\"fail2\" title=\"Label\"></select>\n  <textarea id=\"fail3\" title=\"Label\"></textarea>\n  <input type=\"text\" id=\"fail4\" aria-describedby=\"hi\" />\n\n  <label for=\"pass1\">Hi</label>\n  <input type=\"text\" id=\"pass1\" title=\"Hello\" />\n\n  <label>\n    Hi\n    <input type=\"text\" id=\"pass2\" title=\"Hello\" />\n  </label>\n\n  <input type=\"text\" id=\"pass3\" title=\"Hello\" aria-label=\"Hi\" />\n\n  <div id=\"hi\">Hi</div>\n  <input type=\"text\" id=\"pass4\" title=\"Hello\" aria-labelledby=\"hi\" />\n\n  <label for=\"pass5\">Hi</label>\n  <input type=\"text\" id=\"pass5\" aria-describedby=\"hi\" />\n\n  <label>\n    Hi\n    <input type=\"text\" id=\"pass6\" aria-describedby=\"hi\" />\n  </label>\n\n  <input type=\"text\" id=\"pass7\" aria-describedby=\"hi\" aria-label=\"Hi\" />\n  <input id=\"pass8\" aria-describedby=\"hi\" aria-labelledby=\"hi\" />\n\n  <div id=\"hihidden\" aria-hidden=\"true\">Hi hidden</div>\n  <input type=\"text\" id=\"pass9\" aria-labelledby=\"hihidden\" />\n  <input type=\"text\" id=\"pass10\" title=\"Hello\" aria-labelledby=\"hihidden\" />\n  <input\n    type=\"text\"\n    id=\"pass11\"\n    title=\"Hello\"\n    aria-describedby=\"hi\"\n    aria-labelledby=\"hihidden\"\n  />\n</form>\n"
  },
  {
    "path": "test/integration/rules/label-title-only/label-title-only.json",
    "content": "{\n  \"description\": \"label-title-only test\",\n  \"rule\": \"label-title-only\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/landmark-unique/frame.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf8\" />\n    <title>landmark-unique test</title>\n    <script src=\"/axe.js\"></script>\n  </head>\n  <body>\n    <main id=\"violation-main-2\">Second main</main>\n    <div id=\"form-label-3\">iframe-form-with-label</div>\n    <div role=\"form\" aria-labelledby=\"form-label-3\"></div>\n    <div role=\"navigation\"></div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/landmark-unique/landmark-unique-fail.html",
    "content": "<main id=\"violation-main-1\">First main</main>\n<iframe\n  src=\"/integration/rules/landmark-unique/frame.html\"\n  title=\"iframe with main\"\n  id=\"frame\"\n></iframe>\n\n<header id=\"violation-header-1\">First header</header>\n<header id=\"violation-header-2\">Second header</header>\n\n<form id=\"violation-form-aria-label-1\" aria-label=\"form-label\"></form>\n<form id=\"violation-form-aria-label-2\" aria-label=\"form-label\"></form>\n\n<div id=\"form-label-1\">form-with-label</div>\n<div id=\"form-label-2\">form-with-label</div>\n<form\n  id=\"violation-form-aria-labelledby-1\"\n  aria-labelledby=\"form-label-1\"\n></form>\n<form\n  id=\"violation-form-aria-labelledby-2\"\n  aria-labelledby=\"form-label-2\"\n></form>\n\n<form id=\"violation-aside-aria-label-1\" aria-label=\"aside-label\"></form>\n<form id=\"violation-aside-aria-label-2\" aria-label=\"aside-label\"></form>\n\n<div id=\"aside-label-1\">aside-with-label</div>\n<div id=\"aside-label-2\">aside-with-label</div>\n<form\n  id=\"violation-aside-aria-labelledby-1\"\n  aria-labelledby=\"aside-label-1\"\n></form>\n<form\n  id=\"violation-aside-aria-labelledby-2\"\n  aria-labelledby=\"aside-label-2\"\n></form>\n\n<footer id=\"violation-footer-1\">First footer</footer>\n<footer id=\"violation-footer-2\">Second footer</footer>\n\n<div id=\"form-label-3\">iframe-form-with-label</div>\n<div\n  id=\"violation-form-through-iframe-1\"\n  role=\"form\"\n  aria-labelledby=\"form-label-3\"\n></div>\n\n<div id=\"violation-nav-through-iframe-1\" role=\"navigation\"></div>\n\n<div\n  id=\"violation-role-banner\"\n  aria-label=\"duplicate label\"\n  role=\"banner\"\n></div>\n<div\n  id=\"violation-role-banner-2\"\n  aria-label=\"duplicate label\"\n  role=\"banner\"\n></div>\n\n<div id=\"violation-role-complementary\" role=\"complementary\"></div>\n<div id=\"violation-role-complementary-2\" role=\"complementary\"></div>\n\n<div\n  id=\"violation-role-contentinfo\"\n  aria-label=\"duplicate label for contentinfo\"\n  role=\"contentinfo\"\n></div>\n<div\n  id=\"violation-role-contentinfo-2\"\n  aria-label=\"duplicate label for contentinfo\"\n  role=\"contentinfo\"\n></div>\n\n<div\n  id=\"violation-role-main\"\n  aria-label=\"duplicate label for main\"\n  role=\"main\"\n></div>\n<div\n  id=\"violation-role-main-2\"\n  aria-label=\"duplicate label for main\"\n  role=\"main\"\n></div>\n\n<div id=\"violation-role-region\" role=\"region\"></div>\n<div id=\"violation-role-region-2\" role=\"region\"></div>\n\n<div id=\"violation-role-search\" role=\"search\"></div>\n<div id=\"violation-role-search-2\" role=\"search\"></div>\n\n<nav id=\"violation-nav\" aria-label=\"duplicate label for nav\"></nav>\n<nav id=\"violation-nav-2\" aria-label=\"duplicate label for nav\"></nav>\n\n<section\n  id=\"violation-section\"\n  aria-label=\"duplicate label for section\"\n></section>\n<section\n  id=\"violation-section-2\"\n  aria-label=\"duplicate label for section\"\n></section>\n"
  },
  {
    "path": "test/integration/rules/landmark-unique/landmark-unique-fail.json",
    "content": "{\n  \"description\": \"landmark-unique-fail tests\",\n  \"rule\": \"landmark-unique\",\n  \"violations\": [\n    [\"#violation-main-1\"],\n    [\"#violation-header-1\"],\n    [\"#violation-form-aria-label-1\"],\n    [\"#violation-form-aria-labelledby-1\"],\n    [\"#violation-aside-aria-label-1\"],\n    [\"#violation-aside-aria-labelledby-1\"],\n    [\"#violation-footer-1\"],\n    [\"#frame\", \"div[role=\\\"form\\\"]\"],\n    [\"#frame\", \"div[role=\\\"navigation\\\"]\"],\n    [\"#violation-role-banner\"],\n    [\"#violation-role-complementary\"],\n    [\"#violation-role-contentinfo\"],\n    [\"#violation-role-main\"],\n    [\"#violation-role-region\"],\n    [\"#violation-role-search\"],\n    [\"#violation-nav\"],\n    [\"#violation-section\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/landmark-unique/landmark-unique-pass.html",
    "content": "<main id=\"pass-main\">Only main</main>\n\n<header id=\"pass-header\">Only header</header>\n\n<form id=\"pass-form-aria-label-1\" aria-label=\"form-label-1\"></form>\n<form id=\"pass-form-aria-label-2\" aria-label=\"form-label-2\"></form>\n\n<div id=\"form-label-1\">form-with-label-1</div>\n<div id=\"form-label-2\">form-with-label-2</div>\n<form id=\"pass-form-aria-labelledby-1\" aria-labelledby=\"form-label-1\"></form>\n<form id=\"pass-form-aria-labelledby-2\" aria-labelledby=\"form-label-2\"></form>\n\n<form id=\"pass-aside-aria-label-1\" aria-label=\"aside-label-1\"></form>\n<form id=\"pass-aside-aria-label-2\" aria-label=\"aside-label-2\"></form>\n\n<div id=\"aside-label-1\">aside-with-label-1</div>\n<div id=\"aside-label-2\">aside-with-label-2</div>\n<form id=\"pass-aside-aria-labelledby-1\" aria-labelledby=\"aside-label-1\"></form>\n<form id=\"pass-aside-aria-labelledby-2\" aria-labelledby=\"aside-label-2\"></form>\n\n<footer id=\"pass-footer\">Only footer</footer>\n"
  },
  {
    "path": "test/integration/rules/landmark-unique/landmark-unique-pass.json",
    "content": "{\n  \"description\": \"landmark-unique-pass tests\",\n  \"rule\": \"landmark-unique\",\n  \"passes\": [\n    [\"#pass-main\"],\n    [\"#pass-header\"],\n    [\"#pass-form-aria-label-1\"],\n    [\"#pass-form-aria-label-2\"],\n    [\"#pass-form-aria-labelledby-1\"],\n    [\"#pass-form-aria-labelledby-2\"],\n    [\"#pass-aside-aria-label-1\"],\n    [\"#pass-aside-aria-label-2\"],\n    [\"#pass-aside-aria-labelledby-1\"],\n    [\"#pass-aside-aria-labelledby-2\"],\n    [\"#pass-footer\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/link-in-text-block/link-in-text-block.html",
    "content": "<h1>General applicability tests</h1>\n<p><a href=\"#\" id=\"ignore1\">Link text</a></p>\n<ul>\n  <li><a href=\"#\" id=\"ignore2\">Link text</a></li>\n</ul>\n<p>paragraph <a id=\"ignore3\">Link</a></p>\n<p>paragraph <a href=\"#\" id=\"ignore4\"> </a></p>\n<p>\n  paragraph <a href=\"#\" id=\"ignore5\"><img src=\"\" alt=\"some text\" /> </a>\n</p>\n\n<p style=\"display: none\">\n  paragraph\n  <a style=\"\" href=\"#\" id=\"ignore6\">Link text</a>\n</p>\n\n<p>\n  <!-- ignore blocks with mostly links -->\n  Tags: <a href=\"#\" id=\"ignore7\">Link text</a> /\n  <a href=\"#\" id=\"ignore8\">Other link</a>\n</p>\n\n<p>\n  <!-- take line breaks into account -->\n  paragraph of text <br />\n  <a href=\"#\" id=\"ignore9\">Link text</a>\n</p>\n\n<p>\n  hello world, goodbye world\n  <a href=\"#\" id=\"ignore10\" style=\"text-decoration: none; background: #eee\">\n    <style>\n      a {\n        color: #333;\n      }\n    </style>\n    <img width=\"50px\" height=\"50px\" src=\"some-img.png\" alt=\"\" />\n  </a>\n</p>\n\n<h1>Default styling tests</h1>\n\n<p style=\"color: black\">\n  paragraph of text (default)\n  <a style=\"\" href=\"#\" id=\"pass-default-styling\">Link text</a>\n</p>\n\n<p aria-hidden=\"true\">\n  paragraph of text (default + aria-hidden)\n  <a style=\"\" href=\"#\" id=\"pass-default-styling-aria-hidden\">Link text</a>\n</p>\n\n<h1>Non-color change tests</h1>\n\n<p style=\"color: black\">\n  paragraph of text (pass: link is bolder than paragraph)\n  <a\n    style=\"text-decoration: none; font-weight: bolder; color: black\"\n    href=\"#\"\n    id=\"pass-different-weight\"\n  >\n    Link text</a\n  >\n</p>\n\n<p style=\"color: black\">\n  paragraph of text (pass: link is larger that paragraph)\n  <a\n    style=\"text-decoration: none; font-size: larger; color: black\"\n    href=\"#\"\n    id=\"pass-different-size\"\n  >\n    Link text</a\n  >\n</p>\n\n<h1>Color change tests</h1>\n<b\n  >Note that these tests limit changes to one variable per test. No attempt is\n  made to<br />\n  provide good color contrast <i>within the link</i>. We leave that to the\n  <i>color-contrast</i> rule</b\n>\n\n<p style=\"color: black; background-color: white\">\n  paragraph of text (pass: background color has sufficient contrast against\n  paragraph)\n  <a\n    style=\"text-decoration: none; color: blue; background-color: black\"\n    href=\"#\"\n    id=\"pass-background-color\"\n  >\n    Link text</a\n  >\n</p>\n\n<p style=\"color: black\">\n  paragraph of text (pass: text color has sufficient contrast against paragraph)\n  <a\n    style=\"text-decoration: none; color: lightblue\"\n    href=\"#\"\n    id=\"pass-text-color\"\n  >\n    Link text</a\n  >\n</p>\n\n<p style=\"color: black\">\n  paragraph of text (pass: text color has sufficient contrast against paragraph)\n  <a style=\"text-decoration: none; color: black\" href=\"#\" id=\"pass-same-colors\">\n    Link text</a\n  >\n</p>\n\n<p style=\"color: black\">\n  paragraph of text (fail: text color has insufficient contrast against\n  paragraph)\n  <a\n    style=\"text-decoration: none; color: blue\"\n    href=\"#\"\n    id=\"fail-insufficient-text-contrast\"\n  >\n    Link text</a\n  >\n</p>\n\n<p style=\"color: black\">\n  paragraph of text (fail: background color has insufficient contrast against\n  paragraph)\n  <a\n    style=\"text-decoration: none; color: black; background-color: pink\"\n    href=\"#\"\n    id=\"fail-insufficient-background-contrast\"\n  >\n    Link text</a\n  >\n</p>\n\n<h1>Incomplete tests</h1>\n\n<p style=\"color: black; background-image: linear-gradient(red, yellow)\">\n  paragraph of text (incomplete: link has insufficient text contrast and\n  paragraph has a background gradient)\n  <a\n    style=\"text-decoration: none; color: black\"\n    href=\"#\"\n    id=\"incomplete-low-contrast-parent-has-gradient\"\n  >\n    Link test</a\n  >\n</p>\n\n<style>\n  #incomplete-pseudo-before {\n    text-decoration: none;\n    position: relative;\n  }\n  #incomplete-pseudo-before:before {\n    content: '';\n    position: absolute;\n    width: 100%;\n    height: 1px;\n    background: purple;\n    bottom: 2px;\n  }\n</style>\n<p id=\"pseudo\">\n  Lorem <a href=\"#\" id=\"incomplete-pseudo-before\">ipsum</a> dolor sit.\n</p>\n"
  },
  {
    "path": "test/integration/rules/link-in-text-block/link-in-text-block.json",
    "content": "{\n  \"description\": \"link-in-text-block tests\",\n  \"rule\": \"link-in-text-block\",\n  \"violations\": [\n    [\"#fail-insufficient-text-contrast\"],\n    [\"#fail-insufficient-background-contrast\"]\n  ],\n  \"passes\": [\n    [\"#pass-default-styling\"],\n    [\"#pass-default-styling-aria-hidden\"],\n    [\"#pass-different-weight\"],\n    [\"#pass-different-size\"],\n    [\"#pass-background-color\"],\n    [\"#pass-text-color\"],\n    [\"#pass-same-colors\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete-low-contrast-parent-has-gradient\"],\n    [\"#incomplete-pseudo-before\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/link-name/link-name.html",
    "content": "<a href=\"#\" id=\"pass1\">This link has text</a>\n<a href=\"#\" id=\"pass2\" aria-label=\"link text\"></a>\n<a href=\"#\" id=\"pass3\" aria-labelledby=\"linklabel\"></a>\n<a href=\"#\" id=\"pass4\" title=\"title text\"></a>\n<a href=\"#\" id=\"pass5\">\n  <article>Some sectioning content</article>\n</a>\n\n<div id=\"linklabel\">Text</div>\n\n<a href=\"#\" id=\"violation1\"></a>\n<a href=\"#\" id=\"violation2\" aria-labelledby=\"nonexistent\"></a>\n<a href=\"#\" id=\"violation3\" aria-labelledby=\"empty-label\"></a>\n<a href=\"#\" id=\"violation4\" role=\"none\"></a>\n<a href=\"#\" id=\"violation5\" role=\"presentation\"></a>\n\n<div id=\"empty-label\"></div>\n\n<span id=\"inapplicable1\" role=\"link\">Does not apply</span>\n"
  },
  {
    "path": "test/integration/rules/link-name/link-name.json",
    "content": "{\n  \"description\": \"link-name tests\",\n  \"rule\": \"link-name\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/list/list.html",
    "content": "<ul id=\"emptyul\"></ul>\n<ul id=\"scriptemptyul\">\n  <script></script>\n  <template></template>\n</ul>\n<ul id=\"divul\">\n  <div>This shouldn't be here.</div>\n</ul>\n<ul id=\"mixedul\">\n  <li>Correct.</li>\n  <div>Wrong.</div>\n</ul>\n<ul id=\"properul\">\n  <li>One.</li>\n  <li>Two.</li>\n</ul>\n<ul id=\"scriptproperul\">\n  <script></script>\n  <template></template>\n  <li>One.</li>\n  <li>Two.</li>\n</ul>\n<ol id=\"emptyol\"></ol>\n<ol id=\"scriptemptyol\">\n  <script></script>\n  <template></template>\n</ol>\n<ol id=\"divol\">\n  <div>This shouldn't be here.</div>\n</ol>\n<ol id=\"mixedol\">\n  <li>Correct.</li>\n  <div>Wrong.</div>\n</ol>\n<ol id=\"properol\">\n  <li>One.</li>\n  <li>Two.</li>\n</ol>\n<ol id=\"scriptproperol\">\n  <script></script>\n  <template></template>\n  <li>One.</li>\n  <li>Two.</li>\n</ol>\n<ul id=\"ulrolledallli\">\n  <li role=\"menuitem\">One.</li>\n  <li role=\"menuitem\">Two.</li>\n</ul>\n<ul id=\"ulrolledli\">\n  <li>One.</li>\n  <li role=\"menuitem\">Two</li>\n</ul>\n<ol id=\"olrolledallli\">\n  <li role=\"menuitem\">One.</li>\n  <li role=\"menuitem\">Two.</li>\n</ol>\n<ol id=\"olrolledli\">\n  <li>One.</li>\n  <li role=\"menuitem\">Two</li>\n</ol>\n"
  },
  {
    "path": "test/integration/rules/list/list.json",
    "content": "{\n  \"description\": \"list test\",\n  \"rule\": \"list\",\n  \"violations\": [\n    [\"#divul\"],\n    [\"#mixedul\"],\n    [\"#divol\"],\n    [\"#mixedol\"],\n    [\"#ulrolledallli\"],\n    [\"#olrolledallli\"],\n    [\"#ulrolledli\"],\n    [\"#olrolledli\"]\n  ],\n  \"passes\": [\n    [\"#emptyul\"],\n    [\"#scriptemptyul\"],\n    [\"#properul\"],\n    [\"#scriptproperul\"],\n    [\"#emptyol\"],\n    [\"#scriptemptyol\"],\n    [\"#properol\"],\n    [\"#scriptproperol\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/listitem/listitem.html",
    "content": "<li id=\"uncontained\">Should belong to a list.</li>\n<ul>\n  <li id=\"contained\">Does belong to a list.</li>\n</ul>\n<ol>\n  <li id=\"alsocontained\">Also belongs to a list.</li>\n</ol>\n<ul role=\"presentation\">\n  <li id=\"presentation\">Does belong to a list.</li>\n</ul>\n<ul role=\"none\">\n  <li id=\"none\">Does belong to a list.</li>\n</ul>\n<ul role=\"menubar\">\n  <li id=\"ulrolechanged\">Also does not belong to a list.</li>\n</ul>\n<ol role=\"menubar\">\n  <li id=\"olrolechanged\">I too do not belong to a list.</li>\n</ol>\n\n<menu>\n  <li id=\"menuitem\"><button>Copy</button></li>\n</menu>\n"
  },
  {
    "path": "test/integration/rules/listitem/listitem.json",
    "content": "{\n  \"description\": \"listitem test\",\n  \"rule\": \"listitem\",\n  \"violations\": [[\"#uncontained\"], [\"#ulrolechanged\"], [\"#olrolechanged\"]],\n  \"passes\": [\n    [\"#contained\"],\n    [\"#alsocontained\"],\n    [\"#presentation\"],\n    [\"#none\"],\n    [\"#menuitem\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/marquee/marquee.html",
    "content": "<marquee id=\"marqueemarq\">This content is inside a marquee.</marquee>\n"
  },
  {
    "path": "test/integration/rules/marquee/marquee.json",
    "content": "{\n  \"description\": \"marquee Test\",\n  \"rule\": \"marquee\",\n  \"violations\": [[\"#marqueemarq\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-refresh/meta-refresh.html",
    "content": "<meta id=\"fail1\" http-equiv=\"refresh\" content=\"3000\" />\n"
  },
  {
    "path": "test/integration/rules/meta-refresh/meta-refresh.json",
    "content": "{\n  \"description\": \"meta-refresh test\",\n  \"rule\": \"meta-refresh\",\n  \"violations\": [[\"#fail1\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/frames/iframe-with-zooming-disabled.html",
    "content": "<html lang=\"en\">\n  <head>\n    <script src=\"/axe.js\"></script>\n    <meta name=\"viewport\" content=\"maximum-scale=yes\" />\n    <title>Test Page</title>\n  </head>\n  <body>\n    <main>\n      <h1>hello world</h1>\n    </main>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-fail1.html",
    "content": "<meta\n  name=\"viewport\"\n  id=\"mvp\"\n  content=\"User-Scalable= no;\n\t\t\t\t\t\t\t\t\t\t maximum-scale=2;\"\n/>\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-fail1.json",
    "content": "{\n  \"description\": \"meta-viewport fail test 1\",\n  \"rule\": \"meta-viewport\",\n  \"violations\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-fail2.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=1.5;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-fail2.json",
    "content": "{\n  \"description\": \"meta-viewport fail test 2\",\n  \"rule\": \"meta-viewport\",\n  \"violations\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass1.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=2;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass1.json",
    "content": "{\n  \"description\": \"meta-viewport pass test 1\",\n  \"rule\": \"meta-viewport\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass2.html",
    "content": "<meta\n  name=\"viewport\"\n  id=\"mvp\"\n  content=\"width=device-width, initial-scale=1.0\"\n/>\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass2.json",
    "content": "{\n  \"description\": \"meta-viewport pass test 2\",\n  \"rule\": \"meta-viewport\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass3.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta id=\"mvp\" name=\"viewport\" content=\"maximum-scale=2;\" />\n  </head>\n\n  <body>\n    <div id=\"mocha\"></div>\n    <!-- iframe with zooming disabled should not fail the rule-->\n    <iframe\n      src=\"/integration/rules/meta-viewport/frames/iframe-with-zooming-disabled.html\"\n    ></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/integration/rules/meta-viewport/meta-viewport-pass3.json",
    "content": "{\n  \"description\": \"meta-viewport passes when zooming is disabled on an iframe on the page\",\n  \"rule\": \"meta-viewport\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-fail1.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=2;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-fail1.json",
    "content": "{\n  \"description\": \"meta-viewport-large fail test 1\",\n  \"rule\": \"meta-viewport-large\",\n  \"violations\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-fail2.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=4;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-fail2.json",
    "content": "{\n  \"description\": \"meta-viewport-large fail test 2\",\n  \"rule\": \"meta-viewport-large\",\n  \"violations\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass1.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=10;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass1.json",
    "content": "{\n  \"description\": \"meta-viewport-large pass test 1\",\n  \"rule\": \"meta-viewport-large\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass2.html",
    "content": "<meta\n  name=\"viewport\"\n  id=\"mvp\"\n  content=\"width=device-width, initial-scale=1.0\"\n/>\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass2.json",
    "content": "{\n  \"description\": \"meta-viewport-large pass test 2\",\n  \"rule\": \"meta-viewport-large\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass3.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"User-Scalable= no;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass3.json",
    "content": "{\n  \"description\": \"meta-viewport-large pass test 3\",\n  \"rule\": \"meta-viewport-large\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass4.html",
    "content": "<meta name=\"viewport\" id=\"mvp\" content=\"maximum-scale=1.5;\" />\n"
  },
  {
    "path": "test/integration/rules/meta-viewport-large/meta-viewport-large-pass4.json",
    "content": "{\n  \"description\": \"meta-viewport-large pass test 4\",\n  \"rule\": \"meta-viewport-large\",\n  \"passes\": [[\"#mvp\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/nested-interactive/nested-interactive.html",
    "content": "<button id=\"pass1\">pass</button>\n<button id=\"pass2\"><span tabindex=\"-1\">pass</span></button>\n<div role=\"button\" id=\"pass3\">pass</div>\n<div role=\"tab\" id=\"pass4\">pass</div>\n<div role=\"checkbox\" id=\"pass5\">pass</div>\n<div role=\"radio\" id=\"pass6\"><span>pass</span></div>\n<div role=\"radio\" id=\"pass7\"><span tabindex=\"-1\">pass</span></div>\n\n<button id=\"pass8\"><span tabindex=\"0\">pass</span></button>\n<div role=\"button\" id=\"fail1\"><input /></div>\n<div role=\"tab\" id=\"fail2\">\n  <button id=\"pass9\">div fails, button passes</button>\n</div>\n<div role=\"checkbox\" id=\"fail3\"><a href=\"foo.html\">fail</a></div>\n<div role=\"radio\" id=\"pass10\"><span tabindex=\"0\">pass</span></div>\n<div role=\"radio\" id=\"fail4\">\n  <button id=\"pass11\" tabindex=\"-1\">not really hidden</button>\n</div>\n<div role=\"radio\" id=\"fail5\">\n  <button aria-hidden=\"true\" tabindex=\"-1\">not really hidden</button>\n</div>\n\n<a id=\"ignored1\" href=\"foo.html\">ignored</a>\n<span id=\"ignored2\">ignored</span>\n"
  },
  {
    "path": "test/integration/rules/nested-interactive/nested-interactive.json",
    "content": "{\n  \"description\": \"nested-interactive tests\",\n  \"rule\": \"nested-interactive\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/object-alt/object-alt.html",
    "content": "<object\n  id=\"pass1\"\n  title=\"This object has text\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"pass2\"\n  aria-label=\"this object has text\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<span id=\"label1\" data=\"data:text/html,Object%20content\"\n  >this object has text</span\n>\n<object\n  id=\"pass3\"\n  aria-labelledby=\"label1\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"pass4\"\n  role=\"presentation\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object id=\"pass5\" role=\"none\" data=\"data:text/html,Object%20content\"></object>\n\n<object id=\"violation1\" data=\"data:text/html,Object%20content\"></object>\n<object id=\"violation2\" data=\"data:text/html,Object%20content\">\n  <div></div>\n</object>\n<object id=\"violation3\" data=\"data:text/html,Object%20content\">\n  This object has text.\n</object>\n<object\n  id=\"violation4\"\n  role=\"none\"\n  tabindex=\"0\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"violation5\"\n  role=\"presentation\"\n  tabindex=\"0\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"violation6\"\n  role=\"none\"\n  aria-live=\"assertive\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"violation7\"\n  role=\"presentation\"\n  aria-live=\"assertive\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"violation8\"\n  role=\"img\"\n  data=\"data:text/html,Object%20content\"\n></object>\n<object\n  id=\"violation9\"\n  role=\"separator\"\n  tabindex=\"0\"\n  data=\"data:text/html,Object%20content\"\n></object>\n\n<object id=\"inapplicable1\"><!-- no data attribute --></object>\n<object id=\"inapplicable2\" data=\"\">Fallback content</object>\n<object\n  id=\"inapplicable3\"\n  role=\"separator\"\n  data=\"data:text/html,Object%20content\"\n></object>\n"
  },
  {
    "path": "test/integration/rules/object-alt/object-alt.json",
    "content": "{\n  \"description\": \"object-alt tests\",\n  \"rule\": \"object-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation8\"],\n    [\"#violation9\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/p-as-heading/p-as-heading.html",
    "content": "<!-- inapplicable -->\n<div>\n  <p id=\"inapplicable1\"><b>Some text</b></p>\n</div>\n\n<div>\n  <p id=\"inapplicable2\"><b>A paragraph! With text!</b></p>\n  <p>A paragraph!</p>\n</div>\n\n<div>\n  <p>Some text</p>\n  <blockquote>\n    <p id=\"inapplicable3\">Some text</p>\n  </blockquote>\n</div>\n\n<div>\n  <article>\n    <p id=\"inapplicable4\"><b>Some text</b></p>\n  </article>\n  <p>Some text</p>\n</div>\n\n<!-- Passes -->\n<div>\n  <p id=\"passed1\">Some text</p>\n  <p>A paragraph!</p>\n</div>\n\n<div>\n  <p id=\"passed2\"><b>Some text</b></p>\n  <p><b>A paragraph!</b></p>\n</div>\n\n<div>\n  <p id=\"passed3\" style=\"font-style: bold\">Some text</p>\n  <p><b>A paragraph!</b></p>\n</div>\n\n<div>\n  <p id=\"passed4\">Some text</p>\n  <p><b>A paragraph!</b></p>\n</div>\n\n<div>\n  <p id=\"passed6\">Hello World</p>\n  <p>Hello</p>\n</div>\n\n<!-- Failures -->\n<div>\n  <p id=\"failed1\" style=\"font-weight: bold; font-size: 120%\">Some text</p>\n  <p>A paragraph, A paragraph!</p>\n</div>\n\n<div>\n  <p id=\"failed2\" style=\"font-size: 150%\">Some text</p>\n  <p>A paragraph, A paragraph!</p>\n</div>\n\n<div>\n  <p id=\"failed3\">\n    <b><i>Some text</i></b>\n  </p>\n  <p>A paragraph, A paragraph!</p>\n</div>\n\n<!-- Cant Tell -->\n<div>\n  <p id=\"passed5\"><b style=\"font-size: 120%\">Some text</b></p>\n  <p id=\"canttell1\"><b style=\"font-size: 120%\">Some text</b></p>\n  <p>A paragraph, A paragraph!</p>\n</div>\n\n<div>\n  <blockquote>\n    <p id=\"canttell2\"><b style=\"font-size: 120%\">Some text</b></p>\n    <p>A pragraph!</p>\n  </blockquote>\n</div>\n"
  },
  {
    "path": "test/integration/rules/p-as-heading/p-as-heading.json",
    "content": "{\n  \"description\": \"p-as-heading test\",\n  \"rule\": \"p-as-heading\",\n  \"violations\": [[\"#failed1\"], [\"#failed2\"], [\"#failed3\"]],\n  \"passes\": [\n    [\"#passed1\"],\n    [\"#passed2\"],\n    [\"#passed3\"],\n    [\"#passed4\"],\n    [\"#passed5\"],\n    [\"#passed6\"]\n  ],\n  \"incomplete\": [[\"#canttell1\"], [\"#canttell2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/preprocessor.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar extRegex = /\\.json$/;\nvar template = fs.readFileSync(path.join(__dirname, 'runner.js'), 'utf-8');\n\n/**\n * Turn each rule.json integration test JSON into a js file using\n * the runner.js script. This allow us to load the JSON files in\n * the karma config and they'll run as js files.\n */\nvar createIntegrationPreprocessor = function (logger) {\n  var log = logger.create('preprocessor.integration');\n\n  return function (content, file, done) {\n    try {\n      log.debug('Processing \"%s\".', file.originalPath);\n      file.path = file.originalPath.replace(extRegex, '.js');\n\n      // turn the json file into the a test file using the js test template\n      // and add the test data to it\n      var htmlpath = file.originalPath.replace(extRegex, '.html');\n      var html = fs.readFileSync(htmlpath, 'utf-8');\n      try {\n        var test = JSON.parse(content);\n      } catch {\n        throw new Error('Unable to parse content of ' + file.originalPath);\n      }\n      test.content = html;\n\n      var result = template.replace('{}; /*tests*/', JSON.stringify(test));\n\n      done(null, result);\n    } catch (e) {\n      console.log('e:', e);\n      done(e, null);\n    }\n  };\n};\n\ncreateIntegrationPreprocessor.$inject = ['logger'];\n\n// PUBLISH DI MODULE\nmodule.exports = {\n  'preprocessor:integration': ['factory', createIntegrationPreprocessor]\n};\n"
  },
  {
    "path": "test/integration/rules/presentation-role-conflict/presentation-role-conflict.html",
    "content": "<h1 id=\"pass1\" role=\"none\">Just some big text</h1>\n<h1 id=\"pass2\" role=\"presentation\">Just some big text</h1>\n<img id=\"pass3\" alt=\"\" src=\"img.png\" />\n\n<li id=\"fail1\" role=\"presentation\" aria-label=\"My Heading\">Hello</li>\n<h1 id=\"fail2\" role=\"none\" aria-label=\"My Heading\">Hello</h1>\n<img id=\"fail3\" alt aria-label=\"foo\" src=\"img.png\" />\n<img id=\"fail4\" alt=\"\" tabindex=\"0\" src=\"img.png\" />\n<img id=\"fail5\" alt=\"\" aria-live=\"assertive\" src=\"img.png\" />\n\n<div id=\"inapplicable1\" role=\"presentation\">Test</div>\n<div id=\"inapplicable2\" role=\"presentation\">Test Button</div>\n<img id=\"inapplicable3\" alg=\"foo\" src=\"img.png\" />\n<img id=\"inapplicable4\" aria-label=\"foo\" src=\"img.png\" />\n"
  },
  {
    "path": "test/integration/rules/presentation-role-conflict/presentation-role-conflict.json",
    "content": "{\n  \"rule\": \"presentation-role-conflict\",\n  \"description\": \"presentation-role-conflict tests\",\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"]],\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/role-img-alt/role-img-alt.html",
    "content": "<div id=\"match\">Bananas</div>\n<div id=\"hidden-match\" style=\"display: none\">Banana bombs</div>\n\n<div role=\"img\" aria-label=\"blah\" id=\"pass1\"></div>\n<div role=\"img\" aria-labelledby=\"match\" id=\"pass2\"></div>\n<div role=\"img\" aria-labelledby=\"hidden-match\" id=\"pass3\"></div>\n<div role=\"img\" title=\"title\" id=\"pass4\"></div>\n\n<div role=\"img\" id=\"violation1\"></div>\n<div role=\"img\" aria-label=\"\" id=\"violation2\"></div>\n<div role=\"img\" alt=\"blah\" id=\"violation3\"></div>\n<div role=\"img\" aria-labelledby=\"no-match\" id=\"violation4\"></div>\n<div role=\"img\" title=\"\" id=\"violation5\"></div>\n\n<svg\n  id=\"inapplicable1\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <title>I am a circle</title>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n"
  },
  {
    "path": "test/integration/rules/role-img-alt/role-img-alt.json",
    "content": "{\n  \"description\": \"role-img-alt tests\",\n  \"rule\": \"role-img-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/runner.js",
    "content": "(() => {\n  // this line is replaced with the test object in preprocessor.js\n  const testObj = {}; /*tests*/\n\n  const ruleId = testObj.rule;\n  const testName = testObj.description || ruleId + ' test';\n\n  function flattenResult(results) {\n    return {\n      passes: results.passes[0],\n      violations: results.violations[0],\n      incomplete: results.incomplete[0]\n    };\n  }\n\n  function waitForFrames(context, cb) {\n    let loaded = 0;\n    const frames = context.querySelectorAll('iframe');\n\n    function loadListener() {\n      loaded++;\n      if (loaded === length) {\n        cb();\n      }\n    }\n    if (!frames.length) {\n      return cb();\n    }\n    for (let index = 0, length = frames.length; index < length; index++) {\n      frames[index].addEventListener('load', loadListener);\n    }\n  }\n\n  const fixture = document.getElementById('fixture');\n  const rule = axe.utils.getRule(ruleId);\n\n  if (!rule) {\n    return;\n  }\n\n  // don't run rules that are deprecated and disabled\n  const deprecated = rule.tags.indexOf('deprecated') !== -1;\n  (deprecated && !rule.enabled ? describe.skip : describe)(ruleId, () => {\n    describe(testName, () => {\n      function runTest(test, collection) {\n        if (!test[collection]) {\n          return;\n        }\n\n        describe(collection, () => {\n          let nodes;\n          before(() => {\n            if (typeof results[collection] === 'object') {\n              nodes = results[collection].nodes;\n            }\n          });\n\n          test[collection].forEach(selector => {\n            it('should find ' + JSON.stringify(selector), () => {\n              if (!nodes) {\n                assert(false, 'there are no ' + collection);\n                return;\n              }\n\n              const matches = nodes.filter(node => {\n                for (let i = 0; i < selector.length; i++) {\n                  if (node.target[i] !== selector[i]) {\n                    return false;\n                  }\n                }\n                return node.target.length === selector.length;\n              });\n              matches.forEach(node => {\n                // remove each node we find\n                nodes.splice(nodes.indexOf(node), 1);\n              });\n\n              if (matches.length === 0) {\n                assert(false, 'Element not found');\n              } else if (matches.length === 1) {\n                assert(true, 'Element found');\n              } else {\n                assert(\n                  false,\n                  'Found ' + matches.length + ' elements which match the target'\n                );\n              }\n            });\n          });\n\n          it('should not return other results', () => {\n            if (typeof nodes !== 'undefined') {\n              const targets = nodes.map(node => {\n                return node.target;\n              });\n              // check that all nodes are removed\n              assert.equal(JSON.stringify(targets), '[]');\n            } else {\n              assert.lengthOf(\n                test[collection],\n                0,\n                'there are no ' + collection\n              );\n            }\n          });\n        });\n      }\n\n      let results;\n      before(done => {\n        fixture.innerHTML = testObj.content;\n        waitForFrames(fixture, () => {\n          // The setTimeout is a workaround for a Firefox bug. See:\n          // - https://github.com/dequelabs/axe-core/issues/4556\n          // - https://bugzilla.mozilla.org/show_bug.cgi?id=1912115\n          setTimeout(() => {\n            axe.run(\n              fixture,\n              {\n                /**\n                 * The debug flag helps log errors in a fairly detailed fashion,\n                 * when tests fail in webdriver\n                 */\n                debug: true,\n                performanceTimer: false,\n                runOnly: { type: 'rule', values: [ruleId] }\n              },\n              (err, r) => {\n                // assert that there are no errors - if error exists a stack trace is logged.\n                const errStack = err && err.stack ? err.stack : '';\n                assert.isNull(err, 'Error should be null. ' + errStack);\n                // assert that result is defined\n                assert.isDefined(r, 'Results are defined.');\n                // assert that result has certain keys\n                assert.hasAnyKeys(r, ['incomplete', 'violations', 'passes']);\n                // assert incomplete(s) does not have error\n                r.incomplete.forEach(incomplete => {\n                  assert.isUndefined(incomplete.error);\n                });\n                // flatten results\n                results = flattenResult(r);\n                done();\n              }\n            );\n          }, 0);\n        });\n      });\n      runTest(testObj, 'passes');\n      runTest(testObj, 'violations');\n      if (testObj.incomplete) {\n        runTest(testObj, 'incomplete');\n      }\n    });\n  });\n})();\n"
  },
  {
    "path": "test/integration/rules/scope-attr-valid/scope-attr-valid.html",
    "content": "<table>\n  <tr>\n    <th id=\"badvalue\" scope=\"whatever\"></th>\n  </tr>\n</table>\n\n<table>\n  <tr>\n    <td id=\"tdrow\" scope=\"row\"></td>\n  </tr>\n</table>\n\n<table>\n  <tr>\n    <th id=\"throw\" scope=\"row\"></th>\n  </tr>\n</table>\n\n<table>\n  <tr>\n    <td id=\"tdcol\" scope=\"col\"></td>\n  </tr>\n</table>\n\n<table>\n  <tr>\n    <th id=\"thcol\" scope=\"colgroup\"></th>\n  </tr>\n</table>\n"
  },
  {
    "path": "test/integration/rules/scope-attr-valid/scope-attr-valid.json",
    "content": "{\n  \"rule\": \"scope-attr-valid\",\n  \"violations\": [[\"#badvalue\"], [\"#tdrow\"], [\"#tdcol\"]],\n  \"passes\": [[\"#throw\"], [\"#thcol\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.html",
    "content": "<!-- pass -->\n<div id=\"pass1\" style=\"overflow-y: scroll; height: 5px\">\n  <input type=\"text\" />\n</div>\n\n<div id=\"pass2\" style=\"height: 20px; overflow: auto\">\n  <input type=\"text\" tabindex=\"-1\" />\n  <select tabindex=\"-1\"></select>\n  <textarea tabindex=\"-1\"></textarea>\n  <p style=\"height: 200px\" tabindex=\"0\"></p>\n</div>\n\n<!-- fail -->\n<div id=\"fail1\" style=\"height: 5px; overflow: auto\">\n  <input type=\"text\" tabindex=\"-1\" />\n</div>\n\n<div id=\"fail2\" style=\"height: 5px; overflow: auto\">\n  <input type=\"text\" tabindex=\"-1\" />\n  <select tabindex=\"-1\"></select>\n  <textarea tabindex=\"-1\"></textarea>\n</div>\n\n<div id=\"fail3\" style=\"width: 300px; overflow-y: auto\">\n  <p style=\"width: 600px\">Contents</p>\n  <p style=\"width: 600px\">\n    Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium\n    doloremque laudantium.\n  </p>\n</div>\n\n<div id=\"fail4\" style=\"width: 300px; overflow-y: auto\">\n  <p width=\"600px\">Contents</p>\n  <img src=\"#\" width=\"600\" height=\"600\" />\n</div>\n\n<!-- inapplicable -->\n<section id=\"inapplicable1\">This element is not scrollable</section>\n\n<div id=\"inapplicable2\" style=\"height: 200px; width: 200px; overflow: auto\">\n  <div style=\"height: 10px; width: 100x\">Content</div>\n</div>\n\n<p\n  id=\"inapplicable3\"\n  style=\"width: 12em; height: 2em; border: dotted; overflow: visible\"\n>\n  Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium\n  doloremque laudantium.\n</p>\n\n<div\n  id=\"inapplicable4\"\n  style=\"display: none; height: 200px; overflow: hidden\"\n  tabindex=\"0\"\n>\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div\n  id=\"inapplicable5\"\n  aria-hidden=\"true\"\n  style=\"height: 200px; overflow: hidden\"\n  tabindex=\"0\"\n>\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div id=\"inapplicable6\" style=\"height: 200px; width: 200px; overflow: hidden\">\n  <div style=\"height: 2000px; width: 100px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div role=\"combobox\">\n  <div\n    id=\"inapplicable7\"\n    role=\"listbox\"\n    style=\"height: 200px; overflow-y: auto\"\n  >\n    <div role=\"option\" style=\"height: 2000px\">\n      <p>Content</p>\n    </div>\n  </div>\n</div>\n\n<div role=\"combobox\" aria-owns=\"inapplicable8\">\n  <div id=\"inapplicable8\" role=\"grid\" style=\"height: 200px; overflow-y: auto\">\n    <div role=\"option\" style=\"height: 2000px\">\n      <p>Content</p>\n    </div>\n  </div>\n\n  <div role=\"combobox\" aria-controls=\"inapplicable9\">\n    <div\n      id=\"inapplicable9\"\n      role=\"dialog\"\n      style=\"height: 200px; overflow-y: auto\"\n    >\n      <div role=\"option\" style=\"height: 2000px\">\n        <p>Content</p>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div id=\"inapplicable10\" style=\"height: 200px; overflow-y: clip\">\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<select size=\"2\" id=\"inapplicable11\">\n  <option value=\"First\">First</option>\n  <option value=\"Second\">Second</option>\n  <option value=\"Third\">Third</option>\n</select>\n\n<textarea rows=\"2\" cols=\"20\" id=\"inapplicable12\">\n  test\n  test\n  test\n</textarea>\n\n<div id=\"inapplicable13\" style=\"height: 200px; overflow-y: auto\" tabindex=\"0\">\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div\n  id=\"inapplicable14\"\n  style=\"height: 200px; overflow-y: auto\"\n  contenteditable=\"true\"\n>\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div\n  id=\"inapplicable15\"\n  style=\"height: 200px; overflow-y: auto\"\n  contenteditable=\"true\"\n>\n  <div style=\"height: 2000px\">\n    <div\n      id=\"inapplicable16\"\n      style=\"height: 200px; overflow-y: auto\"\n      contenteditable=\"invalid\"\n    >\n      <div style=\"height: 2000px\">\n        <p>Content</p>\n      </div>\n    </div>\n  </div>\n</div>\n\n<div\n  id=\"inapplicable16\"\n  style=\"height: 200px; overflow-y: auto\"\n  contenteditable=\"false\"\n>\n  <div style=\"height: 2000px\">\n    <p>Content</p>\n  </div>\n</div>\n\n<div id=\"inapplicable17\" style=\"width: 300px\">\n  <p style=\"width: 600px\">Contents</p>\n</div>\n\n<div id=\"inapplicable18\" style=\"width: 300px; overflow-y: auto\">\n  <img src=\"#\" width=\"100\" height=\"100\" />\n</div>\n"
  },
  {
    "path": "test/integration/rules/scrollable-region-focusable/scrollable-region-focusable.json",
    "content": "{\n  \"description\": \"scrollable-region-focusable tests\",\n  \"rule\": \"scrollable-region-focusable\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/select-name/select-name.html",
    "content": "<form action=\"#\">\n  <select id=\"fail1\"></select>\n  <select aria-label=\"label\" id=\"pass1\"></select>\n  <select aria-labelledby=\"label\" id=\"pass2\"></select>\n  <div id=\"label\">Label</div>\n  <label>\n    Label\n    <select id=\"pass3\"></select>\n  </label>\n\n  <label><select id=\"fail2\"></select></label>\n  <label for=\"fail3\">\n    <select id=\"fail3\">\n      <option>Thing</option>\n    </select>\n  </label>\n  <label for=\"pass4\">Label</label>\n  <select id=\"pass4\"></select>\n\n  <select id=\"pass5\" title=\"Label\"></select>\n\n  <div>\n    <label>\n      <select id=\"fail4\">\n        <option selected=\"selected\">Chosen</option>\n        <option>Not Selected</option>\n      </select>\n    </label>\n  </div>\n\n  <select id=\"fail5\" role=\"presentation\"></select>\n  <select id=\"fail6\" role=\"none\"></select>\n\n  <select id=\"pass6\" role=\"presentation\" disabled></select>\n  <select id=\"pass7\" role=\"none\" disabled></select>\n\n  <label>\n    <select id=\"fail7\">\n      <option>val</option>\n    </select>\n  </label>\n  <label for=\"fail8\">\n    <select id=\"fail8\">\n      <option>val</option>\n    </select>\n  </label>\n  <div id=\"lbl-f9\">\n    <select id=\"fail9\" aria-labelledby=\"lbl-f9\">\n      <option>val</option>\n    </select>\n  </div>\n</form>\n"
  },
  {
    "path": "test/integration/rules/select-name/select-name.json",
    "content": "{\n  \"description\": \"select-name test\",\n  \"rule\": \"select-name\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail8\"],\n    [\"#fail9\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/server-side-image-map/server-side-image-map.html",
    "content": "<p><img ismap src=\"image.jpg\" id=\"incomplete\" /></p>\n<p><img src=\"image.jpg\" id=\"pass\" /></p>\n"
  },
  {
    "path": "test/integration/rules/server-side-image-map/server-side-image-map.json",
    "content": "{\n  \"description\": \"server-side-image-map test\",\n  \"rule\": \"server-side-image-map\",\n  \"incomplete\": [[\"#incomplete\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/summary-name/summary-name.html",
    "content": "<details>\n  <summary id=\"empty-fail\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"text-pass\">name</summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"aria-label-pass\" aria-label=\"Name\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"aria-label-fail\" aria-label=\"\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"aria-labelledby-pass\" aria-labelledby=\"labeldiv\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"aria-labelledby-fail\" aria-labelledby=\"nonexistent\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"aria-labelledby-empty-fail\" aria-labelledby=\"emptydiv\"></summary>\n  Hello world\n</details>\n<div id=\"labeldiv\">summary label</div>\n<div id=\"emptydiv\"></div>\n\n<details>\n  <summary id=\"combo-pass\" aria-label=\"Aria Name\">Name</summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"title-pass\" title=\"Title\"></summary>\n  Hello world\n</details>\n\n<details>\n  <summary id=\"presentation-role-fail\" role=\"presentation\"></summary>\n  Conflict resolution gets this to be ignored\n</details>\n\n<details>\n  <summary id=\"none-role-fail\" role=\"none\"></summary>\n  Conflict resolution gets this to be ignored\n</details>\n\n<details>\n  <summary id=\"heading-role-fail\" role=\"heading\"></summary>\n  Conflict resolution gets this to be ignored\n</details>\n\n<details>\n  <summary id=\"button-role-fail\" role=\"button\"></summary>\n  Hello world\n</details>\n\n<!-- Invalid naming methods -->\n\n<details>\n  <summary id=\"value-attr-fail\" value=\"Button Name\"></summary>\n  Not a valid method for giving a name\n</details>\n\n<details>\n  <summary id=\"alt-attr-fail\" alt=\"Button Name\"></summary>\n  Not a valid method for giving a name\n</details>\n\n<label>\n  <details>\n    <summary id=\"label-elm-fail\"></summary>\n    Text here\n  </details>\n  Not a valid method for giving a name\n</label>\n"
  },
  {
    "path": "test/integration/rules/summary-name/summary-name.json",
    "content": "{\n  \"description\": \"summary-name test\",\n  \"rule\": \"summary-name\",\n  \"violations\": [\n    [\"#empty-fail\"],\n    [\"#aria-label-fail\"],\n    [\"#aria-labelledby-fail\"],\n    [\"#aria-labelledby-empty-fail\"],\n    [\"#presentation-role-fail\"],\n    [\"#none-role-fail\"],\n    [\"#heading-role-fail\"],\n    [\"#value-attr-fail\"],\n    [\"#alt-attr-fail\"],\n    [\"#label-elm-fail\"],\n    [\"#button-role-fail\"]\n  ],\n  \"passes\": [\n    [\"#text-pass\"],\n    [\"#aria-label-pass\"],\n    [\"#aria-labelledby-pass\"],\n    [\"#combo-pass\"],\n    [\"#title-pass\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/svg-img-alt/svg-img-alt.html",
    "content": "<h2>Passed</h2>\n\n<svg\n  id=\"pass1\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <title>I am a circle</title>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  id=\"pass2\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n  aria-label=\"I am a circle\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  id=\"pass3\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n  title=\"I am a circle\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<span id=\"circle-label1\">I am a circle</span>\n<svg\n  id=\"pass4\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n  aria-labelledby=\"circle-label1\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  id=\"pass5\"\n  role=\"graphics-document\"\n  width=\"100\"\n  height=\"100\"\n>\n  <title>I am a circle</title>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle\n    id=\"pass6\"\n    role=\"graphics-symbol\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n    aria-label=\"I am a circle\"\n  ></circle>\n</svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle\n    id=\"pass7\"\n    role=\"graphics-symbol\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n    aria-labelledby=\"circle-label1\"\n  ></circle>\n</svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle\n    id=\"pass8\"\n    role=\"graphics-symbol\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n    title=\"I am a circle\"\n  ></circle>\n</svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle\n    id=\"pass9\"\n    role=\"graphics-symbol\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n  >\n    <title>I am a circle</title>\n  </circle>\n</svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle id=\"pass10\" role=\"img\" cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\">\n    <title>I am a circle</title>\n  </circle>\n</svg>\n\n<h2>Failed</h2>\n\n<svg xmlns=\"http://www.w3.org/2000/svg\" role=\"img\" id=\"violation1\">\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg xmlns=\"http://www.w3.org/2000/svg\" role=\"img\" id=\"violation2\">\n  <title></title>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  id=\"violation3\"\n  aria-label=\" \"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  id=\"violation4\"\n  aria-labelledby=\"not-an-id\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  id=\"violation5\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <text>I am a circle</text>\n</svg>\n\n<svg\n  id=\"violation6\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\">\n    <!-- Title must be a child -->\n    <title>I am a circle</title>\n  </circle>\n</svg>\n\n<svg\n  id=\"violation7\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <metadata>\n    <!-- Title must be a child -->\n    <title>I am a circle</title>\n  </metadata>\n</svg>\n\n<svg\n  id=\"violation8\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  width=\"100\"\n  height=\"100\"\n>\n  <desc>I am a circle</desc>\n</svg>\n\n<svg\n  id=\"violation9\"\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"graphics-document\"\n  width=\"100\"\n  height=\"100\"\n></svg>\n\n<svg xmlns=\"https://www.w3.org/2000/svg\">\n  <circle\n    id=\"violation10\"\n    role=\"img\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n  ></circle>\n</svg>\n\n<h2>Inapplicable</h2>\n\n<svg xmlns=\"http://www.w3.org/2000/svg\">\n  <circle id=\"inapplicable1\" cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg\n  xmlns=\"http://www.w3.org/2000/svg\"\n  role=\"img\"\n  aria-hidden=\"true\"\n  id=\"inapplicable2\"\n>\n  <circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"yellow\"></circle>\n</svg>\n\n<svg xmlns=\"http://www.w3.org/2000/svg\">\n  <circle\n    role=\"graphics-object\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    fill=\"yellow\"\n    id=\"inapplicable3\"\n  ></circle>\n</svg>\n\n<div id=\"inapplicable4\" role=\"img\">\n  <h1>Hello world</h1>\n</div>\n"
  },
  {
    "path": "test/integration/rules/svg-img-alt/svg-img-alt.json",
    "content": "{\n  \"description\": \"svg-img-alt tests\",\n  \"rule\": \"svg-img-alt\",\n  \"violations\": [\n    [\"#violation1\"],\n    [\"#violation2\"],\n    [\"#violation3\"],\n    [\"#violation4\"],\n    [\"#violation5\"],\n    [\"#violation6\"],\n    [\"#violation7\"],\n    [\"#violation8\"],\n    [\"#violation9\"],\n    [\"#violation10\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/tabindex/tabindex.html",
    "content": "<p tabindex=\"-1\" id=\"negative\">Paragraph.</p>\n<p tabindex=\"0\" id=\"zero\">Paragraph.</p>\n<p tabindex=\"1\" id=\"positive\">Paragraph.</p>\n"
  },
  {
    "path": "test/integration/rules/tabindex/tabindex.json",
    "content": "{\n  \"description\": \"tabindex test\",\n  \"rule\": \"tabindex\",\n  \"violations\": [[\"#positive\"]],\n  \"passes\": [[\"#negative\"], [\"#zero\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/table-duplicate-name/table-duplicate-name.html",
    "content": "<table id=\"pass1\">\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass2\" summary=\"\">\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"fail1\" summary=\"some table\">\n  <caption>\n    some table\n  </caption>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n</table>\n"
  },
  {
    "path": "test/integration/rules/table-duplicate-name/table-duplicate-name.json",
    "content": "{\n  \"rule\": \"table-duplicate-name\",\n  \"violations\": [[\"#fail1\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/table-fake-caption/table-fake-caption.html",
    "content": "<table id=\"pass1\" role=\"grid\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass2\" role=\"grid\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr></tr>\n</table>\n\n<table id=\"fail1\" role=\"grid\">\n  <tr>\n    <th colspan=\"2\">AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr></tr>\n</table>\n\n<table id=\"fail2\" role=\"grid\">\n  <tr>\n    <td colspan=\"2\">AXE</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr></tr>\n</table>\n\n<table id=\"ignored1\" role=\"presentation\">\n  <tr>\n    <td colspan=\"2\">AXE</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr></tr>\n</table>\n"
  },
  {
    "path": "test/integration/rules/table-fake-caption/table-fake-caption.json",
    "content": "{\n  \"rule\": \"table-fake-caption\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/target-size/target-size.html",
    "content": "<style>\n  /* Ensure sufficient space between tests */\n  p {\n    margin: 30px;\n  }\n  img {\n    vertical-align: bottom;\n  }\n</style>\n\n<!-- These fall on top of the elements immediately below it \nthis should not effect their outcome -->\n<p style=\"position: fixed\">\n  <a href=\"#\" id=\"pass-fixed\">Wide link</a>\n  <a href=\"#\" id=\"fail-fixed\">x</a>\n</p>\n\n<p>\n  <button id=\"pass1\" style=\"display: inline-block; margin-right: 20px\">\n    x\n  </button>\n  <select id=\"pass2\">\n    <option>opt 1</option>\n    <option>opt 2</option>\n  </select>\n  <textarea id=\"pass3\" tabindex=\"-1\">\n    Test elements with tabindex=\"-1\".\n  </textarea>\n</p>\n\n<div>\n  <button id=\"pass9\">x</button>\n  <div role=\"alertdialog\" tabindex=\"0\" id=\"ignore1\">Cookies</div>\n</div>\n\n<div>\n  <button id=\"pass10\">x</button>\n  <div role=\"dialog\" tabindex=\"0\" id=\"ignore2\">Cookies</div>\n</div>\n\n<div>\n  <button id=\"pass11\">x</button>\n  <div role=\"alert\" tabindex=\"0\" id=\"ignore3\">Cookies</div>\n</div>\n\n<div>\n  <button id=\"pass12\">x</button>\n  <div role=\"log\" tabindex=\"0\" id=\"ignore4\">Log</div>\n</div>\n\n<div>\n  <button id=\"pass13\">x</button>\n  <div role=\"marquee\" tabindex=\"0\" id=\"ignore4\">Marquee</div>\n</div>\n\n<div>\n  <button id=\"pass14\">x</button>\n  <div role=\"status\" tabindex=\"0\" id=\"ignore5\">Status</div>\n</div>\n\n<div>\n  <button id=\"pass15\">x</button>\n  <div role=\"tabpanel\" tabindex=\"0\" id=\"ignore6\">Tabpanel</div>\n</div>\n\n<div>\n  <button id=\"pass16\">x</button>\n  <div role=\"timer\" tabindex=\"0\" id=\"ignore7\">Timer</div>\n</div>\n\n<!-- Nested controls-->\n<p>\n  <a\n    role=\"link\"\n    aria-label=\"play\"\n    tabindex=\"0\"\n    style=\"display: inline-block\"\n    id=\"pass4\"\n  >\n    <button\n      style=\"margin: 1px; line-height: 20px\"\n      id=\"inapplicable7\"\n      tabindex=\"-1\"\n    >\n      Play\n    </button>\n  </a>\n</p>\n\n<p>\n  <a role=\"link\" tabindex=\"0\" id=\"pass5\">\n    Wide link <button id=\"fail7\">y</button>\n  </a>\n</p>\n\n<div role=\"tabpanel\" tabindex=\"0\">\n  Focusable tabpanel containing small control\n  <button id=\"pass17\">x</button>\n</div>\n\n<!-- Failed examples -->\n<p>\n  <button id=\"fail1\">x</button\n  ><span role=\"button\" tabindex=\"0\" id=\"fail2\">x</span>\n</p>\n\n<ul>\n  <li><a href=\"#\" id=\"fail3\">Item1</a></li>\n  <li><a href=\"#\" id=\"fail4\">Item2</a></li>\n</ul>\n\n<p>\n  <input type=\"checkbox\" id=\"fail5\" /><input type=\"checkbox\" id=\"fail6\" /><input\n    type=\"text\"\n    id=\"pass-adjacent\"\n  />\n</p>\n\n<!-- Incomplete examples -->\n<ul>\n  <li><a href=\"#\" tabindex=\"-1\" id=\"incomplete1\">Item1</a></li>\n  <li><a href=\"#\" tabindex=\"-1\" id=\"incomplete2\">Item2</a></li>\n</ul>\n\n<p>\n  <a href=\"#\" id=\"incomplete3\" tabindex=\"-1\">x</a>\n  <a href=\"#\" id=\"pass6\" tabindex=\"-1\">Wide link</a>\n</p>\n\n<p>\n  <a href=\"#\" id=\"incomplete4\" tabindex=\"-1\">abc</a>\n  <a\n    href=\"#\"\n    id=\"pass7\"\n    tabindex=\"-1\"\n    style=\"display: inline-block; margin-left: -1em\"\n    >Wide link</a\n  >\n  <a\n    href=\"#\"\n    id=\"pass8\"\n    tabindex=\"-1\"\n    style=\"display: inline-block; margin-left: -1em\"\n    >Wide link</a\n  >\n</p>\n\n<div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.3\">\n  <a id=\"pass18\" href=\"/foo\" class=\"A\"> Hello hello hello</a>\n  <a id=\"pass19\" href=\"/bar\" class=\"B\">Hello hello hello</a>\n</div>\n\n<div style=\"font-size: 18px; margin: 1em auto; width: 6em; line-height: 1.3\">\n  <a id=\"pass20\" href=\"/foo\" class=\"A\"> Hello hello</a>\n  <a id=\"pass21\" href=\"/bar\" class=\"B\">Hello hello hello</a>\n  <a id=\"pass22\" href=\"/bar\" class=\"C\">Hello hello hello</a>\n</div>\n\n<div style=\"position: relative\">\n  <button id=\"pass23\" style=\"width: 80px; height: 40px\">X</button>\n  <button\n    id=\"pass24\"\n    style=\"width: 80px; height: 40px; position: absolute; left: 30px; top: 20px\"\n  >\n    x\n  </button>\n</div>\n\n<p>\n  <!-- These links touch, images overflow on the left and right -->\n  <a href=\"#\" id=\"incomplete5\"\n    >&nbsp;\n    <img\n      src=\"https://www.w3.org/StyleSheets/TR/2016/logos/W3C\"\n      width=\"50\"\n      height=\"33\"\n      alt=\"M1\"\n      style=\"float: left\"\n    />\n  </a>\n  <a href=\"#\" id=\"incomplete6\"\n    >&nbsp;\n    <img\n      src=\"https://www.w3.org/StyleSheets/TR/2016/logos/W3C\"\n      width=\"50\"\n      height=\"33\"\n      alt=\"M4\"\n      style=\"position: absolute\"\n    />\n  </a>\n</p>\n\n<p>\n  <!-- These links touch, images overflow on the top and bottom -->\n  <a href=\"#\" id=\"incomplete7\">\n    <img\n      src=\"https://www.w3.org/StyleSheets/TR/2016/logos/W3C\"\n      width=\"50\"\n      height=\"33\"\n      alt=\"M3\"\n    />\n  </a>\n  <br />\n  <a href=\"#\" id=\"incomplete8\" style=\"display: inline-block; height: 18px\">\n    <img\n      src=\"https://www.w3.org/StyleSheets/TR/2016/logos/W3C\"\n      width=\"50\"\n      height=\"33\"\n      alt=\"M2\"\n    />\n  </a>\n</p>\n\n<!-- Inapplicable -->\n<p>\n  The quick <a href=\"#\" id=\"inapplicable1\">brown</a><br />\n  fox jumped <a href=\"#\" id=\"inapplicable2\">over</a> the lazy dog.\n</p>\n<ul>\n  <li>Inapplicable <a href=\"#\" id=\"inapplicable3\">Item 1</a></li>\n  <li>Inapplicable <a href=\"#\" id=\"inapplicable4\">Item 2</a></li>\n</ul>\n\n<svg>\n  <a href=\"#\" id=\"inapplicable5\"><text y=\"15\">Hello</text></a>\n  <circle\n    tabindex=\"0\"\n    role=\"button\"\n    cx=\"50\"\n    cy=\"50\"\n    r=\"40\"\n    stroke=\"black\"\n    stroke-width=\"3\"\n    fill=\"red\"\n  />\n</svg>\n\n<img usemap=\"#imagemap\" src=\"#\" alt=\"\" />\n<map name=\"imagemap\">\n  <area id=\"inapplicable6\" shape=\"circle\" coords=\"75 75 75\" href=\"#\" />\n</map>\n"
  },
  {
    "path": "test/integration/rules/target-size/target-size.json",
    "content": "{\n  \"description\": \"target-size test\",\n  \"rule\": \"target-size\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"],\n    [\"#fail7\"],\n    [\"#fail-fixed\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"],\n    [\"#pass13\"],\n    [\"#pass14\"],\n    [\"#pass15\"],\n    [\"#pass16\"],\n    [\"#pass17\"],\n    [\"#pass18\"],\n    [\"#pass19\"],\n    [\"#pass20\"],\n    [\"#pass21\"],\n    [\"#pass22\"],\n    [\"#pass23\"],\n    [\"#pass24\"],\n    [\"#pass-adjacent\"],\n    [\"#pass-fixed\"]\n  ],\n  \"incomplete\": [\n    [\"#incomplete1\"],\n    [\"#incomplete2\"],\n    [\"#incomplete3\"],\n    [\"#incomplete4\"],\n    [\"#incomplete5\"],\n    [\"#incomplete6\"],\n    [\"#incomplete7\"],\n    [\"#incomplete8\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/td-has-header/td-has-header.html",
    "content": "<table id=\"ignored1\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"ignored2\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass1\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n    <th>AXE</th>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass2\">\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass3\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n    <th>AXE</th>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass4\">\n  <tr>\n    <td role=\"columnheader\">AXE</td>\n    <td role=\"columnheader\">AXE</td>\n    <td role=\"columnheader\">AXE</td>\n    <td role=\"columnheader\">AXE</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass5\">\n  <tr>\n    <td role=\"rowheader\">AXE</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td role=\"rowheader\">AXE</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td role=\"rowheader\">AXE</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td role=\"rowheader\">AXE</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"fail1\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"fail2\">\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n  <tr>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n    <td>axe</td>\n  </tr>\n</table>\n"
  },
  {
    "path": "test/integration/rules/td-has-header/td-has-header.json",
    "content": "{\n  \"description\": \"td-has-header test\",\n  \"rule\": \"td-has-header\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/td-headers-attr/td-headers-attr.html",
    "content": "<table id=\"pass1\">\n  <th>Hello</th>\n  <td>World</td>\n</table>\n\n<table id=\"pass2\" role=\"grid\">\n  <th id=\"p2h\">Hello</th>\n  <td headers=\"p2h\">World</td>\n</table>\n\n<table id=\"pass3\" role=\"treegrid\">\n  <th id=\"p3h1\">Hello</th>\n  <th id=\"p3h2\">Hello</th>\n  <td headers=\"p3h1 p3h2\">World</td>\n</table>\n\n<table id=\"pass4\" role=\"treegrid\">\n  <th id=\"p4h\">Hello</th>\n  <td id=\"self\" headers=\"self\" hidden>World</td>\n</table>\n\n<table id=\"pass5\">\n  <td role=\"rowheader\" id=\"hdr1\">Hello</td>\n  <td headers=\"hdr1\">World</td>\n</table>\n\n<table id=\"fail1\">\n  <th id=\"f1h1\">Hello</th>\n  <td headers=\"f1h1 non-existing\">World</td>\n</table>\n\n<table id=\"fail2\" role=\"table\">\n  <td id=\"self\" headers=\"self\">World</td>\n</table>\n\n<table id=\"fail3\" role=\"none\" tabindex=\"0\">\n  <td id=\"self\" headers=\"self\">World</td>\n</table>\n\n<table id=\"fail4\">\n  <td id=\"hdr1\">Hello</td>\n  <td headers=\"hdr1\">World</td>\n</table>\n\n<table id=\"fail5\">\n  <th role=\"cell\" id=\"th-role-cell-hdr\">Hello</th>\n  <td headers=\"th-role-cell-hdr\">World</td>\n</table>\n\n<table id=\"fail6\">\n  <th role=\"button\" id=\"th-role-button-hdr\">Hello</th>\n  <td headers=\"th-role-button-hdr\">World</td>\n</table>\n\n<table id=\"inapplicable1\" role=\"none\">\n  <td id=\"self\" headers=\"self\">World</td>\n</table>\n\n<table id=\"inapplicable2\" role=\"presentation\">\n  <td id=\"self\" headers=\"self\">World</td>\n</table>\n\n<table id=\"inapplicable3\" role=\"region\">\n  <td id=\"self\" headers=\"self\">World</td>\n</table>\n"
  },
  {
    "path": "test/integration/rules/td-headers-attr/td-headers-attr.json",
    "content": "{\n  \"description\": \"td-headers-attr test\",\n  \"rule\": \"td-headers-attr\",\n  \"violations\": [\n    [\"#fail1\"],\n    [\"#fail2\"],\n    [\"#fail3\"],\n    [\"#fail4\"],\n    [\"#fail5\"],\n    [\"#fail6\"]\n  ],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"], [\"#pass3\"], [\"#pass4\"], [\"#pass5\"]]\n}\n"
  },
  {
    "path": "test/integration/rules/th-has-data-cells/th-has-data-cells.html",
    "content": "<table id=\"ignored1\" role=\"presentation\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <th>axe</th>\n  </tr>\n</table>\n\n<table id=\"pass1\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass2\">\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass3\">\n  <tr>\n    <td headers=\"hd1\">axe</td>\n    <th id=\"hd1\">AXE</th>\n  </tr>\n</table>\n\n<table id=\"pass4\">\n  <tr>\n    <td aria-labelledby=\"hd2\">axe</td>\n    <th id=\"hd2\">AXE</th>\n  </tr>\n</table>\n\n<table id=\"pass5\">\n  <tr>\n    <th></th>\n  </tr>\n</table>\n\n<table id=\"pass6\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <th>AXE</th>\n    <td>axe</td>\n  </tr>\n</table>\n\n<table id=\"pass7\">\n  <tr>\n    <th>AXE</th>\n    <td><input type=\"text\" /></td>\n  </tr>\n</table>\n\n<table id=\"pass8\">\n  <tr>\n    <th>hi</th>\n    <td></td>\n  </tr>\n  <tr>\n    <th>hi</th>\n    <td></td>\n  </tr>\n</table>\n\n<table id=\"canttell1\">\n  <tr>\n    <td>axe</td>\n    <th>AXE</th>\n  </tr>\n</table>\n\n<table id=\"canttell2\">\n  <tr>\n    <th>AXE</th>\n  </tr>\n  <tr>\n    <th>AXE</th>\n  </tr>\n</table>\n\n<table id=\"canttell3\">\n  <tr>\n    <th>AXE</th>\n    <th>AXE</th>\n  </tr>\n</table>\n\n<table id=\"canttell4\">\n  <tr>\n    <td>axe</td>\n    <td role=\"columnheader\">AXE</td>\n  </tr>\n</table>\n"
  },
  {
    "path": "test/integration/rules/th-has-data-cells/th-has-data-cells.json",
    "content": "{\n  \"description\": \"th-has-data-cells test\",\n  \"rule\": \"th-has-data-cells\",\n  \"incomplete\": [\n    [\"#canttell1\"],\n    [\"#canttell2\"],\n    [\"#canttell3\"],\n    [\"#canttell4\"]\n  ],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/valid-lang/valid-lang.html",
    "content": "<p lang=\"en-US\" id=\"pass1\">English</p>\n<p xml:lang=\"en-US\" id=\"pass2\">English</p>\n<p xml:lang=\"en-US\" lang=\"en-US\" id=\"pass3\">Mix</p>\n<p xml:lang=\"de\" lang=\"de\" id=\"pass4\">Deutsch</p>\n<p lang=\"af\" id=\"pass5\">Afrikaans</p>\n<p lang=\"oma\" id=\"pass6\">Omaha (3 letter)</p>\n<p lang=\"isv\" id=\"pass7\">Interslavic</p>\n\n<p lang=\"gibberish-US\" id=\"fail1\">Not English</p>\n<p xml:lang=\"gibberish-US\" id=\"fail2\">Not English</p>\n<p xml:lang=\"en-US\" lang=\"gibberish-US\" id=\"fail3\">Mix</p>\n<p lang=\" \" id=\"fail4\">English</p>\n\n<p lang=\"invalid\" id=\"pass8\"><!-- ignore --></p>\n<p lang=\"invalid\" id=\"pass9\"><img alt=\"\" /></p>\n<p lang=\"invalid\" id=\"pass10\">\n  <span lang=\"en\" id=\"pass11\">English</span>\n</p>\n<p lang=\"hidden\" id=\"pass12\">\n  <span hidden>English</span>\n</p>\n\n<article lang=\"english\" id=\"fail5\">\n  <p aria-hidden=\"true\">\n    They wandered into a strange Tiki bar on the edge of the small beach town.\n  </p>\n</article>\n"
  },
  {
    "path": "test/integration/rules/valid-lang/valid-lang.json",
    "content": "{\n  \"description\": \"valid-lang test\",\n  \"rule\": \"valid-lang\",\n  \"violations\": [[\"#fail1\"], [\"#fail2\"], [\"#fail3\"], [\"#fail4\"], [\"#fail5\"]],\n  \"passes\": [\n    [\"#pass1\"],\n    [\"#pass2\"],\n    [\"#pass3\"],\n    [\"#pass4\"],\n    [\"#pass5\"],\n    [\"#pass6\"],\n    [\"#pass7\"],\n    [\"#pass8\"],\n    [\"#pass9\"],\n    [\"#pass10\"],\n    [\"#pass11\"],\n    [\"#pass12\"]\n  ]\n}\n"
  },
  {
    "path": "test/integration/rules/video-caption/video-caption.html",
    "content": "<video id=\"incomplete1\"></video>\n<video id=\"incomplete2\"><track kind=\"descriptions\" /></video>\n<video id=\"pass1\"><track kind=\"captions\" /></video>\n<video id=\"pass2\">\n  <track kind=\"descriptions\" />\n  <track kind=\"captions\" />\n</video>\n\n<!-- Inapplicable -->\n<!-- inapplicable 1 -->\n<video src=\"\" controls style=\"display: none\"></video>\n"
  },
  {
    "path": "test/integration/rules/video-caption/video-caption.json",
    "content": "{\n  \"description\": \"video-caption test\",\n  \"rule\": \"video-caption\",\n  \"incomplete\": [[\"#incomplete1\"], [\"#incomplete2\"]],\n  \"passes\": [[\"#pass1\"], [\"#pass2\"]]\n}\n"
  },
  {
    "path": "test/integration/virtual-rules/area-alt.js",
    "content": "describe('area-alt virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        'aria-label': 'foobar'\n      }\n    });\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for alt', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        alt: 'foobar'\n      }\n    });\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    child.children = [];\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        alt: ' \\t   \\n   '\n      }\n    });\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        'aria-label': ''\n      }\n    });\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'map'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'area',\n      attributes: {\n        href: 'foobar',\n        title: ''\n      }\n    });\n    child.children = [];\n    child.parent = node;\n    node.children = [child];\n\n    var results = axe.runVirtualRule('area-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-allowed-attr.js",
    "content": "describe('aria-allowed-attr virtual-rule', function () {\n  it('should pass for required attributes', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for allowed attributes', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'radio',\n        'aria-required': true,\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for invalid attributes', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'dialog',\n        'aria-cats': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for global attributes and element with no role', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        'aria-busy': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for non-global attributes and element with no role', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for unallowed attributes', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'link',\n        'aria-selected': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for unallowed attributes - implicit role', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'a',\n      attributes: {\n        href: '#',\n        'aria-selected': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for unsupported attributes', function () {\n    axe.configure({\n      standards: {\n        ariaAttrs: {\n          'aria-mccheddarton': {\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        'aria-mccheddarton': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for non-global attributes and custom element', function () {\n    var results = axe.runVirtualRule('aria-allowed-attr', {\n      nodeName: 'custom-elm1',\n      attributes: {\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-allowed-role.js",
    "content": "describe('aria-allowed-role virtual-rule', function () {\n  afterEach(function () {\n    axe.reset();\n  });\n\n  it('should pass for allowed role', function () {\n    var results = axe.runVirtualRule('aria-allowed-role', {\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for unallowed role', function () {\n    var results = axe.runVirtualRule('aria-allowed-role', {\n      nodeName: 'dd',\n      attributes: {\n        role: 'link'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for element with ignored option', function () {\n    axe.configure({\n      checks: [\n        {\n          id: 'aria-allowed-role',\n          options: {\n            ignoredTags: ['dd']\n          }\n        }\n      ]\n    });\n\n    var results = axe.runVirtualRule('aria-allowed-role', {\n      nodeName: 'dd',\n      attributes: {\n        role: 'link'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for hidden element', function () {\n    var results = axe.runVirtualRule('aria-allowed-role', {\n      nodeName: 'dd',\n      attributes: {\n        'aria-hidden': true,\n        role: 'link'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should incomplete for hidden element parent', function () {\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'dd',\n      attributes: {\n        role: 'link'\n      }\n    });\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-hidden': true\n      }\n    });\n    parent.children = [vNode];\n    vNode.parent = parent;\n\n    var results = axe.runVirtualRule('aria-allowed-role', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-braille-equivalent.js",
    "content": "describe('aria-braille-equivalent virtual-rule', () => {\n  afterEach(() => {\n    axe.reset();\n  });\n\n  it('passes when aria-braillelabel is not empty', () => {\n    const results = axe.runVirtualRule('aria-braille-equivalent', {\n      nodeName: 'img',\n      attributes: {\n        alt: 'Hello world',\n        'aria-braillelabel': 'Hello world'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('incompletes when accessible text is empty but braille label is not', () => {\n    const results = axe.runVirtualRule('aria-braille-equivalent', {\n      nodeName: 'img',\n      attributes: {\n        alt: '',\n        'aria-braillelabel': 'hello world'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('passes when roledescription and brailleroledescription are not empty', () => {\n    const results = axe.runVirtualRule('aria-braille-equivalent', {\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Hello world',\n        'aria-brailleroledescription': 'Hello world'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('incompletes when roledescription is empty but brailleroledescription is not', () => {\n    const results = axe.runVirtualRule('aria-braille-equivalent', {\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': '',\n        'aria-brailleroledescription': 'Hello world'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('incompletes if the subtree fails to compute with aria-braillelabel', () => {\n    const results = axe.runVirtualRule('aria-braille-equivalent', {\n      nodeName: 'button',\n      attributes: {\n        'aria-braillelabel': 'Hello world'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-command-name.js",
    "content": "describe('aria-command-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'link',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-command-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-command-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitem',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-command-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'link',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-command-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-command-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-conditional-attr.js",
    "content": "describe('aria-conditional-attr virtual-rule', function () {\n  it('passes when aria-checked is consistent with native checkbox state', () => {\n    const results = axe.runVirtualRule('aria-conditional-attr', {\n      nodeName: 'input',\n      checked: true,\n      attributes: {\n        type: 'checkbox',\n        'aria-checked': 'true'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('fails when aria-checked is inconsistent with native checkbox state', () => {\n    const results = axe.runVirtualRule('aria-conditional-attr', {\n      nodeName: 'input',\n      checked: true,\n      attributes: {\n        type: 'checkbox',\n        'aria-checked': 'false'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('passes conditional row attributes on detached rows', () => {\n    const results = axe.runVirtualRule('aria-conditional-attr', {\n      nodeName: 'tr',\n      attributes: {\n        role: 'row',\n        'aria-level': '1',\n        'aria-posinset': '1',\n        'aria-setsize': '1',\n        'aria-expanded': 'true'\n      }\n    });\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('fails conditional row attributes on table', () => {\n    const table = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n    const tr = new axe.SerialVirtualNode({\n      nodeName: 'tr',\n      attributes: {\n        role: 'row',\n        'aria-level': '1',\n        'aria-posinset': '1',\n        'aria-setsize': '1',\n        'aria-expanded': 'true'\n      }\n    });\n    const td = new axe.SerialVirtualNode({ nodeName: 'td' });\n    table.children = [tr];\n    tr.children = [td];\n    td.parent = tr;\n    tr.parent = table;\n    table.parent = null;\n\n    const results = axe.runVirtualRule('aria-conditional-attr', tr);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n\n    const { invalidAttrs } = results.violations[0].nodes[0].all[0].data;\n    invalidAttrs.sort();\n    assert.deepEqual(invalidAttrs, [\n      'aria-expanded',\n      'aria-level',\n      'aria-posinset',\n      'aria-setsize'\n    ]);\n  });\n\n  it('passes conditional row attributes on treegrid', () => {\n    const table = new axe.SerialVirtualNode({\n      nodeName: 'table',\n      attributes: { role: 'treegrid' }\n    });\n    const tr = new axe.SerialVirtualNode({\n      nodeName: 'tr',\n      attributes: {\n        role: 'row',\n        'aria-level': '1',\n        'aria-posinset': '1',\n        'aria-setsize': '1',\n        'aria-expanded': 'true'\n      }\n    });\n    const td = new axe.SerialVirtualNode({ nodeName: 'td' });\n    table.children = [tr];\n    tr.children = [td];\n    td.parent = tr;\n    tr.parent = table;\n    table.parent = null;\n\n    const results = axe.runVirtualRule('aria-conditional-attr', tr);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-dialog-name.js",
    "content": "describe('aria-dialog-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'dialog',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-dialog-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'dialog',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-dialog-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'alertdialog',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-dialog-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'alertdialog',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-dialog-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'dialog',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-dialog-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-input-field-name.js",
    "content": "describe('aria-input-field-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'combobox',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'listbox',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'searchbox',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'spinbutton',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'textbox',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'combobox',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete if has explicit and implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'listbox',\n        'aria-label': 'name'\n      }\n    });\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'first name'\n    });\n    node.parent = parent;\n    node.children = [];\n    parent.children = [child, node];\n\n    var results = axe.runVirtualRule('aria-input-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-meter-name.js",
    "content": "describe('aria-meter-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'meter',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-meter-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'meter',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-meter-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'meter',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-meter-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'meter',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-meter-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'meter',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-meter-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-progressbar-name.js",
    "content": "describe('aria-progressbar-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'progressbar',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-progressbar-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'progressbar',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-progressbar-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'progressbar',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-progressbar-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'progressbar',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-progressbar-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'progressbar',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-progressbar-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-prohibited-attr.js",
    "content": "describe('aria-prohibited-attr virtual-rule', () => {\n  it('should pass for required attributes', () => {\n    const results = axe.runVirtualRule('aria-prohibited-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for allowed attributes', () => {\n    const results = axe.runVirtualRule('aria-prohibited-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'radio',\n        'aria-required': true,\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for invalid attributes', () => {\n    const results = axe.runVirtualRule('aria-prohibited-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'dialog',\n        'aria-cats': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for prohibited attributes', () => {\n    const vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'code',\n        'aria-label': 'foo'\n      }\n    });\n    vNode.children = [];\n\n    const results = axe.runVirtualRule('aria-prohibited-attr', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid role', () => {\n    const vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'foo',\n        'aria-label': 'foo'\n      }\n    });\n    vNode.children = [];\n\n    const results = axe.runVirtualRule('aria-prohibited-attr', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for fallback roles', () => {\n    const results = axe.runVirtualRule('aria-prohibited-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'foo dialog',\n        'aria-label': 'foo'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for multiple invalid roles', () => {\n    const vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'foo bar',\n        'aria-label': 'foo'\n      }\n    });\n    vNode.children = [];\n\n    const results = axe.runVirtualRule('aria-prohibited-attr', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-required-attr.js",
    "content": "describe('aria-required-attr virtual-rule', function () {\n  it('should pass for required attributes', function () {\n    var results = axe.runVirtualRule('aria-required-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        'aria-checked': true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for missing attributes', function () {\n    var results = axe.runVirtualRule('aria-required-attr', {\n      nodeName: 'div',\n      attributes: {\n        role: 'switch'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-roledescription.js",
    "content": "describe('aria-roledescription virtual-rule', function () {\n  it('should pass for elements with an implicit supported role', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        'aria-roledescription': 'Awesome Button'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for elements with an explicit supported role', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Awesome Radio',\n        role: 'radio'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for elements with an unsupported role', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Awesome Main',\n        role: 'main'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail for elements without role', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Awesome Main'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for elements with role=presentation', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Awesome Main',\n        role: 'presentation'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for elements with role=none', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        'aria-roledescription': 'Awesome Main',\n        role: 'none'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roledescription', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-roles.js",
    "content": "describe('aria-roles virtual-rule', function () {\n  it('should pass for valid role', function () {\n    var results = axe.runVirtualRule('aria-roles', {\n      nodeName: 'div',\n      attributes: {\n        role: 'alert'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for abstract role', function () {\n    var results = axe.runVirtualRule('aria-roles', {\n      nodeName: 'div',\n      attributes: {\n        role: 'command'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid role', function () {\n    var results = axe.runVirtualRule('aria-roles', {\n      nodeName: 'div',\n      attributes: {\n        role: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for fallback role', function () {\n    var results = axe.runVirtualRule('aria-roles', {\n      nodeName: 'div',\n      attributes: {\n        role: 'presentation none'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for unsupported role', function () {\n    axe.configure({\n      standards: {\n        ariaRoles: {\n          alert: {\n            unsupported: true\n          }\n        }\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-roles', {\n      nodeName: 'div',\n      attributes: {\n        role: 'alert'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-tab-name.js",
    "content": "describe('aria-tab-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tab',\n        'aria-label': 'tabname'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tab-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tab',\n        'aria-labelledby': 'tabname'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tab-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tab',\n        title: 'tabname'\n      }\n    });\n\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tab-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tab',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-tab-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tab',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-tab-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-text.js",
    "content": "describe('aria-text virtual-rule', function () {\n  it('should incomplete for element with undefined children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail for focusable widget children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button',\n        tabindex: 0\n      }\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for children with native focus', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass if element only has descendants that are not focusable', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'span'\n    });\n    var grandchild = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'hello'\n    });\n\n    child.children = [grandchild];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for deeply nested focusable children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'div'\n    });\n    var grandchild = new axe.SerialVirtualNode({\n      nodeName: 'div'\n    });\n    var greatgrandchild = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    greatgrandchild.parent = grandchild;\n    grandchild.children = [greatgrandchild];\n    child.children = [grandchild];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when tabIndex is negative', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        tabindex: '-1',\n        href: '#'\n      }\n    });\n\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass when an anchor has no href and has implicit role of link', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        tabindex: '-1'\n      }\n    });\n    child.children = [];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.passes, 1);\n  });\n\n  it('should incomplete when vNode has no children and is type 1', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text',\n        nodeType: 1\n      }\n    });\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n    assert.lengthOf(results.passes, 0);\n  });\n\n  it('should fail when tabIndex is NaN', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'text'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        tabindex: 'foo',\n        href: '#'\n      }\n    });\n\n    node.children = [child];\n\n    var results = axe.runVirtualRule('aria-text', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-toggle-field-name.js",
    "content": "describe('aria-toggle-field-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemcheckbox',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('aria-toggle-field-name', {\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemradio',\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'checkbox',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for visible text content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'radio'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.children = [child];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for hidden text', function () {\n    var button = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'radio'\n      }\n    });\n\n    var span = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        'aria-hidden': true\n      }\n    });\n    span.parent = button;\n    button.children = [span];\n\n    var text = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    text.parent = span;\n    span.children = [text];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', button);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when children are missing', function () {\n    var results = axe.runVirtualRule('aria-toggle-field-name', {\n      nodeName: 'div',\n      attributes: {\n        role: 'switch'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail children contain no visible text', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'option'\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemcheckbox',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemcheckbox',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemcheckbox',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete if has explicit and implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'menuitemcheckbox',\n        'aria-label': 'name'\n      }\n    });\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'first name'\n    });\n    node.parent = parent;\n    node.children = [];\n    parent.children = [child, node];\n\n    var results = axe.runVirtualRule('aria-toggle-field-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-tooltip-name.js",
    "content": "describe('aria-tooltip-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tooltip',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tooltip-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tooltip',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tooltip-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tooltip',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-tooltip-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tooltip',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-tooltip-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'tooltip',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-tooltip-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-treeitem-name.js",
    "content": "describe('aria-treeitem-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'treeitem',\n        'aria-label': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-treeitem-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'treeitem',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-treeitem-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'treeitem',\n        title: 'foobar'\n      }\n    });\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = null;\n\n    var results = axe.runVirtualRule('aria-treeitem-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'treeitem',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-treeitem-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'treeitem',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('aria-treeitem-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-valid-attr-value.js",
    "content": "describe('aria-valid-attr-value virtual-rule', function () {\n  it('should pass for valid values', function () {\n    var results = axe.runVirtualRule('aria-valid-attr-value', {\n      nodeName: 'div',\n      attributes: {\n        role: 'slider',\n        'aria-valuemin': 1,\n        'aria-valuetext': '10',\n        'aria-expanded': false,\n        'aria-haspopup': 'grid',\n        'aria-valuenow': 2.1\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid values', function () {\n    var results = axe.runVirtualRule('aria-valid-attr-value', {\n      nodeName: 'div',\n      attributes: {\n        role: 'slider',\n        'aria-valuemin': true,\n        'aria-expanded': 'grid',\n        'aria-haspopup': 'Range',\n        'aria-valuenow': false\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n\n    assert.lengthOf(results.violations[0].nodes[0].all[0].data, 4);\n  });\n\n  it('should only mark invalid values', function () {\n    var results = axe.runVirtualRule('aria-valid-attr-value', {\n      nodeName: 'div',\n      attributes: {\n        role: 'slider',\n        // valid\n        'aria-valuemin': 0,\n        'aria-valuetext': '10',\n        // invalid\n        'aria-expanded': 'grid',\n        'aria-haspopup': 'Range',\n        'aria-valuenow': false\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n\n    assert.lengthOf(results.violations[0].nodes[0].all[0].data, 3);\n  });\n\n  it('should incomplete for idref attributes', function () {\n    var results = axe.runVirtualRule('aria-valid-attr-value', {\n      nodeName: 'div',\n      attributes: {\n        role: 'slider',\n        'aria-labelledby': 'el1'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for empty idref attributes', function () {\n    var results = axe.runVirtualRule('aria-valid-attr-value', {\n      nodeName: 'div',\n      attributes: {\n        role: 'slider',\n        'aria-labelledby': ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/aria-valid-attr.js",
    "content": "describe('aria-valid-attr virtual-rule', function () {\n  it('should pass for valid aria attrs', function () {\n    var results = axe.runVirtualRule('aria-valid-attr', {\n      nodeName: 'button',\n      attributes: {\n        'aria-expanded': true,\n        'aria-label': 'Expand'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid aria attr', function () {\n    var results = axe.runVirtualRule('aria-valid-attr', {\n      nodeName: 'button',\n      attributes: {\n        'aria-expanded': true,\n        'aria-undefined': 'Expand'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass when configured to accept attribute names', function () {\n    axe.configure({\n      checks: [\n        {\n          id: 'aria-valid-attr',\n          options: ['aria-undefined']\n        }\n      ]\n    });\n\n    var results = axe.runVirtualRule('aria-valid-attr', {\n      nodeName: 'button',\n      attributes: {\n        'aria-expanded': true,\n        'aria-undefined': 'Expand'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/autocomplete-valid.js",
    "content": "describe('autocomplete-valid virtual-rule', function () {\n  it('should pass when autocomplete is valid', function () {\n    var results = axe.runVirtualRule('autocomplete-valid', {\n      nodeName: 'input',\n      attributes: {\n        type: 'text',\n        autocomplete: 'country-name'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass when autocomplete is correctly used', function () {\n    var results = axe.runVirtualRule('autocomplete-valid', {\n      nodeName: 'input',\n      attributes: {\n        type: 'text',\n        autocomplete: 'email'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail autocomplete is not valid', function () {\n    var results = axe.runVirtualRule('autocomplete-valid', {\n      nodeName: 'input',\n      attributes: {\n        type: 'text',\n        autocomplete: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/button-name.js",
    "content": "describe('button-name virtual-rule', () => {\n  it('should pass for aria-label', () => {\n    const results = axe.runVirtualRule('button-name', {\n      nodeName: 'button',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', () => {\n    const results = axe.runVirtualRule('button-name', {\n      nodeName: 'button',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation when disabled', () => {\n    const results = axe.runVirtualRule('button-name', {\n      nodeName: 'button',\n      attributes: {\n        role: 'presentation',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none when disabled', () => {\n    const results = axe.runVirtualRule('button-name', {\n      nodeName: 'button',\n      attributes: {\n        role: 'none',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for visible text content', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    const child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.children = [child];\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for hidden text', () => {\n    const button = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n\n    const span = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        'aria-hidden': true\n      }\n    });\n    span.parent = button;\n    button.children = [span];\n\n    const text = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    text.parent = span;\n    span.children = [text];\n    button.parent = null;\n\n    const results = axe.runVirtualRule('button-name', button);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when alt and children are missing', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {}\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail children contain no visible text', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt contains only whitespace', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        alt: ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        alt: ''\n      }\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        title: ''\n      }\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for implicit label', function () {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    const parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    const child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.parent = parent;\n    node.children = [];\n    parent.children = [child, node];\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for explicit label', function () {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        id: 'foobar'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail for role=presentation', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=none', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        role: 'none'\n      }\n    });\n    node.children = [];\n    node.parent = null;\n\n    const results = axe.runVirtualRule('button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/definition-list.js",
    "content": "describe('definition-list virtual-rule', () => {\n  it('passes when there are no invalid child nodes', () => {\n    const dl = new axe.SerialVirtualNode({ nodeName: 'dl' });\n    const dt = new axe.SerialVirtualNode({ nodeName: 'dt' });\n    const dd = new axe.SerialVirtualNode({ nodeName: 'dd' });\n    dl.children = [dt, dd];\n    dt.parent = dl;\n    dd.parent = dl;\n\n    const results = axe.runVirtualRule('definition-list', dl);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('fails when there no dd / dt pair', () => {\n    const dl = new axe.SerialVirtualNode({ nodeName: 'dl' });\n    const dd1 = new axe.SerialVirtualNode({ nodeName: 'dd' });\n    const dd2 = new axe.SerialVirtualNode({ nodeName: 'dd' });\n    dl.children = [dd1, dd2];\n    dd1.parent = dl;\n    dd2.parent = dl;\n\n    const results = axe.runVirtualRule('definition-list', dl);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('fails when there is an invalid child nodes', () => {\n    const dl = new axe.SerialVirtualNode({ nodeName: 'dl' });\n    const span = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {}\n    });\n    dl.children = [span];\n    span.parent = dl;\n\n    const results = axe.runVirtualRule('definition-list', dl);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('is incomplete without child nodes', () => {\n    const dl = new axe.SerialVirtualNode({ nodeName: 'dl' });\n    const results = axe.runVirtualRule('definition-list', dl);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/empty-heading.js",
    "content": "describe('empty-heading virtual-rule', function () {\n  it('should pass with visible text', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'h1',\n      attributes: {}\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'OK',\n      attributes: {}\n    });\n\n    node.children = [child];\n\n    var results = axe.runVirtualRule('empty-heading', node);\n\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.passes, 1);\n  });\n\n  it('should incomplete if no other properties are set', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'h1'\n    });\n\n    var results = axe.runVirtualRule('empty-heading', node);\n\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n    assert.lengthOf(results.passes, 0);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('empty-heading', {\n      nodeName: 'h1',\n      attributes: {\n        title: 'it has a title'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass on explicit role', function () {\n    var results = axe.runVirtualRule('empty-heading', {\n      nodeName: 'span',\n      attributes: {\n        role: 'heading',\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass on implicit role', function () {\n    var results = axe.runVirtualRule('empty-heading', {\n      nodeName: 'h1',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria-label', function () {\n    var results = axe.runVirtualRule('empty-heading', {\n      nodeName: 'h1',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'h1',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('empty-heading', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('empty-heading', {\n      nodeName: 'h1',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'h1',\n      attributes: {\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('empty-heading', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail in order to account for presentation conflict resolution', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'h1',\n      attributes: {\n        role: 'none',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('empty-heading', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/empty-table-header.js",
    "content": "describe('empty-table-header virtual-rule', function () {\n  it('should fail when children contain no visible text', function () {\n    var thNode = new axe.SerialVirtualNode({\n      nodeName: 'th'\n    });\n    thNode.children = [];\n\n    var results = axe.runVirtualRule('empty-table-header', thNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when children are missing', function () {\n    var thNode = new axe.SerialVirtualNode({\n      nodeName: 'th'\n    });\n\n    var results = axe.runVirtualRule('empty-table-header', thNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail for role=rowheader', function () {\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'rowheader'\n      }\n    });\n    vNode.children = [];\n\n    var results = axe.runVirtualRule('empty-table-header', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=columnheader', function () {\n    var vNode = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'columnheader'\n      }\n    });\n    vNode.children = [];\n\n    var results = axe.runVirtualRule('empty-table-header', vNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with a table header', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n\n    var trNode = new axe.SerialVirtualNode({\n      nodeName: 'tr'\n    });\n    trNode.parent = tableNode;\n\n    var thNode = new axe.SerialVirtualNode({\n      nodeName: 'th'\n    });\n    thNode.parent = trNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    textNode.parent = thNode;\n\n    thNode.children = [textNode];\n    trNode.children = [thNode];\n    tableNode.children = [trNode];\n\n    var results = axe.runVirtualRule('empty-table-header', tableNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with scope of row', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n\n    var trNode = new axe.SerialVirtualNode({\n      nodeName: 'tr'\n    });\n    trNode.parent = tableNode;\n\n    var thNode = new axe.SerialVirtualNode({\n      nodeName: 'th',\n      attributes: {\n        scope: 'row'\n      }\n    });\n    thNode.parent = trNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    textNode.parent = thNode;\n\n    thNode.children = [textNode];\n    trNode.children = [thNode];\n    tableNode.children = [trNode];\n    var results = axe.runVirtualRule('empty-table-header', thNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with scope of col', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n\n    var trNode = new axe.SerialVirtualNode({\n      nodeName: 'tr'\n    });\n    trNode.parent = tableNode;\n\n    var thNode = new axe.SerialVirtualNode({\n      nodeName: 'th',\n      attributes: {\n        scope: 'col'\n      }\n    });\n    thNode.parent = trNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    textNode.parent = thNode;\n\n    thNode.children = [textNode];\n    trNode.children = [thNode];\n    tableNode.children = [trNode];\n\n    var results = axe.runVirtualRule('empty-table-header', thNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with a table definition of role rowheader', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'td',\n      attributes: {\n        role: 'rowheader'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('empty-table-header', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should be inapplicable when the th has role of cell', function () {\n    var table = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n\n    var tr = new axe.SerialVirtualNode({\n      nodeName: 'tr'\n    });\n\n    var th = new axe.SerialVirtualNode({\n      nodeName: 'th',\n      attributes: {\n        role: 'cell'\n      }\n    });\n\n    tr.children = [th];\n    tr.parent = table;\n    th.parent = tr;\n    th.children = [];\n    table.children = [tr];\n\n    var results = axe.runVirtualRule('empty-table-header', th);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.inapplicable, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/frame-title.js",
    "content": "describe('frame-title virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        role: 'none'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        title: ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=presentation and title', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        role: 'presentation',\n        title: ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=none and title', function () {\n    var results = axe.runVirtualRule('frame-title', {\n      nodeName: 'iframe',\n      attributes: {\n        role: 'none',\n        title: ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/html-has-lang.js",
    "content": "describe('html-has-lang virtual-rule', function () {\n  it('should pass for lang', function () {\n    var results = axe.runVirtualRule('html-has-lang', {\n      nodeName: 'html',\n      attributes: {\n        lang: 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for lang and xml:lang', function () {\n    var results = axe.runVirtualRule('html-has-lang', {\n      nodeName: 'html',\n      attributes: {\n        lang: 'en',\n        'xml:lang': 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for missing lang', function () {\n    var results = axe.runVirtualRule('html-has-lang', {\n      nodeName: 'html'\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for just xml:lang', function () {\n    var results = axe.runVirtualRule('html-has-lang', {\n      nodeName: 'html',\n      attributes: {\n        'xml:lang': 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/html-lang-valid.js",
    "content": "describe('html-lang-valid virtual-rule', function () {\n  it('is inapplicable without lang or xml:lang', function () {\n    // Error caught by html-has-lang instead\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {}\n    });\n\n    assert.lengthOf(results.inapplicable, 1);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('is inapplicable with empty lang or xml:lang', function () {\n    // Error caught by html-has-lang instead\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        lang: ''\n      }\n    });\n\n    assert.lengthOf(results.inapplicable, 1);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with a valid lang', function () {\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        lang: 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass with a valid xml:lang', function () {\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        'xml:lang': 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for both lang and xml:lang', function () {\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        lang: 'en',\n        'xml:lang': 'en'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail with an invalid lang', function () {\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        lang: 'invalid'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail with an invalid xml:lang', function () {\n    var results = axe.runVirtualRule('html-lang-valid', {\n      nodeName: 'html',\n      attributes: {\n        'xml:lang': 'invalid'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail with an invalid lang, and explicitly no children', function () {\n    var html = new axe.SerialVirtualNode({\n      nodeName: 'html',\n      attributes: {\n        lang: 'invalid'\n      }\n    });\n    html.children = [];\n\n    var results = axe.runVirtualRule('html-lang-valid', html);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/image-alt.js",
    "content": "describe('image-alt virtual-rule', function () {\n  it('should pass for alt', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        alt: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for empty alt', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        alt: ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria-label', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        role: 'none'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt is missing', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {}\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt contains only whitespace', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        alt: ' \\t   \\n   '\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        title: ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/input-button-name.js",
    "content": "describe('input-button-name virtual-rule', () => {\n  it('should pass for type=submit without value', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'submit'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for value', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        value: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria-label', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation when disabled', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        role: 'presentation',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none when disabled', () => {\n    const results = axe.runVirtualRule('input-button-name', {\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        role: 'none',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for implicit label', function () {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button'\n      }\n    });\n    const parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    const child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.parent = parent;\n    node.children = [];\n    parent.children = [child, node];\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for explicit label', function () {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button'\n      }\n    });\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when no other attributes are passed', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when value is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        value: ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt contains only whitespace', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        alt: ' \\t   \\n   '\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        'aria-label': ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        title: ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=presentation', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        role: 'presentation'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=none', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'button',\n        role: 'none'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-button-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/input-image-alt.js",
    "content": "describe('input-image-alt virtual-rule', () => {\n  it('should pass for alt', () => {\n    const results = axe.runVirtualRule('input-image-alt', {\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        alt: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria-label', () => {\n    const results = axe.runVirtualRule('input-image-alt', {\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        'aria-labelledby': 'foobar'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', () => {\n    const results = axe.runVirtualRule('image-alt', {\n      nodeName: 'img',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for implicit label', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image'\n      }\n    });\n    const parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    const child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.parent = parent;\n    node.children = [];\n    parent.children = [child, node];\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for explicit label', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image'\n      }\n    });\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when alt is missing', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image'\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        alt: ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        'aria-label': ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', () => {\n    const node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        type: 'image',\n        title: ''\n      }\n    });\n    node.parent = null;\n\n    const results = axe.runVirtualRule('input-image-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/label.js",
    "content": "describe('label virtual-rule', function () {\n  it('should not apply if input type is hidden', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        type: 'hidden'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.inapplicable, 1);\n  });\n\n  it('should pass for aria-label (input)', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria-label (textarea)', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'textarea',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input'\n    });\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.parent = parent;\n    parent.children = [child, node];\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for explicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        id: 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation when disabled', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        role: 'presentation',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none when disabled', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        role: 'none',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for both missing aria-label and implicit label', function () {\n    var results = axe.runVirtualRule('label', {\n      nodeName: 'input',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when aria-label contains only whitespace and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        title: ''\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=presentation', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for role=none', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'input',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('label', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/link-name.js",
    "content": "describe('link-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        title: 'foobar'\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when aria-label and children are missing', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html'\n      }\n    });\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete if anchor is still focusable and missing children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        role: 'presentation'\n      }\n    });\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail if anchor is still focusable and no children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        role: 'presentation'\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        href: '/foo.html',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('link-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/list.js",
    "content": "describe('list virtual-rule', () => {\n  it('passes when there are no invalid child nodes', () => {\n    const ul = new axe.SerialVirtualNode({ nodeName: 'ul' });\n    const li = new axe.SerialVirtualNode({ nodeName: 'li' });\n    ul.children = [li];\n    li.parent = ul;\n\n    const results = axe.runVirtualRule('list', ul);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('fails when there is an invalid child node', () => {\n    const ul = new axe.SerialVirtualNode({ nodeName: 'ul' });\n    const span = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {}\n    });\n    ul.children = [span];\n    span.parent = ul;\n\n    const results = axe.runVirtualRule('list', ul);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('is incomplete without child nodes', () => {\n    const ul = new axe.SerialVirtualNode({ nodeName: 'ul' });\n    const results = axe.runVirtualRule('list', ul);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/meta-refresh-no-exceptions.js",
    "content": "describe('meta-refresh-no-exceptions virtual-rule', function () {\n  it('should be inapplicable for missing content', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.inapplicable, 1);\n  });\n\n  it('should pass for content=0', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '0'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for content=0 and url', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '0;url=http://example.com/'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for content equal to 72000', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '72000'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for content other greater than 72000', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '72001'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for content other than 0 and url', function () {\n    var results = axe.runVirtualRule('meta-refresh-no-exceptions', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '72001;url=http://example.com/'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/meta-refresh.js",
    "content": "describe('meta-refresh virtual-rule', function () {\n  it('should be inapplicable for missing content', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.inapplicable, 1);\n  });\n\n  it('should pass for content=0', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '0'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for content=0 and url', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '0;url=http://example.com/'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for content other than 0', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '300'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for content other than 0 and url', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '20;url=http://example.com/'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for content greater than 20 hours', function () {\n    var results = axe.runVirtualRule('meta-refresh', {\n      nodeName: 'meta',\n      attributes: {\n        'http-equiv': 'refresh',\n        content: '72001'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/meta-viewport.js",
    "content": "describe('meta-viewport virtual-rule', function () {\n  it('should pass for missing user-scalable', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'foo=bar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for user-scalable=yes', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'user-scalable=yes'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass comma-separated list', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'foo=bar, user-scalable=yes'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass semicolon-separated list', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'foo=bar; user-scalable=yes'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for user-scalable=no', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'user-scalable=no'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for user-scalable=0', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'user-scalable=0'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for maximum-scale=yes', function () {\n    var results = axe.runVirtualRule('meta-viewport', {\n      nodeName: 'meta',\n      attributes: {\n        name: 'viewport',\n        content: 'maximum-scale=yes'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/nested-interactive.js",
    "content": "describe('nested-interactive virtual-rule', function () {\n  it('should pass for element without focusable content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'Hello World'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for aria element without focusable content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'Hello World'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for element with non-widget content which has negative tabindex', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        tabindex: -1\n      }\n    });\n    child.children = [];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for empty element without', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button'\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for element with non-widget content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'span',\n      attributes: {\n        tabindex: 1\n      }\n    });\n    child.children = [];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for element with native widget content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'button'\n      }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    child.children = [];\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should return incomplete if element has undefined children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should return incomplete if descendant has undefined children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: 'span'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('nested-interactive', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/object-alt.js",
    "content": "describe('object-alt virtual-rule', function () {\n  const data = `data:text/html,Object%20content`;\n\n  it('is inapplicable when the object has no data attribute', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {}\n    });\n    assert.lengthOf(results.inapplicable, 1);\n  });\n\n  it('should pass for aria-label', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {\n        'aria-label': 'foobar',\n        data\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {\n        'aria-labelledby': 'foobar',\n        data\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {\n        title: 'foobar',\n        data\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {\n        role: 'presentation',\n        data\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: {\n        role: 'none',\n        data\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for visible text content', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'object',\n      attributes: { data }\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.children = [child];\n\n    var results = axe.runVirtualRule('object-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt and children are missing', function () {\n    var results = axe.runVirtualRule('object-alt', {\n      nodeName: 'object',\n      attributes: { data }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail children contain no visible text', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'object',\n      attributes: { data }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('object-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when alt contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'object',\n      attributes: {\n        alt: ' \\t   \\n   ',\n        data\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('object-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'object',\n      attributes: {\n        'aria-label': '',\n        data\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('object-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'object',\n      attributes: {\n        title: '',\n        data\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('object-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/presentation-role-conflict.js",
    "content": "describe('presentation-role-conflict virtual-rule', function () {\n  it('fails img[alt=\"\"] with aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'img',\n      attributes: {\n        alt: '',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when role is presentation and aria-label is present', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'li',\n      attributes: {\n        role: 'presentation',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when role is none and aria-label is present', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'li',\n      attributes: {\n        role: 'none',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should be inapplicable when explicit role is presentation for element without conflict', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.inapplicable, 1);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should be inapplicable when explicit role is none for element without conflict', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'none'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.inapplicable, 1);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for element with implicit role in chromium', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail if element has native focusability', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'a',\n      attributes: {\n        role: 'presentation',\n        href: '#'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass if element has native focusability but is disabled', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'button',\n      attributes: {\n        role: 'presentation',\n        disabled: 'disabled'\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail if element is focusable with tabIndex', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'presentation',\n        tabindex: 1\n      }\n    });\n\n    var results = axe.runVirtualRule('presentation-role-conflict', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/role-img-alt.js",
    "content": "describe('role-img-alt virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        title: 'foobar'\n      }\n    });\n\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'div',\n      attributes: {\n        role: 'img',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('role-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/select-name.js",
    "content": "describe('select-name virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        'aria-label': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select'\n    });\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'label'\n    });\n    var child = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n    node.parent = parent;\n    parent.children = [child, node];\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for explicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        id: 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        title: 'foobar'\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation when disabled', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        role: 'presentation',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none when disabled', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        role: 'none',\n        disabled: true\n      }\n    });\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for both missing aria-label and implicit label', function () {\n    var results = axe.runVirtualRule('select-name', {\n      nodeName: 'select',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when aria-label contains only whitespace and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        'aria-label': ''\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty and no implicit label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        title: ''\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=presentation', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        role: 'presentation'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for role=none', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'select',\n      attributes: {\n        role: 'none'\n      }\n    });\n    node.parent = null;\n\n    var results = axe.runVirtualRule('select-name', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/summary-name.js",
    "content": "function appendSerialChild(parent, child) {\n  if (child instanceof axe.SerialVirtualNode === false) {\n    child = new axe.SerialVirtualNode(child);\n  }\n  child.parent = parent;\n  parent.children ??= [];\n  parent.children.push(child);\n  return child;\n}\n\ndescribe('summary-name virtual-rule', () => {\n  let vDetails;\n  beforeEach(() => {\n    vDetails = new axe.SerialVirtualNode({\n      nodeName: 'details',\n      attributes: {}\n    });\n    appendSerialChild(vDetails, { nodeName: '#text', nodeValue: 'text' });\n  });\n\n  it('fails without children', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: {}\n    });\n    vSummary.children = [];\n    appendSerialChild(vDetails, vSummary);\n    const results = axe.runVirtualRule('summary-name', vSummary);\n    console.log(results);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('passes with text content', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: {}\n    });\n    appendSerialChild(vSummary, { nodeName: '#text', nodeValue: 'text' });\n    appendSerialChild(vDetails, vSummary);\n\n    const results = axe.runVirtualRule('summary-name', vSummary);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('passes with aria-label', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: { 'aria-label': 'foobar' }\n    });\n    appendSerialChild(vDetails, vSummary);\n    const results = axe.runVirtualRule('summary-name', vSummary);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('passes with title', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: { title: 'foobar' }\n    });\n    appendSerialChild(vDetails, vSummary);\n    const results = axe.runVirtualRule('summary-name', vSummary);\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('incompletes with aria-labelledby', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: { 'aria-labelledby': 'foobar' }\n    });\n    appendSerialChild(vDetails, vSummary);\n    const results = axe.runVirtualRule('summary-name', vSummary);\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('throws without a parent', () => {\n    const vSummary = new axe.SerialVirtualNode({\n      nodeName: 'summary',\n      attributes: { 'aria-labelledby': 'foobar' }\n    });\n    vSummary.children = [];\n    assert.throws(() => {\n      axe.runVirtualRule('summary-name', vSummary);\n    });\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/svg-img-alt.js",
    "content": "describe('svg-img-alt virtual-rule', function () {\n  it('should pass for aria-label', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img',\n        'aria-label': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete for aria-labelledby', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'graphics-document',\n        'aria-labelledby': 'foobar'\n      }\n    });\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass for title', function () {\n    var parent = new axe.SerialVirtualNode({\n      nodeName: 'svg'\n    });\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'circle',\n      attributes: {\n        role: 'graphics-symbol',\n        title: 'foobar'\n      }\n    });\n\n    // children are required since titleText comes after subtree text\n    // in accessible name calculation\n    node.children = [];\n    node.parent = parent;\n    parent.children = [node];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for title element', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img'\n      }\n    });\n    var title = new axe.SerialVirtualNode({\n      nodeName: 'title'\n    });\n    var text = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: 'foobar'\n    });\n\n    title.children = [text];\n    title.parent = node;\n    node.children = [title];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when aria-label and children are missing', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img'\n      }\n    });\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should fail when aria-label contains only whitespace', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img',\n        'aria-label': ' \\t   \\n   '\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when aria-label is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img',\n        'aria-label': ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when title is empty', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img',\n        title: ''\n      }\n    });\n    node.children = [];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should incomplete when title element has missing children', function () {\n    var node = new axe.SerialVirtualNode({\n      nodeName: 'svg',\n      attributes: {\n        role: 'img'\n      }\n    });\n    var title = new axe.SerialVirtualNode({\n      nodeName: 'title'\n    });\n\n    title.parent = node;\n    node.children = [title];\n\n    var results = axe.runVirtualRule('svg-img-alt', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/tabindex.js",
    "content": "describe('tabindex virtual-rule', function () {\n  it('should pass for tabindex = 0', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        tabindex: 0\n      }\n    };\n\n    var results = axe.runVirtualRule('tabindex', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for tabindex = -1', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        tabindex: -1\n      }\n    };\n\n    var results = axe.runVirtualRule('tabindex', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for tabindex > 0', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        tabindex: 1\n      }\n    };\n\n    var results = axe.runVirtualRule('tabindex', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/table-duplicate-name.js",
    "content": "describe('table-duplicate-name virtual-rule', function () {\n  it('should incomplete on table element with children undefined', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n    tableNode.children = undefined;\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 1);\n  });\n\n  it('should pass on table element', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table'\n    });\n\n    tableNode.children = [];\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass when table has empty summary', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table',\n      attributes: {\n        summary: ''\n      }\n    });\n    tableNode.children = [];\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass when table has empty caption and summary', function () {\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table',\n      attributes: {\n        summary: ''\n      }\n    });\n\n    var captionNode = new axe.SerialVirtualNode({\n      nodeName: 'caption'\n    });\n    captionNode.parent = tableNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: ''\n    });\n    textNode.parent = captionNode;\n\n    captionNode.children = [textNode];\n    tableNode.children = [captionNode];\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when table summary and <caption> have the same text', function () {\n    var DUPLICATED_TEXT = 'foobar';\n\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table',\n      attributes: {\n        summary: DUPLICATED_TEXT\n      }\n    });\n\n    var captionNode = new axe.SerialVirtualNode({\n      nodeName: 'caption'\n    });\n    captionNode.parent = tableNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: DUPLICATED_TEXT\n    });\n    textNode.parent = captionNode;\n\n    captionNode.children = [textNode];\n    tableNode.children = [captionNode];\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail when table summary and <caption> have the same text, excluding whitespace', function () {\n    var DUPLICATED_TEXT = 'foobar';\n\n    var tableNode = new axe.SerialVirtualNode({\n      nodeName: 'table',\n      attributes: {\n        summary: '  ' + DUPLICATED_TEXT\n      }\n    });\n\n    var captionNode = new axe.SerialVirtualNode({\n      nodeName: 'caption'\n    });\n    captionNode.parent = tableNode;\n\n    var textNode = new axe.SerialVirtualNode({\n      nodeName: '#text',\n      nodeType: 3,\n      nodeValue: ' \\t  ' + DUPLICATED_TEXT\n    });\n    textNode.parent = captionNode;\n\n    captionNode.children = [textNode];\n    tableNode.children = [captionNode];\n\n    var results = axe.runVirtualRule('table-duplicate-name', tableNode);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n});\n"
  },
  {
    "path": "test/integration/virtual-rules/valid-lang.js",
    "content": "describe('valid-lang virtual-rule', function () {\n  it('should pass for valid lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        lang: 'en'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for valid xml:lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        'xml:lang': 'en'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for valid L10N lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        lang: 'en-GB'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should pass for valid L10N xml:lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        'xml:lang': 'en-GB'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 1);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        lang: 'a'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should fail for invalid L10N xml:lang value', function () {\n    var node = {\n      nodeName: 'div',\n      attributes: {\n        'xml:lang': 'a'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 1);\n    assert.lengthOf(results.incomplete, 0);\n  });\n\n  it('should not apply for html element', function () {\n    var node = {\n      nodeName: 'html',\n      attributes: {\n        'xml:lang': 'a'\n      }\n    };\n\n    var results = axe.runVirtualRule('valid-lang', node);\n\n    assert.lengthOf(results.passes, 0);\n    assert.lengthOf(results.violations, 0);\n    assert.lengthOf(results.incomplete, 0);\n    assert.lengthOf(results.inapplicable, 1);\n  });\n});\n"
  },
  {
    "path": "test/karma.conf.js",
    "content": "var path = require('path');\n\n// allow running only certain directories\nvar testDirs = [\n  'core',\n  'commons',\n  'rule-matches',\n  'checks',\n  'api',\n  'integration',\n  'virtual-rules'\n];\nvar testFiles = [];\nvar debugPort = 9765; // arbitrary, sync with .vscode/launch.json\nvar args = process.argv.slice(2);\n\nargs.forEach(function (arg) {\n  // pattern: testDir=commons,core\n  var parts = arg.split('=');\n  if (parts[0] === 'testDirs') {\n    testDirs = parts[1].split(',');\n  }\n  // pattern: testFiles=path/to/file\n  else if (parts[0] === 'testFiles') {\n    testFiles = parts[1].split(',');\n  }\n  // pattern: debugPort=1234\n  else if (parts[0] === 'debugPort') {\n    debugPort = parseInt(parts[1], 10);\n  }\n});\n\nvar testPaths = [];\nif (testFiles.length) {\n  testPaths = testFiles.map(function (file) {\n    var basename = path.basename(file);\n    var extname = path.extname(file);\n\n    // do not transform test files unless it is the integration/rule\n    // html, in which case run the json test file\n    if (file.includes('test/')) {\n      if (file.includes('integration/rules') && extname === '.html') {\n        return file.replace('.html', '.json');\n      }\n\n      return file;\n    } else if (basename.includes('-matches.js')) {\n      return path.join('test/rule-matches', basename);\n    } else {\n      var filePath = file.replace('lib/', 'test/');\n\n      if (file.includes('-evaluate.js')) {\n        return filePath.replace('-evaluate.js', '.js');\n      }\n\n      return filePath;\n    }\n  });\n} else if (testDirs.length) {\n  testPaths = testDirs.map(function (dir) {\n    if (dir === 'integration') {\n      return path.join('test', dir, '**/*.json');\n    }\n    if (['virtual-rules', 'api'].includes(dir)) {\n      return path.join('test', 'integration', dir, '**/*.js');\n    }\n    return path.join('test', dir, '**/*.js');\n  });\n}\n\nmodule.exports = function (config) {\n  config.set({\n    basePath: '../',\n    singleRun: true,\n    autoWatch: false,\n    plugins: [\n      'karma-mocha',\n      'karma-chai',\n      'karma-sinon',\n      'karma-spec-reporter',\n      'karma-chrome-launcher',\n      'karma-firefox-launcher',\n      'karma-ie-launcher',\n      require('./integration/rules/preprocessor')\n    ],\n    frameworks: ['mocha', 'chai', 'sinon'],\n    files: [\n      { pattern: 'test/mock/**/*.html', included: false, served: true },\n      { pattern: 'test/integration/**/*.css', included: false, served: true },\n      {\n        pattern: 'test/integration/**/*.mjs',\n        included: false,\n        served: true,\n        type: 'module'\n      },\n      { pattern: 'test/assets/**/*.*', included: false, served: true },\n      {\n        pattern: 'test/integration/rules/**/*.html',\n        included: false,\n        served: true\n      },\n      'axe.js',\n      { pattern: 'axe.min.js', included: false, served: true },\n      'test/testutils.js'\n    ].concat(testPaths),\n    proxies: {\n      '/test': '/base/test',\n      '/mock': '/base/test/mock',\n      '/integration': '/base/test/integration',\n      '/axe.js': '/base/axe.js',\n      '/axe.min.js': '/base/axe.min.js'\n    },\n    browsers: ['ChromeHeadless'],\n    reporters: ['spec'],\n    preprocessors: {\n      'test/integration/rules/**/*.json': ['integration']\n    },\n    client: {\n      useIframe: false,\n      mocha: {\n        timeout: 4000,\n        reporter: 'html'\n      }\n    },\n    customLaunchers: {\n      ChromeDebugging: {\n        base: 'Chrome',\n        flags: ['--remote-debugging-port=' + debugPort]\n      }\n    }\n  });\n};\n"
  },
  {
    "path": "test/mock/frames/context.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n  <head>\n    <title>Context Fixture</title>\n  </head>\n  <body>\n    <div id=\"foo\">\n      <div id=\"bar\"></div>\n    </div>\n    <div id=\"target\"></div>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'div#target',\n            selector: '#target',\n            any: ['has-target']\n          },\n          {\n            id: 'first-div',\n            selector: 'div',\n            any: ['first-div']\n          }\n        ],\n        checks: [\n          {\n            id: 'has-target',\n            evaluate: function () {\n              return true;\n            }\n          },\n          {\n            id: 'first-div',\n            evaluate: function (node) {\n              this.relatedNodes([node]);\n              return false;\n            },\n            after: function (results) {\n              if (results.length) {\n                results[0].result = true;\n              }\n              return [results[0]];\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/e2e.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Message Iframe Fixture</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'html',\n            selector: 'html',\n            any: ['html']\n          }\n        ],\n        checks: [\n          {\n            id: 'html',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n    <script>\n      axe.registerPlugin({\n        id: 'multi',\n        run: function (id, action, options, callback) {\n          this._registry[id][action].call(\n            this._registry[id],\n            options,\n            callback\n          );\n        },\n        add: function (impl) {\n          this._registry[impl.id] = impl;\n        },\n        commands: [\n          {\n            id: 'run-multi',\n            callback: function (data, callback) {\n              return axe.plugins.multi.run(\n                data.parameter,\n                data.action,\n                data.options,\n                callback\n              );\n            }\n          }\n        ]\n      });\n      axe.plugins.multi.add({\n        id: 'hideall',\n        run: function (options, callback) {\n          var frames;\n          var q = axe.utils.queue();\n\n          frames = axe.utils.toArray(\n            document.querySelectorAll('iframe, frame')\n          );\n          if (frames.length) {\n            frames.forEach(function (frame) {\n              q.defer(function (done) {\n                axe.utils.sendCommandToFrame(\n                  frame,\n                  {\n                    options: options,\n                    command: 'run-multi',\n                    parameter: 'hideall',\n                    action: 'run'\n                  },\n                  done\n                );\n              });\n            });\n          }\n          q.defer(function (done) {\n            // implementation\n            done('ola!');\n          });\n          q.then(function (data) {\n            // done with all the frames\n            var results = [];\n            data.forEach(function (datum) {\n              if (datum) {\n                results = results.concat(datum);\n              }\n            });\n            callback(results);\n          });\n        }\n      });\n    </script>\n  </head>\n  <body>\n    <input type=\"text\" />\n    <a href=\"#\">link</a>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/frame-frame.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Frame</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'html',\n            selector: 'html',\n            any: ['html']\n          }\n        ],\n        checks: [\n          {\n            id: 'html',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n    <script>\n      axe.registerPlugin({\n        id: 'multi',\n        run: function (id, action, options, callback) {\n          this._registry[id][action].call(\n            this._registry[id],\n            options,\n            callback\n          );\n        },\n        add: function (impl) {\n          this._registry[impl.id] = impl;\n        },\n        commands: [\n          {\n            id: 'run-multi',\n            callback: function (data, callback) {\n              return axe.plugins.multi.run(\n                data.parameter,\n                data.action,\n                data.options,\n                callback\n              );\n            }\n          }\n        ]\n      });\n      axe.plugins.multi.add({\n        id: 'hideall',\n        run: function (options, callback) {\n          var frames;\n          var q = axe.utils.queue();\n\n          frames = axe.utils.toArray(\n            document.querySelectorAll('iframe, frame')\n          );\n          if (frames.length) {\n            frames.forEach(function (frame) {\n              q.defer(function (done) {\n                axe.utils.sendCommandToFrame(\n                  frame,\n                  {\n                    options: options,\n                    command: 'run-multi',\n                    parameter: 'hideall',\n                    action: 'run'\n                  },\n                  done\n                );\n              });\n            });\n          }\n          q.defer(function (done) {\n            // implementation\n            done('ola!');\n          });\n          q.then(function (data) {\n            // done with all the frames\n            var results = [];\n            data.forEach(function (datum) {\n              if (datum) {\n                results = results.concat(datum);\n              }\n            });\n            callback(results);\n          });\n        }\n      });\n    </script>\n  </head>\n  <body>\n    <iframe src=\"e2e.html\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/frame1.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Frame</title>\n  </head>\n  <body>\n    <h1>I am a test frame</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/nested0.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Nest Frame</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'iframe',\n            selector: 'iframe',\n            any: ['iframe']\n          }\n        ],\n        checks: [\n          {\n            id: 'iframe',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n  </head>\n  <body>\n    <iframe src=\"nested1.html\" id=\"level1\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/nested1.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Nest Frame</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'iframe',\n            selector: 'iframe',\n            any: ['iframe']\n          }\n        ],\n        checks: [\n          {\n            id: 'iframe',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n  </head>\n  <body>\n    <iframe src=\"nested2.html\" id=\"level2a\"></iframe>\n    <iframe src=\"nested2.html\" id=\"level2b\"></iframe>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/nested2.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Nest Frame</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'iframe',\n            selector: 'iframe',\n            any: ['iframe']\n          }\n        ],\n        checks: [\n          {\n            id: 'iframe',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ],\n        messages: {}\n      });\n    </script>\n  </head>\n  <body>\n    Hi\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/noHtml-config.html",
    "content": "<!doctype html>\n<html lang=\"en-US\">\n  <head>\n    <title>Context Fixture</title>\n  </head>\n  <body>\n    <div id=\"foo\">\n      <div id=\"bar\"></div>\n    </div>\n    <div id=\"target\"></div>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'div#target',\n            selector: '#target',\n            any: ['has-target']\n          },\n          {\n            id: 'first-div',\n            selector: 'div',\n            any: ['first-div']\n          }\n        ],\n        checks: [\n          {\n            id: 'has-target',\n            evaluate: function () {\n              return true;\n            }\n          },\n          {\n            id: 'first-div',\n            evaluate: function (node) {\n              this.relatedNodes([node]);\n              return false;\n            },\n            after: function (results) {\n              if (results.length) {\n                results[0].result = true;\n              }\n              return [results[0]];\n            }\n          }\n        ],\n        messages: {}\n      });\n      axe.configure({ noHtml: true });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/nocode.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head> </head>\n  <body>\n    <h2>Hidden Iframe</h2>\n    <p>Not really hidden, rather, has no axe code</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/results-timeout.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Message Iframe Fixture</title>\n  </head>\n  <body>\n    <script src=\"/axe.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/rule-error.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Rule error</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'incomplete-1',\n            selector: '*',\n            none: ['undeffed']\n          },\n          {\n            id: 'incomplete-2',\n            selector: '*',\n            none: ['thrower']\n          }\n        ],\n        checks: [\n          {\n            id: 'undeffed',\n            evaluate: function () {\n              return undefined;\n            }\n          },\n          {\n            id: 'thrower',\n            evaluate: function () {\n              throw new Error('Check failed to complete');\n            }\n          }\n        ]\n      });\n    </script>\n  </head>\n  <body>\n    <h1 class=\"nogo\">header 1</h1>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/test.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Test Frame</title>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._load({\n        rules: [\n          {\n            id: 'html',\n            selector: '#target',\n            none: ['fred']\n          }\n        ],\n        checks: [\n          {\n            id: 'fred',\n            evaluate: function (node) {\n              return true;\n            }\n          }\n        ]\n      });\n    </script>\n  </head>\n  <body>\n    <div id=\"target\">Target in iframe</div>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/throwing.html",
    "content": "<!doctype html>\n<html id=\"frameContent\">\n  <head>\n    <title>Error returning frame frame</title>\n  </head>\n  <body>\n    <script src=\"/axe.js\"></script>\n    <script>\n      axe._audit.run = function () {\n        throw new Error('error in axe.throw');\n      };\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/with-echo-axe.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Echo frame with axe-core</title>\n    <meta charset=\"utf8\" />\n    <script src=\"/axe.js\"></script>\n    <script>\n      // sync this rule to the one in test/core/public/run.js\n      axe._load({\n        rules: [\n          {\n            id: 'html',\n            selector: '#target',\n            none: ['fred']\n          }\n        ],\n        checks: [\n          {\n            id: 'fred',\n            evaluate: function () {\n              return true;\n            }\n          }\n        ]\n      });\n    </script>\n  </head>\n  <body>\n    <h1>Frame with axe-core</h1>\n    <div id=\"target\">Target in iframe</div>\n    <script>\n      window.addEventListener(\n        'message',\n        function (event) {\n          window.top.postMessage(event.data, '*');\n        },\n        false\n      );\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/with-echo.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Echo frame without axe-core</title>\n    <meta charset=\"utf8\" />\n  </head>\n  <body>\n    <h1>Frame without axe-core</h1>\n    <div id=\"target\">Target in iframe</div>\n    <script>\n      window.addEventListener(\n        'message',\n        function (event) {\n          window.top.postMessage(event.data, '*');\n        },\n        false\n      );\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/mock/frames/zombie-frame.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Zombie Frame</title>\n  </head>\n  <body>\n    Zombie Frame\n  </body>\n</html>\n"
  },
  {
    "path": "test/node/jsdom.js",
    "content": "const axe = require('../../');\nconst jsdom = require('jsdom');\nconst assert = require('assert');\n\nconst domStr =\n  '<!DOCTYPE html>' +\n  '<html lang=\"en\">' +\n  '<head>' +\n  '<meta charset=\"UTF-8\">' +\n  '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">' +\n  '<title>Document</title>' +\n  '</head>' +\n  '<body>' +\n  'Hello' +\n  '<a id=\"hash-link\" href=\"#main\">Main</a>' +\n  '<a id=\"skip\" href=\"https://page.com#main\">Skip Link</a>' +\n  '</body>' +\n  '</html>';\n\ndescribe('jsdom axe-core', () => {\n  it('should run without setting globals', () => {\n    const dom = new jsdom.JSDOM(domStr);\n\n    return axe\n      .run(dom.window.document.documentElement, {\n        rules: { 'color-contrast': { enabled: false } }\n      })\n      .then(function (results) {\n        assert.notEqual(results.violations.length, 0);\n      });\n  });\n\n  it('should unset globals so it can run with a new set of globals', () => {\n    let dom = new jsdom.JSDOM(domStr);\n\n    return axe\n      .run(dom.window.document.documentElement, {\n        rules: { 'color-contrast': { enabled: false } }\n      })\n      .then(function (results) {\n        assert.notStrictEqual(results.violations.length, 0);\n\n        dom = new jsdom.JSDOM(domStr);\n\n        return axe\n          .run(dom.window.document.documentElement, {\n            rules: { 'color-contrast': { enabled: false } }\n          })\n          .then(function (res) {\n            assert.notStrictEqual(res.violations.length, 0);\n          });\n      });\n  });\n\n  describe('audit', () => {\n    const audit = axe._audit;\n\n    it('should have an empty allowedOrigins', () => {\n      // JSDOM does not have window.location, so there is no default origin\n      assert.strictEqual(audit.allowedOrigins.length, 0);\n    });\n  });\n\n  describe('isCurrentPageLink', () => {\n    // because axe only sets the window global when calling axe.run,\n    // we'll have to create a custom rule that calls\n    // isCurrentPageLink to gain access to the middle of a run with\n    // the proper window object\n    afterEach(() => {\n      axe.teardown();\n    });\n\n    it('should return true if url starts with #', () => {\n      const dom = new jsdom.JSDOM(domStr);\n      const anchor = dom.window.document.getElementById('hash-link');\n\n      axe.configure({\n        checks: [\n          {\n            id: 'check-current-page-link',\n            evaluate: () => {\n              return axe.commons.dom.isCurrentPageLink(anchor) === true;\n            }\n          }\n        ],\n        rules: [\n          {\n            id: 'check-current-page-link',\n            any: ['check-current-page-link']\n          }\n        ]\n      });\n\n      return axe\n        .run(dom.window.document.documentElement, {\n          runOnly: ['check-current-page-link']\n        })\n        .then(function (results) {\n          assert.strictEqual(results.passes.length, 1);\n        });\n    });\n\n    it('should return null for absolute link when url is not set', () => {\n      const dom = new jsdom.JSDOM(domStr);\n      const anchor = dom.window.document.getElementById('skip');\n\n      axe.configure({\n        checks: [\n          {\n            id: 'check-current-page-link',\n            evaluate: () => {\n              return axe.commons.dom.isCurrentPageLink(anchor) === null;\n            }\n          }\n        ],\n        rules: [\n          {\n            id: 'check-current-page-link',\n            any: ['check-current-page-link']\n          }\n        ]\n      });\n\n      return axe\n        .run(dom.window.document.documentElement, {\n          runOnly: ['check-current-page-link']\n        })\n        .then(function (results) {\n          assert.strictEqual(results.passes.length, 1);\n        });\n    });\n\n    it('should return true for absolute link when url is set', () => {\n      const dom = new jsdom.JSDOM(domStr, { url: 'https://page.com' });\n      const anchor = dom.window.document.getElementById('skip');\n\n      axe.configure({\n        checks: [\n          {\n            id: 'check-current-page-link',\n            evaluate: () => {\n              return axe.commons.dom.isCurrentPageLink(anchor) === true;\n            }\n          }\n        ],\n        rules: [\n          {\n            id: 'check-current-page-link',\n            any: ['check-current-page-link']\n          }\n        ]\n      });\n\n      return axe\n        .run(dom.window.document.documentElement, {\n          runOnly: ['check-current-page-link']\n        })\n        .then(function (results) {\n          assert.strictEqual(results.passes.length, 1);\n        });\n    });\n  });\n\n  describe('axe.setup()', () => {\n    afterEach(() => {\n      axe.teardown();\n    });\n\n    it('sets up the tree', function () {\n      const { document } = new jsdom.JSDOM(domStr).window;\n      const tree = axe.setup(document.body);\n      assert.equal(tree, axe._tree[0]);\n      assert.equal(tree.actualNode, document.body);\n    });\n\n    it('can use commons after axe.setup()', () => {\n      const { document } = new jsdom.JSDOM(domStr).window;\n      axe.setup(document);\n\n      const skipLink = document.querySelector('#skip');\n      assert.equal(axe.commons.aria.getRole(skipLink), 'link');\n      assert.equal(axe.commons.text.accessibleText(skipLink), 'Skip Link');\n    });\n\n    it('is cleaned up with axe.teardown()', () => {\n      const { document } = new jsdom.JSDOM(domStr).window;\n      axe.setup(document);\n      axe.teardown();\n      const skipLink = document.querySelector('#skip');\n\n      assert.throws(() => {\n        assert.equal(axe.commons.aria.getRole(skipLink), 'link');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/node/node.js",
    "content": "// this file is purposefully written without mocha and in es5 syntax in order\n// to be compatible with node 4+\n\nvar axe = require('../../');\nvar assert = require('assert');\nvar spawn = require('child_process').spawn;\nvar fs = require('fs');\nvar path = require('path');\n\ninitJsdom(function (err, window) {\n  assert.equal(err, null);\n\n  console.log('running axe');\n  axe.run(\n    window.document.documentElement,\n    {\n      preload: false,\n      rules: { 'color-contrast': { enabled: false } }\n    },\n    function (axeError, results) {\n      assert.equal(axeError, null);\n      assert.notEqual(results.violations.length, 0);\n      console.log('axe ran successfully');\n    }\n  );\n});\n\n/**\n * Install a version of jsdom that is compatible with the currently running node\n * version and return the jsdom window object.\n * @param {Function} callback - callback function when jsdom is installed.\n * Is passed any error object and the jsdom window object.\n */\nfunction initJsdom(callback) {\n  try {\n    var nodeToDeps = {\n      4: ['jsdom@9.12.0', 'sax@1.4.1'], // last jsdom version that supported this node version\n      6: ['jsdom@11.12.0', 'sax@1.4.1'],\n      8: ['jsdom@15.2.1'],\n      10: ['jsdom@16.7.0'],\n      12: ['jsdom@19.0.0'],\n      14: ['jsdom@21.1.2'],\n      16: ['jsdom@22.1.0'],\n      18: ['jsdom@26.1.0']\n    };\n\n    var majorNodeVersion = process.versions.node.split('.')[0];\n    console.log('node version detected as: v' + majorNodeVersion);\n\n    var deps = nodeToDeps[majorNodeVersion] || ['jsdom@latest'];\n    var nodeInstallArgs = ['install', '--no-save'];\n    for (var dep of deps) {\n      console.log('installing ' + dep);\n      nodeInstallArgs.push(dep);\n    }\n\n    var child = spawn('npm', nodeInstallArgs, { cwd: __dirname });\n    child.stdout.setEncoding('utf8');\n    child.stderr.setEncoding('utf8');\n    child.stdout.on('data', function (data) {\n      console.log(data);\n    });\n    child.stderr.on('data', function (data) {\n      console.error(data);\n    });\n    child.on('close', function () {\n      console.log('installed');\n      var jsdom = require('jsdom');\n      var domStr = fs.readFileSync(\n        path.join('test', 'integration', 'full', 'all-rules', 'all-rules.html'),\n        'utf8'\n      );\n\n      // jsdom 9\n      if (jsdom.env) {\n        jsdom.env(domStr, function (jsdomError, window) {\n          if (jsdomError) {\n            callback(jsdomError);\n          }\n\n          callback(null, window);\n        });\n      }\n      // jsdom 11+\n      else {\n        var dom = new jsdom.JSDOM(domStr);\n        callback(null, dom.window);\n      }\n    });\n  } catch (err) {\n    callback(err);\n  }\n}\n"
  },
  {
    "path": "test/node/package.json",
    "content": "{\n  \"description\": \"This package.json is intentionally left empty so running the node.js test does not install jsdom at the root level node_modules\",\n  \"dependencies\": {}\n}\n"
  },
  {
    "path": "test/playground.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <title>Axe Playground</title>\n  <!-- The playground is used for quick local host axe-core during development -->\n\n  <main>\n    <h1>Hello World</h1>\n  </main>\n\n  <script src=\"/axe.js\"></script>\n  <script src=\"/test/testutils.js\"></script>\n  <script>\n    axe.testUtils.awaitNestedLoad(\n      window,\n      () => {\n        axe.run(\n          {\n            runOnly: 'color-contrast',\n            elementRef: true\n          },\n          (err, results) => {\n            console.log(err || results);\n          }\n        );\n      },\n      err => console.error(err)\n    );\n  </script>\n</html>\n"
  },
  {
    "path": "test/rule-matches/aria-allowed-attr-matches.js",
    "content": "describe('aria-allowed-attr-matches', function () {\n  'use strict';\n\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-allowed-attr');\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('should return true on elements that have aria attributes', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-label=\"Thing 1\" aria-mccheddarton=\"Unsupported thing 2\"></div>'\n    );\n\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return false on elements that have no aria attributes', function () {\n    const vNode = queryFixture('<div role=\"button\" id=\"target\"></div>');\n\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/aria-allowed-role-matches.js",
    "content": "describe('aria-allowed-role-matches', function () {\n  'use strict';\n\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-allowed-role');\n  });\n\n  it('return false (no matches) for a <link> with a href to have any invalid role', function () {\n    const vNode = queryFixture(\n      '<link id=\"target\" href=\"/example.com\" role=\"invalid-role\"></link>'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('return true for input with redundant role', function () {\n    const vNode = queryFixture(\n      '<input id=\"target\" type=\"text\" role=\"textbox\"/>'\n    );\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('return true for element with valid role', function () {\n    const vNode = queryFixture('<ol id=\"target\" role=\"listbox\"/>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/aria-has-attr-matches.js",
    "content": "describe('aria-has-attr-matches', function () {\n  'use strict';\n\n  const queryFixture = axe.testUtils.queryFixture;\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-valid-attr-value');\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('should return false if an element has no attributes', function () {\n    const vNode = fixtureSetup('<div></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('should return false if an element has no ARIA attributes', function () {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n  it('should return true if an element has ARIA attributes', function () {\n    const vNode = queryFixture('<div id=\"target\" aria-bats=\"monkeys\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/aria-hidden-focus-matches.js",
    "content": "describe('aria-hidden-focus-matches', function () {\n  'use strict';\n\n  let rule;\n  const queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-hidden-focus');\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('return true when there is no parent with aria-hidden', function () {\n    const vNode = queryFixture('<div id=\"target\">' + '</div>');\n    const actual = rule.matches(vNode.actualNode);\n    assert.isTrue(actual);\n  });\n\n  it('return false when has a parent element with aria-hidden', function () {\n    const vNode = queryFixture(\n      '<div aria-hidden=\"true\">' +\n        '<div id=\"target\" aria-hidden=\"true\">' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(vNode.actualNode);\n    assert.isFalse(actual);\n  });\n\n  it('return false when has any parent element with aria-hidden', function () {\n    const vNode = queryFixture(\n      '<div aria-hidden=\"true\">' +\n        '<div>' +\n        '<div id=\"target\" aria-hidden=\"true\">' +\n        '</div>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(vNode.actualNode);\n    assert.isFalse(actual);\n  });\n\n  it('return false when has any parent element with aria-hidden', function () {\n    const vNode = queryFixture(\n      '<div aria-hidden=\"true\">' +\n        '<div aria-hidden=\"true\">' +\n        '<button id=\"target\">btn</button>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(vNode.actualNode);\n    assert.isFalse(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/aria-required-children-matches.js",
    "content": "describe('aria-required-children-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-required-children');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true for a role that requires children', function () {\n    const vNode = queryFixture('<div id=\"target\" role=\"list\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return false for a role that does not require children', function () {\n    const vNode = queryFixture('<div id=\"target\" role=\"alert\"></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/aria-required-parent-matches.js",
    "content": "describe('aria-required-parent-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-required-parent');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true for a role that requires parent', function () {\n    const vNode = queryFixture('<div id=\"target\" role=\"listitem\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return false for a role that does not require parent', function () {\n    const vNode = queryFixture('<div id=\"target\" role=\"alert\"></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/autocomplete-matches.js",
    "content": "describe('autocomplete-matches', function () {\n  'use strict';\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const rule = axe.utils.getRule('autocomplete-valid');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('returns true for input elements', function () {\n    const vNode = queryFixture('<input id=\"target\" autocomplete=\"foo\">');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns true for select elements', function () {\n    const vNode = queryFixture('<select id=\"target\" autocomplete=\"foo\">');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns true for textarea elements', function () {\n    const vNode = queryFixture('<textarea id=\"target\" autocomplete=\"foo\">');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns false for buttons elements', function () {\n    const vNode = queryFixture('<button id=\"target\" autocomplete=\"foo\">');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('should return false for non-form field elements', function () {\n    const vNode = queryFixture('<div id=\"target\" autocomplete=\"foo\">');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns false for input buttons', function () {\n    ['reset', 'submit', 'button'].forEach(function (type) {\n      const vNode = queryFixture(\n        '<input id=\"target\" type=\"' + type + '\" autocomplete=\"foo\">'\n      );\n      assert.isFalse(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns false for elements with an empty autocomplete', function () {\n    const vNode = queryFixture('<input id=\"target\" autocomplete=\"  \">');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns false for intput[type=hidden]', function () {\n    const vNode = queryFixture(\n      '<input id=\"target\" type=\"hidden\" autocomplete=\"foo\">'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns false for disabled fields', function () {\n    ['input', 'select', 'textarea'].forEach(function (tagName) {\n      const vNode = queryFixture(\n        '<' + tagName + ' id=\"target\" disabled autocomplete=\"foo\">'\n      );\n      assert.isFalse(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns false for aria-disabled=true fields', function () {\n    ['input', 'select', 'textarea'].forEach(function (tagName) {\n      const vNode = queryFixture(\n        '<' + tagName + ' id=\"target\" aria-disabled=\"true\" autocomplete=\"foo\">'\n      );\n      assert.isFalse(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns true for aria-disabled=false fields', function () {\n    ['input', 'select', 'textarea'].forEach(function (tagName) {\n      const vNode = queryFixture(\n        '<' + tagName + ' id=\"target\" aria-disabled=\"false\" autocomplete=\"foo\">'\n      );\n      assert.isTrue(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns false for non-widget roles with tabindex=-1', function () {\n    const nonWidgetRoles = ['application', 'fakerole', 'main'];\n    nonWidgetRoles.forEach(function (role) {\n      const vNode = queryFixture(\n        '<input id=\"target\" role=\"' +\n          role +\n          '\" tabindex=\"-1\" autocomplete=\"foo\">'\n      );\n      assert.isFalse(\n        rule.matches(null, vNode),\n        'Expect role=' + role + ' to be ignored when it has tabindex=-1'\n      );\n    });\n  });\n\n  it('returns true for form fields with a widget role with tabindex=-1', function () {\n    const nonWidgetRoles = ['button', 'menuitem', 'slider'];\n    nonWidgetRoles.forEach(function (role) {\n      const vNode = queryFixture(\n        '<input id=\"target\" role=\"' +\n          role +\n          '\" tabindex=\"-1\" autocomplete=\"foo\">'\n      );\n      assert.isTrue(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns true for form fields with tabindex=-1', function () {\n    ['input', 'select', 'textarea'].forEach(function (tagName) {\n      const vNode = queryFixture(\n        '<' + tagName + ' id=\"target\" tabindex=\"-1\" autocomplete=\"foo\">'\n      );\n      assert.isTrue(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns false for off screen and hidden form fields with tabindex=-1', function () {\n    const vNode = queryFixture(\n      '<div aria-hidden=\"true\">' +\n        '<input id=\"target\" tabindex=\"-1\" style=\"position:absolute; top:-9999em\" autocomplete=\"foo\">' +\n        '</div>'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns false if readonly attribute is placed whether autocomplete is not valid', function () {\n    var vNode = queryFixture(\n      '<input readonly autocomplete=\"some invalid value\" id=\"target\" />'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns false if aria-readonly attribute is true whether autocomplete is not valid', function () {\n    var vNode = queryFixture(\n      '<input aria-readonly=\"true\" autocomplete=\"some invalid value\" id=\"target\" />'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns true if aria-readonly attribute is false whether autocomplete is not valid', function () {\n    var vNode = queryFixture(\n      '<input aria-readonly=\"false\" autocomplete=\"some invalid value\" id=\"target\" />'\n    );\n    assert.isTrue(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/color-contrast-matches.js",
    "content": "describe('color-contrast-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const shadowSupport =\n    document.body && typeof document.body.attachShadow === 'function';\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('color-contrast');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('should not match when there is no text', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n      ' </div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match when there is text', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n      'My text</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match when text only contains emoji', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n      '🌎</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match when text only contains punctuation', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n      '&rlm;</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match when text only contains nonBmp unicode', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; background-color: white;\" id=\"target\">' +\n      '◓</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match when there is text that is out of the container', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; text-indent: -9999px; background-color: white;\" id=\"target\">' +\n      'My text</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match input when there is text that is out of the container', function () {\n    fixture.innerHTML =\n      '<input style=\"text-indent: -9999px\" type=\"submit\" value=\"Search\" />';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match select when there is text that is out of the container', function () {\n    fixture.innerHTML =\n      '<select style=\"text-indent: -9999px\">' +\n      '<option selected>My text</option>' +\n      '</select>';\n    const target = fixture.querySelector('select');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match textarea when there is text that is out of the container', function () {\n    fixture.innerHTML =\n      '<textarea style=\"text-indent: -9999px\">My text</textarea>';\n    const target = fixture.querySelector('textarea');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match when there is text that is out of the container with overflow hidden', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; width: 100px; overflow: hidden; text-indent: 200px; background-color: white;\" id=\"target\">' +\n      'text</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match when there is text that is in the scroll reach of container', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; width: 100px; overflow: scroll; text-indent: 200px; background-color: white;\" id=\"target\">' +\n      'text</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match when there is text that is only partially out of the container', function () {\n    fixture.innerHTML =\n      '<div style=\"color: yellow; text-indent: -20px; background-color: white;\" id=\"target\">' +\n      'My text</div>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match <input type=\"text\">', function () {\n    fixture.innerHTML = '<input type=\"text\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <input type=\"hidden\">', function () {\n    fixture.innerHTML = '<input type=\"hidden\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <input type=\"checkbox\">', function () {\n    fixture.innerHTML = '<input type=\"checkbox\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <input type=\"radio\">', function () {\n    fixture.innerHTML = '<input type=\"radio\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <input type=\"color\">', function () {\n    fixture.innerHTML = '<input type=\"color\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    // Some browsers will fallback to type=text for unknown input types (looking at you IE)\n    if (target.type === 'color') {\n      assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n    }\n  });\n\n  it('should not match <input type=\"range\">', function () {\n    fixture.innerHTML = '<input type=\"range\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    // Some browsers will fallback to type=text for unknown input types (looking at you IE)\n    if (target.type === 'range') {\n      assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n    }\n  });\n\n  it('should match <select> with options', function () {\n    fixture.innerHTML = '<select><option>Hello</option></select>';\n    const target = fixture.querySelector('select');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <select> with no options', function () {\n    fixture.innerHTML = '<select></select>';\n    const target = fixture.querySelector('select');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match <textarea>', function () {\n    fixture.innerHTML = '<textarea></textarea>';\n    const target = fixture.querySelector('textarea');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <option>', function () {\n    fixture.innerHTML = '<select><option>hi</option></select>';\n    const target = fixture.querySelector('option');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match inputs that are disabled', function () {\n    fixture.innerHTML = '<input type=\"text\" disabled>';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a disabled implicit label child', function () {\n    fixture.innerHTML =\n      '<label>' +\n      '<input type=\"checkbox\" style=\"position: absolute;display: inline-block;width: 1.5rem;height: 1.5rem;opacity: 0;\" disabled checked>' +\n      '<span style=\"background-color:rgba(0, 0, 0, 0.26);display:inline-block;width: 1.5rem;height: 1.5rem;\" aria-hidden=\"true\"></span>' +\n      '<span style=\"color:rgba(0, 0, 0, 0.38);\" id=\"target\">Baseball</span>' +\n      '</label>';\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    const result = rule.matches(target, axe.utils.getNodeFromTree(target));\n    assert.isFalse(result);\n  });\n\n  it('should not match <textarea disabled>', function () {\n    fixture.innerHTML = '<textarea disabled></textarea>';\n    const target = fixture.querySelector('textarea');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <select> with options', function () {\n    fixture.innerHTML = '<select disabled><option>Hello</option></select>';\n    const target = fixture.querySelector('select');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match <button>', function () {\n    fixture.innerHTML = '<button>hi</button>';\n    const target = fixture.querySelector('button');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <button disabled>', function () {\n    fixture.innerHTML = '<button disabled>hi</button>';\n    const target = fixture.querySelector('button');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match <button disabled><span></span></button>', function () {\n    fixture.innerHTML = '<button disabled><span>Hi</span></button>';\n    const target = fixture.querySelector('button');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      rule.matches(\n        target.querySelector('span'),\n        axe.utils.getNodeFromTree(target.querySelector('span'))\n      )\n    );\n  });\n\n  it('should not match <button disabled><span><i></i></span></button>', function () {\n    fixture.innerHTML = '<button disabled><span><i>Hi</i></span></button>';\n    const target = fixture.querySelector('button');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(\n      rule.matches(\n        target.querySelector('i'),\n        axe.utils.getNodeFromTree(target.querySelector('i'))\n      )\n    );\n  });\n\n  it('should not match <input type=image>', function () {\n    fixture.innerHTML = '<input type=\"image\">';\n    const target = fixture.querySelector('input');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a disabled input's label - explicit label\", function () {\n    fixture.innerHTML =\n      '<label for=\"t1\">Test</label><input type=\"text\" id=\"t1\" disabled>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match an aria-disabled input's label - explicit label\", function () {\n    fixture.innerHTML =\n      '<label for=\"t1\">Test</label><input type=\"text\" id=\"t1\" aria-disabled=\"true\">';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a parent aria-disabled input's label - explicit label\", function () {\n    fixture.innerHTML =\n      '<label for=\"t1\">Test</label><div aria-disabled=\"true\"><input type=\"text\" id=\"t1\"></div>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a disabled input's label - implicit label (input)\", function () {\n    fixture.innerHTML = '<label>Test<input type=\"text\" disabled></label>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a disabled input's label - implicit label (textarea)\", function () {\n    fixture.innerHTML = '<label>Test<textarea disabled>Hi</textarea></label>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a disabled input's label - implicit label (select)\", function () {\n    fixture.innerHTML =\n      '<label>Test<select disabled><option>Test</option></select></label>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match a disabled input's label - aria-labelledby\", function () {\n    fixture.innerHTML =\n      '<div id=\"t1\">Test</div><input type=\"text\" aria-labelledby=\"bob t1 fred\" disabled>';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should match when at least one input is not disabled - aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"t1\">Test</div>' +\n      '<input type=\"text\" aria-labelledby=\"t1\" disabled />' +\n      '<input type=\"text\" aria-labelledby=\"t1\" />';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isTrue(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match when inputs are disabled by an ancestor - aria-labelledby', function () {\n    fixture.innerHTML =\n      '<div id=\"t1\">Test</div>' +\n      '<fieldset disabled>' +\n      '<input type=\"text\" aria-labelledby=\"t1\" />' +\n      '<input type=\"text\" aria-labelledby=\"t1\" />' +\n      '</fieldset>';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match aria-disabled=true', function () {\n    fixture.innerHTML = '<div aria-disabled=\"true\">hi</div>';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a descendant of aria-disabled=true', function () {\n    fixture.innerHTML = '<div aria-disabled=\"true\"><span>hi</span></div>';\n    const target = fixture.querySelector('span');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it(\"should not match an aria-disabled input's label - aria-labelledby\", function () {\n    fixture.innerHTML =\n      '<div id=\"t1\"><span>Test</span></div>' +\n      '<div role=\"textbox\" aria-labelledby=\"bob t1 fred\" aria-disabled=\"true\"></div>';\n    const target = fixture.querySelector('span');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a descendant of a disabled fieldset', function () {\n    fixture.innerHTML =\n      '<fieldset disabled><label>hi <input type=\"checkbox\"></label></fieldset>';\n    const target = fixture.querySelector('label');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a descendant of an explicit label for a disabled input', function () {\n    fixture.innerHTML =\n      '<input id=\"input\" type=\"checkbox\" disabled><label for=\"input\"><span>hi</span></label>';\n    const target = fixture.querySelector('span');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a descendant of an implicit label for a disabled input', function () {\n    fixture.innerHTML =\n      '<label for=\"input\"><span>hi</span><input id=\"input\" type=\"checkbox\" disabled></label>';\n    const target = fixture.querySelector('span');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match inert', function () {\n    fixture.innerHTML = '<div inert>hi</div>';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match a descendant of inert', function () {\n    fixture.innerHTML = '<div inert><span>hi</span></div>';\n    const target = fixture.querySelector('span');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match text outside overflow', () => {\n    fixture.innerHTML = `\n      <div style=\"overflow: hidden; width: 50px;\">\n        <div style=\"overflow: hidden; width: 25px\">\n          <div id=\"target\" style=\"padding-left: 65px;\">Hello World</div>\n        </div>\n      </div>\n    `;\n    const target = fixture.querySelector('#target');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  it('should not match text with font-size: 0', () => {\n    fixture.innerHTML = '<div style=\"font-size: 0\">hi</div>';\n    const target = fixture.querySelector('div');\n    axe.testUtils.flatTreeSetup(fixture);\n    assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n  });\n\n  if (shadowSupport) {\n    it('should match a descendant of an element across a shadow boundary', function () {\n      fixture.innerHTML =\n        '<div id=\"parent\" style=\"background-color: #000;\">' + '</div>';\n\n      const shadowRoot = document\n        .getElementById('parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"shadowTarget\" style=\"color: #333\">Text</div>';\n\n      const shadowTarget =\n        fixture.firstChild.shadowRoot.querySelector('#shadowTarget');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isTrue(\n        rule.matches(shadowTarget, axe.utils.getNodeFromTree(shadowTarget))\n      );\n    });\n\n    it('should look at the correct root node when looking up an explicit label and disabled input', function () {\n      fixture.innerHTML = '<div id=\"parent\">' + '<input id=\"input\">' + '</div>';\n\n      const shadowRoot = document\n        .getElementById('parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"shadowParent\">' +\n        '<label for=\"input\" id=\"shadowLabel\">Label</label>' +\n        '<input id=\"input\" disabled>' +\n        '</div>';\n\n      const shadowLabel =\n        fixture.firstChild.shadowRoot.querySelector('#shadowLabel');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(\n        rule.matches(shadowLabel, axe.utils.getNodeFromTree(shadowLabel))\n      );\n    });\n\n    it('should look at the correct root node when looking up implicit label and disabled input', function () {\n      fixture.innerHTML = '<div id=\"parent\">' + '<input>' + '</div>';\n\n      const shadowRoot = document\n        .getElementById('parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"shadowParent\">' +\n        '<label id=\"shadowLabel\">Label <input disabled></label>' +\n        '</div>';\n\n      const shadowLabel =\n        fixture.firstChild.shadowRoot.querySelector('#shadowLabel');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(\n        rule.matches(shadowLabel, axe.utils.getNodeFromTree(shadowLabel))\n      );\n    });\n\n    it(\"should look at the correct root node for a disabled control's label associated w/ aria-labelledby\", function () {\n      fixture.innerHTML = '<div id=\"parent\">' + '<input id=\"input\">' + '</div>';\n\n      const shadowRoot = document\n        .getElementById('parent')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        '<div id=\"shadowParent\">' +\n        '<label id=\"shadowLabel\">Label</label>' +\n        '<input aria-labelledby=\"shadowLabel\" disabled>' +\n        '</div>';\n\n      const shadowLabel =\n        fixture.firstChild.shadowRoot.querySelector('#shadowLabel');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(\n        rule.matches(shadowLabel, axe.utils.getNodeFromTree(shadowLabel))\n      );\n    });\n\n    it('should handle input/label spread across the shadow boundary', function () {\n      fixture.innerHTML = '<label>Text <div id=\"firstChild\"></div></label>';\n\n      const container = document.getElementById('firstChild');\n      const shadowRoot = container.attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML = '<input type=\"text\" id=\"input\" disabled />';\n\n      const shadowTarget = container.shadowRoot.querySelector('#input');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(\n        rule.matches(shadowTarget, axe.utils.getNodeFromTree(shadowTarget))\n      );\n    });\n\n    it('should look at the children of a virtual node for overlap', function () {\n      fixture.innerHTML =\n        '<div id=\"parent\">' +\n        '<div id=\"firstChild\" style=\"background-color: #ccc; color: #fff;\"></div>' +\n        '</div>';\n\n      const shadowRoot = document\n        .getElementById('firstChild')\n        .attachShadow({ mode: 'open' });\n      shadowRoot.innerHTML =\n        'Some text' +\n        '<p style=\"color: #fff;\" id=\"shadowTarget\">Other text</p>';\n\n      const firstChild = fixture.querySelector('#firstChild');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isTrue(\n        rule.matches(firstChild, axe.utils.getNodeFromTree(firstChild))\n      );\n    });\n\n    it('should handle an input added through slotted content', function () {\n      fixture.innerHTML =\n        '<label>Option 1 <span class=\"slotted\">' +\n        '<input type=\"text\" name=\"slottedLabel\" />' +\n        '</span></label>';\n\n      function createContentSlotted() {\n        const group = document.createElement('span');\n        group.innerHTML = '<slot></slot>';\n        return group;\n      }\n\n      const slotted = fixture.querySelector('.slotted');\n      const shadowRoot = slotted.attachShadow({ mode: 'open' });\n      shadowRoot.appendChild(createContentSlotted());\n\n      const input = slotted.querySelector('input');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isTrue(rule.matches(input, axe.utils.getNodeFromTree(input)));\n    });\n  }\n\n  describe('with font icons', () => {\n    before(async () => {\n      const materialFont = new FontFace(\n        'Material Icons',\n        'url(/test/assets/MaterialIcons.woff2)'\n      );\n      await materialFont.load();\n      document.fonts.add(materialFont);\n    });\n\n    it('is false for a single icon', () => {\n      fixture.innerHTML =\n        '<div id=\"target\" style=\"font-family: \\'Material Icons\\'\">delete</div>';\n      const target = fixture.querySelector('#target');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n    });\n\n    it('is false for multiple icons', () => {\n      fixture.innerHTML = `<div id=\"target\" style=\"font-family: \\'Material Icons\\'\">\n          check star favorite\n        </div>`;\n      const target = fixture.querySelector('#target');\n      axe.testUtils.flatTreeSetup(fixture);\n      assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/data-table-matches.js",
    "content": "describe('data-table-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('th-has-data-cells');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('should return false if table has role=\"presentation\"', function () {\n    fixtureSetup(\n      '<table role=\"presentation\" id=\"target\">' +\n        '  <tr> <th>hi</th> <td></td> </tr>' +\n        '  <tr> <th>hi</th> <td></td> </tr>' +\n        '</table>'\n    );\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('should return false if table has role=\"none\"', function () {\n    fixtureSetup(\n      '<table role=\"none\" id=\"target\">' +\n        '  <tr> <th>hi</th> <td></td> </tr>' +\n        '  <tr> <th>hi</th> <td></td> </tr>' +\n        '</table>'\n    );\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('should return true if table is a data table', function () {\n    fixtureSetup(\n      '<table id=\"target\">' +\n        '\t<caption>Table caption</caption>' +\n        '\t<tr><th scope=\"col\">Heading 1</th><th scope=\"col\">Heading 2</th></tr>' +\n        '\t<tr><td>Thing 1</td><td>Thing 2</td></tr>' +\n        '</table>'\n    );\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'table')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/duplicate-id-active-matches.js",
    "content": "describe('duplicate-id-active matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('duplicate-id-active');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('returns false if the ID is of an inactive non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an inactive non-referenced element with a duplicate', function () {\n    fixtureSetup('<div id=\"foo\"></div><span id=\"foo\"></span>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is of an active non-referenced element', function () {\n    fixtureSetup('<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is a duplicate of an active non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an inactive ARIA referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<div aria-labelledby=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of an inactive ARIA referenced element', function () {\n    fixtureSetup(\n      '<div id=\"foo\"></div>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' + '<div aria-labelledby=\"foo\"></div>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/duplicate-id-aria-matches.js",
    "content": "describe('duplicate-id-aria matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('duplicate-id-aria');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('returns false if the ID is of an inactive non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an inactive non-referenced element with a duplicate', function () {\n    fixtureSetup('<div id=\"foo\"></div><span id=\"foo\"></span>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an active non-referenced element', function () {\n    fixtureSetup('<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of an active non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is of an inactive ARIA referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<div aria-labelledby=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is a duplicate of an inactive ARIA referenced element', function () {\n    fixtureSetup(\n      '<div id=\"foo\"></div>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' + '<div aria-labelledby=\"foo\"></div>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is a duplicate of of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/duplicate-id-misc-matches.js",
    "content": "describe('duplicate-id-misc matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('duplicate-id');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('returns true if the ID is of an inactive non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true if the ID is of an inactive non-referenced element with a duplicate', function () {\n    fixtureSetup('<div id=\"foo\"></div><span id=\"foo\"></span>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an active non-referenced element', function () {\n    fixtureSetup('<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of an active non-referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<button id=\"foo\"></button>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an inactive ARIA referenced element', function () {\n    fixtureSetup('<div id=\"foo\"></div>' + '<div aria-labelledby=\"foo\"></div>');\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'div[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of an inactive ARIA referenced element', function () {\n    fixtureSetup(\n      '<div id=\"foo\"></div>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' + '<div aria-labelledby=\"foo\"></div>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'button[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false if the ID is a duplicate of of an active ARIA referenced element', function () {\n    fixtureSetup(\n      '<button id=\"foo\"></button>' +\n        '<div aria-labelledby=\"foo\"></div>' +\n        '<span id=\"foo\"></span>'\n    );\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'span[id=foo]')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/frame-focusable-content-matches.js",
    "content": "describe('frame-focusable-content-matches', function () {\n  'use strict';\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('frame-focusable-content');\n  });\n\n  it('returns false for the top-level context', function () {\n    const result = rule.matches(null, null, {\n      initiator: true,\n      focusable: false,\n      size: {\n        width: 100,\n        height: 100\n      }\n    });\n    assert.isFalse(result);\n  });\n\n  it('returns false for focusable iframes', function () {\n    const result = rule.matches(null, null, {\n      initiator: false,\n      focusable: true,\n      size: {\n        width: 100,\n        height: 100\n      }\n    });\n    assert.isFalse(result);\n  });\n\n  it('returns false for non-focusable iframes that are too small (1x1)', function () {\n    const result = rule.matches(null, null, {\n      initiator: false,\n      focusable: false,\n      size: {\n        width: 1,\n        height: 1\n      }\n    });\n    assert.isFalse(result);\n  });\n\n  it('returns false for non-focusable iframes that are too small (0x0)', function () {\n    const result = rule.matches(null, null, {\n      initiator: false,\n      focusable: false,\n      size: {\n        width: 0,\n        height: 0\n      }\n    });\n    assert.isFalse(result);\n  });\n\n  it('returns true for non-focusable iframes', function () {\n    const result = rule.matches(null, null, {\n      initiator: false,\n      focusable: false,\n      size: {\n        width: 2,\n        height: 1\n      }\n    });\n    assert.isTrue(result);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/frame-title-has-text-matches.js",
    "content": "describe('layout-table-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('frame-title-unique');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if title attribute has text', function () {\n    fixture.innerHTML = '<iframe title=\"hello\"></iframe>';\n    const node = fixture.firstChild;\n    assert.isTrue(rule.matches(node));\n  });\n\n  it('should return false if title attribute is empty', function () {\n    fixture.innerHTML = '<iframe title=\"\"></iframe>';\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if title attribute contains only whitespace', function () {\n    fixture.innerHTML = '<iframe title=\"    \"></iframe>';\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/has-implicit-chromium-role-matches.js",
    "content": "describe('has-implicit-chromium-role-matches', function () {\n  'use strict';\n\n  let rule;\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('presentation-role-conflict');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('matches elements with an implicit role', function () {\n    const vNode = queryFixture('<main id=\"target\"></main>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('does not match elements with no implicit role', function () {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('matches elements with an implicit role in chromium', function () {\n    const vNode = queryFixture('<svg id=\"target\"></svg>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('does not match elements with no implicit role even if they are focusable and have an explicit role', function () {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"none\" tabindex=\"1\"></div>'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/heading-matches.js",
    "content": "describe('heading-matches', function () {\n  'use strict';\n  const queryFixture = axe.testUtils.queryFixture;\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('empty-heading');\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('should return false on elements that are not headings', function () {\n    const vNode = fixtureSetup('<div></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('should return true on elements with role=\"heading\"', function () {\n    const vNode = queryFixture('<div role=\"heading\" id=\"target\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return true on regular headings without roles', function () {\n    for (let i = 1; i <= 6; i++) {\n      const vNode = queryFixture('<h' + i + ' id=\"target\"></h' + i + '>');\n      assert.isTrue(rule.matches(null, vNode));\n    }\n  });\n\n  it('should return false on headings with their role changes', function () {\n    const vNode = queryFixture('<h1 role=\"banner\" id=\"target\"></h1>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('should return true on headings with their role changes to an invalid role', function () {\n    const vNode = queryFixture('<h1 role=\"bruce\" id=\"target\"></h1>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return true on headings with their role changes to an abstract role', function () {\n    const vNode = queryFixture('<h1 role=\"widget\" id=\"target\"></h1>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return true on headings with explicit role=\"none\" and an empty aria-label to account for presentation conflict resolution', function () {\n    const vNode = queryFixture(\n      '<h1 aria-label=\"\" role=\"none\" id=\"target\"></h1>'\n    );\n    assert.isTrue(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/html-namespace-matches.js",
    "content": "describe('html-namespace-matches', function () {\n  'use strict';\n  let rule;\n  let fixture;\n  let axeFixtureSetup;\n\n  beforeEach(function () {\n    fixture = document.getElementById('fixture');\n    axeFixtureSetup = axe.testUtils.fixtureSetup;\n    rule = axe.utils.getRule('role-img-alt');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true when passed an HTML element', function () {\n    axeFixtureSetup('<h1>Hello world</h1>');\n    const node = fixture.querySelector('h1');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(rule.matches(node, virtualNode));\n  });\n\n  it('returns true when passed a custom HTML element', function () {\n    axeFixtureSetup('<xx-heading>Hello world</xx-heading>');\n    const node = fixture.querySelector('xx-heading');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(rule.matches(node, virtualNode));\n  });\n\n  it('returns false when passed an SVG element', function () {\n    axeFixtureSetup('<svg><title>Pretty picture</title></svg>');\n    const node = fixture.querySelector('svg');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  it('returns false when passed an SVG circle element', function () {\n    axeFixtureSetup(\n      '<svg><circle><title>Pretty picture</title></circle></svg>'\n    );\n    const node = fixture.querySelector('circle');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  describe('Serial Virtual Node', function () {\n    it('returns true when passed an HTML element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'h1'\n      });\n      serialNode.parent = null;\n\n      assert.isTrue(rule.matches(null, serialNode));\n    });\n\n    it('returns true when passed a custom HTML element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'xx-heading'\n      });\n      serialNode.parent = null;\n\n      assert.isTrue(rule.matches(null, serialNode));\n    });\n\n    it('returns false when passed an SVG circle element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'circle'\n      });\n      const parent = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      serialNode.parent = parent;\n      parent.children = [serialNode];\n\n      assert.isFalse(rule.matches(null, serialNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/html-xml-lang-mismatch.js",
    "content": "describe('xml-lang-mismatch-matches', function () {\n  'use strict';\n\n  // nested this on a per rule basis, for future-proofing writing tests for multiple rules using the same matches\n  describe('for rule: html-xml-lang-mismatch', function () {\n    let rule;\n    let dom;\n    const fixture = document.getElementById('fixture');\n\n    beforeEach(function () {\n      rule = axe.utils.getRule('html-xml-lang-mismatch');\n      dom = document.createElement('html');\n    });\n\n    afterEach(function () {\n      fixture.innerHTML = '';\n    });\n\n    it('is a function', function () {\n      const actual = rule.matches;\n      assert.isFunction(actual);\n    });\n\n    it('returns false if the element does not contain lang or xml:lang attribute', function () {\n      const actual = rule.matches(dom);\n      assert.isFalse(actual);\n    });\n\n    it('returns false if the element contains either/ only one of the lang or xml:lang attribute', function () {\n      dom.setAttribute('lang', 'nl');\n      const actual = rule.matches(dom);\n      assert.isFalse(actual);\n    });\n\n    it('returns true if the element contains both lang and xml:lang attribute', function () {\n      dom.setAttribute('lang', 'en');\n      dom.setAttribute('xml:lang', 'nl');\n      const actual = rule.matches(dom);\n      assert.isTrue(actual);\n    });\n\n    it('returns false for element of type that is not HTML', function () {\n      const node = document.createElement('svg');\n      node.setAttribute('lang', '');\n      node.setAttribute('xml:lang', 'nl');\n      const actual = rule.matches(node);\n      assert.isFalse(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/identical-links-same-purpose-matches.js",
    "content": "describe('identical-links-same-purpose-matches tests', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const rule = axe.utils.getRule('identical-links-same-purpose');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('returns false when native link without accessible name', function () {\n    const vNode = queryFixture('<a id=\"target\" href=\"\"></a>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for ARIA link without accessible name', function () {\n    const vNode = queryFixture('<span role=\"link\" id=\"target\"></span>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for native link with a role !== link', function () {\n    const vNode = queryFixture(\n      '<a id=\"target\" href=\"#\" role=\"button\">Go to Checkout</a>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when `area` has no parent `map` element', function () {\n    const vNode = queryFixture(\n      '<area id=\"target\" role=\"link\" href=\"\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\" />'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when `area` has parent `map` that is not referred by `img[usemap]`', function () {\n    const vNode = queryFixture(\n      '<map name=\"iam-not-referred\">' +\n        '<area id=\"target\" role=\"link\" href=\"\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\" />' +\n        '</map>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when native link without href', function () {\n    const vNode = queryFixture('<a id=\"target\">Book Now</a>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when ARIA link without href', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" role=\"link\">Book Now</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when native link has an accessible name', function () {\n    const vNode = queryFixture(\n      '<a id=\"target\" href=\"https://developer.mozilla.org/\" aria-label=\"Go to MDN website\"></a>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true for ARIA link has an accessible name', function () {\n    const vNode = queryFixture(\n      '<span role=\"link\" id=\"target\">Book Tour</span>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when `area` has parent `map` that is referred by `img`', function () {\n    const vNode = queryFixture(\n      '<map name=\"iam-referred\">' +\n        '<area id=\"target\" role=\"link\" href=\"\" shape=\"circle\" coords=\"130,136,60\" aria-label=\"MDN\" />' +\n        '</map>' +\n        '<img usemap=\"#iam-referred\" alt=\"MDN infographic\" />'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/inserted-into-focus-order-matches.js",
    "content": "describe('inserted-into-focus-order-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const flatTreeSetup = axe.testUtils.flatTreeSetup;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('focus-order-semantics');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should return true for a non-focusable element with tabindex > -1', function () {\n    fixture.innerHTML = '<div tabindex=\"0\"></div>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n    assert.isTrue(rule.matches(node));\n  });\n\n  it('should return false for a non-focusable element with tabindex == -1', function () {\n    fixture.innerHTML = '<div tabindex=\"-1\"></div>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false for a native focusable element with tabindex > 0', function () {\n    fixture.innerHTML = '<button tabindex=\"0\"></button>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false for a native focusable element with no tabindex', function () {\n    fixture.innerHTML = '<a href=\"#\"></a>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false for non-numeric tabindex value', function () {\n    fixture.innerHTML = '<div tabindex=\"abc\"></div>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n    assert.isFalse(rule.matches(node));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/is-initiator-matches.js",
    "content": "describe('is-initiator-matches', function () {\n  'use strict';\n\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('html-has-lang');\n  });\n\n  afterEach(function () {\n    const fixture = document.getElementById('fixture');\n    fixture.innerHTML = '';\n  });\n\n  it('should return true if the context is the initiator', function () {\n    assert.isTrue(rule.matches(null, null, { initiator: true }));\n  });\n\n  it('should return false if the context is not the initiator', function () {\n    assert.isFalse(rule.matches(null, null, { initiator: false }));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/is-visible-matches.js",
    "content": "describe('is-visible-matches', function () {\n  'use strict';\n\n  let isVisibleMatches =\n    axe._thisWillBeDeletedDoNotUse.base.metadataFunctionMap[\n      'is-visible-matches'\n    ];\n  let fixture = document.getElementById('fixture');\n  let fixtureSetup = axe.testUtils.fixtureSetup;\n\n  it('returns true for visible elements', function () {\n    fixtureSetup('<p id=\"target\">Hello world</p>');\n    assert.isTrue(isVisibleMatches(fixture.firstChild));\n  });\n\n  it('returns false for elements with hidden', function () {\n    fixtureSetup('<p id=\"target\" hidden>Hello world</p>');\n    assert.isFalse(isVisibleMatches(fixture.firstChild));\n  });\n\n  it('returns true for visible elements with aria-hidden=\"true\"', function () {\n    fixtureSetup('<p id=\"target\" aria-hidden=\"true\">Hello world</p>');\n    assert.isTrue(isVisibleMatches(fixture.firstChild));\n  });\n\n  it('returns false for opacity:0 elements with accessible text', function () {\n    fixtureSetup('<p id=\"target\" style=\"opacity:0\">Hello world</p>');\n    assert.isFalse(isVisibleMatches(fixture.firstChild));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/is-visible-on-screen-matches.js",
    "content": "describe('is-visible-on-screen-matches', () => {\n  'use strict';\n\n  const rule = axe.utils.getRule('avoid-inline-spacing');\n  const queryFixture = axe.testUtils.queryFixture;\n\n  it('returns true for visible elements', () => {\n    const vNode = queryFixture('<p id=\"target\">Hello world</p>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns false for elements with hidden', () => {\n    const vNode = queryFixture('<p id=\"target\" hidden>Hello world</p>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns true for visible elements with aria-hidden=\"true\"', () => {\n    const vNode = queryFixture(\n      '<p id=\"target\" aria-hidden=\"true\">Hello world</p>'\n    );\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns false for opacity:0 elements with accessible text', () => {\n    const vNode = queryFixture(\n      '<p id=\"target\" style=\"opacity:0\">Hello world</p>'\n    );\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/label-content-name-mismatch-matches.js",
    "content": "describe('label-content-name-mismatch-matches tests', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const rule = axe.utils.getRule('label-content-name-mismatch');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('returns false if given element has no role', function () {\n    const vNode = queryFixture(\n      '<div id=\"target\" aria-label=\"what color is the sky?\"></div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false if element role is not supported with name from contents', function () {\n    const vNode = queryFixture(\n      '<div aria-label=\"choose your age\" id=\"target\" role=\"progressbar\" aria-valuenow=\"20\" aria-valuemin=\"0\" aria-valuemax=\"100\">20 %</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false if implicit element role is overridden to a role that does not support name from contents', function () {\n    const vNode = queryFixture(\n      '<div id=\"labelForStatusMsg\">Status message</div>' +\n        '<button id=\"target\" role=\"status\" aria-labelledby=\"labelForStatusMsg\">Your changes were automatically saved.</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false if element does not have accessible name attributes (`aria-label` or `aria-labelledby`)', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" name=\"link\">Until the very end.</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false if element has empty accessible name via `aria-label`', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" name=\"link\" aria-label=\"\">Until the very end.</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true if element has accessible name via `aria-label`', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" name=\"link\" aria-label=\"Until\">Until the very end.</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true if element has accessible name via `aria-labelledby`', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-labelledby=\"foo\">some content</div>' +\n        '<div id=\"foo\">Foo text</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns false if element has empty accessible name (`aria-labelledby`)', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-labelledby=\"foo\">some content</div>' +\n        '<div id=\"foo\"></div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false if element has empty accessible name (`aria-labelledby`) because idref does not exist', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-labelledby=\"doesNotExist\">some content</div>' +\n        '<div id=\"idExists\">Right Label</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true if element has accessible name (`aria-labelledby`) - multiple refs', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\" aria-labelledby=\"bar baz foo\">some content</div>' +\n        '<div id=\"foo\">Foo</div>' +\n        '<div id=\"bar\">Bar</div>' +\n        '<div id=\"baz\">Baz</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns false for non-widget role', function () {\n    const vNode = queryFixture(\n      '<a role=\"contentinfo\" id=\"target\" aria-label=\"some content\">Content Information</a>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for non-widget role that does support name from content', function () {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"tooltip\" aria-label=\"OK\">Next</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for empty text content', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"close\"></button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for non text content', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"close\"><i class=\"fa fa-icon-close\"></i></button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for hidden (non visible) text content', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"close\"><span style=\"display:none\">I am hidden</span></button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when visible text is combination of alphanumeric and emoji characters', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"I would like a burger\">I would like a 🍔 </button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when visible text is combination of alphanumeric and punctuation characters', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" aria-label=\"next page\">next page &gt;</button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/label-matches.js",
    "content": "describe('label-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    fixture.innerHTML = '';\n    rule = axe.utils.getRule('label');\n  });\n\n  it('returns true for non-input elements', function () {\n    const vNode = queryFixture('<textarea id=\"target\"></textarea>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns true for input elements without type', function () {\n    const vNode = queryFixture('<input id=\"target\" />');\n\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('returns false for input buttons', function () {\n    ['button', 'submit', 'image', 'reset'].forEach(function (type) {\n      const vNode = queryFixture('<input id=\"target\" type=\"' + type + '\" />');\n      assert.isFalse(rule.matches(null, vNode));\n    });\n  });\n\n  it('returns false for input elements type=hidden', function () {\n    const vNode = queryFixture('<input id=\"target\" type=\"hidden\" />');\n\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('returns true for other input types', function () {\n    ['text', 'password', 'url', 'range', 'date', 'checkbox', 'radio'].forEach(\n      function (type) {\n        const vNode = queryFixture('<input id=\"target\" type=\"' + type + '\" />');\n        assert.isTrue(rule.matches(null, vNode));\n      }\n    );\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/landmark-has-body-context.js",
    "content": "describe('landmark-has-body-context', function () {\n  'use strict';\n  const fixtureSetup = axe.testUtils.fixtureSetup;\n  let rule;\n  const shadowSupport = axe.testUtils.shadowSupport.v1;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('landmark-banner-is-top-level');\n  });\n\n  it('returns true for elements with a role', function () {\n    fixtureSetup('<main><footer role=\"contentinfo\"></footer></main>');\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'footer')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns true for elements not contained in a landmark', function () {\n    fixtureSetup('<div><footer role=\"contentinfo\"></footer></div>');\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'footer')[0];\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it('returns false for elements contained in a landmark', function () {\n    fixtureSetup('<main><footer></footer></main>');\n\n    const vNode = axe.utils.querySelectorAll(axe._tree[0], 'footer')[0];\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  (shadowSupport ? it : xit)(\n    'returns false for elements contained in a landmark in a shadow DOM tree',\n    function () {\n      // Safari has a bug in 12.0 that throws an error when calling\n      // attachShadow on <main>\n      // @see https://bugs.webkit.org/show_bug.cgi?id=197726\n      const article = document.createElement('article');\n      const shadow = article.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<footer></footer>';\n\n      fixtureSetup(article);\n      const vNode = axe.utils.querySelectorAll(axe._tree[0], 'footer')[0];\n      assert.isFalse(rule.matches(vNode.actualNode, vNode));\n    }\n  );\n});\n"
  },
  {
    "path": "test/rule-matches/landmark-unique-matches.js",
    "content": "describe('landmark-unique-matches', function () {\n  'use strict';\n  let rule;\n  let fixture;\n  let axeFixtureSetup;\n  const shadowSupport = axe.testUtils.shadowSupport.v1;\n  const sectioningContentElements = ['article', 'aside', 'nav', 'section'];\n  const excludedDescendantsForHeadersFooters =\n    sectioningContentElements.concat('main');\n  const headerFooterElements = ['header', 'footer'];\n\n  beforeEach(function () {\n    fixture = document.getElementById('fixture');\n    axeFixtureSetup = axe.testUtils.fixtureSetup;\n    rule = axe.utils.getRule('landmark-unique');\n  });\n\n  it('should not match because not a landmark', function () {\n    axeFixtureSetup('<h1>some heading</h1>');\n    const node = fixture.querySelector('h1');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  it('should pass because is a landmark', function () {\n    axeFixtureSetup('<div role=\"banner\">some banner</div>');\n    const node = fixture.querySelector('div');\n    fixture.appendChild(node);\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(rule.matches(node, virtualNode));\n  });\n\n  it('should not match because landmark is hidden', function () {\n    axeFixtureSetup('<div role=\"banner\">some banner</div>');\n    const node = fixture.querySelector('div');\n    node.style.display = 'none';\n    fixture.appendChild(node);\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  describe('form and section elements must have accessible names to be matched', function () {\n    const sectionFormElements = ['section', 'form'];\n\n    sectionFormElements.forEach(function (elementType) {\n      it(\n        'should match because it is a ' + elementType + ' with a label',\n        function () {\n          axeFixtureSetup(\n            '<' +\n              elementType +\n              ' aria-label=\"sample label\">some ' +\n              elementType +\n              '</' +\n              elementType +\n              '>'\n          );\n          const node = fixture.querySelector(elementType);\n          const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n          assert.isTrue(rule.matches(node, virtualNode));\n        }\n      );\n\n      it(\n        'should not match because it is a ' + elementType + ' without a label',\n        function () {\n          axeFixtureSetup(\n            '<' +\n              elementType +\n              '>some ' +\n              elementType +\n              '</' +\n              elementType +\n              '>'\n          );\n          const node = fixture.querySelector(elementType);\n          const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n          assert.isFalse(rule.matches(node, virtualNode));\n        }\n      );\n    });\n  });\n\n  describe('header/footers should only match when not inside the excluded descendants', function () {\n    headerFooterElements.forEach(function (elementType) {\n      excludedDescendantsForHeadersFooters.forEach(function (exclusionType) {\n        it(\n          'should not match because ' +\n            elementType +\n            ' is contained inside an ' +\n            exclusionType,\n          function () {\n            axeFixtureSetup(\n              '<' +\n                exclusionType +\n                ' aria-label=\"sample label\"><' +\n                elementType +\n                '>an element</' +\n                elementType +\n                '></' +\n                exclusionType +\n                '>'\n            );\n            const node = fixture.querySelector(elementType);\n            const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n            assert.isFalse(rule.matches(node, virtualNode));\n          }\n        );\n      });\n\n      it(\n        'should match because ' +\n          elementType +\n          ' is not contained inside the excluded descendants',\n        function () {\n          axeFixtureSetup(\n            '<' + elementType + '>an element</' + elementType + '>'\n          );\n          const node = fixture.querySelector(elementType);\n          const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n          assert.isTrue(rule.matches(node, virtualNode));\n        }\n      );\n    });\n  });\n\n  describe('aside should not match when scoped to a sectioning content element unless it has an accessible name', function () {\n    sectioningContentElements.forEach(function (exclusionType) {\n      it(\n        'should not match because aside is scoped to ' +\n          exclusionType +\n          ' and has no label',\n        function () {\n          axeFixtureSetup(\n            '<' +\n              exclusionType +\n              '><aside data-test>an element</aside></' +\n              exclusionType +\n              '>'\n          );\n          const node = fixture.querySelector('aside[data-test]');\n          const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n          assert.isFalse(rule.matches(node, virtualNode));\n        }\n      );\n\n      it(\n        'should match because aside within ' + exclusionType + ' has a label',\n        function () {\n          axeFixtureSetup(\n            '<' +\n              exclusionType +\n              '><aside aria-label=\"sample label\" data-test>an element</aside></' +\n              exclusionType +\n              '>'\n          );\n          const node = fixture.querySelector('aside[data-test]');\n          const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n          assert.isTrue(rule.matches(node, virtualNode));\n        }\n      );\n    });\n\n    it('should match because aside is not scoped to a sectioning content element', function () {\n      axeFixtureSetup('<aside>an element</aside>');\n      const node = fixture.querySelector('aside');\n      const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n      assert.isTrue(rule.matches(node, virtualNode));\n    });\n  });\n\n  if (shadowSupport) {\n    it('return true for landmarks contained within shadow dom', function () {\n      const container = document.createElement('div');\n      const shadow = container.attachShadow({ mode: 'open' });\n      shadow.innerHTML = '<footer></footer>';\n\n      axeFixtureSetup(container);\n      const vNode = axe.utils.querySelectorAll(axe._tree[0], 'footer')[0];\n      assert.isTrue(rule.matches(vNode.actualNode, vNode));\n    });\n\n    describe('header/footers should only match when not inside the excluded descendants within shadow dom', function () {\n      let container;\n      let shadow;\n\n      beforeEach(function () {\n        container = document.createElement('div');\n        shadow = container.attachShadow({ mode: 'open' });\n      });\n\n      headerFooterElements.forEach(function (elementType) {\n        excludedDescendantsForHeadersFooters.forEach(function (exclusionType) {\n          it(\n            'should not match because ' +\n              elementType +\n              ' is contained inside an ' +\n              exclusionType +\n              '',\n            function () {\n              shadow.innerHTML =\n                '<' +\n                exclusionType +\n                ' aria-label=\"sample label\"><' +\n                elementType +\n                '>an element</' +\n                elementType +\n                '></' +\n                exclusionType +\n                '>';\n\n              axeFixtureSetup(container);\n              const virtualNode = axe.utils.querySelectorAll(\n                axe._tree[0],\n                elementType\n              )[0];\n              assert.isFalse(rule.matches(virtualNode.actualNode, virtualNode));\n            }\n          );\n        });\n\n        it(\n          'should match because ' +\n            elementType +\n            ' is not contained inside the excluded descendants',\n          function () {\n            shadow.innerHTML =\n              '<' + elementType + '>an element</' + elementType + '>';\n            axeFixtureSetup(container);\n            const virtualNode = axe.utils.querySelectorAll(\n              axe._tree[0],\n              elementType\n            )[0];\n            assert.isTrue(rule.matches(virtualNode.actualNode, virtualNode));\n          }\n        );\n      });\n    });\n\n    describe('aside should match inside shadow dom unless it is both within sectioning content and has no accessible name', function () {\n      let container;\n      let shadow;\n\n      beforeEach(function () {\n        container = document.createElement('div');\n        shadow = container.attachShadow({ mode: 'open' });\n      });\n\n      sectioningContentElements.forEach(function (exclusionType) {\n        it(\n          'should not match because aside is scoped to ' +\n            exclusionType +\n            ' and has no label',\n          function () {\n            shadow.innerHTML =\n              '<' +\n              exclusionType +\n              ' aria-label=\"sample label\"><aside data-test>an element</aside></' +\n              exclusionType +\n              '>';\n\n            axeFixtureSetup(container);\n            const virtualNode = axe.utils.querySelectorAll(\n              axe._tree[0],\n              'aside[data-test]'\n            )[0];\n            assert.isFalse(rule.matches(virtualNode.actualNode, virtualNode));\n          }\n        );\n\n        it(\n          'should match because aside within ' + exclusionType + ' has a label',\n          function () {\n            shadow.innerHTML =\n              '<' +\n              exclusionType +\n              '><aside aria-label=\"sample label\" data-test>an element</aside></' +\n              exclusionType +\n              '>';\n            axeFixtureSetup(container);\n            const virtualNode = axe.utils.querySelectorAll(\n              axe._tree[0],\n              'aside[data-test]'\n            )[0];\n            assert.isTrue(rule.matches(virtualNode.actualNode, virtualNode));\n          }\n        );\n      });\n\n      it('should match because aside is not scoped to a sectioning content element', function () {\n        shadow.innerHTML = '<aside>an element</aside>';\n        axeFixtureSetup(container);\n        const virtualNode = axe.utils.querySelectorAll(\n          axe._tree[0],\n          'aside'\n        )[0];\n        assert.isTrue(rule.matches(virtualNode.actualNode, virtualNode));\n      });\n    });\n  }\n});\n"
  },
  {
    "path": "test/rule-matches/layout-table-matches.js",
    "content": "describe('layout-table-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const flatTreeSetup = axe.testUtils.flatTreeSetup;\n  let rule;\n\n  beforeEach(function () {\n    axe.configure({\n      rules: [\n        {\n          id: 'layout-rule',\n          matches: 'layout-table-matches'\n        }\n      ]\n    });\n\n    rule = axe.utils.getRule('layout-rule');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe.reset();\n  });\n\n  it('should return false for data table', function () {\n    fixture.innerHTML = '<table><caption>Hello></caption></table>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if the table is focusable', function () {\n    fixture.innerHTML = '<table tabindex=\"0\"></table>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return true if table has role=presentation', function () {\n    fixture.innerHTML = '<table role=\"presentation\"></table>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n\n    assert.isTrue(rule.matches(node));\n  });\n\n  it('should return true if table has role=none', function () {\n    fixture.innerHTML = '<table role=\"none\"></table>';\n    flatTreeSetup(fixture);\n    const node = fixture.firstChild;\n\n    assert.isTrue(rule.matches(node));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/link-in-text-block-matches.js",
    "content": "describe('link-in-text-block-matches', () => {\n  const { fixtureSetup } = axe.testUtils;\n  const rule = axe.utils.getRule('link-in-text-block');\n\n  it('should return true if link is in text block', () => {\n    fixtureSetup(\n      '<p>Some paragraph with text <a id=\"target\" href=\"#\">world</a></p>'\n    );\n    const node = document.getElementById('target');\n    assert.isTrue(rule.matches(node));\n  });\n\n  it('should return false if element has a non-link role', () => {\n    fixtureSetup(\n      '<p>Some paragraph with text <a id=\"target\" href=\"#\" role=\"button\">hello</a></p>'\n    );\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should should return false if element does not have text', () => {\n    fixtureSetup(\n      '<p>Some paragraph with text <a id=\"target\" href=\"#\"></a></p>'\n    );\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if element has <style>', () => {\n    fixtureSetup(`\n      <p>Some paragraph with text\n        <a id=\"target\" href=\"#\">\n          <style>a { color: #333 }</style>\n        </a>\n      </p>\n    `);\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if element has <script>', () => {\n    fixtureSetup(`\n      <p>Some paragraph with text\n        <a id=\"target\" href=\"#\">\n          <script>console.log('foo')</script>\n        </a>\n      </p>\n    `);\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if element is hidden', () => {\n    fixtureSetup(\n      '<p>Some paragraph with text <a id=\"target\" href=\"#\"\" style=\"display: none\">hello</a></p>'\n    );\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if link is not in text block', () => {\n    fixtureSetup('<a id=\"target\" href=\"#\">hello</a>');\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if link is only text in block', () => {\n    fixtureSetup('<p><a id=\"target\" href=\"#\">world</a></p>');\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n\n  it('should return false if link is display block', () => {\n    fixtureSetup(\n      '<p>Some paragraph with text <a id=\"target\" href=\"#\" style=\"display: block\">world</a></p>'\n    );\n    const node = document.getElementById('target');\n    assert.isFalse(rule.matches(node));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/nested-interactive-matches.js",
    "content": "describe('nested-interactive-matches', function () {\n  const fixture = document.querySelector('#fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('nested-interactive');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('should match if element has children presentational', function () {\n    const vNode = queryFixture('<button id=\"target\"></button>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should match if aria element has children presentational', function () {\n    const vNode = queryFixture('<div role=\"button\" id=\"target\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should not match if element does not have children presentational', function () {\n    const vNode = queryFixture('<a href=\"foo.html\" id=\"target\"></a>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('should not match if element has no role', function () {\n    const vNode = queryFixture('<span id=\"target\"></span>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-autoplay-audio-matches.js",
    "content": "describe('no-autoplay-audio-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const preloadOptions = { preload: { assets: ['media'] } };\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('no-autoplay-audio');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns false for <audio> element that has no src attribute', function (done) {\n    const vNode = queryFixture('<audio id=\"target\"></audio>');\n    axe.utils.preload(preloadOptions).then(function () {\n      const actual = rule.matches(vNode.actualNode);\n      assert.isFalse(actual);\n      done();\n    });\n  });\n\n  it('returns false for <video> element that is paused', function (done) {\n    const vNode = queryFixture(\n      '<video id=\"target\" autoplay=\"true\" paused=\"true\">' +\n        '<source src=\"/test/assets/video.webm\" type=\"video/webm\" />' +\n        '<source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />' +\n        '</video>'\n    );\n    axe.utils.preload(preloadOptions).then(function () {\n      const actual = rule.matches(vNode.actualNode);\n      assert.isFalse(actual);\n      done();\n    });\n  });\n\n  it('returns false for <video> element that is muted', function (done) {\n    const vNode = queryFixture(\n      '<video id=\"target\" autoplay=\"true\" muted=\"true\">' +\n        '<source src=\"/test/assets/video.webm\" type=\"video/webm\" />' +\n        '<source src=\"/test/assets/video.mp4\" type=\"video/mp4\" />' +\n        '</video>'\n    );\n    axe.utils.preload(preloadOptions).then(function () {\n      const actual = rule.matches(vNode.actualNode);\n      assert.isFalse(actual);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-empty-role-matches.js",
    "content": "describe('no-role-empty-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('aria-roles');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns false when element does not have `role` attribute', function () {\n    const vNode = queryFixture('<div id=\"target\">Some Content</div>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has role attribute has no value', function () {\n    const vNode = queryFixture('<div role id=\"target\">Some Content</div>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has empty role attribute', function () {\n    const vNode = queryFixture('<div role=\"\" id=\"target\">Some Content</div>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has role attribute consisting of only whitespace', function () {\n    const vNode = queryFixture('<div role=\" \" id=\"target\">Some Content</div>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when element has role attribute', function () {\n    const vNode = queryFixture(\n      '<div role=\"button\" id=\"target\">Some Content</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has multiple roles', function () {\n    const vNode = queryFixture(\n      '<div role=\"button link\" id=\"target\">Some Content</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has invalid role', function () {\n    const vNode = queryFixture(\n      '<div role=\"xyz\" id=\"target\">Some Content</div>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-explicit-name-required-matches.js",
    "content": "describe('no-explicit-name-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  let rule;\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('button-name');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true when element does not have `role` attribute', function () {\n    const vNode = queryFixture('<button id=\"target\"></button>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has an explicit role of presentation', function () {\n    const vNode = queryFixture(\n      '<button id=\"target\" role=\"presentation\"></button>'\n    );\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has an explicit role of none', function () {\n    const vNode = queryFixture('<button id=\"target\" role=\"none\"></button>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has an invalid explicit role', function () {\n    const vNode = queryFixture('<button id=\"target\" role=\"foo\"></button>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when element has an explicit role that requires an accessible name', function () {\n    const vNode = queryFixture('<button id=\"target\" role=\"button\"></button>');\n    const actual = rule.matches(vNode.actualNode, vNode);\n    assert.isTrue(actual);\n  });\n\n  describe('with a role that does not require an accessible name', function () {\n    it('returns true when element is focusable', function () {\n      const vNode = queryFixture(\n        '<button id=\"target\" role=\"separator\"></button>'\n      );\n      const actual = rule.matches(vNode.actualNode, vNode);\n      assert.isTrue(actual);\n    });\n\n    it('returns false when element is not focusable', function () {\n      const vNode = queryFixture(\n        '<button id=\"target\" role=\"separator\" disabled></button>'\n      );\n      const actual = rule.matches(vNode.actualNode, vNode);\n      assert.isFalse(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-naming-method-matches.js",
    "content": "describe('no-naming-method-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const rule = axe.utils.getRule('aria-toggle-field-name');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns false for node `a[href]`', function () {\n    const vNode = queryFixture('<a href=\"# role=\"checkbox\" id=\"target\"></a>');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for node `map area[href]`', function () {\n    const vNode = queryFixture(\n      '<map><area id=\"target\" href=\"#\" role=\"checkbox\"></map>'\n    );\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when node is either INPUT, SELECT or TEXTAREA', function () {\n    ['input', 'select', 'textarea'].forEach(function (node) {\n      const vNode = queryFixture(\n        '<' + node + ' role=\"menuitemcheckbox\" id=\"target\"><' + node + '>'\n      );\n      const actual = rule.matches(null, vNode);\n      assert.isFalse(actual);\n    });\n  });\n\n  it('returns false when node is IMG', function () {\n    const vNode = queryFixture('<img id=\"target\" role=\"menuitemradio\">');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when node is not SVG', function () {\n    const vNode = queryFixture('<svg id=\"target\"></svg>');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when node is BUTTON', function () {\n    const vNode = queryFixture('<button id=\"target\"></button>');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when node is SUMMARY', function () {\n    const vNode = queryFixture('<summary id=\"target\"></summary>');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for INPUT of type `BUTTON`, `SUBMIT` or `RESET`', function () {\n    ['button', 'submit', 'reset'].forEach(function (type) {\n      const vNode = queryFixture(\n        '<input id=\"target\" role=\"radio\" type=\"' + type + '\">'\n      );\n      const actual = rule.matches(null, vNode);\n      assert.isFalse(actual);\n    });\n  });\n\n  it('returns false when role=`combobox` has a child input', function () {\n    const vNode = queryFixture(\n      '<div id=\"target\" role=\"combobox\"><input type=\"text\"/></div>'\n    );\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for the listbox popup of a role=`combobox`', function () {\n    const vNode = queryFixture(\n      '<div role=\"combobox\" aria-controls=\"target\"></div>' +\n        '<div id=\"target\" role=\"listbox\"></div>'\n    );\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n\n  it('returns true for the dialog popup of a role=`combobox`', function () {\n    const vNode = queryFixture(\n      '<div role=\"combobox\" aria-controls=\"target\"></div>' +\n        '<div id=\"target\" role=\"dialog\"></div>'\n    );\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true for a div with role=`button`', function () {\n    const vNode = queryFixture('<div id=\"target\" role=\"button\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-negative-tabindex.js",
    "content": "describe('no-negative-tabindex-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const rule = axe.utils.getRule('frame-title');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true for nodes with no tabindex', function () {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true for nodes with a zero tabindex', function () {\n    const vNode = queryFixture('<div id=\"target\" tabindex=\"0\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true for nodes with a positive tabindex', function () {\n    const vNode = queryFixture('<div id=\"target\" tabindex=\"4\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns true for nodes with an invalid tabindex', function () {\n    const vNode = queryFixture('<div id=\"target\" tabindex=\"foo\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isTrue(actual);\n  });\n\n  it('returns false for nodes with a negative tabindex', function () {\n    const vNode = queryFixture('<div id=\"target\" tabindex=\"-10\"></div>');\n    const actual = rule.matches(null, vNode);\n    assert.isFalse(actual);\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/no-role-matches.js",
    "content": "describe('link-in-text-block-matches', () => {\n  const rule = axe.utils.getRule('definition-list');\n  const { queryFixture } = axe.testUtils;\n\n  it('should return true if element does not have a role attribute', () => {\n    const vNode = queryFixture('<div id=\"target\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return true if element has an empty role attribute', () => {\n    const vNode = queryFixture('<div role=\"\" id=\"target\"></div>');\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('should return false if element has a role attribute', () => {\n    const vNode = queryFixture('<div role=\"button\" id=\"target\"></div>');\n    assert.isFalse(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/object-is-loaded-matches.js",
    "content": "describe('object-is-loaded-matches', () => {\n  let rule, fixture;\n  const data = `data:text/html,Object%20content`;\n\n  // Give enough time for the browser to load / decide it cannot load objects\n  async function delayedQueryFixture(html, delay = 250) {\n    fixture.innerHTML = html;\n    await new Promise(r => setTimeout(r, delay));\n    const tree = axe.setup();\n    return axe.utils.querySelectorAll(tree, '#target')[0];\n  }\n\n  before(() => {\n    fixture = document.querySelector('#fixture');\n    rule = axe.utils.getRule('object-alt');\n  });\n\n  afterEach(() => {\n    fixture.innerHTML = '';\n  });\n\n  it(`returns true objects with hidden fallback content`, async () => {\n    const vNode = await delayedQueryFixture(\n      `<object data=\"${data}\" height=\"30\" id=\"target\">\n          Fallback content\n        </object>`\n    );\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it(`returns false if the object shows any content`, async () => {\n    const vNode = await delayedQueryFixture(\n      `<object data=\"invalid\" height=\"30\" id=\"target\">\n          Fallback content\n        </object>`\n    );\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it(`returns true if the object shows no content`, async () => {\n    const vNode = await delayedQueryFixture(\n      `<object data=\"invalid\" height=\"30\" id=\"target\"></object>`\n    );\n    // Ideally, this should be false, don't know it can be done\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it(`returns false if the object has a role that doesn't require a name`, async () => {\n    const vNode = await delayedQueryFixture(\n      `<object data=\"${data}\" height=\"30\" id=\"target\" role=\"grid\">\n          Fallback content\n        </object>`\n    );\n    assert.isFalse(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/p-as-heading-matches.js",
    "content": "describe('p-as-heading-matches', function () {\n  'use strict';\n\n  let rule;\n  const fixture = document.getElementById('fixture');\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('p-as-heading');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('matches p elements', function () {\n    fixture.innerHTML = '<p id=\"target\">some text</p><p>some other text</p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isTrue(rule.matches(target));\n  });\n\n  it('ignores the last p element in a list of children', function () {\n    fixture.innerHTML = '<p>some text</p><p id=\"target\">some other text</p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isFalse(rule.matches(target));\n  });\n\n  it('ignores p elements that contains punctuation marks', function () {\n    fixture.innerHTML =\n      '<p id=\"target\">A text. Paragraph?</p><p>some other text</p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isFalse(rule.matches(target));\n  });\n\n  it('matches p elements with a single punctuation mark', function () {\n    fixture.innerHTML = '<p id=\"target\">A paragraph?</p><p>some other text</p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isTrue(rule.matches(target));\n  });\n\n  it('ignores p elements that have no text-like characters', function () {\n    fixture.innerHTML = '<p id=\"target\"> \\n\\t\\r </p><p>some other text</p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isFalse(rule.matches(target));\n  });\n\n  it('ignores siblings that are not p elements', function () {\n    fixture.innerHTML =\n      '<p id=\"target\">some text</p><div></div><p>some other text</p>';\n    let target = fixture.querySelector('#target');\n\n    assert.isTrue(rule.matches(target));\n\n    fixture.innerHTML = '<p id=\"target\">some text</p><div></div>';\n    target = fixture.querySelector('#target');\n\n    assert.isFalse(rule.matches(target));\n  });\n\n  it('ignores empty p elements as siblings', function () {\n    fixture.innerHTML =\n      '<p id=\"target\">some text</p><p> <!-- nothing here --><img src=\"\" alt=\"\" /></p>';\n    const target = fixture.querySelector('#target');\n\n    assert.isFalse(rule.matches(target));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/scrollable-region-focusable-matches.js",
    "content": "describe('scrollable-region-focusable-matches', function () {\n  'use strict';\n\n  const fixture = document.getElementById('fixture');\n  const queryFixture = axe.testUtils.queryFixture;\n  const shadowSupported = axe.testUtils.shadowSupport.v1;\n  const rule = axe.utils.getRule('scrollable-region-focusable');\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns false when element is not scrollable', function () {\n    const target = queryFixture(\n      '<section id=\"target\">This element is not scrollable</section>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has no visible children', function () {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px;\">' +\n        '<div style=\"display:none; height: 2000px; width: 100px;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element does not overflow', function () {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: auto;\">' +\n        '<div style=\"height: 10px; width: 100x;\">Content</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element is not scrollable (overflow=hidden)', function () {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: hidden\">' +\n        '<div style=\"height: 2000px; width: 100px; background-color: pink;\">' +\n        '<p> Content </p>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when element is scrollable (overflow=auto)', function () {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"height: 200px; width: 200px; overflow: auto\">' +\n        '<div style=\"height: 10px; width: 2000px; background-color: red;\">' +\n        '<p> Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium </p>' +\n        '</div>' +\n        '</div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when element overflow is visible', function () {\n    const target = queryFixture(\n      '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: visible;\">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when element overflow is scroll', function () {\n    const target = queryFixture(\n      '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when element overflow is scroll but has no content', function () {\n    const target = queryFixture(\n      '<div id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><div style=\"height: 15rem\"></div></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element has combobox ancestor', function () {\n    const target = queryFixture(\n      '<div role=\"combobox\"><ul id=\"target\" role=\"listbox\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element is owned by combobox', function () {\n    const target = queryFixture(\n      '<input role=\"combobox\" aria-owns=\"foo target\"/><ul id=\"target\" role=\"listbox\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when element is controlled by combobox', function () {\n    const target = queryFixture(\n      '<input role=\"combobox\" aria-controls=\"foo target\"/><ul id=\"target\" role=\"listbox\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for combobox with tree', function () {\n    const target = queryFixture(\n      '<div role=\"combobox\"><ul id=\"target\" role=\"tree\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for combobox with grid', function () {\n    const target = queryFixture(\n      '<div role=\"combobox\"><ul id=\"target\" role=\"grid\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false for combobox with dialog', function () {\n    const target = queryFixture(\n      '<div role=\"combobox\"><ul id=\"target\" role=\"dialog\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li></ul></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns true for combobox with non-valid role', function () {\n    const target = queryFixture(\n      '<div role=\"combobox\"><ul id=\"target\" role=\"section\" style=\"width: 12em; height: 2em; border: dotted; overflow: scroll;\"><li role=\"option\" style=\"height: 15rem\">Option</li><li role=\"option\" style=\"height: 15rem\">Option</li><li role=\"option\" style=\"height: 15rem\">Option</li><li role=\"option\" style=\"height: 15rem\">Option</li></ul></div>'\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isTrue(actual);\n  });\n\n  it('returns false when all text is visible without scrolling', () => {\n    const target = queryFixture(\n      `<div id=\"target\" style=\"width: 300px; overflow-y: auto\">\n        <p style=\"width: 600px\">Contents</p>\n      </div>`\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns false when all visible content is visible without scrolling', () => {\n    const target = queryFixture(\n      `<div id=\"target\" style=\"width: 300px; overflow-y: auto\">\n        <img src=\"#\" width=\"100\" height=\"100\"/>\n      </div>`\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isFalse(actual);\n  });\n\n  it('returns true when any visible text is not visible without scrolling', () => {\n    const target = queryFixture(\n      `<div id=\"target\" style=\"width: 300px; overflow-y: auto\">\n        <p style=\"width: 600px\">Contents</p>\n        <p style=\"width: 600px\">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium. </p>\n      </div>`\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isTrue(actual);\n  });\n\n  it('returns true when any visible content is not visible without scrolling', () => {\n    const target = queryFixture(\n      `<div id=\"target\" style=\"width: 300px; overflow-y: auto\">\n        <p width=\"600px\">Contents</p>\n        <img src=\"#\" width=\"600\" height=\"600\"/>\n      </div>`\n    );\n    const actual = rule.matches(target.actualNode, target);\n    assert.isTrue(actual);\n  });\n\n  describe('shadowDOM - scrollable-region-focusable-matches', function () {\n    before(function () {\n      if (!shadowSupported) {\n        this.skip();\n      }\n    });\n\n    afterEach(function () {\n      axe._tree = undefined;\n    });\n\n    it('returns false when shadowDOM element does not overflow', function () {\n      fixture.innerHTML = '<div></div>';\n\n      const root = fixture.firstChild.attachShadow({ mode: 'open' });\n      const slotted = document.createElement('div');\n      slotted.innerHTML =\n        '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted;\">Sed.</p>';\n      root.appendChild(slotted);\n      const tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild));\n      const target = axe.utils.querySelectorAll(tree, 'p')[0];\n      const actual = rule.matches(target.actualNode, target);\n      assert.isFalse(actual);\n    });\n\n    it('returns true when shadowDOM element has overflow', function () {\n      fixture.innerHTML = '<div></div>';\n\n      const root = fixture.firstChild.attachShadow({ mode: 'open' });\n      const slotted = document.createElement('div');\n      slotted.innerHTML =\n        '<p id=\"target\" style=\"width: 12em; height: 2em; border: dotted; overflow: auto;\">This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence, This is a repeated long sentence.</p>';\n      root.appendChild(slotted);\n      const tree = (axe._tree = axe.utils.getFlattenedTree(fixture.firstChild));\n      const target = axe.utils.querySelectorAll(tree, 'p')[0];\n      const actual = rule.matches(target.actualNode, target);\n      assert.isTrue(actual);\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/skip-link-matches.js",
    "content": "describe('skip-link-matches', function () {\n  'use strict';\n\n  let rule, link;\n  const fixture = document.getElementById('fixture');\n\n  beforeEach(function () {\n    rule = axe.utils.getRule('skip-link');\n    fixture.innerHTML =\n      '<a href=\"\" id=\"target\" style=\"position: absolute; left: -10000px;\">Click me</a><div id=\"main\"></div>';\n    link = fixture.querySelector('#target');\n    axe._tree = axe.utils.getFlattenedTree(fixture);\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n    axe._tree = undefined;\n  });\n\n  it('is a function', function () {\n    assert.isFunction(rule.matches);\n  });\n\n  it('returns false if the links is onscreen', function () {\n    link.removeAttribute('style');\n    link.href = '#main';\n    assert.isFalse(rule.matches(link));\n  });\n\n  it('returns false if the href attribute does not start with #', function () {\n    link.href = 'foo#bar';\n    assert.isFalse(rule.matches(link));\n  });\n\n  it('returns false if the href attribute is `#`', function () {\n    link.href = '#';\n    assert.isFalse(rule.matches(link));\n  });\n\n  it('returns false if the href attribute starts with #!', function () {\n    link.href = '#!foo';\n    assert.isFalse(rule.matches(link));\n  });\n\n  it('returns false if the href attribute starts with #/', function () {\n    link.href = '#/foo';\n    assert.isFalse(rule.matches(link));\n  });\n\n  it('returns true if the href attribute starts with #', function () {\n    link.href = '#main';\n    assert.isTrue(rule.matches(link));\n  });\n\n  it('returns true if the href attribute starts with /# (angular)', function () {\n    link.href = '/#main';\n    assert.isTrue(rule.matches(link));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/summary-interactive-matches.js",
    "content": "describe('summary-interactive-matches', () => {\n  const rule = axe.utils.getRule('summary-name');\n  const { queryFixture, queryShadowFixture, html } = axe.testUtils;\n\n  it('is true for an interactive summary', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('is false for summary without a details parent', () => {\n    const vNode = queryFixture(html`\n      <summary id=\"target\">Summary</summary>\n      text\n    `);\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('is false for summary with a details ancestor', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <div>\n          <summary id=\"target\">Summary</summary>\n          text\n        </div>\n      </details>\n    `);\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('is false for a non-first summary', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary>Summary</summary>\n        <summary id=\"target\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('is false for details parent in a different DOM tree', () => {\n    const vFixture = queryShadowFixture(\n      html`\n        <div id=\"shadow\">\n          <summary>Hello World</summary>\n        </div>\n      `,\n      html`\n        <details>\n          <slot></slot>\n          text\n        </details>\n      `\n    );\n    const vNode = axe.utils.querySelectorAll(vFixture, 'summary')[0];\n    assert.isFalse(rule.matches(null, vNode));\n  });\n\n  it('is true even if summary has role=none', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\" role=\"none\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('is true the element has a widget role', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\" role=\"button\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('is true the element has a non-interactive role', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\" role=\"heading\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('is true even if summary has tabindex=-1', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\" tabindex=\"-1\">Summary</summary>\n        text\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n\n  it('is true even if summary is the only child', () => {\n    const vNode = queryFixture(html`\n      <details>\n        <summary id=\"target\">Summary</summary>\n      </details>\n    `);\n    assert.isTrue(rule.matches(null, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/svg-namespace-matches.js",
    "content": "describe('svg-namespace-matches', function () {\n  'use strict';\n  let rule;\n  let fixture;\n  let axeFixtureSetup;\n\n  beforeEach(function () {\n    fixture = document.getElementById('fixture');\n    axeFixtureSetup = axe.testUtils.fixtureSetup;\n    rule = axe.utils.getRule('svg-img-alt');\n  });\n\n  afterEach(function () {\n    fixture.innerHTML = '';\n  });\n\n  it('returns true when passed an SVG element', function () {\n    axeFixtureSetup('<svg><title>Pretty picture</title></svg>');\n    const node = fixture.querySelector('svg');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(rule.matches(node, virtualNode));\n  });\n\n  it('returns true when passed an SVG circle element', function () {\n    axeFixtureSetup(\n      '<svg><circle><title>Pretty picture</title></circle></svg>'\n    );\n    const node = fixture.querySelector('circle');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isTrue(rule.matches(node, virtualNode));\n  });\n\n  it('returns false when passed an HTML element', function () {\n    axeFixtureSetup('<h1>Hello world</h1>');\n    const node = fixture.querySelector('h1');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  it('returns false when passed a custom HTML element', function () {\n    axeFixtureSetup('<xx-heading>Hello world</xx-heading>');\n    const node = fixture.querySelector('xx-heading');\n    const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], node);\n    assert.isFalse(rule.matches(node, virtualNode));\n  });\n\n  describe('Serial Virtual Node', function () {\n    it('should return true when passed an SVG element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      const child = new axe.SerialVirtualNode({\n        nodeName: 'title',\n        nodeValue: 'Pretty picture'\n      });\n      child.parent = serialNode;\n      serialNode.children = [child];\n\n      assert.isTrue(rule.matches(null, serialNode));\n    });\n\n    it('returns true when passed an SVG circle element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      const child = new axe.SerialVirtualNode({\n        nodeName: 'circle'\n      });\n      child.parent = serialNode;\n      serialNode.children = [child];\n\n      assert.isTrue(rule.matches(null, child));\n    });\n\n    it('returns false when passed an HTML element', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'h1',\n        nodeValue: 'Hello world'\n      });\n\n      assert.isFalse(rule.matches(null, serialNode));\n    });\n\n    it('should return false when passed an svg element without a parent', function () {\n      const serialNode = new axe.SerialVirtualNode({\n        nodeName: 'circle'\n      });\n      const parent = new axe.SerialVirtualNode({\n        nodeName: 'svg'\n      });\n      parent.children = [serialNode];\n      serialNode.parent = parent;\n\n      assert.isTrue(rule.matches(null, serialNode));\n    });\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/table-or-grid-role-matches.js",
    "content": "describe('table-or-grid-role-matches', () => {\n  const { queryFixture } = axe.testUtils;\n  const rule = axe.utils.getRule('td-headers-attr');\n\n  it(`returns true for tables without role`, () => {\n    const vNode = queryFixture(`<table id=\"target\">\n        <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n      </table>`);\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  ['table', 'grid', 'treegrid'].forEach(role => {\n    it(`returns true for tables with role=${role}`, () => {\n      const vNode = queryFixture(`<table id=\"target\" role=\"${role}\">\n          <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n        </table>`);\n      assert.isTrue(rule.matches(vNode.actualNode, vNode));\n    });\n  });\n\n  ['region', 'presentation', 'none'].forEach(role => {\n    it(`returns false for tables with role=${role}`, () => {\n      const vNode = queryFixture(`<table id=\"target\" role=\"${role}\">\n          <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n        </table>`);\n      assert.isFalse(rule.matches(vNode.actualNode, vNode));\n    });\n  });\n\n  it(`returns true for tables with an invalid role`, () => {\n    const vNode = queryFixture(`<table id=\"target\" role=\"invalid-aria-role\">\n        <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n      </table>`);\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it(`returns true for focusable tables with role=none`, () => {\n    const vNode = queryFixture(`<table id=\"target\" role=\"none\" tabindex=\"0\">\n        <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n      </table>`);\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n\n  it(`returns true for tables with role=none but with a global ARIA attribute`, () => {\n    const vNode =\n      queryFixture(`<table id=\"target\" role=\"none\" aria-live=\"assertive\">\n        <th id=\"h\">foo</th> <td headers=\"h\">bar</td>\n      </table>`);\n    assert.isTrue(rule.matches(vNode.actualNode, vNode));\n  });\n});\n"
  },
  {
    "path": "test/rule-matches/widget-not-inline-matches.js",
    "content": "describe('widget-not-inline-matches', () => {\n  let rule;\n  const { queryFixture, queryShadowFixture } = axe.testUtils;\n\n  beforeEach(() => {\n    rule = axe.utils.getRule('target-size');\n  });\n\n  it('returns true for native widgets', () => {\n    const vNode = queryFixture('<button id=\"target\"></button>');\n    const node = vNode.actualNode;\n    assert.isTrue(rule.matches(node, vNode));\n  });\n\n  it('returns false for non-widget elements', () => {\n    const vNode = queryFixture('<div role=\"banner\" id=\"target\"></div>');\n    const node = vNode.actualNode;\n    assert.isFalse(rule.matches(node, vNode));\n  });\n\n  it('returns false for non-focusable native widgets', () => {\n    const vNode = queryFixture('<button disabled id=\"target\"></button>');\n    const node = vNode.actualNode;\n    assert.isFalse(rule.matches(node, vNode));\n  });\n\n  it('returns false for non-focusable custom widgets', () => {\n    const vNode = queryFixture('<div role=\"button\" id=\"target\"></div>');\n    const node = vNode.actualNode;\n    assert.isFalse(rule.matches(node, vNode));\n  });\n\n  describe('non-native components', () => {\n    it('returns true for a tabbable button', () => {\n      const vNode = queryFixture(\n        '<div role=\"button\" tabindex=\"0\" id=\"target\"></div>'\n      );\n      const node = vNode.actualNode;\n      assert.isTrue(rule.matches(node, vNode));\n    });\n\n    it('returns true for button with tabindex=\"-1\"', () => {\n      const vNode = queryFixture(\n        '<div role=\"button\" tabindex=\"-1\" id=\"target\"></div>'\n      );\n      const node = vNode.actualNode;\n      assert.isTrue(rule.matches(node, vNode));\n    });\n\n    it('returns false for a non-tabbable button (widgets)', () => {\n      const vNode = queryFixture('<div role=\"button\" id=\"target\"></div>');\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n\n    it('returns true for a listbox (component)', () => {\n      const vNode = queryFixture(\n        '<div role=\"listbox\" tabindex=\"0\" id=\"target\"></div>'\n      );\n      const node = vNode.actualNode;\n      assert.isTrue(rule.matches(node, vNode));\n    });\n\n    it('returns true for a combobox (component)', () => {\n      const vNode = queryFixture(\n        '<div role=\"combobox\" tabindex=\"0\" id=\"target\"></div>'\n      );\n      const node = vNode.actualNode;\n      assert.isTrue(rule.matches(node, vNode));\n    });\n  });\n\n  describe('inline components', () => {\n    it('returns false for elements inline with text', () => {\n      const vNode = queryFixture(\n        '<p>Some ' + ' <a href=\"#\" id=\"target\">link</a>' + '</p>'\n      );\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n\n    it('returns false for multiple inline links', () => {\n      const vNode = queryFixture(\n        '<p>' +\n          ' <a href=\"#\" id=\"target\">link 1</a>, ' +\n          ' <a href=\"#\">link 2</a>, ' +\n          ' <a href=\"#\">link 3</a>' +\n          '</p>'\n      );\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n\n    it('returns true if the widget is the only element in its block parent', () => {\n      const vNode = queryFixture('<p><a href=\"#\" id=\"target\">link</a></p>');\n      const node = vNode.actualNode;\n      assert.isTrue(rule.matches(node, vNode));\n    });\n  });\n\n  describe('graphics (for which size may be essential)', () => {\n    it('returns false for area elements', () => {\n      const vNode = queryFixture(\n        '<img usemap=\"#imgmap\" src=\"#\" alt=\"img\"/>' +\n          '<map name=\"imgmap\">' +\n          '  <area id=\"target\" shape=\"circle\" coords=\"10,10,10\" href=\"#\" alt=\"map\" />' +\n          '</map>'\n      );\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n\n    it('returns false SVG elements', () => {\n      const vNode = queryFixture(\n        '<svg role=\"button\" tabindex=\"0\" id=\"target\"></svg>'\n      );\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n\n    it('returns false descendants of SVG elements', () => {\n      const vNode = queryFixture(\n        '<svg><a href=\"#\" id=\"target\">' +\n          '  <text y=\"15\">link</text></a>' +\n          '</svg>'\n      );\n      const node = vNode.actualNode;\n      assert.isFalse(rule.matches(node, vNode));\n    });\n  });\n\n  describe('nested widget', () => {\n    describe('that are is in the tab order', () => {\n      it('is true when the target is in the tab order', () => {\n        const vNode = queryFixture(`\n          <span role=\"link\" tabindex=\"0\">\n            Link text\n            <a href=\"#\" id=\"target\">Nested link</a>\n          </span>`);\n        const node = vNode.actualNode;\n        assert.isTrue(rule.matches(node, vNode));\n      });\n\n      it('is false when the target is not in the tab order', () => {\n        const vNode = queryFixture(`\n          <span role=\"link\" tabindex=\"0\">\n            Link text\n            <a href=\"#\" id=\"target\" tabindex=\"-1\">Nested link</a>\n          </span>`);\n        const node = vNode.actualNode;\n        assert.isFalse(rule.matches(node, vNode));\n      });\n\n      describe('within a shadow DOM tree', () => {\n        it('is true when the target is in the tab order', () => {\n          const vNode = queryShadowFixture(\n            `<div role=\"link\" tabindex=\"0\"><span id=\"shadow\"></span></div>`,\n            `<a href=\"#\" id=\"target\">Nested link</a>`\n          );\n          const node = vNode.actualNode;\n          assert.isTrue(rule.matches(node, vNode));\n        });\n\n        it('is false when the target is not in the tab order', () => {\n          const vNode = queryShadowFixture(\n            `<div role=\"link\" tabindex=\"0\"><span id=\"shadow\"></span></div>`,\n            `<a href=\"#\" id=\"target\" tabindex=\"-1\">Nested link</a>`\n          );\n          const node = vNode.actualNode;\n          assert.isFalse(rule.matches(node, vNode));\n        });\n      });\n    });\n\n    describe('that is not in the tab order', () => {\n      it('is true when the target is in the tab order', () => {\n        const vNode = queryFixture(`\n          <span role=\"link\" tabindex=\"-1\">\n            Link text\n            <a href=\"#\" id=\"target\">Nested link</a>\n          </span>`);\n        const node = vNode.actualNode;\n        assert.isTrue(rule.matches(node, vNode));\n      });\n\n      it('is true when the target is not in the tab order', () => {\n        const vNode = queryFixture(`\n          <span role=\"link\" tabindex=\"-1\">\n            Link text\n            <a href=\"#\" id=\"target\" tabindex=\"-1\">Nested link</a>\n          </span>`);\n        const node = vNode.actualNode;\n        assert.isTrue(rule.matches(node, vNode));\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/test-locales.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar assert = require('assert');\nvar glob = require('glob');\nvar axe = require(path.join(__dirname, '../axe'));\n\nvar localeFiles = glob.sync(path.join(__dirname, '../locales/*.json'));\n\ndescribe('locales', function () {\n  localeFiles.forEach(function (localeFile) {\n    var localeName = path.basename(localeFile);\n    it(localeName + ' should be valid', function () {\n      var localeData = fs.readFileSync(localeFile, 'utf-8');\n      var locale = JSON.parse(localeData);\n      function fn() {\n        axe.configure({ locale: locale });\n      }\n\n      assert.doesNotThrow(fn);\n    });\n  });\n});\n"
  },
  {
    "path": "test/test-rule-help-version.js",
    "content": "var https = require('https');\nvar path = require('path');\nvar assert = require('assert');\nvar packageJSON = require(path.join(__dirname, '../package.json'));\n\nvar versions = packageJSON.version.split('.');\nvar version = versions[0] + '.' + versions[1];\n\nit(\n  'latest axe version (' + version + ') rule help docs should be active',\n  function (done) {\n    https.get(\n      'https://dequeuniversity.com/rules/axe/' + version,\n      function (res) {\n        assert(res.statusCode >= 200 && res.statusCode <= 299);\n        done();\n      }\n    );\n  }\n);\n"
  },
  {
    "path": "test/test-virtual-rules.js",
    "content": "var path = require('path');\nvar assert = require('chai').assert;\nvar glob = require('glob');\nvar axe = require('../axe');\n\nvar files = glob.sync(path.join(__dirname, 'integration/virtual-rules/*.js'));\n\nbefore(function () {\n  global.axe = axe;\n  global.assert = assert;\n});\n\nafter(function () {\n  delete global.axe;\n  delete global.assert;\n});\n\ndescribe('virtual-rule node tests', function () {\n  files.forEach(function (file) {\n    // load the test file and run with global axe and assert now defined\n    require(file);\n  });\n});\n"
  },
  {
    "path": "test/testutils.js",
    "content": "/*eslint no-unused-vars: 0*/\n\n// these are global to the entire test suite so need to be declared at the global\n// level (old architecture that should not be relied on in any new code)\nvar checks;\nvar commons;\n\n(() => {\n  // Let the user know they need to disable their axe/attest extension before running the tests.\n  if (window.__AXE_EXTENSION__) {\n    throw new Error(\n      'You must disable your axe/attest browser extension in order to run the test suite.'\n    );\n  }\n\n  const testUtils = (axe.testUtils = {});\n\n  const originalChecks = (checks = axe._audit.checks);\n  const originalAudit = axe._audit;\n  const originalRules = axe._audit.rules;\n  const originalCommons = (commons = axe.commons);\n\n  // Global chai configuration\n  if (window.chai) {\n    window.chai.config.truncateThreshold = 0;\n  }\n\n  // add fixture to the body if it's not already\n  let fixture = document.getElementById('fixture');\n  if (!fixture) {\n    fixture = document.createElement('div');\n    fixture.setAttribute('id', 'fixture');\n    document.body.insertBefore(fixture, document.body.firstChild);\n  }\n\n  // determine which checks are used only in the `none` array of rules\n  const noneChecks = [];\n\n  function verifyIsNoneCheck(check) {\n    const index = noneChecks.indexOf(check);\n    if (index !== -1) {\n      noneChecks.splice(index, 1);\n    }\n  }\n\n  axe._audit.rules.forEach(function (rule) {\n    rule.none.forEach(function (check) {\n      check = check.id || check;\n      if (noneChecks.indexOf(check) === -1) {\n        noneChecks.push(check);\n      }\n    });\n  });\n\n  axe._audit.rules.forEach(function (rule) {\n    rule.any.forEach(verifyIsNoneCheck);\n    rule.all.forEach(verifyIsNoneCheck);\n  });\n\n  /**\n   * Create a check context for mocking/resetting data and relatedNodes in tests\n   *\n   * @return Object\n   */\n  testUtils.MockCheckContext = function () {\n    'use strict';\n    return {\n      _relatedNodes: [],\n      _data: null,\n      // When using this.async() in a check, assign a function to _onAsync\n      // to catch the response.\n      _onAsync: null,\n      async: function () {\n        const self = this;\n        return function (result) {\n          // throws if _onAsync isn't set\n          self._onAsync(result, self);\n        };\n      },\n      data: function (d) {\n        this._data = d;\n      },\n      relatedNodes: function (nodes) {\n        this._relatedNodes = Array.isArray(nodes) ? nodes : [nodes];\n      },\n      reset: function () {\n        this._data = null;\n        this._relatedNodes = [];\n        this._onAsync = null;\n      }\n    };\n  };\n\n  /**\n   * Provide an API for determining Shadow DOM v0 and v1 support in tests.\n   *\n   * @param HTMLDocumentElement\t\tThe document of the current context\n   * @return Object\n   */\n  testUtils.shadowSupport = (function (document) {\n    'use strict';\n    const v0 =\n        document.body && typeof document.body.createShadowRoot === 'function',\n      v1 = document.body && typeof document.body.attachShadow === 'function';\n\n    return {\n      v0: v0 === true,\n      v1: v1 === true,\n      undefined:\n        document.body &&\n        typeof document.body.attachShadow === 'undefined' &&\n        typeof document.body.createShadowRoot === 'undefined'\n    };\n  })(document);\n\n  /**\n   * Return the fixture element\n   * @return HTMLElement\n   */\n  testUtils.getFixture = function () {\n    'use strict';\n    return fixture;\n  };\n\n  /**\n   * Method for injecting content into a fixture\n   * @param {String|Node} content Stuff to go into the fixture (html or DOM node)\n   * @return HTMLElement\n   */\n  testUtils.injectIntoFixture = function (content) {\n    'use strict';\n    if (typeof content !== 'undefined') {\n      fixture.innerHTML = '';\n    }\n\n    if (typeof content === 'string') {\n      fixture.innerHTML = content;\n    } else if (content instanceof Node) {\n      fixture.appendChild(content);\n    } else if (Array.isArray(content)) {\n      content.forEach(function (node) {\n        fixture.appendChild(node);\n      });\n    }\n\n    return fixture;\n  };\n\n  /**\n   * Method for injecting content into a fixture and caching\n   * the flattened DOM tree (light and Shadow DOM together)\n   *\n   * @param {String|Node} content Stuff to go into the fixture (html or DOM node)\n   * @return HTMLElement\n   */\n  testUtils.fixtureSetup = function (content) {\n    'use strict';\n    testUtils.injectIntoFixture(content);\n    axe.teardown();\n    return axe.setup(fixture);\n  };\n\n  /**\n   * Create check arguments\n   *\n   * @param Node|String \tStuff to go into the fixture (html or node)\n   * @param Object\t\t\t\tOptions argument for the check (optional, default: {})\n   * @param String\t\t\t\tTarget for the check, CSS selector (default: '#target')\n   * @return Array\n   */\n  testUtils.checkSetup = function (content, options, target) {\n    'use strict';\n    // Normalize the params\n    if (typeof options !== 'object') {\n      target = options;\n      options = {};\n    }\n    // Normalize target, allow it to be the inserted node or '#target'\n    target = target || (content instanceof Node ? content : '#target');\n    const rootNode = testUtils.fixtureSetup(content);\n\n    let node;\n    if (typeof target === 'string') {\n      node = axe.utils.querySelectorAll(rootNode, target)[0];\n    } else if (target instanceof Node) {\n      node = axe.utils.getNodeFromTree(target);\n    } else {\n      node = target;\n    }\n    return [node.actualNode, options, node];\n  };\n\n  /**\n   * Create a shadow DOM tree on #shadow and setup axe for it, returning #target\n   *\n   * @param Node|String \tStuff to go into the fixture (html string or DOM Node)\n   * @param Node|String \tStuff to go into the shadow boundary (html or node)\n   * @param String\t\t\t\tTarget selector for the check, can be inside or outside of Shadow DOM (optional, default: '#target')\n   * @return VirtualNode\n   */\n  testUtils.queryShadowFixture = function (\n    content,\n    shadowContent,\n    targetSelector\n  ) {\n    'use strict';\n\n    // Normalize target, allow it to be the provided string or use '#target' to query composed tree\n    if (typeof targetSelector !== 'string') {\n      targetSelector = '#target';\n    }\n\n    const fixtureNode = testUtils.injectIntoFixture(content);\n    let targetCandidate = fixtureNode.querySelector(targetSelector);\n    let container = targetCandidate;\n    if (!targetCandidate) {\n      // check if content specifies a shadow container\n      container = fixtureNode.querySelector('#shadow');\n      if (!container) {\n        container = fixtureNode.firstChild;\n      }\n    }\n    // attach a shadowRoot with the content provided\n    const shadowRoot = container.attachShadow({ mode: 'open' });\n    if (typeof shadowContent === 'string') {\n      shadowRoot.innerHTML = shadowContent;\n    } else if (content instanceof Node) {\n      shadowRoot.appendChild(shadowContent);\n    }\n\n    if (!targetCandidate) {\n      targetCandidate = shadowRoot.querySelector(targetSelector);\n    }\n    if (!targetSelector && !targetCandidate) {\n      throw 'shadowCheckSetup requires at least one fragment to have #target, or a provided targetSelector';\n    }\n\n    // query the composed tree AFTER shadowDOM has been attached\n    const vFixture = axe.setup(fixtureNode);\n    return axe.utils.getNodeFromTree(targetCandidate) || vFixture;\n  };\n\n  /**\n   * Create check arguments with Shadow DOM. Target can be inside or outside of Shadow DOM, queried by\n   * adding `id=\"target\"` to a fragment. Or specify a custom selector as the `targetSelector` argument.\n   *\n   * @param Node|String \tStuff to go into the fixture (html string or DOM Node)\n   * @param Node|String \tStuff to go into the shadow boundary (html or node)\n   * @param Object\t\t\t\tOptions argument for the check (optional, default: {})\n   * @param String\t\t\t\tTarget selector for the check, can be inside or outside of Shadow DOM (optional, default: '#target')\n   * @return Array\n   */\n  testUtils.shadowCheckSetup = function (\n    content,\n    shadowContent,\n    options,\n    targetSelector\n  ) {\n    // Normalize the object params\n    if (typeof options !== 'object') {\n      options = {};\n    }\n    const node = testUtils.queryShadowFixture(\n      content,\n      shadowContent,\n      targetSelector\n    );\n    return [node.actualNode, options, node];\n  };\n\n  /**\n   * Setup axe._tree flat tree\n   * @param Node   Stuff to go in the flat tree\n   * @returns vNode[]\n   */\n  testUtils.flatTreeSetup = function (content) {\n    axe._tree = axe.utils.getFlattenedTree(content);\n    return axe._tree;\n  };\n\n  /**\n   * Wait for all nested frames to be loaded\n   *\n   * @param Object\t\t\t\tWindow to wait for (optional)\n   * @param function\t\t\tCallback, called once resolved\n   * @param function      Callback, called once rejected\n   */\n  testUtils.awaitNestedLoad = function awaitNestedLoad(win, cb, errCb) {\n    'use strict';\n    if (typeof win === 'function') {\n      errCb = cb;\n      cb = win;\n      win = window;\n    }\n    const document = win.document;\n    const q = axe.utils.queue();\n\n    // Wait for page load\n    q.defer(function (resolve) {\n      if (document.readyState === 'complete') {\n        resolve();\n      } else {\n        win.addEventListener('load', resolve);\n      }\n    });\n\n    // Wait for all frames to be loaded\n    Array.from(document.querySelectorAll('iframe')).forEach(function (frame) {\n      q.defer(function (resolve) {\n        return awaitNestedLoad(frame.contentWindow, resolve);\n      });\n    });\n\n    // Complete (don't pass the args on to the callback)\n    q.then(function () {\n      cb();\n    });\n\n    if (errCb) {\n      q.catch(errCb);\n    }\n  };\n\n  /**\n   * Add a given stylesheet dynamically to the document\n   *\n   * @param {Object} data composite object containing properties to create stylesheet\n   * @property {String} data.href relative or absolute url for stylesheet to be loaded\n   * @property {Boolean} data.mediaPrint boolean to represent if the constructed sheet is for print media\n   * @property {String} data.text text contents to be written to the stylesheet\n   * @property {String} data.id id reference to link or style to be added to document\n   * @param {Object} rootNode document/fragment to which to append style\n   * @returns {Object} axe.utils.queue\n   */\n  testUtils.addStyleSheet = function addStyleSheet(data, rootNode) {\n    const doc = rootNode ? rootNode : document;\n    const q = axe.utils.queue();\n    if (data.href) {\n      q.defer(function (resolve, reject) {\n        const link = doc.createElement('link');\n        link.rel = 'stylesheet';\n        link.href = data.href;\n        if (data.id) {\n          link.id = data.id;\n        }\n        if (data.mediaPrint) {\n          link.media = 'print';\n        }\n        link.onload = function () {\n          setTimeout(function () {\n            resolve();\n          });\n        };\n        link.onerror = function () {\n          reject();\n        };\n        doc.head.appendChild(link);\n      });\n    } else {\n      q.defer(function (resolve, reject) {\n        const style = doc.createElement('style');\n        if (data.id) {\n          style.id = data.id;\n        }\n        style.type = 'text/css';\n        style.appendChild(doc.createTextNode(data.text));\n        doc.head.appendChild(style);\n        // In Firefox, there is a delay between adding the element and it appearing in\n        // document.styleSheets, so we poll until we see it there.\n        const timeoutAt = Date.now() + 500;\n        const interval = setInterval(function () {\n          const isLoaded = Array.from(doc.styleSheets).some(\n            sheet => sheet.ownerNode === style\n          );\n          if (isLoaded) {\n            clearInterval(interval);\n            resolve();\n          } else if (Date.now() > timeoutAt) {\n            clearInterval(interval);\n            reject(\n              new Error(\n                'Added <style> element was not reflected in doc.styleSheets within timeout'\n              )\n            );\n          }\n        }, 5);\n      });\n    }\n    return q;\n  };\n\n  /**\n   * Add a list of stylesheets\n   *\n   * @param {Object} sheets array of sheets data object\n   * @returns {Object} axe.utils.queue\n   */\n  testUtils.addStyleSheets = function addStyleSheets(sheets, rootNode) {\n    const q = axe.utils.queue();\n    sheets.forEach(function (data) {\n      q.defer(axe.testUtils.addStyleSheet(data, rootNode));\n    });\n    return q;\n  };\n\n  /**\n   * Remove a list of stylesheets from the document\n   * @param {Array<Object>} sheets array of sheets data object\n   * @returns {Object} axe.utils.queue\n   */\n  testUtils.removeStyleSheets = function removeStyleSheets(sheets) {\n    const q = axe.utils.queue();\n    sheets.forEach(function (data) {\n      q.defer(function (resolve, reject) {\n        const node = document.getElementById(data.id);\n        if (!node || !node.parentNode) {\n          reject();\n        }\n        node.parentNode.removeChild(node);\n        resolve();\n      });\n    });\n    return q;\n  };\n\n  /**\n   * Assert a given stylesheet against selectorText and cssText\n   *\n   * @param {Object} sheet CSS Stylesheet\n   * @param {String} selectorText CSS Selector\n   * @param {String} cssText CSS Values\n   * @param {Boolean} includes (Optional) flag to check if existence of selectorText within cssText\n   */\n  testUtils.assertStylesheet = function assertStylesheet(\n    sheet,\n    selectorText,\n    cssText,\n    includes\n  ) {\n    assert.isDefined(sheet);\n    assert.property(sheet, 'cssRules');\n    if (includes) {\n      assert.isTrue(cssText.includes(selectorText));\n    } else {\n      assert.equal(sheet.cssRules[0].selectorText, selectorText);\n\n      // compare the selector properties\n      const styleEl = document.createElement('style');\n      styleEl.type = 'text/css';\n      styleEl.innerHTML = cssText;\n      document.body.appendChild(styleEl);\n\n      const testSheet = document.styleSheets[document.styleSheets.length - 1];\n      const sheetRule = sheet.cssRules[0];\n      const testRule = testSheet.cssRules[0];\n\n      try {\n        for (let i = 0; i < testRule.style.length; i++) {\n          const property = testRule.style[i];\n          assert.equal(sheetRule.style[property], testRule.style[property]);\n        }\n      } finally {\n        styleEl.parentNode.removeChild(styleEl);\n      }\n    }\n  };\n\n  /**\n   * Injecting content into a fixture and return queried element within fixture\n   *\n   * @param {String|Node} html - content to go into the fixture (html or DOM node)\n   * @param {String} [query=#target] - the CSS selector query to find target DOM node\n   * @return {VirtualNode}\n   */\n  testUtils.queryFixture = function queryFixture(html, query) {\n    query = query || '#target';\n    const rootNode = testUtils.fixtureSetup(html);\n    const vNode = axe.utils.querySelectorAll(rootNode, query)[0];\n    assert.exists(\n      vNode,\n      'Node does not exist in query `' +\n        query +\n        '`. This is usually fixed by adding the default target (`id=\"target\"`) to your html parameter. If you do not intend on querying the fixture for #target, consider using `axe.testUtils.fixtureSetup()` instead.'\n    );\n    return vNode;\n  };\n\n  /**\n   * Return the checks evaluate method and apply default options\n   * @param {string} checkId - ID of the check\n   * @param {} testOptions - Options for the test\n   * @returns {evaluateWrapper} evaluateWrapper - Check evaluation wrapper\n   */\n  testUtils.getCheckEvaluate = function getCheckEvaluate(checkId, testOptions) {\n    const check = checks[checkId];\n    testOptions = testOptions || {};\n\n    /**\n     * Wraps a check for evaluation using .call()\n     * @param {HTMLElement} node\n     * @param {*} options\n     * @param {VirtualNode} virtualNode\n     * @param {Context} context\n     */\n    const evaluateWrapper = function (node, options, virtualNode, context) {\n      const opts = check.getOptions(options);\n\n      const result = check.evaluate.call(\n        this,\n        node,\n        opts,\n        virtualNode,\n        context\n      );\n\n      // ensure that every result has a corresponding message\n      if (testOptions.verifyMessage !== false) {\n        const messages = axe._audit.data.checks[checkId].messages;\n        const messageKey = this._data && this._data.messageKey;\n\n        // see how the check is used to know where to find the message\n        // e.g. a check used only in the `none` array of a rule will look at\n        // the messageKey of a passing result in the `fail` messages\n        let keyResult = result;\n        const isNoneCheck = noneChecks.indexOf(checkId) !== -1;\n        if (isNoneCheck) {\n          keyResult =\n            result === true ? false : result === false ? true : result;\n        }\n\n        const key =\n          keyResult === true\n            ? 'pass'\n            : keyResult === false\n              ? 'fail'\n              : 'incomplete';\n        const noneCheckMessage = isNoneCheck\n          ? '. Note that since this check is only used in the \"none\" array of all rules, the messages use the inverse of the result (e.g. a result of false uses the \"pass\" messages)'\n          : '';\n\n        assert.exists(\n          messages[key],\n          'Missing \"' +\n            key +\n            '\" message for check result of ' +\n            result +\n            noneCheckMessage\n        );\n        if (messageKey) {\n          assert.exists(\n            messages[key][messageKey],\n            'Missing ' +\n              key +\n              ' message key \"' +\n              messageKey +\n              '\" for check result of ' +\n              result +\n              noneCheckMessage\n          );\n\n          const message = axe.utils.processMessage(\n            messages[key][messageKey],\n            this._data\n          );\n          assert.isTrue(\n            message.indexOf('${') === -1,\n            'Data object missing properties for ' +\n              key +\n              ' message key \"' +\n              messageKey +\n              '\": \"' +\n              message +\n              '\"'\n          );\n        } else {\n          const message = axe.utils.processMessage(messages[key], this._data);\n          assert.isTrue(\n            message.indexOf('${') === -1,\n            'Data object missing properties for ' +\n              key +\n              ' message: \"' +\n              message +\n              '\"'\n          );\n        }\n      }\n\n      return result;\n    };\n    return evaluateWrapper;\n  };\n\n  if (typeof beforeEach !== 'undefined' && typeof afterEach !== 'undefined') {\n    // prevent setting read-only properties\n    // @see https://github.com/dequelabs/axe-core/issues/3837\n    const readonlyRect = new DOMRectReadOnly();\n    const proto = Object.getPrototypeOf(readonlyRect);\n    ['left', 'right', 'top', 'bottom'].forEach(prop => {\n      Object.defineProperty(proto, prop, {\n        set(value) {\n          throw new TypeError(`setting getter-only property \"${prop}\"`);\n        }\n      });\n    });\n\n    beforeEach(function () {\n      // reset from axe._load overriding\n      checks = originalChecks;\n      axe._audit = originalAudit;\n      axe._audit.rules = originalRules;\n      commons = axe.commons = originalCommons;\n    });\n\n    afterEach(function () {\n      axe.teardown();\n      fixture.innerHTML = '';\n\n      // remove all attributes from fixture (otherwise a leftover\n      // style attribute would cause avoid-inline-spacing integration\n      // test to fail with [#fixture] being included in the results)\n      const attrs = fixture.attributes;\n      for (let i = 0; i < attrs.length; i++) {\n        const attrName = attrs[i].name;\n        if (attrName !== 'id') {\n          fixture.removeAttribute(attrs[i].name);\n        }\n      }\n\n      // reset html and body styles\n      document.body.removeAttribute('style');\n      document.documentElement.removeAttribute('style');\n    });\n  }\n\n  testUtils.captureError = function captureError(cb, errorHandler) {\n    return function () {\n      try {\n        cb.apply(null, arguments);\n      } catch (e) {\n        errorHandler(e);\n      }\n    };\n  };\n\n  testUtils.runPartialRecursive = function runPartialRecursive(\n    context,\n    options,\n    win\n  ) {\n    options = options || {};\n    win = win || window;\n    const axe = win.axe;\n    const frameContexts = axe.utils.getFrameContexts(context);\n    let promiseResults = [axe.runPartial(context, options)];\n\n    frameContexts.forEach(function (c) {\n      const frame = testUtils.shadowQuerySelector(\n        c.frameSelector,\n        win.document\n      );\n      const frameWin = frame.contentWindow;\n      const frameResults = testUtils.runPartialRecursive(\n        c.frameContext,\n        options,\n        frameWin\n      );\n      promiseResults = promiseResults.concat(frameResults);\n    });\n    return promiseResults;\n  };\n\n  testUtils.shadowQuerySelector = function shadowQuerySelector(\n    axeSelector,\n    doc\n  ) {\n    let elm;\n    doc = doc || document;\n    axeSelector = Array.isArray(axeSelector) ? axeSelector : [axeSelector];\n    axeSelector.forEach(function (selectorStr) {\n      elm = doc && doc.querySelector(selectorStr);\n      doc = elm && elm.shadowRoot;\n    });\n    return elm;\n  };\n\n  testUtils.createNestedShadowDom = function createFixtureShadowTree(\n    fixtureNode,\n    ...htmlCodes\n  ) {\n    if (htmlCodes.length <= 1) {\n      throw new Error(\n        'createNestedShadowDom must contain at least two HTML snippets'\n      );\n    }\n    let htmlCode;\n    while ((htmlCode = htmlCodes.shift())) {\n      appendHtml(fixtureNode, htmlCode);\n      if (htmlCodes.length) {\n        const query = fixtureNode.querySelectorAll('#shadowHost, .shadowHost');\n        fixtureNode = query[query.length - 1];\n        fixtureNode = fixtureNode.attachShadow({ mode: 'open' });\n      }\n    }\n    return fixtureNode.querySelector('#target');\n  };\n\n  /**\n   * Enables test code like html` <img /> ` to get code highlighting\n   * @param {array} strings\n   * @param  {...any} values\n   * @returns {string}\n   */\n  testUtils.html = (strings, ...values) => {\n    return strings.reduce((total, string, i) => {\n      return total + string + (values[i] ?? '');\n    }, '');\n  };\n\n  function appendHtml(fixtureNode, htmlCode) {\n    const tmp = document.createElement('div');\n    tmp.innerHTML = htmlCode;\n    // Append to avoid clobbering other shadow trees with innerHTML\n    for (let child of tmp.children) {\n      fixtureNode.appendChild(child.cloneNode(true));\n    }\n  }\n\n  testUtils.assertResultsDeepEqual = (\n    expected,\n    actual,\n    ignoredPaths = [\n      'timestamp',\n      // testEnvironment is ignored by default because Chrome's UI animations for the\n      // \"an automated test is controlling this browser\" notification can cause\n      // inconsistencies in windowHeight for otherwise-identical scans.\n      'testEnvironment'\n    ],\n    keyPath = 'result'\n  ) => {\n    const typeObj1 = getType(expected);\n    const typeObj2 = getType(actual);\n\n    axe.utils.assert(\n      typeObj1 === typeObj2,\n      `Expected type of ${keyPath} to equal ${typeObj1} but got ${typeObj2}`\n    );\n\n    if (typeObj1 === 'object') {\n      const res1Keys = Object.keys(expected);\n      const res2Keys = Object.keys(actual);\n\n      axe.utils.assert(\n        res1Keys.length === res2Keys.length &&\n          res1Keys.every(key => res2Keys.includes(key)),\n        `Expected ${keyPath} to have keys \"${JSON.stringify(res1Keys)}\" but got \"${JSON.stringify(res2Keys)}\"`\n      );\n\n      for (const key of res1Keys) {\n        if (ignoredPaths.includes(key)) {\n          continue;\n        }\n\n        testUtils.assertResultsDeepEqual(\n          expected[key],\n          actual[key],\n          ignoredPaths,\n          `${keyPath}.${key}`\n        );\n      }\n    } else if (typeObj1 === 'array') {\n      axe.utils.assert(\n        expected.length === actual.length,\n        `Expected ${keyPath} to have length of \"${expected.length}\" but got \"${actual.length}\"`\n      );\n\n      for (let i = 0; i < expected.length; i++) {\n        testUtils.assertResultsDeepEqual(\n          expected[i],\n          actual[i],\n          ignoredPaths,\n          `${keyPath}[${i}]`\n        );\n      }\n    } else {\n      axe.utils.assert(\n        expected === actual,\n        `Expected ${keyPath} to equal \"${expected}\" but got \"${actual}\"`\n      );\n    }\n  };\n\n  function isObject(obj) {\n    return obj && typeof obj === 'object' && !Array.isArray(obj);\n  }\n\n  function getType(obj) {\n    if (isObject(obj)) {\n      return 'object';\n    }\n    if (Array.isArray(obj)) {\n      return 'array';\n    }\n    if (obj === null) {\n      return 'null';\n    }\n\n    return typeof obj;\n  }\n})();\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"noImplicitAny\": true\n  },\n  \"include\": [\"typings/axe-core/axe-core-tests.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "typings/axe-core/axe-core-tests.ts",
    "content": "import * as axe from '../../axe';\n\nvar context: any = document;\nvar $fixture = [document];\nvar options: axe.RunOptions = {\n  iframes: false,\n  selectors: false,\n  elementRef: false\n};\noptions.reporter = 'rawEnv';\noptions.reporter = 'custom';\n\naxe.run(context, {}, (error: Error, results: axe.AxeResults) => {\n  if (error) {\n    console.log(error);\n  }\n  console.log(results.passes.length);\n  console.log(results.incomplete.length);\n  const errors = results.incomplete.map(result => result.error);\n  console.log(\n    errors.map(\n      ({ message, stack, ruleId, method }) =>\n        `${message} ${ruleId} ${method}\\n\\n${stack}`\n    )\n  );\n  console.log(results.inapplicable.length);\n  console.log(results.violations.length);\n  console.log(results.violations[0].nodes[0].failureSummary);\n});\naxe.run().then(function (done: any) {\n  done();\n});\n// additional configuration options\naxe.run(context, options, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results.passes.length);\n});\n// axe.run include/exclude\naxe.run(\n  { include: [['#id1'], ['#id2']] },\n  {},\n  (error: Error, results: axe.AxeResults) => {\n    console.log(error || results);\n  }\n);\n// axe.run preload: boolean\naxe.run({ preload: false });\naxe.run({ preload: true });\n// axe.run preload: options\naxe.run({ preload: { assets: ['cssom'] } });\naxe.run({ preload: { assets: ['cssom'], timeout: 50000 } });\n\nexport async function runAsync() {\n  await axe.run('main'); // Single selector\n  await axe.run(['main']); // Array of one selector\n  await axe.run([['main']]); // Selecting in the outer frame\n  // @ts-expect-error // Shadow DOM selectors must be at least 2 items long\n  await axe.run([[['main']]]);\n  await axe.run([[['#app', 'main']]]); // Selecting in the outer frame\n\n  await axe.run(document.querySelector('main'));\n  await axe.run(document.querySelectorAll('main'));\n  // axe.run with frameContext context\n  await axe.run({ fromShadowDom: ['#app', '#main', '#inner'] });\n  // @ts-expect-error // Must be two long:\n  await axe.run({ fromShadowDom: ['#app'] });\n  // @ts-expect-error // Must be two long:\n  await axe.run({ fromFrames: ['#app'] });\n  // axe.run with fromFrames context\n  await axe.run({\n    fromFrames: ['#frame', { fromShadowDom: ['#app', '#main'] }]\n  });\n  // Mixed type array\n  await axe.run([\n    'main',\n    document.head,\n    { fromShadowDom: ['#app', '#header', '#search'] },\n    { fromFrames: ['#frame', '#main'] }\n  ]);\n  // Combined fromFrames & fromContext\n  await axe.run({\n    include: { fromShadowDom: ['#frame', '#main'] },\n    exclude: [\n      'footer',\n      document.head,\n      { fromFrames: ['#frame', { fromShadowDom: ['#app', '#main'] }] }\n    ]\n  });\n}\n\nlet ctxt: axe.ContextObject;\n// @ts-expect-error\nctxt = {};\nctxt = { exclude: ['foo'] };\nctxt.include = ['bard'];\nctxt = { include: ['foo'] };\nctxt.exclude = ['bar'];\n\nlet serialContext: axe.SerialContextObject;\n// @ts-expect-error\nserialContext = {};\nserialContext = { exclude: ['foo'] };\nserialContext.include = ['bard'];\nserialContext = { include: ['foo'] };\nserialContext.exclude = ['bar'];\n\naxe.run(\n  { exclude: [$fixture[0]] },\n  {},\n  (error: Error, results: axe.AxeResults) => {\n    console.log(error || results);\n  }\n);\n\nexport async function frameContextTypes() {\n  let { frameContext, frameSelector } = axe.utils.getFrameContexts()[0];\n  await axe.run(frameContext);\n  await axe.runPartial(frameContext, {});\n  axe.utils.shadowSelect(frameSelector);\n}\n\nexport async function serialContextType() {\n  // @ts-expect-error\n  const exclude: axe.SerialSelector = document.body;\n  // @ts-expect-error\n  const include: axe.SerialSelectorList = [document.body];\n\n  let serialContext: axe.SerialContextObject;\n  // @ts-expect-error\n  serialContext = {}; // At lease one of the props is required\n  serialContext = { include, exclude };\n  serialContext = { include };\n  serialContext = { exclude };\n  await axe.runPartial(serialContext, {});\n}\n\nexport async function customReporters() {\n  type MyReport = { issues: any[] };\n  let report: MyReport;\n\n  report = await axe.run<MyReport>();\n  report = await axe.run<MyReport>(document);\n  report = await axe.run<MyReport>({});\n  report = await axe.run<MyReport>(document, {});\n  axe.run<MyReport>((_, results) => (report = results));\n  axe.run<MyReport>(document, (_, results) => (report = results));\n  axe.run<MyReport>({}, (_, results) => (report = results));\n  axe.run<MyReport>(document, {}, (_, results) => (report = results));\n}\n\n// additional configuration options\naxe.run(context, options, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results.passes.length);\n});\nvar tagConfigRunOnly: axe.RunOnly = {\n  type: 'tag',\n  values: ['wcag2a']\n};\nvar tagConfig = {\n  runOnly: tagConfigRunOnly\n};\naxe.run(context, tagConfig, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\naxe.run(\n  context,\n  {\n    runOnly: {\n      type: 'tags',\n      values: ['wcag2a', 'wcag2aa']\n    } as axe.RunOnly\n  },\n  (error: Error, results: axe.AxeResults) => {\n    console.log(error || results);\n  }\n);\naxe.run(\n  context,\n  {\n    runOnly: ['wcag2a', 'wcag2aa']\n  },\n  (error: Error, results: axe.AxeResults) => {\n    console.log(error || results);\n  }\n);\naxe.run(\n  context,\n  {\n    runOnly: ['color-contrast', 'heading-order']\n  },\n  (error: Error, results: axe.AxeResults) => {\n    console.log(error || results);\n  }\n);\n\nvar someRulesConfig = {\n  rules: {\n    'color-contrast': { enabled: false },\n    'heading-order': { enabled: true }\n  }\n};\naxe.run(context, someRulesConfig, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\n\n// just context\naxe.run(context).then(function (done: any) {\n  done();\n});\n// just options\naxe.run(options).then(function (done: any) {\n  done();\n});\n// just callback\naxe.run((error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\n// context and callback\naxe.run(context, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\n// options and callback\naxe.run(options, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\n// context and options\naxe.run(context, options).then(function (done: any) {\n  done();\n});\n// context, options, and callback\naxe.run(context, options, (error: Error, results: axe.AxeResults) => {\n  console.log(error || results);\n});\n\n// axe.configure\nvar spec: axe.Spec = {\n  branding: {\n    brand: 'foo',\n    application: 'bar'\n  },\n  reporter: 'v1',\n  checks: [\n    {\n      id: 'custom-check',\n      evaluate: function (node) {\n        this.relatedNodes([node]);\n        this.data('some data');\n        return true;\n      },\n      after: function (results) {\n        const id = results[0].id;\n        return results;\n      },\n      metadata: {\n        impact: 'minor',\n        messages: {\n          pass: 'yes',\n          fail: 'nope',\n          incomplete: {\n            maybe: 'maybe',\n            or: 'maybe not'\n          }\n        }\n      }\n    },\n    {\n      id: 'async-check',\n      evaluate: function (node) {\n        const done = this.async();\n        done(true);\n      }\n    }\n  ],\n  standards: {\n    ...axe.utils.getStandards(),\n    ariaRoles: {\n      'custom-role': {\n        type: 'widget',\n        requiredAttrs: ['aria-label']\n      }\n    },\n    ariaAttrs: {\n      'custom-attr': {\n        type: 'boolean'\n      }\n    },\n    htmlElms: {\n      'custom-elm': {\n        contentTypes: ['flow'],\n        allowedRoles: false\n      }\n    },\n    cssColors: {\n      customColor: [0, 1, 2, 3]\n    }\n  },\n  rules: [\n    {\n      id: 'custom-rule',\n      any: ['custom-check'],\n      matches: function (node) {\n        return node.tagName === 'BODY';\n      },\n      tags: ['a'],\n      actIds: ['b'],\n      metadata: {\n        description: 'custom rule',\n        help: 'different help',\n        helpUrl: 'https://example.com'\n      }\n    }\n  ]\n};\naxe.configure(spec);\n\nvar source = axe.source;\nvar version = axe.version;\n\naxe.reset();\n\naxe.getRules(['wcag2aa']);\ntypeof axe.getRules() === 'object';\n\nconst rules = axe.getRules();\nrules.forEach(rule => {\n  rule.ruleId.substr(1234);\n});\n\naxe.configure({\n  locale: {\n    checks: {\n      foo: {\n        fail: 'failure',\n        pass: 'success',\n        incomplete: {\n          foo: 'nar'\n        }\n      }\n    }\n  }\n});\n\naxe.configure({\n  locale: {\n    lang: 'foo',\n    rules: {\n      foo: {\n        description: 'desc',\n        help: 'help'\n      }\n    },\n    checks: {\n      foo: {\n        pass: 'pass',\n        fail: 'fail',\n        incomplete: {\n          foo: 'bar'\n        }\n      },\n      bar: {\n        pass: 'pass',\n        fail: 'fail'\n      }\n    }\n  }\n});\n\nconst results: axe.RawResult[] = [\n  {\n    id: 'the-best-rule',\n    result: 'passed',\n    pageLevel: false,\n    impact: null,\n    tags: ['best-practice'],\n    description: 'Be cool',\n    help: 'No, cooler',\n    helpUrl:\n      'https://dequeuniversity.com/rules/axe/4.8/the-best-rule?application=axeAPI',\n    inapplicable: [],\n    passes: [\n      {\n        any: [\n          {\n            id: 'the-best-check',\n            data: null,\n            impact: 'serious',\n            message: 'Element has sufficient color contrast of 21',\n            relatedNodes: [\n              new axe.utils.DqElement(document.body),\n              new axe.utils.DqElement(document.body).toJSON()\n            ]\n          }\n        ],\n        all: [],\n        none: [],\n        impact: null,\n        result: 'passed',\n        node: new axe.utils.DqElement(document.body)\n      }\n    ],\n    incomplete: [],\n    violations: []\n  }\n];\n\n// Reporters\nlet fooReporter = (\n  results: axe.RawResult[],\n  options: axe.RunOptions,\n  resolve: (out: 'foo') => void,\n  reject: (err: Error) => void\n) => {\n  reject && resolve('foo');\n};\n\naxe.addReporter<'foo'>('foo', fooReporter, true);\naxe.configure({ reporter: fooReporter });\nfooReporter = axe.getReporter<'foo'>('foo');\nconst hasFoo: boolean = axe.hasReporter('foo');\n\n// setup & teardown\naxe.setup();\naxe.setup(document);\naxe.setup(document.createElement('div'));\naxe.teardown();\n\n// Plugins\nvar pluginSrc: axe.AxePlugin = {\n  id: 'doStuff',\n  run: (data: any, callback: Function) => {\n    callback();\n  },\n  commands: [\n    {\n      id: 'run-doStuff',\n      callback: (data: any, callback: Function) => {\n        axe.plugins['doStuff'].run(data, callback);\n      }\n    }\n  ]\n};\naxe.registerPlugin(pluginSrc);\naxe.cleanup();\n\n// Utils\nconst dqElement = new axe.utils.DqElement(document.body);\nconst element = axe.utils.shadowSelect(dqElement.selector[0]);\nconst uuid = axe.utils.uuid() as string;\nlet unknownContext: unknown = JSON.parse('{ foo: \"bar\" }');\nif (axe.utils.isLabelledShadowDomSelector(unknownContext)) {\n  let context: axe.LabelledShadowDomSelector = unknownContext;\n} else if (axe.utils.isLabelledFramesSelector(unknownContext)) {\n  let context: axe.LabelledFramesSelector = unknownContext;\n} else if (axe.utils.isContextObject(unknownContext)) {\n  let context: axe.ContextObject = unknownContext;\n} else if (axe.utils.isContextProp(unknownContext)) {\n  let context: axe.ContextProp = unknownContext;\n} else if (axe.utils.isContextSpec(unknownContext)) {\n  let context: axe.ContextSpec = unknownContext;\n}\naxe.utils.nodeSerializer.update({\n  toSpec(dqElm: axe.DqElement) {\n    return dqElm.toJSON();\n  },\n  mergeSpecs(childSpec: axe.SerialDqElement, parentSpec: axe.SerialDqElement) {\n    return axe.utils.DqElement.mergeSpecs(childSpec, parentSpec);\n  }\n});\nconst spec2: axe.SerialDqElement = axe.utils.nodeSerializer.toSpec(element);\nconst spec3: axe.SerialDqElement = axe.utils.nodeSerializer.dqElmToSpec(\n  dqElement,\n  options\n);\n\n// Commons\naxe.commons.aria.getRoleType('img');\naxe.commons.dom.isFocusable(document.body);\naxe.commons.dom.isNativelyFocusable(document.body);\naxe.commons.dom.getNodeGrid(document.body);\naxe.commons.text.accessibleText(document.body);\n"
  }
]