From 2fb74b32295b693cb3a92144e8f98b7f06fdbd5f Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 27 Oct 2022 01:32:05 +0300 Subject: [PATCH 1/6] Improve error messages --- src/__tests__/element-queries.js | 38 ++++++++++++++++++++++-- src/__tests__/get-by-errors.js | 4 +-- src/helpers.ts | 2 +- src/hints-helpers.ts | 28 ++++++++++++++++++ src/matches.ts | 2 +- src/queries/alt-text.ts | 43 +++++++++++++++++++++++---- src/queries/display-value.ts | 37 ++++++++++++++++++++--- src/queries/placeholder-text.ts | 46 ++++++++++++++++++++++++---- src/queries/text.ts | 51 ++++++++++++++++++++++++-------- src/queries/title.ts | 37 ++++++++++++++++++++--- types/matches.d.ts | 7 +++-- 11 files changed, 256 insertions(+), 39 deletions(-) create mode 100644 src/hints-helpers.ts diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 364de4aa..590cd245 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -28,7 +28,7 @@ test('query can return null', () => { expect(queryByAltText('LucyRicardo')).toBeNull() }) -test('get throws a useful error message', () => { +test('get throws a useful error message - not found', () => { const { getByLabelText, getByDisplayValue, @@ -76,6 +76,40 @@ test('get throws a useful error message', () => {
`) + expect(() => + getByText(function LucyRicardo() { + return false + }), + ).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: 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. + + Ignored nodes: comments, script, style +
+
+
+ `) + expect(() => getByText(() => false)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function]. 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. + + Ignored nodes: comments, script, style +
+
+
+ `) + + function something() { + return false + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByText(something)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo. 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. + + Ignored nodes: comments, script, style +
+
+
+ `) expect(() => getByText('LucyRicardo', {selector: 'span'})) .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the text: LucyRicardo, which matches selector 'span'. 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. @@ -1117,7 +1151,7 @@ test('the default value for `ignore` is used in errors', () => { const {getByText} = render('
Hello
') expect(() => getByText(/hello/i)).toThrowErrorMatchingInlineSnapshot(` - Unable to find an element with the text: /hello/i. 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 that its text match the regex: /hello/i. 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. Ignored nodes: comments, div
diff --git a/src/__tests__/get-by-errors.js b/src/__tests__/get-by-errors.js index c7e8cadf..01e359a3 100644 --- a/src/__tests__/get-by-errors.js +++ b/src/__tests__/get-by-errors.js @@ -103,7 +103,7 @@ describe('*ByDisplayValue queries throw an error when there are multiple element ``, ) expect(() => getByDisplayValue(/his/)).toThrow( - /multiple elements with the display value:/i, + /multiple elements that its display value match the regex:/i, ) }) test('queryByDisplayValue', () => { @@ -111,7 +111,7 @@ describe('*ByDisplayValue queries throw an error when there are multiple element ``, ) expect(() => queryByDisplayValue(/his/)).toThrow( - /multiple elements with the display value:/i, + /multiple elements that its display value match the regex:/i, ) }) }) diff --git a/src/helpers.ts b/src/helpers.ts index 5a068300..0eb96062 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -38,7 +38,7 @@ function getWindowFromNode(node: any) { throw new Error( `It looks like the window object is not available for the provided node.`, ) - } else if (node.then instanceof Function) { + } else if (typeof node.then === 'function') { throw new Error( `It looks like you passed a Promise object instead of a DOM node. Did you do something like \`fireEvent.click(screen.findBy...\` when you meant to use a \`getBy\` query \`fireEvent.click(screen.getBy...\`, or await the findBy query \`fireEvent.click(await screen.findBy...\`?`, ) diff --git a/src/hints-helpers.ts b/src/hints-helpers.ts new file mode 100644 index 00000000..fc320fb7 --- /dev/null +++ b/src/hints-helpers.ts @@ -0,0 +1,28 @@ +import {Matcher} from '../types' + +function getMatcherHint(matcher: Matcher, prefixForRegex: string) { + if (matcher instanceof RegExp) { + return `${ + prefixForRegex ? `${prefixForRegex}` : 'that' + } match the regex: ${matcher}` + } + + if (typeof matcher === 'function') { + let customMatcherText: string = '' + + if ( + matcher.customMatcherText && + typeof matcher.customMatcherText === 'string' + ) { + customMatcherText = matcher.customMatcherText + } else { + customMatcherText = matcher.name || '[anonymous function]' + } + + return `that match the custom matcher${ + customMatcherText ? `: ${customMatcherText}` : '' + }` + } +} + +export {getMatcherHint} diff --git a/src/matches.ts b/src/matches.ts index c49c7cca..70a8cff1 100644 --- a/src/matches.ts +++ b/src/matches.ts @@ -53,7 +53,7 @@ function matches( assertNotNullOrUndefined(matcher) const normalizedText = normalizer(textToMatch) - if (matcher instanceof Function) { + if (typeof matcher === 'function') { return matcher(normalizedText, node) } else if (matcher instanceof RegExp) { return matchRegExp(matcher, normalizedText) diff --git a/src/queries/alt-text.ts b/src/queries/alt-text.ts index b3c83a00..7897e15e 100644 --- a/src/queries/alt-text.ts +++ b/src/queries/alt-text.ts @@ -6,9 +6,12 @@ import {checkContainerType} from '../helpers' import { AllByBoundAttribute, GetErrorFunction, + Matcher, MatcherOptions, + SelectorMatcherOptions, } from '../../types' -import {buildQueries} from './all-utils' +import {getMatcherHint} from '../hints-helpers' +import {buildQueries, makeNormalizer} from './all-utils' // Valid tags are img, input, area and custom elements const VALID_TAG_REGEXP = /^(img|input|area|.+-.+)$/i @@ -24,10 +27,40 @@ const queryAllByAltText: AllByBoundAttribute = ( ) } -const getMultipleError: GetErrorFunction<[unknown]> = (c, alt) => - `Found multiple elements with the alt text: ${alt}` -const getMissingError: GetErrorFunction<[unknown]> = (c, alt) => - `Unable to find an element with the alt text: ${alt}` +const getMultipleError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + alt, + options, +) => { + return `Found multiple elements ${getMatcherHintOrDefault(alt, options)}` +} +const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + alt, + options, +) => `Unable to find an element ${getMatcherHintOrDefault(alt, options)}` + +function getMatcherHintOrDefault( + matcher: Matcher, + options: MatcherOptions = {}, +) { + const matcherHint = getMatcherHint(matcher, 'that its alt text') + + if (matcherHint) { + return matcherHint + } + + const {normalizer} = options + const matchNormalizer = makeNormalizer({normalizer}) + const normalizedText = matchNormalizer(matcher.toString()) + const isNormalizedDifferent = normalizedText !== matcher.toString() + + return `with the alt text: ${ + isNormalizedDifferent + ? `${normalizedText} (normalized from '${matcher}')` + : matcher + }` +} const queryAllByAltTextWithSuggestions = wrapAllByQueryWithSuggestion< // @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment diff --git a/src/queries/display-value.ts b/src/queries/display-value.ts index 7177a84a..4998a42e 100644 --- a/src/queries/display-value.ts +++ b/src/queries/display-value.ts @@ -6,6 +6,7 @@ import { Matcher, MatcherOptions, } from '../../types' +import {getMatcherHint} from '../hints-helpers' import { getNodeText, matches, @@ -43,10 +44,38 @@ const queryAllByDisplayValue: AllByBoundAttribute = ( }) } -const getMultipleError: GetErrorFunction<[unknown]> = (c, value) => - `Found multiple elements with the display value: ${value}.` -const getMissingError: GetErrorFunction<[unknown]> = (c, value) => - `Unable to find an element with the display value: ${value}.` +const getMultipleError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + value, + options = {}, +) => `Found multiple elements ${getMatcherHintOrDefault(value, options)}.` +const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + value, + options = {}, +) => `Unable to find an element ${getMatcherHintOrDefault(value, options)}.` + +function getMatcherHintOrDefault( + matcher: Matcher, + options: MatcherOptions = {}, +) { + const matcherHint = getMatcherHint(matcher, 'that its display value') + + if (matcherHint) { + return matcherHint + } + + const {normalizer} = options + const matchNormalizer = makeNormalizer({normalizer}) + const normalizedText = matchNormalizer(matcher.toString()) + const isNormalizedDifferent = normalizedText !== matcher.toString() + + return `with the display value: ${ + isNormalizedDifferent + ? `${normalizedText} (normalized from '${matcher}')` + : matcher + }` +} const queryAllByDisplayValueWithSuggestions = wrapAllByQueryWithSuggestion< // @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment diff --git a/src/queries/placeholder-text.ts b/src/queries/placeholder-text.ts index 42559954..089556c9 100644 --- a/src/queries/placeholder-text.ts +++ b/src/queries/placeholder-text.ts @@ -1,16 +1,50 @@ import {wrapAllByQueryWithSuggestion} from '../query-helpers' import {checkContainerType} from '../helpers' -import {AllByBoundAttribute, GetErrorFunction} from '../../types' -import {queryAllByAttribute, buildQueries} from './all-utils' +import { + AllByBoundAttribute, + GetErrorFunction, + Matcher, + MatcherOptions, +} from '../../types' +import {getMatcherHint} from '../hints-helpers' +import {queryAllByAttribute, buildQueries, makeNormalizer} from './all-utils' const queryAllByPlaceholderText: AllByBoundAttribute = (...args) => { checkContainerType(args[0]) return queryAllByAttribute('placeholder', ...args) } -const getMultipleError: GetErrorFunction<[unknown]> = (c, text) => - `Found multiple elements with the placeholder text of: ${text}` -const getMissingError: GetErrorFunction<[unknown]> = (c, text) => - `Unable to find an element with the placeholder text of: ${text}` +const getMultipleError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + text, + options = {}, +) => `Found multiple elements ${getMatcherHintOrDefault(text, options)}` +const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + text, + options = {}, +) => `Unable to find an element ${getMatcherHintOrDefault(text, options)}` + +function getMatcherHintOrDefault( + matcher: Matcher, + options: MatcherOptions = {}, +) { + const matcherHint = getMatcherHint(matcher, 'that its placeholder text') + + if (matcherHint) { + return matcherHint + } + + const {normalizer} = options + const matchNormalizer = makeNormalizer({normalizer}) + const normalizedText = matchNormalizer(matcher.toString()) + const isNormalizedDifferent = normalizedText !== matcher.toString() + + return `with the placeholder text of: ${ + isNormalizedDifferent + ? `${normalizedText} (normalized from '${matcher}')` + : matcher + }` +} const queryAllByPlaceholderTextWithSuggestions = wrapAllByQueryWithSuggestion< // @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment diff --git a/src/queries/text.ts b/src/queries/text.ts index 41594ce7..a3788308 100644 --- a/src/queries/text.ts +++ b/src/queries/text.ts @@ -6,6 +6,7 @@ import { SelectorMatcherOptions, Matcher, } from '../../types' +import {getMatcherHint} from '../hints-helpers' import { fuzzyMatches, matches, @@ -45,27 +46,53 @@ const queryAllByText: AllByText = ( ) } -const getMultipleError: GetErrorFunction<[unknown]> = (c, text) => - `Found multiple elements with the text: ${text}` +const getMultipleError: GetErrorFunction<[Matcher, SelectorMatcherOptions]> = ( + c, + text, + options = {}, +) => { + const {selector} = options + const isCustomSelector = (options.selector ?? '*') !== '*' + + return `Found multiple elements ${getMatcherHintOrDefault(text, options)}${ + isCustomSelector ? `, which matches selector '${selector}'` : '' + }` +} const getMissingError: GetErrorFunction<[Matcher, SelectorMatcherOptions]> = ( c, text, options = {}, ) => { - const {collapseWhitespace, trim, normalizer, selector} = options - const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer}) - const normalizedText = matchNormalizer(text.toString()) - const isNormalizedDifferent = normalizedText !== text.toString() - const isCustomSelector = (selector ?? '*') !== '*' - return `Unable to find an element with the text: ${ - isNormalizedDifferent - ? `${normalizedText} (normalized from '${text}')` - : text - }${ + const {selector} = options + const isCustomSelector = (options.selector ?? '*') !== '*' + + return `Unable to find an element ${getMatcherHintOrDefault(text, options)}${ isCustomSelector ? `, which matches selector '${selector}'` : '' }. 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.` } +function getMatcherHintOrDefault( + matcher: Matcher, + options: SelectorMatcherOptions = {}, +) { + const matcherHint = getMatcherHint(matcher, 'that its text') + + if (matcherHint) { + return matcherHint + } + + const {collapseWhitespace, trim, normalizer} = options + const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer}) + const normalizedText = matchNormalizer(matcher.toString()) + const isNormalizedDifferent = normalizedText !== matcher.toString() + + return `with the text: ${ + isNormalizedDifferent + ? `${normalizedText} (normalized from '${matcher}')` + : matcher + }` +} + const queryAllByTextWithSuggestions = wrapAllByQueryWithSuggestion< // @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment [text: Matcher, options?: MatcherOptions] diff --git a/src/queries/title.ts b/src/queries/title.ts index 7366855f..5660fdb9 100644 --- a/src/queries/title.ts +++ b/src/queries/title.ts @@ -6,6 +6,7 @@ import { Matcher, MatcherOptions, } from '../../types' +import {getMatcherHint} from '../hints-helpers' import { fuzzyMatches, matches, @@ -36,10 +37,38 @@ const queryAllByTitle: AllByBoundAttribute = ( ) } -const getMultipleError: GetErrorFunction<[unknown]> = (c, title) => - `Found multiple elements with the title: ${title}.` -const getMissingError: GetErrorFunction<[unknown]> = (c, title) => - `Unable to find an element with the title: ${title}.` +const getMultipleError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + title, + options = {}, +) => `Found multiple elements ${getMatcherHintOrDefault(title, options)}.` +const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( + c, + title, + options = {}, +) => `Unable to find an element ${getMatcherHintOrDefault(title, options)}.` + +function getMatcherHintOrDefault( + matcher: Matcher, + options: MatcherOptions = {}, +) { + const matcherHint = getMatcherHint(matcher, 'that its title') + + if (matcherHint) { + return matcherHint + } + + const {normalizer} = options + const matchNormalizer = makeNormalizer({normalizer}) + const normalizedText = matchNormalizer(matcher.toString()) + const isNormalizedDifferent = normalizedText !== matcher.toString() + + return `with the title: ${ + isNormalizedDifferent + ? `${normalizedText} (normalized from '${matcher}')` + : matcher + }` +} const queryAllByTitleWithSuggestions = wrapAllByQueryWithSuggestion< // @ts-expect-error -- See `wrapAllByQueryWithSuggestion` Argument constraint comment diff --git a/types/matches.d.ts b/types/matches.d.ts index 13fa3692..6bdbb4cc 100644 --- a/types/matches.d.ts +++ b/types/matches.d.ts @@ -1,9 +1,12 @@ import {ARIARole} from 'aria-query' -export type MatcherFunction = ( +export type MatcherFunction = (( content: string, element: Element | null, -) => boolean +) => boolean) & { + // Allowing the user to specify the matcher explanation + customMatcherText?: string +} export type Matcher = MatcherFunction | RegExp | number | string // Get autocomplete for ARIARole union types, while still supporting another string From 1868ec78274d0f7ff7f8a757f257bcbbb26e45c8 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 27 Oct 2022 01:50:03 +0300 Subject: [PATCH 2/6] remove unused code --- src/__tests__/element-queries.js | 2 +- src/hints-helpers.ts | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 590cd245..1dd152a9 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -28,7 +28,7 @@ test('query can return null', () => { expect(queryByAltText('LucyRicardo')).toBeNull() }) -test('get throws a useful error message - not found', () => { +test('get throws a useful error message', () => { const { getByLabelText, getByDisplayValue, diff --git a/src/hints-helpers.ts b/src/hints-helpers.ts index fc320fb7..9263dde0 100644 --- a/src/hints-helpers.ts +++ b/src/hints-helpers.ts @@ -2,9 +2,7 @@ import {Matcher} from '../types' function getMatcherHint(matcher: Matcher, prefixForRegex: string) { if (matcher instanceof RegExp) { - return `${ - prefixForRegex ? `${prefixForRegex}` : 'that' - } match the regex: ${matcher}` + return `${prefixForRegex} match the regex: ${matcher}` } if (typeof matcher === 'function') { @@ -19,9 +17,7 @@ function getMatcherHint(matcher: Matcher, prefixForRegex: string) { customMatcherText = matcher.name || '[anonymous function]' } - return `that match the custom matcher${ - customMatcherText ? `: ${customMatcherText}` : '' - }` + return `that match the custom matcher: ${customMatcherText}` } } From cd3a686ef2f88903f997a965cf9f985f47096b1e Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Thu, 27 Oct 2022 01:52:03 +0300 Subject: [PATCH 3/6] clean code --- src/hints-helpers.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/hints-helpers.ts b/src/hints-helpers.ts index 9263dde0..7660ea26 100644 --- a/src/hints-helpers.ts +++ b/src/hints-helpers.ts @@ -6,15 +6,13 @@ function getMatcherHint(matcher: Matcher, prefixForRegex: string) { } if (typeof matcher === 'function') { - let customMatcherText: string = '' + let customMatcherText: string = matcher.name || '[anonymous function]' if ( matcher.customMatcherText && typeof matcher.customMatcherText === 'string' ) { customMatcherText = matcher.customMatcherText - } else { - customMatcherText = matcher.name || '[anonymous function]' } return `that match the custom matcher: ${customMatcherText}` From b05fc696d4c36901a50ddbdf9068838d4b74628e Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Sat, 29 Oct 2022 23:57:53 +0300 Subject: [PATCH 4/6] clean error message tests --- src/__tests__/element-queries.js | 250 +++++++++++++++++++++++++------ 1 file changed, 206 insertions(+), 44 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 1dd152a9..11c4b777 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -28,21 +28,20 @@ test('query can return null', () => { expect(queryByAltText('LucyRicardo')).toBeNull() }) -test('get throws a useful error message', () => { - const { - getByLabelText, - getByDisplayValue, - getByPlaceholderText, - getByText, - getByTestId, - getByAltText, - getByTitle, - getByRole, - } = render( - `
`, + ) + }) + + test('ByLabelText', () => { + const {getByLabelText} = renderResult + + expect(() => getByLabelText('LucyRicardo')) + .toThrowErrorMatchingInlineSnapshot(` Unable to find a label with the text of: LucyRicardo Ignored nodes: comments, script, style @@ -50,8 +49,13 @@ test('get throws a useful error message', () => {
`) - expect(() => getByPlaceholderText('LucyRicardo')) - .toThrowErrorMatchingInlineSnapshot(` + }) + + test('ByPlaceholderText', () => { + const {getByPlaceholderText} = renderResult + + expect(() => getByPlaceholderText('LucyRicardo')) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the placeholder text of: LucyRicardo Ignored nodes: comments, script, style @@ -59,37 +63,48 @@ test('get throws a useful error message', () => {
`) - 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. + + expect(() => getByPlaceholderText(/LucyRicardo/)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that its placeholder text match the regex: /LucyRicardo/ Ignored nodes: comments, script, style
`) - expect(() => getByText('Lucy Ricardo')) - .toThrowErrorMatchingInlineSnapshot(` - Unable to find an element with the text: Lucy Ricardo (normalized from 'Lucy Ricardo'). 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(() => getByPlaceholderText(() => false)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function] Ignored nodes: comments, script, style
`) - expect(() => - getByText(function LucyRicardo() { + + function something() { return false - }), - ).toThrowErrorMatchingInlineSnapshot(` - Unable to find an element that match the custom matcher: 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. + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByPlaceholderText(something)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo Ignored nodes: comments, script, style
`) - expect(() => getByText(() => false)).toThrowErrorMatchingInlineSnapshot(` - Unable to find an element that match the custom matcher: [anonymous function]. 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. + }) + + test('ByText', () => { + const {getByText} = renderResult + + 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. Ignored nodes: comments, script, style
@@ -97,29 +112,54 @@ test('get throws a useful error message', () => {
`) - function something() { - return false - } - something.customMatcherText = 'Lucy and Ricardo' + expect(() => getByText('LucyRicardo', {selector: 'span'})) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element with the text: LucyRicardo, which matches selector 'span'. 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(() => getByText(something)).toThrowErrorMatchingInlineSnapshot(` - Unable to find an element that match the custom matcher: Lucy and Ricardo. 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. + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByText(/LucyRicardo/)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that its text match the regex: /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. Ignored nodes: comments, script, style
`) - expect(() => getByText('LucyRicardo', {selector: 'span'})) - .toThrowErrorMatchingInlineSnapshot(` - Unable to find an element with the text: LucyRicardo, which matches selector 'span'. 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(() => getByText(() => false)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function]. 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. Ignored nodes: comments, script, style
`) - expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` + + function something() { + return false + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByText(something)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo. 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. + + Ignored nodes: comments, script, style +
+
+
+ `) + }) + + test('ByTestId', () => { + const {getByTestId} = renderResult + + expect(() => getByTestId('LucyRicardo')) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element by: [data-testid="LucyRicardo"] Ignored nodes: comments, script, style @@ -127,7 +167,13 @@ test('get throws a useful error message', () => {
`) - expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` + }) + + test('ByAltText', () => { + const {getByAltText} = renderResult + + expect(() => getByAltText('LucyRicardo')) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the alt text: LucyRicardo Ignored nodes: comments, script, style @@ -135,7 +181,45 @@ test('get throws a useful error message', () => {
`) - expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` + + expect(() => getByAltText(/LucyRicardo/)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that its alt text match the regex: /LucyRicardo/ + + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByAltText(() => false)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function] + + Ignored nodes: comments, script, style +
+
+
+ `) + + function something() { + return false + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByAltText(something)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo + + Ignored nodes: comments, script, style +
+
+
+ `) + }) + + test('ByTitle', () => { + const {getByTitle} = renderResult + + expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the title: LucyRicardo. Ignored nodes: comments, script, style @@ -143,8 +227,45 @@ test('get throws a useful error message', () => {
`) - expect(() => getByDisplayValue('LucyRicardo')) - .toThrowErrorMatchingInlineSnapshot(` + + expect(() => getByTitle(/LucyRicardo/)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that its title match the regex: /LucyRicardo/. + + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByTitle(() => false)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function]. + + Ignored nodes: comments, script, style +
+
+
+ `) + + function something() { + return false + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByTitle(something)).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo. + + Ignored nodes: comments, script, style +
+
+
+ `) + }) + + test('ByDisplayValue', () => { + const {getByDisplayValue} = renderResult + + expect(() => getByDisplayValue('LucyRicardo')) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the display value: LucyRicardo. Ignored nodes: comments, script, style @@ -152,7 +273,47 @@ test('get throws a useful error message', () => {
`) - expect(() => getByRole('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` + + expect(() => getByDisplayValue(/LucyRicardo/)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that its display value match the regex: /LucyRicardo/. + + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByDisplayValue(() => false)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: [anonymous function]. + + Ignored nodes: comments, script, style +
+
+
+ `) + + function something() { + return false + } + something.customMatcherText = 'Lucy and Ricardo' + + expect(() => getByDisplayValue(something)) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element that match the custom matcher: Lucy and Ricardo. + + Ignored nodes: comments, script, style +
+
+
+ `) + }) + + test('ByRole', () => { + const {getByRole} = renderResult + + expect(() => getByRole('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` 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 @@ -162,6 +323,7 @@ test('get throws a useful error message', () => {
`) + }) }) test('can get elements by matching their text content', () => { From e4fbcb760a7a1d5fea0c38b3358318126ae8f74d Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Sun, 30 Oct 2022 01:07:39 +0300 Subject: [PATCH 5/6] added tests --- .gitignore | 2 + src/__tests__/element-queries.js | 157 ++++++++++++++++++++++++++----- src/queries/display-value.ts | 2 +- src/queries/placeholder-text.ts | 2 +- src/queries/text.ts | 2 +- src/queries/title.ts | 2 +- 6 files changed, 141 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 8e0c70cb..775d03c7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ dist # when working with contributors package-lock.json yarn.lock + +.idea diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 11c4b777..66788486 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -29,16 +29,16 @@ test('query can return null', () => { }) describe('get throws a useful error message', () => { - let renderResult + let missingRenderResult; beforeEach(() => { - renderResult = render( + missingRenderResult = render( `
`, + ); + + expect(() => getByText('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(`Found multiple elements with the text: LucyRicardo + +Here are the matching elements: + +Ignored nodes: comments, script, style + + LucyRicardo + + +Ignored nodes: comments, script, style + + LucyRicardo + + +(If this is intentional, then use the \`*AllBy*\` variant of the query (like \`queryAllByText\`, \`getAllByText\`, or \`findAllByText\`)). + +Ignored nodes: comments, script, style +
+
+ + LucyRicardo + + + LucyRicardo + +
+
`); + + expect(() => getByText('LucyRicardo', {selector: 'span'})) + .toThrowErrorMatchingInlineSnapshot(`Found multiple elements with the text: LucyRicardo, which matches selector 'span' + +Here are the matching elements: + +Ignored nodes: comments, script, style + + LucyRicardo + + +Ignored nodes: comments, script, style + + LucyRicardo + + +(If this is intentional, then use the \`*AllBy*\` variant of the query (like \`queryAllByText\`, \`getAllByText\`, or \`findAllByText\`)). + +Ignored nodes: comments, script, style +
+
+ + LucyRicardo + + + LucyRicardo + +
+
`) + }); + }); test('ByTestId', () => { - const {getByTestId} = renderResult + const {getByTestId} = missingRenderResult expect(() => getByTestId('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` @@ -170,12 +254,22 @@ describe('get throws a useful error message', () => { }) test('ByAltText', () => { - const {getByAltText} = renderResult + const {getByAltText} = missingRenderResult expect(() => getByAltText('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the alt text: LucyRicardo + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByAltText(' LucyRicardo ')) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element with the alt text: LucyRicardo (normalized from ' LucyRicardo ') + Ignored nodes: comments, script, style
@@ -217,11 +311,20 @@ describe('get throws a useful error message', () => { }) test('ByTitle', () => { - const {getByTitle} = renderResult + const {getByTitle} = missingRenderResult expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the title: LucyRicardo. + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByTitle(' LucyRicardo ')).toThrowErrorMatchingInlineSnapshot(` + Unable to find an element with the title: LucyRicardo (normalized from ' LucyRicardo '). + Ignored nodes: comments, script, style
@@ -262,12 +365,22 @@ describe('get throws a useful error message', () => { }) test('ByDisplayValue', () => { - const {getByDisplayValue} = renderResult + const {getByDisplayValue} = missingRenderResult expect(() => getByDisplayValue('LucyRicardo')) .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the display value: LucyRicardo. + Ignored nodes: comments, script, style +
+
+
+ `) + + expect(() => getByDisplayValue(' LucyRicardo ')) + .toThrowErrorMatchingInlineSnapshot(` + Unable to find an element with the display value: LucyRicardo (normalized from ' LucyRicardo '). + Ignored nodes: comments, script, style
@@ -311,7 +424,7 @@ describe('get throws a useful error message', () => { }) test('ByRole', () => { - const {getByRole} = renderResult + const {getByRole} = missingRenderResult expect(() => getByRole('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` Unable to find an accessible element with the role "LucyRicardo" diff --git a/src/queries/display-value.ts b/src/queries/display-value.ts index 4998a42e..75902e71 100644 --- a/src/queries/display-value.ts +++ b/src/queries/display-value.ts @@ -57,7 +57,7 @@ const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( function getMatcherHintOrDefault( matcher: Matcher, - options: MatcherOptions = {}, + options: MatcherOptions, ) { const matcherHint = getMatcherHint(matcher, 'that its display value') diff --git a/src/queries/placeholder-text.ts b/src/queries/placeholder-text.ts index 089556c9..af0af50e 100644 --- a/src/queries/placeholder-text.ts +++ b/src/queries/placeholder-text.ts @@ -26,7 +26,7 @@ const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( function getMatcherHintOrDefault( matcher: Matcher, - options: MatcherOptions = {}, + options: MatcherOptions, ) { const matcherHint = getMatcherHint(matcher, 'that its placeholder text') diff --git a/src/queries/text.ts b/src/queries/text.ts index a3788308..11e179cd 100644 --- a/src/queries/text.ts +++ b/src/queries/text.ts @@ -73,7 +73,7 @@ const getMissingError: GetErrorFunction<[Matcher, SelectorMatcherOptions]> = ( function getMatcherHintOrDefault( matcher: Matcher, - options: SelectorMatcherOptions = {}, + options: SelectorMatcherOptions, ) { const matcherHint = getMatcherHint(matcher, 'that its text') diff --git a/src/queries/title.ts b/src/queries/title.ts index 5660fdb9..b8c87681 100644 --- a/src/queries/title.ts +++ b/src/queries/title.ts @@ -50,7 +50,7 @@ const getMissingError: GetErrorFunction<[Matcher, MatcherOptions]> = ( function getMatcherHintOrDefault( matcher: Matcher, - options: MatcherOptions = {}, + options: MatcherOptions, ) { const matcherHint = getMatcherHint(matcher, 'that its title') From 56dd0e4eabcf3b484534f479cd6fb689891291b4 Mon Sep 17 00:00:00 2001 From: Raz Luvaton <16746759+rluvaton@users.noreply.github.com> Date: Sun, 30 Oct 2022 01:10:07 +0300 Subject: [PATCH 6/6] format --- src/__tests__/element-queries.js | 27 ++++++++++++++++----------- src/queries/display-value.ts | 5 +---- src/queries/placeholder-text.ts | 5 +---- src/queries/title.ts | 5 +---- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/__tests__/element-queries.js b/src/__tests__/element-queries.js index 66788486..ba282664 100644 --- a/src/__tests__/element-queries.js +++ b/src/__tests__/element-queries.js @@ -29,7 +29,7 @@ test('query can return null', () => { }) describe('get throws a useful error message', () => { - let missingRenderResult; + let missingRenderResult beforeEach(() => { missingRenderResult = render( @@ -114,7 +114,8 @@ describe('get throws a useful error message', () => { test('Missing element', () => { const {getByText} = missingRenderResult - expect(() => getByText('LucyRicardo')).toThrowErrorMatchingInlineSnapshot(` + 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. Ignored nodes: comments, script, style @@ -133,7 +134,8 @@ describe('get throws a useful error message', () => {
`) - expect(() => getByText(' LucyRicardo ', {})).toThrowErrorMatchingInlineSnapshot(` + expect(() => getByText(' LucyRicardo ', {})) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element with the text: LucyRicardo (normalized from ' 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. Ignored nodes: comments, script, style @@ -142,7 +144,8 @@ describe('get throws a useful error message', () => {
`) - expect(() => getByText(/LucyRicardo/)).toThrowErrorMatchingInlineSnapshot(` + expect(() => getByText(/LucyRicardo/)) + .toThrowErrorMatchingInlineSnapshot(` Unable to find an element that its text match the regex: /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. Ignored nodes: comments, script, style @@ -173,14 +176,15 @@ describe('get throws a useful error message', () => {
`) - }); + }) test('Multiple elements', () => { const {getByText} = render( `
LucyRicardoLucyRicardo