[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat(browser): export aria tree utils (#10171)

authored by

Hiroshi Ogawa and committed by
GitHub
(Apr 23, 2026, 11:03 AM +0200) c3423014 72a6dc25

+114 -3
+1
tsconfig.base.json
··· 23 23 "@vitest/browser-playwright": ["./packages/browser-playwright/src/index.ts"], 24 24 "@vitest/browser": ["./packages/browser/src/node/index.ts"], 25 25 "@vitest/browser/client": ["./packages/browser/src/client/client.ts"], 26 + "@vitest/browser/internal/vendor-types": ["./packages/browser/src/vendor-types.ts"], 26 27 "~/*": ["./packages/ui/client/*"], 27 28 "vitest": ["./packages/vitest/src/public/index.ts"], 28 29 "vitest/internal/browser": ["./packages/vitest/src/public/browser.ts"],
+18
packages/browser/context.d.ts
··· 2 2 import { StringifyOptions, CDPSession, BrowserCommands } from 'vitest/internal/browser' 3 3 import { ARIARole } from './aria-role.js' 4 4 import {} from './matchers.js' 5 + import { __ivyaAriaTypes } from '@vitest/browser/internal/vendor-types' 5 6 6 7 export type BufferEncoding = 7 8 | 'ascii' ··· 934 935 * Creates "Cannot find element" error. Useful for custom locators. 935 936 */ 936 937 getElementError(selector: string, container?: Element): Error 938 + 939 + /** 940 + * Utilities for generating and working with ARIA trees and templates. 941 + * @experimental 942 + */ 943 + aria: { 944 + /** Captures the ARIA tree for a DOM subtree. */ 945 + generateAriaTree: typeof __ivyaAriaTypes.generateAriaTree 946 + /** Renders a captured ARIA tree to the textual snapshot format. */ 947 + renderAriaTree: typeof __ivyaAriaTypes.renderAriaTree 948 + /** Renders an ARIA template back to text. */ 949 + renderAriaTemplate: typeof __ivyaAriaTypes.renderAriaTemplate 950 + /** Parses textual ARIA snapshot syntax into a template tree. */ 951 + parseAriaTemplate: typeof __ivyaAriaTypes.parseAriaTemplate 952 + /** Matches a captured ARIA tree against a parsed template. */ 953 + matchAriaTree: typeof __ivyaAriaTypes.matchAriaTree 954 + } 937 955 } 938 956 939 957 export const locators: BrowserLocators
+4
packages/browser/package.json
··· 44 44 "./utils": { 45 45 "default": "./dummy.js" 46 46 }, 47 + "./internal/vendor-types": { 48 + "types": "./dist/vendor-types.d.ts", 49 + "default": "./dummy.js" 50 + }, 47 51 "./package.json": "./package.json" 48 52 }, 49 53 "main": "./dist/index.js",
+32
packages/browser/rollup.config.js
··· 157 157 external, 158 158 plugins: dtsUtilsClient.dts(), 159 159 }, 160 + { 161 + input: { 162 + 'vendor-types': './src/vendor-types.ts', 163 + }, 164 + output: { 165 + dir: 'dist', 166 + entryFileNames: '[name].ts', 167 + format: 'esm', 168 + }, 169 + external, 170 + plugins: [ 171 + ...dtsUtils.isolatedDecl(), 172 + ...plugins, 173 + ], 174 + }, 175 + { 176 + input: { 177 + 'vendor-types': './dist/.types/vendor-types.d.ts', 178 + }, 179 + output: { 180 + dir: 'dist', 181 + entryFileNames: '[name].d.ts', 182 + format: 'esm', 183 + }, 184 + external, 185 + plugins: [ 186 + resolve({ 187 + preferBuiltins: true, 188 + }), 189 + dtsUtils.dts(), 190 + ], 191 + }, 160 192 ])
+30
docs/api/browser/context.md
··· 270 270 * Creates "Cannot find element" error. Useful for custom locators. 271 271 */ 272 272 getElementError(selector: string, container?: Element): Error 273 + /** 274 + * Utilities for generating and working with ARIA trees and templates. 275 + * @experimental 276 + */ 277 + aria: { 278 + generateAriaTree(rootElement: Element): AriaNode 279 + renderAriaTree(root: AriaNode): string 280 + renderAriaTemplate(template: AriaTemplateNode): string 281 + parseAriaTemplate(text: string): AriaTemplateNode 282 + matchAriaTree(root: AriaNode, template: AriaTemplateNode): { pass: boolean; resolved: string } 283 + } 273 284 } 274 285 ``` 275 286 ··· 340 351 ::: tip 341 352 This feature is inspired by Testing Library's [`defaultIgnore`](https://testing-library.com/docs/dom-testing-library/api-configuration/#defaultignore) configuration. 342 353 ::: 354 + 355 + ### aria <Version type="experimental">5.0.0</Version> {#aria} 356 + 357 + The `aria` namespace exposes low-level utilities used by Vitest's ARIA snapshot matchers. 358 + 359 + ```ts 360 + import { utils } from 'vitest/browser' 361 + 362 + document.body.innerHTML = ` 363 + <h1>Hello, World!</h1> 364 + <button aria-hidden="true">Hidden</button> 365 + <button>Visible</button> 366 + ` 367 + const tree = utils.aria.generateAriaTree(document.body) 368 + const yaml = utils.aria.renderAriaNode(tree) 369 + console.log(yaml) 370 + // - heading "Hello, World!" [level=1] 371 + // - button "Visible"" 372 + ```
+2
docs/guide/browser/aria-snapshots.md
··· 30 30 31 31 This catches accessibility regressions: missing labels, broken roles, incorrect heading levels, and more — things that DOM snapshots would miss. Even if the underlying HTML structure changes, the assertion would not fail as long as content matches semantically. 32 32 33 + For advanced cases, you can also generate and inspect the ARIA tree through `utils.aria` from `vitest/browser`. See the [Context API](/api/browser/context#aria) for details. 34 + 33 35 ## Snapshot Workflow 34 36 35 37 ARIA snapshots use the same Vitest snapshot workflow as other snapshot assertions. File snapshots, inline snapshots, `--update` / `-u`, watch mode updates, and CI snapshot behavior all work the same way.
+1
packages/browser/src/vendor-types.ts
··· 1 + export type * as __ivyaAriaTypes from 'ivya/aria'
+14
test/browser/test/utils.test.ts
··· 206 206 </div>" 207 207 `) 208 208 }) 209 + 210 + test('aria tree utils', () => { 211 + document.body.innerHTML = ` 212 + <h1>Hello, World!</h1> 213 + <button aria-hidden="true">Hidden</button> 214 + <button>Visible</button> 215 + ` 216 + const { generateAriaTree, renderAriaTree } = utils.aria 217 + expect(`\n${renderAriaTree(generateAriaTree(document.body))}`).toMatchInlineSnapshot(` 218 + " 219 + - heading "Hello, World!" [level=1] 220 + - button "Visible"" 221 + `) 222 + })
+1
packages/browser/src/client/utils.ts
··· 95 95 send: (method: string, params?: Record<string, unknown>) => Promise<unknown> 96 96 emit: (event: string, payload: unknown) => void 97 97 } 98 + aria: typeof import('ivya/aria') 98 99 } 99 100 100 101 /* @__NO_SIDE_EFFECTS__ */
+8 -3
packages/browser/src/client/tester/aria.ts
··· 4 4 AriaNode, 5 5 AriaTemplateNode, 6 6 } from 'ivya/aria' 7 - import { 7 + import * as aria from 'ivya/aria' 8 + import { Snapshots } from 'vitest' 9 + import { getBrowserState } from '../utils' 10 + 11 + getBrowserState().aria = aria 12 + 13 + const { 8 14 generateAriaTree, 9 15 matchAriaTree, 10 16 parseAriaTemplate, 11 17 renderAriaTemplate, 12 18 renderAriaTree, 13 - } from 'ivya/aria' 14 - import { Snapshots } from 'vitest' 19 + } = aria 15 20 16 21 const ariaSnapshotAdapter: DomainSnapshotAdapter<AriaNode, AriaTemplateNode> = { 17 22 name: 'aria',
+3
packages/browser/src/client/tester/context.ts
··· 556 556 debug, 557 557 getElementLocatorSelectors, 558 558 configurePrettyDOM, 559 + get aria() { 560 + return getBrowserState().aria 561 + }, 559 562 }