From 3ae270237425a2bd8a865f727537f5633f3653cd Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 3 Jun 2021 14:27:46 +0200 Subject: [PATCH 1/9] fix: Only use a single clock (#966) BREAKING CHANGE: Remove deprecated `waitFormDOMChange` BREAKING CHANGE: The `timeout` in `waitFor(callback, { interval, timeout } )` now uses the same clock as `interval`. Previously `timeout` was always using the real clock while `interval` was using the global clock which could've been mocked out. For the old behavior I'd recommend `waitFor(callback, { interval, timeout: Number.PositiveInfinity })` and rely on your test runner to timeout considering real timers. --- src/__tests__/deprecation-warnings.js | 30 --------- src/__tests__/fake-timers.js | 13 ++-- src/__tests__/helpers.js | 71 +-------------------- src/__tests__/wait-for-dom-change.js | 54 ---------------- src/helpers.js | 88 +++------------------------ src/index.js | 1 - src/wait-for-dom-change.js | 64 ------------------- src/wait-for.js | 26 ++------ types/index.d.ts | 1 - types/wait-for-dom-change.d.ts | 7 --- 10 files changed, 22 insertions(+), 333 deletions(-) delete mode 100644 src/__tests__/deprecation-warnings.js delete mode 100644 src/__tests__/wait-for-dom-change.js delete mode 100644 src/wait-for-dom-change.js delete mode 100644 types/wait-for-dom-change.d.ts diff --git a/src/__tests__/deprecation-warnings.js b/src/__tests__/deprecation-warnings.js deleted file mode 100644 index 14744159..00000000 --- a/src/__tests__/deprecation-warnings.js +++ /dev/null @@ -1,30 +0,0 @@ -import {waitForElement, waitForDomChange, wait} from '..' - -afterEach(() => { - console.warn.mockClear() -}) - -test('deprecation warnings only warn once', async () => { - await wait(() => {}, {timeout: 1}) - await waitForElement(() => {}, {timeout: 1}).catch(e => e) - await waitForDomChange({timeout: 1}).catch(e => e) - expect(console.warn.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "\`wait\` has been deprecated and replaced by \`waitFor\` instead. In most cases you should be able to find/replace \`wait\` with \`waitFor\`. Learn more: https://testing-library.com/docs/dom-testing-library/api-async#waitfor.", - ], - Array [ - "\`waitForElement\` has been deprecated. Use a \`find*\` query (preferred: https://testing-library.com/docs/dom-testing-library/api-queries#findby) or use \`waitFor\` instead: https://testing-library.com/docs/dom-testing-library/api-async#waitfor", - ], - Array [ - "\`waitForDomChange\` has been deprecated. Use \`waitFor\` instead: https://testing-library.com/docs/dom-testing-library/api-async#waitfor.", - ], - ] - `) - - console.warn.mockClear() - await wait(() => {}, {timeout: 1}) - await waitForElement(() => {}, {timeout: 1}).catch(e => e) - await waitForDomChange({timeout: 1}).catch(e => e) - expect(console.warn).not.toHaveBeenCalled() -}) diff --git a/src/__tests__/fake-timers.js b/src/__tests__/fake-timers.js index af2a09ac..69919771 100644 --- a/src/__tests__/fake-timers.js +++ b/src/__tests__/fake-timers.js @@ -50,12 +50,11 @@ test('times out after 1000ms by default', async () => { ).rejects.toThrowErrorMatchingInlineSnapshot( `"Timed out in waitForElementToBeRemoved."`, ) - // NOTE: this assertion ensures that even when we have fake timers, the - // timeout still takes the full 1000ms - // unfortunately, timeout clocks aren't super accurate, so we simply verify - // that it's greater than or equal to 900ms. That's enough to be confident - // that we're using real timers. - expect(performance.now() - start).toBeGreaterThanOrEqual(900) + // NOTE: this assertion ensures that the timeout runs in the declared (fake) clock + // while in real time the time was only a fraction since the real clock is only bound by the CPU + // So 10ms is really just an approximation on how long the CPU needs to execute our code. + // If people want to timeout in real time they should rely on their test runners. + expect(performance.now() - start).toBeLessThanOrEqual(10) }) test('recursive timers do not cause issues', async () => { @@ -68,7 +67,7 @@ test('recursive timers do not cause issues', async () => { } startTimer() - await runWaitFor({time: 800}, {timeout: 100}) + await runWaitFor({time: 800}, {timeout: 900}) recurse = false }) diff --git a/src/__tests__/helpers.js b/src/__tests__/helpers.js index 07b53007..2033dbde 100644 --- a/src/__tests__/helpers.js +++ b/src/__tests__/helpers.js @@ -1,10 +1,5 @@ import {screen} from '../' -import { - getDocument, - getWindowFromNode, - checkContainerType, - runWithRealTimers, -} from '../helpers' +import {getDocument, getWindowFromNode, checkContainerType} from '../helpers' test('returns global document if exists', () => { expect(getDocument()).toBe(document) @@ -61,67 +56,3 @@ describe('query container validation throws when validation fails', () => { ) }) }) - -describe('run with real timers', () => { - const realSetTimeout = global.setTimeout - - afterEach(() => { - // restore timers replaced by jest.useFakeTimers() - jest.useRealTimers() - // restore setTimeout replaced by assignment - global.setTimeout = realSetTimeout - }) - - test('use real timers when timers are faked with jest.useFakeTimers(legacy)', () => { - // legacy timers use mocks and do not rely on a clock instance - jest.useFakeTimers('legacy') - runWithRealTimers(() => { - expect(global.setTimeout).toBe(realSetTimeout) - }) - expect(global.setTimeout._isMockFunction).toBe(true) - expect(global.setTimeout.clock).toBeUndefined() - }) - - test('use real timers when timers are faked with jest.useFakeTimers(modern)', () => { - // modern timers use a clock instance instead of a mock - jest.useFakeTimers('modern') - runWithRealTimers(() => { - expect(global.setTimeout).toBe(realSetTimeout) - }) - expect(global.setTimeout._isMockFunction).toBeUndefined() - expect(global.setTimeout.clock).toBeDefined() - }) - - test('do not use real timers when timers are not faked with jest.useFakeTimers', () => { - // useFakeTimers is not used, timers are faked in some other way - const fakedSetTimeout = callback => { - callback() - } - fakedSetTimeout.clock = jest.fn() - global.setTimeout = fakedSetTimeout - - runWithRealTimers(() => { - expect(global.setTimeout).toBe(fakedSetTimeout) - }) - expect(global.setTimeout).toBe(fakedSetTimeout) - }) - - describe('run with setImmediate and clearImmediate deleted', () => { - const setImmediate = global.setImmediate - const clearImmediate = global.clearImmediate - - beforeEach(() => { - delete global.setImmediate - delete global.clearImmediate - }) - - afterEach(() => { - global.setImmediate = setImmediate - global.clearImmediate = clearImmediate - }) - - test('safe check for setImmediate and clearImmediate', () => { - expect(() => runWithRealTimers(() => {})).not.toThrow() - }) - }) -}) diff --git a/src/__tests__/wait-for-dom-change.js b/src/__tests__/wait-for-dom-change.js deleted file mode 100644 index 3db4d775..00000000 --- a/src/__tests__/wait-for-dom-change.js +++ /dev/null @@ -1,54 +0,0 @@ -import {waitForDomChange} from '..' -import {renderIntoDocument} from './helpers/test-utils' - -afterEach(() => { - jest.useRealTimers() -}) - -test('waits for the dom to change in the document', async () => { - const {container} = renderIntoDocument('
') - const promise = waitForDomChange() - setTimeout(() => container.firstChild.setAttribute('id', 'foo')) - const mutationResult = await promise - expect(mutationResult).toMatchInlineSnapshot(` - Array [ - Object { - "addedNodes": NodeList [], - "attributeName": "id", - "attributeNamespace": null, - "nextSibling": null, - "oldValue": null, - "previousSibling": null, - "removedNodes": NodeList [], - "target":
, - "type": "attributes", - }, - ] - `) -}) - -test('waits for the dom to change in a specified container', async () => { - const {container} = renderIntoDocument('
') - const promise = waitForDomChange({container}) - setTimeout(() => container.firstChild.setAttribute('id', 'foo')) - const mutationResult = await promise - expect(mutationResult).toMatchInlineSnapshot(` - Array [ - Object { - "addedNodes": NodeList [], - "attributeName": "id", - "attributeNamespace": null, - "nextSibling": null, - "oldValue": null, - "previousSibling": null, - "removedNodes": NodeList [], - "target":
, - "type": "attributes", - }, - ] - `) -}) diff --git a/src/helpers.js b/src/helpers.js index 8cdecc78..c70218cc 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,85 +1,21 @@ -const globalObj = typeof window === 'undefined' ? global : window // Constant node.nodeType for text nodes, see: // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#Node_type_constants const TEXT_NODE = 3 -// Currently this fn only supports jest timers, but it could support other test runners in the future. -function runWithRealTimers(callback) { - return hasJestTimers() - ? runWithJestRealTimers(callback).callbackReturnValue - : // istanbul ignore next - callback() -} - -function hasJestTimers() { - return ( - typeof jest !== 'undefined' && - jest !== null && - typeof jest.useRealTimers === 'function' - ) -} - -function runWithJestRealTimers(callback) { - const timerAPI = { - clearInterval, - clearTimeout, - setInterval, - setTimeout, - } - - // For more on why we have the check here, - // checkout https://github.com/testing-library/dom-testing-library/issues/914 - if (typeof setImmediate === 'function') { - timerAPI.setImmediate = setImmediate - } - if (typeof clearImmediate === 'function') { - timerAPI.clearImmediate = clearImmediate - } - - jest.useRealTimers() - - const callbackReturnValue = callback() - - const usedFakeTimers = Object.entries(timerAPI).some( - ([name, func]) => func !== globalObj[name], - ) - - if (usedFakeTimers) { - jest.useFakeTimers(timerAPI.setTimeout?.clock ? 'modern' : 'legacy') - } - - return { - callbackReturnValue, - usedFakeTimers, - } -} - function jestFakeTimersAreEnabled() { - return hasJestTimers() - ? runWithJestRealTimers(() => {}).usedFakeTimers - : // istanbul ignore next - false -} - -// we only run our tests in node, and setImmediate is supported in node. -// istanbul ignore next -function setImmediatePolyfill(fn) { - return globalObj.setTimeout(fn, 0) -} - -function getTimeFunctions() { - // istanbul ignore next - return { - clearTimeoutFn: globalObj.clearTimeout, - setImmediateFn: globalObj.setImmediate || setImmediatePolyfill, - setTimeoutFn: globalObj.setTimeout, + /* istanbul ignore else */ + if (typeof jest !== 'undefined' && jest !== null) { + return ( + // legacy timers + setTimeout._isMockFunction === true || + // modern timers + Object.prototype.hasOwnProperty.call(setTimeout, 'clock') + ) } + // istanbul ignore next + return false } -const {clearTimeoutFn, setImmediateFn, setTimeoutFn} = runWithRealTimers( - getTimeFunctions, -) - function getDocument() { /* istanbul ignore if */ if (typeof window === 'undefined') { @@ -144,10 +80,6 @@ function checkContainerType(container) { export { getWindowFromNode, getDocument, - clearTimeoutFn as clearTimeout, - setImmediateFn as setImmediate, - setTimeoutFn as setTimeout, - runWithRealTimers, checkContainerType, jestFakeTimersAreEnabled, TEXT_NODE, diff --git a/src/index.js b/src/index.js index 2a279383..660d0849 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,6 @@ export * from './queries' export * from './wait-for' export * from './wait-for-element' export * from './wait-for-element-to-be-removed' -export * from './wait-for-dom-change' export {getDefaultNormalizer} from './matches' export * from './get-node-text' export * from './events' diff --git a/src/wait-for-dom-change.js b/src/wait-for-dom-change.js deleted file mode 100644 index 1344db9d..00000000 --- a/src/wait-for-dom-change.js +++ /dev/null @@ -1,64 +0,0 @@ -import { - getWindowFromNode, - getDocument, - setImmediate, - setTimeout, - clearTimeout, - runWithRealTimers, -} from './helpers' -import {getConfig} from './config' - -let hasWarned = false - -// deprecated... TODO: remove this method. People should use wait instead -// the reasoning is that waiting for just any DOM change is an implementation -// detail. People should be waiting for a specific thing to change. -function waitForDomChange({ - container = getDocument(), - timeout = getConfig().asyncUtilTimeout, - mutationObserverOptions = { - subtree: true, - childList: true, - attributes: true, - characterData: true, - }, -} = {}) { - if (!hasWarned) { - hasWarned = true - console.warn( - `\`waitForDomChange\` has been deprecated. Use \`waitFor\` instead: https://testing-library.com/docs/dom-testing-library/api-async#waitfor.`, - ) - } - return new Promise((resolve, reject) => { - const timer = setTimeout(onTimeout, timeout) - const {MutationObserver} = getWindowFromNode(container) - const observer = new MutationObserver(onMutation) - runWithRealTimers(() => - observer.observe(container, mutationObserverOptions), - ) - - function onDone(error, result) { - clearTimeout(timer) - setImmediate(() => observer.disconnect()) - if (error) { - reject(error) - } else { - resolve(result) - } - } - - function onMutation(mutationsList) { - onDone(null, mutationsList) - } - - function onTimeout() { - onDone(new Error('Timed out in waitForDomChange.'), null) - } - }) -} - -function waitForDomChangeWrapper(...args) { - return getConfig().asyncWrapper(() => waitForDomChange(...args)) -} - -export {waitForDomChangeWrapper as waitForDomChange} diff --git a/src/wait-for.js b/src/wait-for.js index 860b7a15..0a1e75ed 100644 --- a/src/wait-for.js +++ b/src/wait-for.js @@ -5,9 +5,6 @@ import { // We import these from the helpers rather than using the global version // because these will be *real* timers, regardless of whether we're in // an environment that's faked the timers out. - setImmediate, - setTimeout, - clearTimeout, checkContainerType, } from './helpers' import {getConfig, runWithExpensiveErrorDiagnosticsDisabled} from './config' @@ -87,7 +84,10 @@ function waitFor( // of parallelization so we're fine. // https://stackoverflow.com/a/59243586/971592 // eslint-disable-next-line no-await-in-loop - await new Promise(r => setImmediate(r)) + await new Promise(r => { + setTimeout(r, 0) + jest.advanceTimersByTime(0) + }) } } else { try { @@ -187,23 +187,7 @@ function waitForWrapper(callback, options) { ) } -let hasWarned = false - -// deprecated... TODO: remove this method. We renamed this to `waitFor` so the -// code people write reads more clearly. -function wait(...args) { - // istanbul ignore next - const [first = () => {}, ...rest] = args - if (!hasWarned) { - hasWarned = true - console.warn( - `\`wait\` has been deprecated and replaced by \`waitFor\` instead. In most cases you should be able to find/replace \`wait\` with \`waitFor\`. Learn more: https://testing-library.com/docs/dom-testing-library/api-async#waitfor.`, - ) - } - return waitForWrapper(first, ...rest) -} - -export {waitForWrapper as waitFor, wait} +export {waitForWrapper as waitFor} /* eslint diff --git a/types/index.d.ts b/types/index.d.ts index 38830ff4..6f77a69c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -12,7 +12,6 @@ export * from './query-helpers' export * from './screen' export * from './wait' export * from './wait-for' -export * from './wait-for-dom-change' export * from './wait-for-element' export * from './wait-for-element-to-be-removed' export * from './matches' diff --git a/types/wait-for-dom-change.d.ts b/types/wait-for-dom-change.d.ts deleted file mode 100644 index 44a09875..00000000 --- a/types/wait-for-dom-change.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {waitForOptions} from './wait-for' - -/** - * @deprecated `waitForDomChange` has been deprecated. - * Use `waitFor` instead: https://testing-library.com/docs/dom-testing-library/api-async#waitfor. - */ -export function waitForDomChange(options?: waitForOptions): Promise From ce06299c10653614f375b43fb47b16c06d01ad35 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 3 Jun 2021 11:27:03 +0200 Subject: [PATCH 2/9] fix: Consider `` labelable (#968) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e14ded9..a1df7345 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@types/aria-query": "^4.2.0", "aria-query": "^4.2.2", "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.5", + "dom-accessibility-api": "^0.5.6", "lz-string": "^1.4.4", "pretty-format": "^26.6.2" }, From 0f8d912f8a15d65f238ca0d9e0a3fbb4c7c3f64a Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Sun, 6 Jun 2021 13:00:04 +0200 Subject: [PATCH 3/9] fix(types): remove wait types (#971) --- types/index.d.ts | 1 - types/wait.d.ts | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 types/wait.d.ts diff --git a/types/index.d.ts b/types/index.d.ts index 6f77a69c..6aa73594 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -10,7 +10,6 @@ export {queries, queryHelpers, within} export * from './queries' export * from './query-helpers' export * from './screen' -export * from './wait' export * from './wait-for' export * from './wait-for-element' export * from './wait-for-element-to-be-removed' diff --git a/types/wait.d.ts b/types/wait.d.ts deleted file mode 100644 index 88015e71..00000000 --- a/types/wait.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @deprecated `wait` has been deprecated and replaced by `waitFor` instead. - * In most cases you should be able to find/replace `wait` with `waitFor`. - * Learn more: https://testing-library.com/docs/dom-testing-library/api-async#waitfor. - */ -export function wait( - callback?: () => void, - options?: { - timeout?: number - interval?: number - }, -): Promise From c9748e9e706abfe08e415c752d4ba480b9bb6005 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 8 Jun 2021 09:38:36 +0200 Subject: [PATCH 4/9] chore: Update kcd-scripts to 11.x (#962) --- package.json | 2 +- src/__node_tests__/index.js | 6 +- src/__node_tests__/screen.js | 2 +- .../__snapshots__/get-by-errors.js.snap | 4 +- src/__tests__/ariaAttributes.js | 16 +- .../base-queries-warn-on-invalid-container.js | 8 +- src/__tests__/element-queries.js | 367 +++++++++--------- src/__tests__/events.js | 2 +- src/__tests__/fake-timers.js | 18 +- src/__tests__/get-node-text.js | 6 +- src/__tests__/get-user-code-frame.js | 8 +- src/__tests__/helpers.js | 16 +- src/__tests__/matches.js | 4 +- src/__tests__/pretty-dom.js | 6 +- src/__tests__/screen.js | 14 +- src/__tests__/suggestions.js | 16 +- .../wait-for-element-to-be-removed.js | 12 +- src/__tests__/wait-for.js | 6 +- src/get-user-code-frame.js | 6 +- src/queries/role.js | 13 +- src/queries/text.ts | 9 +- src/queries/title.ts | 9 +- src/query-helpers.js | 74 ++-- src/role-helpers.js | 4 +- src/suggestions.js | 4 +- types/role-helpers.d.ts | 6 +- 26 files changed, 311 insertions(+), 327 deletions(-) diff --git a/package.json b/package.json index a1df7345..d43b3236 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "jest-serializer-ansi": "^1.0.3", "jest-watch-select-projects": "^2.0.0", "jsdom": "^16.4.0", - "kcd-scripts": "^7.5.3", + "kcd-scripts": "^11.0.0", "typescript": "^4.1.2" }, "eslintConfig": { diff --git a/src/__node_tests__/index.js b/src/__node_tests__/index.js index 0f88c323..6c663360 100644 --- a/src/__node_tests__/index.js +++ b/src/__node_tests__/index.js @@ -66,13 +66,13 @@ test('works without a browser context on a dom node (JSDOM Fragment)', () => { expect(dtl.getByLabelText(container, /username/i)).toMatchInlineSnapshot(` `) expect(dtl.getByLabelText(container, /password/i)).toMatchInlineSnapshot(` `) }) diff --git a/src/__node_tests__/screen.js b/src/__node_tests__/screen.js index 896393d0..6dba2399 100644 --- a/src/__node_tests__/screen.js +++ b/src/__node_tests__/screen.js @@ -4,6 +4,6 @@ test('the screen export throws a helpful error message when no global document i expect(() => screen.getByText(/hello world/i), ).toThrowErrorMatchingInlineSnapshot( - `"For queries bound to document.body a global document has to be available... Learn more: https://testing-library.com/s/screen-global-error"`, + `For queries bound to document.body a global document has to be available... Learn more: https://testing-library.com/s/screen-global-error`, ) }) diff --git a/src/__tests__/__snapshots__/get-by-errors.js.snap b/src/__tests__/__snapshots__/get-by-errors.js.snap index 45f4cde1..42bc84ae 100644 --- a/src/__tests__/__snapshots__/get-by-errors.js.snap +++ b/src/__tests__/__snapshots__/get-by-errors.js.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`getByLabelText query will throw the custom error returned by config.getElementError 1`] = `"My custom error: Unable to find a label with the text of: TEST QUERY"`; +exports[`getByLabelText query will throw the custom error returned by config.getElementError 1`] = `My custom error: Unable to find a label with the text of: TEST QUERY`; -exports[`getByText query will throw the custom error returned by config.getElementError 1`] = `"My custom error: Unable to find an element with the text: TEST QUERY. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible."`; +exports[`getByText query will throw the custom error returned by config.getElementError 1`] = `My custom error: Unable to find an element with the text: TEST QUERY. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.`; diff --git a/src/__tests__/ariaAttributes.js b/src/__tests__/ariaAttributes.js index 84334230..2f0b76de 100644 --- a/src/__tests__/ariaAttributes.js +++ b/src/__tests__/ariaAttributes.js @@ -5,7 +5,7 @@ test('`selected` throws on unsupported roles', () => { expect(() => getByRole('textbox', {selected: true}), ).toThrowErrorMatchingInlineSnapshot( - `"\\"aria-selected\\" is not supported on role \\"textbox\\"."`, + `"aria-selected" is not supported on role "textbox".`, ) }) @@ -14,7 +14,7 @@ test('`pressed` throws on unsupported roles', () => { expect(() => getByRole('textbox', {pressed: true}), ).toThrowErrorMatchingInlineSnapshot( - `"\\"aria-pressed\\" is not supported on role \\"textbox\\"."`, + `"aria-pressed" is not supported on role "textbox".`, ) }) @@ -23,7 +23,7 @@ test('`checked` throws on unsupported roles', () => { expect(() => getByRole('textbox', {checked: true}), ).toThrowErrorMatchingInlineSnapshot( - `"\\"aria-checked\\" is not supported on role \\"textbox\\"."`, + `"aria-checked" is not supported on role "textbox".`, ) }) @@ -32,7 +32,7 @@ test('`expanded` throws on unsupported roles', () => { expect(() => getByRole('heading', {expanded: true}), ).toThrowErrorMatchingInlineSnapshot( - `"\\"aria-expanded\\" is not supported on role \\"heading\\"."`, + `"aria-expanded" is not supported on role "heading".`, ) }) @@ -142,9 +142,9 @@ test('`selected: true` matches `aria-selected="true"` on supported roles', () => 'selected-listbox-option', ]) - expect( - getAllByRole('rowheader', {selected: true}).map(({id}) => id), - ).toEqual(['selected-rowheader', 'selected-native-rowheader']) + expect(getAllByRole('rowheader', {selected: true}).map(({id}) => id)).toEqual( + ['selected-rowheader', 'selected-native-rowheader'], + ) expect(getAllByRole('treeitem', {selected: true}).map(({id}) => id)).toEqual([ 'selected-treeitem', @@ -208,7 +208,7 @@ test('`level` throws on unsupported roles', () => { expect(() => getByRole('button', {level: 3}), ).toThrowErrorMatchingInlineSnapshot( - `"Role \\"button\\" cannot have \\"level\\" property."`, + `Role "button" cannot have "level" property.`, ) }) diff --git a/src/__tests__/base-queries-warn-on-invalid-container.js b/src/__tests__/base-queries-warn-on-invalid-container.js index c4684005..6796a874 100644 --- a/src/__tests__/base-queries-warn-on-invalid-container.js +++ b/src/__tests__/base-queries-warn-on-invalid-container.js @@ -86,8 +86,8 @@ describe('synchronous queries throw on invalid container type', () => { ])('%s', (_queryName, query) => { expect(() => query('invalid type for container', 'irrelevant text'), - ).toThrowErrorMatchingInlineSnapshot( - `"Expected container to be an Element, a Document or a DocumentFragment but got string."`, + ).toThrowError( + `Expected container to be an Element, a Document or a DocumentFragment but got string.`, ) }) }) @@ -120,8 +120,8 @@ describe('asynchronous queries throw on invalid container type', () => { queryOptions, waitOptions, ), - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Expected container to be an Element, a Document or a DocumentFragment but got string."`, + ).rejects.toThrowError( + `Expected container to be an Element, a Document or a DocumentFragment but got string.`, ) }) }) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 3e3ee6de..bb9d2fbf 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -35,65 +35,65 @@ test('get throws a useful error message', () => { } = render('
') expect(() => getByLabelText('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` -"Unable to find a label with the text of: LucyRicardo + "Unable to find a label with the text of: LucyRicardo -
-
-
" -`) +
+
+
" + `) expect(() => getByPlaceholderText('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element with the placeholder text of: LucyRicardo + "Unable to find an element with the placeholder text of: LucyRicardo -
-
-
" -`) +
+
+
" + `) expect(() => getByText('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element with the text: LucyRicardo. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. + "Unable to find an element with the text: LucyRicardo. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible. -
-
-
" -`) +
+
+
" + `) expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element by: [data-testid="LucyRicardo"] + "Unable to find an element by: [data-testid="LucyRicardo"] -
-
-
" -`) +
+
+
" + `) expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element with the alt text: LucyRicardo + "Unable to find an element with the alt text: LucyRicardo -
-
-
" -`) +
+
+
" + `) expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element with the title: LucyRicardo. + "Unable to find an element with the title: LucyRicardo. -
-
-
" -`) +
+
+
" + `) expect(() => getByDisplayValue('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` -"Unable to find an element with the display value: LucyRicardo. + "Unable to find an element with the display value: LucyRicardo. -
-
-
" -`) +
+
+
" + `) expect(() => getByRole('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` -"Unable to find an accessible element with the role "LucyRicardo" + "Unable to find an accessible element with the role "LucyRicardo" -There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the \`hidden\` option to \`true\`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole + There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the \`hidden\` option to \`true\`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole -
-
-
" -`) +
+
+
" + `) }) test('can get elements by matching their text content', () => { @@ -352,14 +352,14 @@ test('label with no form control', () => { const {getByLabelText, queryByLabelText} = render(``) expect(queryByLabelText(/alone/)).toBeNull() expect(() => getByLabelText(/alone/)).toThrowErrorMatchingInlineSnapshot(` -"Found a label with the text of: /alone/, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. + "Found a label with the text of: /alone/, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. -
- -
" -`) +
+ +
" + `) }) test('label with "for" attribute but no form control and fuzzy matcher', () => { @@ -369,16 +369,16 @@ test('label with "for" attribute but no form control and fuzzy matcher', () => { expect(queryByLabelText('alone', {exact: false})).toBeNull() expect(() => getByLabelText('alone', {exact: false})) .toThrowErrorMatchingInlineSnapshot(` -"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. - -
- -
" -`) + "Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. + +
+ +
" + `) }) test('label with children with no form control', () => { @@ -391,32 +391,32 @@ test('label with children with no form control', () => { expect(queryByLabelText(/alone/, {selector: 'input'})).toBeNull() expect(() => getByLabelText(/alone/, {selector: 'input'})) .toThrowErrorMatchingInlineSnapshot(` -"Found a label with the text of: /alone/, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. + "Found a label with the text of: /alone/, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly. -
- - - -
" -`) +
+ + + +
" + `) }) test('label with non-labellable element', () => { @@ -431,35 +431,35 @@ test('label with non-labellable element', () => { expect(queryByLabelText(/Label/)).toBeNull() expect(() => getByLabelText(/Label/)).toThrowErrorMatchingInlineSnapshot(` -"Found a label with the text of: /Label/, however the element associated with this label (
) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a
, you can use aria-label or aria-labelledby instead. + "Found a label with the text of: /Label/, however the element associated with this label (
) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a
, you can use aria-label or aria-labelledby instead. -
- - -
- - - - - -
+
- Hello - -
- - -
- - -
" -`) + +
+ + + + + +
+ + Hello + +
+ + +
+ + +
" + `) }) test('multiple labels with non-labellable elements', () => { @@ -478,65 +478,65 @@ test('multiple labels with non-labellable elements', () => { expect(queryAllByLabelText(/Label/)).toEqual([]) expect(() => getAllByLabelText(/Label/)).toThrowErrorMatchingInlineSnapshot(` -"Found a label with the text of: /Label/, however the element associated with this label () is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a , you can use aria-label or aria-labelledby instead. + "Found a label with the text of: /Label/, however the element associated with this label () is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a , you can use aria-label or aria-labelledby instead. -Found a label with the text of: /Label/, however the element associated with this label (

) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a

, you can use aria-label or aria-labelledby instead. + Found a label with the text of: /Label/, however the element associated with this label (

) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a

, you can use aria-label or aria-labelledby instead. -

- - -
- - - - - - +
- Hello - - - - - - - -

- World - -

- - -
- - -
" -`) +
+ + + + + + + + Hello + + + + + + + +

+ + World + +

+ + +
+ + +
" + `) }) test('totally empty label', () => { const {getByLabelText, queryByLabelText} = render(`