diff --git a/package.json b/package.json index 5cadd2b5..35cafab6 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@babel/runtime": "^7.6.2", "@sheerun/mutationobserver-shim": "^0.3.2", "@types/testing-library__dom": "^6.0.0", - "aria-query": "3.0.0", + "aria-query": "^4.0.1", "dom-accessibility-api": "^0.3.0", "pretty-format": "^24.9.0", "wait-for-expect": "^3.0.0" diff --git a/src/__tests__/__snapshots__/role-helpers.js.snap b/src/__tests__/__snapshots__/role-helpers.js.snap index 2e5ce3d7..89f55ec9 100644 --- a/src/__tests__/__snapshots__/role-helpers.js.snap +++ b/src/__tests__/__snapshots__/role-helpers.js.snap @@ -3,9 +3,10 @@ exports[`logRoles calls console.log with output from prettyRoles 1`] = ` "region: -Name "": +Name "a region":
-------------------------------------------------- @@ -158,9 +159,10 @@ Name "Cell 3": -------------------------------------------------- form: -Name "": +Name "a form":
-------------------------------------------------- diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index f673b918..82b2ee2b 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -414,6 +414,11 @@ test('queryAllByRole returns semantic html elements', () => {
+ + `) @@ -422,21 +427,27 @@ test('queryAllByRole returns semantic html elements', () => { expect(queryAllByRole(/columnheader/i)).toHaveLength(1) expect(queryAllByRole(/rowheader/i)).toHaveLength(1) expect(queryAllByRole(/grid/i)).toHaveLength(1) - expect(queryAllByRole(/form/i)).toHaveLength(1) + expect(queryAllByRole(/form/i)).toHaveLength(0) expect(queryAllByRole(/button/i)).toHaveLength(1) expect(queryAllByRole(/heading/i)).toHaveLength(6) expect(queryAllByRole('list')).toHaveLength(2) expect(queryAllByRole(/listitem/i)).toHaveLength(3) - expect(queryAllByRole(/textbox/i)).toHaveLength(2) + // TODO: with https://github.com/A11yance/aria-query/pull/42 + // the actual value will match `toHaveLength(2)` + expect(queryAllByRole(/textbox/i)).toHaveLength(1) expect(queryAllByRole(/checkbox/i)).toHaveLength(1) expect(queryAllByRole(/radio/i)).toHaveLength(1) expect(queryAllByRole('row')).toHaveLength(3) expect(queryAllByRole(/rowgroup/i)).toHaveLength(2) - expect(queryAllByRole(/(table)|(textbox)/i)).toHaveLength(3) + // TODO: with https://github.com/A11yance/aria-query/pull/42 + // the actual value will match `toHaveLength(3)` + expect(queryAllByRole(/(table)|(textbox)/i)).toHaveLength(2) expect(queryAllByRole(/img/i)).toHaveLength(1) expect(queryAllByRole('meter')).toHaveLength(1) expect(queryAllByRole('progressbar')).toHaveLength(0) expect(queryAllByRole('progressbar', {queryFallbacks: true})).toHaveLength(1) + expect(queryAllByRole('combobox')).toHaveLength(1) + expect(queryAllByRole('listbox')).toHaveLength(1) }) test('getAll* matchers return an array', () => { diff --git a/src/__tests__/role-helpers.js b/src/__tests__/role-helpers.js index 9563b66c..47a58e9b 100644 --- a/src/__tests__/role-helpers.js +++ b/src/__tests__/role-helpers.js @@ -16,7 +16,7 @@ afterEach(() => { function setup() { const {getByTestId} = render(` -
+
link invalid link @@ -50,7 +50,7 @@ function setup() { -
+ @@ -62,12 +62,16 @@ function setup() {
  • Item 1
  • Item 2
  • + + +
    `) return { - section: getByTestId('a-section'), + unnamedSection: getByTestId('a-section'), + namedSection: getByTestId('named-section'), anchor: getByTestId('a-link'), h1: getByTestId('a-h1'), h2: getByTestId('a-h2'), @@ -88,7 +92,8 @@ function setup() { td1: getByTestId('a-cell-1'), td2: getByTestId('a-cell-2'), td3: getByTestId('a-cell-3'), - form: getByTestId('a-form'), + unnamedForm: getByTestId('a-form'), + namedForm: getByTestId('named-form'), radio: getByTestId('a-radio-1'), radio2: getByTestId('a-radio-2'), input: getByTestId('a-input-1'), @@ -99,7 +104,6 @@ function setup() { test('getRoles returns expected roles for various dom nodes', () => { const { - section, anchor, h1, h2, @@ -120,17 +124,17 @@ test('getRoles returns expected roles for various dom nodes', () => { td1, td2, td3, - form, radio, radio2, input, input2, textarea, + namedSection, + namedForm, } = setup() - expect(getRoles(section)).toEqual({ + expect(getRoles(namedSection)).toEqual({ link: [anchor], - region: [section], heading: [h1, h2, h3], navigation: [nav], radio: [radio, radio2], @@ -140,27 +144,28 @@ test('getRoles returns expected roles for various dom nodes', () => { table: [table], row: [tr], cell: [td1, td2, td3], - form: [form], textbox: [input, input2, textarea], rowgroup: [tbody], command: [menuItem, menuItem2], menuitem: [menuItem, menuItem2], + form: [namedForm], + region: [namedSection], }) }) test('logRoles calls console.log with output from prettyRoles', () => { - const {section} = setup() - logRoles(section) + const {namedSection} = setup() + logRoles(namedSection) expect(console.log).toHaveBeenCalledTimes(1) expect(console.log.mock.calls[0][0]).toMatchSnapshot() }) test('getImplicitAriaRoles returns expected roles for various dom nodes', () => { - const {section, h1, form, radio, input} = setup() + const {namedSection, h1, unnamedForm, radio, input} = setup() - expect(getImplicitAriaRoles(section)).toEqual(['region']) + expect(getImplicitAriaRoles(namedSection)).toEqual(['region']) expect(getImplicitAriaRoles(h1)).toEqual(['heading']) - expect(getImplicitAriaRoles(form)).toEqual(['form']) + expect(getImplicitAriaRoles(unnamedForm)).toEqual([]) expect(getImplicitAriaRoles(radio)).toEqual(['radio']) expect(getImplicitAriaRoles(input)).toEqual(['textbox']) }) diff --git a/src/__tests__/role.js b/src/__tests__/role.js index b68410fe..ec9beff7 100644 --- a/src/__tests__/role.js +++ b/src/__tests__/role.js @@ -214,8 +214,7 @@ test('can be filtered by accessible name', () => { expect(deliveryForm).not.toBeNull() expect( - // TODO: upstream bug in `aria-query`; should be `button` role - getQueriesForElement(deliveryForm).getByRole('textbox', {name: 'Submit'}), + getQueriesForElement(deliveryForm).getByRole('button', {name: 'Submit'}), ).not.toBeNull() const invoiceForm = getByRole('form', {name: 'Delivery Adress'}) @@ -229,11 +228,9 @@ test('can be filtered by accessible name', () => { test('accessible name comparison is case sensitive', () => { const {getByRole} = render(`

    Sign up

    `) - // actual: "Sign up", - // queried: "Sign Up" - expect(() => getByRole('heading', {name: 'Sign Up'})) + expect(() => getByRole('heading', {name: 'something that does not match'})) .toThrowErrorMatchingInlineSnapshot(` -"Unable to find an accessible element with the role "heading" and name "Sign Up" +"Unable to find an accessible element with the role "heading" and name "something that does not match" Here are the accessible roles: @@ -277,9 +274,9 @@ test('accessible name filter implements TextMatch', () => { test('TextMatch serialization in error message', () => { const {getByRole} = render(`

    Sign up

    `) - expect(() => getByRole('heading', {name: /Login/})) + expect(() => getByRole('heading', {name: /something that does not match/})) .toThrowErrorMatchingInlineSnapshot(` -"Unable to find an accessible element with the role "heading" and name \`/Login/\` +"Unable to find an accessible element with the role "heading" and name \`/something that does not match/\` Here are the accessible roles: diff --git a/src/role-helpers.js b/src/role-helpers.js index 6d3ee309..5027ec31 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -76,9 +76,16 @@ function getImplicitAriaRoles(currentNode) { function buildElementRoleList(elementRolesMap) { function makeElementSelector({name, attributes = []}) { return `${name}${attributes - .map(({name: attributeName, value}) => - value ? `[${attributeName}=${value}]` : `[${attributeName}]`, - ) + .map(({name: attributeName, value, constraints = []}) => { + const shouldNotExist = constraints.indexOf('undefined') !== -1 + if (shouldNotExist) { + return `:not([${attributeName}])` + } else if (value) { + return `[${attributeName}="${value}"]` + } else { + return `[${attributeName}]` + } + }) .join('')}` }