Skip to content

chore(refactor): move queries to TS / prettify some code according ABC #948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function runWithExpensiveErrorDiagnosticsDisabled<T>(
}
}

export function configure(newConfig: Partial<Config> | ConfigFn) {
export function configure(newConfig: ConfigFn | Partial<Config>) {
if (typeof newConfig === 'function') {
// Pass the existing config out to the provided function
// and accept a delta in return
Expand Down
4 changes: 2 additions & 2 deletions src/get-node-text.js → src/get-node-text.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {TEXT_NODE} from './helpers'

function getNodeText(node) {
function getNodeText(node: HTMLElement): string {
if (node.matches('input[type=submit], input[type=button]')) {
return node.value
return (node as HTMLInputElement).value
}

return Array.from(node.childNodes)
Expand Down
13 changes: 8 additions & 5 deletions src/label-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Nullish} from '../types'
import {TEXT_NODE} from './helpers'

const labelledNodeNames = [
Expand All @@ -11,7 +12,7 @@ const labelledNodeNames = [
]

function getTextContent(
node: Node | Element | HTMLInputElement,
node: Element | HTMLInputElement | Node,
): string | null {
if (labelledNodeNames.includes(node.nodeName.toLowerCase())) {
return ''
Expand All @@ -24,7 +25,7 @@ function getTextContent(
.join('')
}

function getLabelContent(element: Element): string | null {
function getLabelContent(element: Element): Nullish<string> {
let textContent: string | null
if (element.tagName.toLowerCase() === 'label') {
textContent = getTextContent(element)
Expand Down Expand Up @@ -58,12 +59,14 @@ function getLabels(
container: Element,
element: Element,
{selector = '*'} = {},
) {
): {content: Nullish<string>; formControl: Nullish<HTMLElement>}[] {
const ariaLabelledBy = element.getAttribute('aria-labelledby')
const labelsId = ariaLabelledBy ? ariaLabelledBy.split(' ') : []
return labelsId.length
? labelsId.map(labelId => {
const labellingElement = container.querySelector(`[id="${labelId}"]`)
const labellingElement = container.querySelector<HTMLElement>(
`[id="${labelId}"]`,
)
return labellingElement
? {content: getLabelContent(labellingElement), formControl: null}
: {content: '', formControl: null}
Expand All @@ -73,7 +76,7 @@ function getLabels(
const formControlSelector =
'button, input, meter, output, progress, select, textarea'
const labelledFormControl = Array.from(
label.querySelectorAll(formControlSelector),
label.querySelectorAll<HTMLElement>(formControlSelector),
).filter(formControlElement => formControlElement.matches(selector))[0]
return {content: textToMatch, formControl: labelledFormControl}
})
Expand Down
File renamed without changes.
13 changes: 8 additions & 5 deletions src/queries/alt-text.js → src/queries/alt-text.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {checkContainerType} from '../helpers'
import {AllByBoundAttribute, GetErrorFunction} from '../../types'
import {matches, fuzzyMatches, makeNormalizer, buildQueries} from './all-utils'

function queryAllByAltText(
const queryAllByAltText: AllByBoundAttribute = (
container,
alt,
{exact = true, collapseWhitespace, trim, normalizer} = {},
) {
) => {
checkContainerType(container)
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
return Array.from(container.querySelectorAll('img,input,area')).filter(node =>
return Array.from(
container.querySelectorAll<HTMLElement>('img,input,area'),
).filter(node =>
matcher(node.getAttribute('alt'), node, alt, matchNormalizer),
)
}

const getMultipleError = (c, alt) =>
const getMultipleError: GetErrorFunction = (c, alt) =>
`Found multiple elements with the alt text: ${alt}`
const getMissingError = (c, alt) =>
const getMissingError: GetErrorFunction = (c, alt) =>
`Unable to find an element with the alt text: ${alt}`

const queryAllByAltTextWithSuggestions = wrapAllByQueryWithSuggestion(
Expand Down
42 changes: 24 additions & 18 deletions src/queries/display-value.js → src/queries/display-value.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {checkContainerType} from '../helpers'
import {AllByBoundAttribute, GetErrorFunction} from '../../types'
import {
getNodeText,
matches,
Expand All @@ -8,33 +9,38 @@ import {
buildQueries,
} from './all-utils'

function queryAllByDisplayValue(
const queryAllByDisplayValue: AllByBoundAttribute = (
container,
value,
{exact = true, collapseWhitespace, trim, normalizer} = {},
) {
) => {
checkContainerType(container)
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
return Array.from(container.querySelectorAll(`input,textarea,select`)).filter(
node => {
if (node.tagName === 'SELECT') {
const selectedOptions = Array.from(node.options).filter(
option => option.selected,
)
return selectedOptions.some(optionNode =>
matcher(getNodeText(optionNode), optionNode, value, matchNormalizer),
)
} else {
return matcher(node.value, node, value, matchNormalizer)
}
},
)
return Array.from(
container.querySelectorAll<HTMLElement>(`input,textarea,select`),
).filter(node => {
if (node.tagName === 'SELECT') {
const selectedOptions = Array.from(
(node as HTMLSelectElement).options,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

', ' in the end seems redandant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's their eslint standard because saw it in other places, also it passed my eslint precommit

).filter(option => option.selected)
return selectedOptions.some(optionNode =>
matcher(getNodeText(optionNode), optionNode, value, matchNormalizer),
)
} else {
return matcher(
(node as HTMLInputElement).value,
node,
value,
matchNormalizer,
)
}
})
}

const getMultipleError = (c, value) =>
const getMultipleError: GetErrorFunction = (c, value) =>
`Found multiple elements with the display value: ${value}.`
const getMissingError = (c, value) =>
const getMissingError: GetErrorFunction = (c, value) =>
`Unable to find an element with the display value: ${value}.`

const queryAllByDisplayValueWithSuggestions = wrapAllByQueryWithSuggestion(
Expand Down
File renamed without changes.
38 changes: 26 additions & 12 deletions src/queries/label-text.js → src/queries/label-text.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {getConfig} from '../config'
import {checkContainerType} from '../helpers'
import {getLabels, getRealLabels, getLabelContent} from '../label-helpers'
import {AllByText, GetErrorFunction, Nullish} from '../../types'
import {
fuzzyMatches,
matches,
Expand All @@ -12,19 +13,21 @@ import {
wrapSingleQueryWithSuggestion,
} from './all-utils'

function queryAllLabels(container) {
return Array.from(container.querySelectorAll('label,input'))
function queryAllLabels(
container: HTMLElement,
): {textToMatch: Nullish<string>; node: HTMLElement}[] {
return Array.from(container.querySelectorAll<HTMLElement>('label,input'))
.map(node => {
return {node, textToMatch: getLabelContent(node)}
})
.filter(({textToMatch}) => textToMatch !== null)
}

function queryAllLabelsByText(
const queryAllLabelsByText: AllByText = (
container,
text,
{exact = true, trim, collapseWhitespace, normalizer} = {},
) {
) => {
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})

Expand All @@ -37,27 +40,32 @@ function queryAllLabelsByText(
.map(({node}) => node)
}

function queryAllByLabelText(
const queryAllByLabelText: AllByText = (
container,
text,
{selector = '*', exact = true, collapseWhitespace, trim, normalizer} = {},
) {
) => {
checkContainerType(container)

const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
const matchingLabelledElements = Array.from(container.querySelectorAll('*'))
const matchingLabelledElements = Array.from(
container.querySelectorAll<HTMLElement>('*'),
)
.filter(element => {
return (
getRealLabels(element).length || element.hasAttribute('aria-labelledby')
)
})
.reduce((labelledElements, labelledElement) => {
.reduce<HTMLElement[]>((labelledElements, labelledElement) => {
const labelList = getLabels(container, labelledElement, {selector})
labelList
.filter(label => Boolean(label.formControl))
.forEach(label => {
if (matcher(label.content, label.formControl, text, matchNormalizer))
if (
matcher(label.content, label.formControl, text, matchNormalizer) &&
label.formControl
)
labelledElements.push(label.formControl)
})
const labelsValue = labelList
Expand Down Expand Up @@ -92,6 +100,9 @@ function queryAllByLabelText(
return labelledElements
}, [])
.concat(
// TODO: Remove ignore after `queryAllByAttribute` will be moved to TS
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
queryAllByAttribute('aria-label', container, text, {
exact,
normalizer: matchNormalizer,
Expand All @@ -110,7 +121,7 @@ function queryAllByLabelText(
// )
// however, we can give a more helpful error message than the generic one,
// so we're writing this one out by hand.
const getAllByLabelText = (container, text, ...rest) => {
const getAllByLabelText: AllByText = (container, text, ...rest) => {
const els = queryAllByLabelText(container, text, ...rest)
if (!els.length) {
const labels = queryAllLabelsByText(container, text, ...rest)
Expand Down Expand Up @@ -146,7 +157,10 @@ const getAllByLabelText = (container, text, ...rest) => {
return els
}

function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
function getTagNameOfElementAssociatedWithLabelViaFor(
container: Element,
label: Element,
): Nullish<string> {
const htmlFor = label.getAttribute('for')
if (!htmlFor) {
return null
Expand All @@ -157,7 +171,7 @@ function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
}

// the reason mentioned above is the same reason we're not using buildQueries
const getMultipleError = (c, text) =>
const getMultipleError: GetErrorFunction = (c, text) =>
`Found multiple elements with the text of: ${text}`
const queryByLabelText = wrapSingleQueryWithSuggestion(
makeSingleQuery(queryAllByLabelText, getMultipleError),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {checkContainerType} from '../helpers'
import {AllByBoundAttribute, GetErrorFunction} from '../../types'
import {queryAllByAttribute, buildQueries} from './all-utils'

function queryAllByPlaceholderText(...args) {
checkContainerType(...args)
const queryAllByPlaceholderText: AllByBoundAttribute = (...args) => {
checkContainerType(args[0])
// TODO: Remove ignore after `queryAllByAttribute` will be moved to TS
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return queryAllByAttribute('placeholder', ...args)
}
const getMultipleError = (c, text) =>
const getMultipleError: GetErrorFunction = (c, text) =>
`Found multiple elements with the placeholder text of: ${text}`
const getMissingError = (c, text) =>
const getMissingError: GetErrorFunction = (c, text) =>
`Unable to find an element with the placeholder text of: ${text}`

const queryAllByPlaceholderTextWithSuggestions = wrapAllByQueryWithSuggestion(
Expand Down
12 changes: 8 additions & 4 deletions src/queries/test-id.js → src/queries/test-id.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import {checkContainerType} from '../helpers'
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {AllByBoundAttribute, GetErrorFunction} from '../../types'
import {queryAllByAttribute, getConfig, buildQueries} from './all-utils'

const getTestIdAttribute = () => getConfig().testIdAttribute

function queryAllByTestId(...args) {
checkContainerType(...args)
const queryAllByTestId: AllByBoundAttribute = (...args) => {
checkContainerType(args[0])
// TODO: Remove ignore after `queryAllByAttribute` will be moved to TS
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return queryAllByAttribute(getTestIdAttribute(), ...args)
}

const getMultipleError = (c, id) =>
const getMultipleError: GetErrorFunction = (c, id) =>
`Found multiple elements by: [${getTestIdAttribute()}="${id}"]`
const getMissingError = (c, id) =>
const getMissingError: GetErrorFunction = (c, id) =>
`Unable to find an element by: [${getTestIdAttribute()}="${id}"]`

const queryAllByTestIdWithSuggestions = wrapAllByQueryWithSuggestion(
Expand Down
23 changes: 15 additions & 8 deletions src/queries/text.js → src/queries/text.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {checkContainerType} from '../helpers'
import {DEFAULT_IGNORE_TAGS} from '../config'
import {AllByText, GetErrorFunction} from '../../types'
import {
fuzzyMatches,
matches,
Expand All @@ -9,7 +10,7 @@ import {
buildQueries,
} from './all-utils'

function queryAllByText(
const queryAllByText: AllByText = (
container,
text,
{
Expand All @@ -20,22 +21,28 @@ function queryAllByText(
ignore = DEFAULT_IGNORE_TAGS,
normalizer,
} = {},
) {
) => {
checkContainerType(container)
const matcher = exact ? matches : fuzzyMatches
const matchNormalizer = makeNormalizer({collapseWhitespace, trim, normalizer})
let baseArray = []
let baseArray: HTMLElement[] = []
if (typeof container.matches === 'function' && container.matches(selector)) {
baseArray = [container]
}
return [...baseArray, ...Array.from(container.querySelectorAll(selector))]
.filter(node => !ignore || !node.matches(ignore))
.filter(node => matcher(getNodeText(node), node, text, matchNormalizer))
return (
[
...baseArray,
...Array.from(container.querySelectorAll<HTMLElement>(selector)),
]
// TODO: `matches` according lib.dom.d.ts can get only `string` but according our code it can handle also boolean :)
.filter(node => !ignore || !node.matches(ignore as string))
.filter(node => matcher(getNodeText(node), node, text, matchNormalizer))
)
}

const getMultipleError = (c, text) =>
const getMultipleError: GetErrorFunction = (c, text) =>
`Found multiple elements with the text: ${text}`
const getMissingError = (c, text) =>
const getMissingError: GetErrorFunction = (c, text) =>
`Unable to find an element with the text: ${text}. 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.`

const queryAllByTextWithSuggestions = wrapAllByQueryWithSuggestion(
Expand Down
Loading