Skip to content

feat: migrate some files to typescript #848

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
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"node": ">=10"
},
"scripts": {
"build": "kcd-scripts build --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --bundle --no-clean",
"build": "kcd-scripts build --no-ts-defs --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --no-ts-defs --bundle --no-clean",
"lint": "kcd-scripts lint",
"setup": "npm install && npm run validate -s",
"test": "kcd-scripts test",
Expand Down Expand Up @@ -53,7 +53,7 @@
"jest-serializer-ansi": "^1.0.3",
"jest-watch-select-projects": "^2.0.0",
"jsdom": "^16.4.0",
"kcd-scripts": "^7.5.1",
"kcd-scripts": "^7.5.3",
"typescript": "^4.1.2"
},
"eslintConfig": {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {configure, getConfig} from '../config'
import {configure, getConfig} from '../config.ts'
Copy link
Member

Choose a reason for hiding this comment

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

Interesting. Is the extension required? I didn't think it was and would prefer to not have to specify it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't like too, but I didn't find a way to not specify it. eslint gave me some trouble.

Copy link
Member

Choose a reason for hiding this comment

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

We can deal with that later. Not a ship stopper.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll leave the point open, maybe someone could help


describe('configuration API', () => {
let originalConfig
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/element-queries.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {configure} from '../config'
import {configure} from '../config.ts'
import {render, renderIntoDocument} from './helpers/test-utils'

beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/get-by-errors.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cases from 'jest-in-case'
import {screen} from '../'
import {configure, getConfig} from '../config'
import {configure, getConfig} from '../config.ts'
import {render} from './helpers/test-utils'

const originalConfig = getConfig()
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/label-helpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {getRealLabels} from '../label-helpers'
import {getRealLabels} from '../label-helpers.ts'

test('hidden inputs are not labelable', () => {
const element = document.createElement('input')
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/matches.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {fuzzyMatches, matches} from '../matches'
import {fuzzyMatches, matches} from '../matches.ts'

// unit tests for text match utils

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/query-helper.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as queryHelpers from '../query-helpers'
import {configure, getConfig} from '../config'
import {configure, getConfig} from '../config.ts'

const originalConfig = getConfig()
beforeEach(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/role.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {configure, getConfig} from '../config'
import {configure, getConfig} from '../config.ts'
import {getQueriesForElement} from '../get-queries-for-element'
import {render, renderIntoDocument} from './helpers/test-utils'

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/suggestions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {configure} from '../config'
import {configure} from '../config.ts'
import {screen, getSuggestedQuery} from '..'
import {renderIntoDocument, render} from './helpers/test-utils'

Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/wait-for.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {waitFor} from '../'
import {configure, getConfig} from '../config'
import {configure, getConfig} from '../config.ts'
import {renderIntoDocument} from './helpers/test-utils'

function deferred() {
Expand Down
14 changes: 11 additions & 3 deletions src/config.js → src/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import {Config, ConfigFn} from '../types/config'
import {prettyDOM} from './pretty-dom'

type Callback<T> = () => T
interface InternalConfig extends Config {
_disableExpensiveErrorDiagnostics: boolean
}

// It would be cleaner for this to live inside './queries', but
// other parts of the code assume that all exports from
// './queries' are query functions.
let config = {
let config: InternalConfig = {
testIdAttribute: 'data-testid',
asyncUtilTimeout: 1000,
// this is to support React's async `act` function.
Expand Down Expand Up @@ -36,7 +42,9 @@ let config = {
}

export const DEFAULT_IGNORE_TAGS = 'script, style'
export function runWithExpensiveErrorDiagnosticsDisabled(callback) {
export function runWithExpensiveErrorDiagnosticsDisabled<T>(
callback: Callback<T>,
) {
try {
config._disableExpensiveErrorDiagnostics = true
return callback()
Expand All @@ -45,7 +53,7 @@ export function runWithExpensiveErrorDiagnosticsDisabled(callback) {
}
}

export function configure(newConfig) {
export function configure(newConfig: Partial<Config> | ConfigFn) {
if (typeof newConfig === 'function') {
// Pass the existing config out to the provided function
// and accept a delta in return
Expand Down
17 changes: 10 additions & 7 deletions src/events.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {getConfig} from './config'
import {getConfig} from './config.ts'
import {getWindowFromNode} from './helpers'
import {eventMap, eventAliasMap} from './event-map'

Expand Down Expand Up @@ -71,12 +71,15 @@ function createEvent(
/* istanbul ignore if */
if (typeof window.DataTransfer === 'function') {
Object.defineProperty(event, dataTransferKey, {
value: Object
.getOwnPropertyNames(dataTransferValue)
.reduce((acc, propName) => {
Object.defineProperty(acc, propName, {value: dataTransferValue[propName]});
return acc;
}, new window.DataTransfer())
value: Object.getOwnPropertyNames(dataTransferValue).reduce(
(acc, propName) => {
Object.defineProperty(acc, propName, {
value: dataTransferValue[propName],
})
return acc
},
new window.DataTransfer(),
),
})
} else {
Object.defineProperty(event, dataTransferKey, {
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ 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 {getDefaultNormalizer} from './matches.ts'
export * from './get-node-text'
export * from './events'
export * from './get-queries-for-element'
export * from './screen'
export * from './query-helpers'
export {getRoles, logRoles, isInaccessible} from './role-helpers'
export * from './pretty-dom'
export {configure, getConfig} from './config'
export {configure, getConfig} from './config.ts'
export * from './suggestions'

export {
Expand Down
37 changes: 23 additions & 14 deletions src/label-helpers.js → src/label-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const labelledNodeNames = [
'input',
]

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

function getLabelContent(node) {
function getLabelContent(element: Element | HTMLInputElement) {
let textContent
if (node.tagName.toLowerCase() === 'label') {
textContent = getTextContent(node)
if (element.tagName.toLowerCase() === 'label') {
textContent = getTextContent(element)
} else if ('value' in element) {
return element.value
} else {
textContent = node.value || node.textContent
textContent = element.textContent
}
return textContent
}

// Based on https://github.com/eps1lon/dom-accessibility-api/pull/352
function getRealLabels(element) {
if (element.labels !== undefined) return element.labels ?? []
function getRealLabels(element: Element | HTMLInputElement) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if ('labels' in element && element.labels !== undefined)
return element.labels ?? []

if (!isLabelable(element)) return []

const labels = element.ownerDocument.querySelectorAll('label')
return Array.from(labels).filter(label => label.control === element)
}

function isLabelable(element) {
function isLabelable(element: Element) {
const labelableRegex = /BUTTON|METER|OUTPUT|PROGRESS|SELECT|TEXTAREA/
return (
element.tagName.match(/BUTTON|METER|OUTPUT|PROGRESS|SELECT|TEXTAREA/) ||
labelableRegex.test(element.tagName) ||
(element.tagName === 'INPUT' && element.getAttribute('type') !== 'hidden')
)
}

function getLabels(container, element, {selector = '*'} = {}) {
const labelsId = element.getAttribute('aria-labelledby')
? element.getAttribute('aria-labelledby').split(' ')
: []
function getLabels(
container: Element,
element: Element,
{selector = '*'} = {},
) {
const ariaLabelledBy = element.getAttribute('aria-labelledby')
const labelsId = ariaLabelledBy ? ariaLabelledBy.split(' ') : []
return labelsId.length
? labelsId.map(labelId => {
const labellingElement = container.querySelector(`[id="${labelId}"]`)
Expand All @@ -67,7 +77,6 @@ function getLabels(container, element, {selector = '*'} = {}) {
const labelledFormControl = Array.from(
label.querySelectorAll(formControlSelector),
).filter(formControlElement => formControlElement.matches(selector))[0]

return {content: textToMatch, formControl: labelledFormControl}
})
}
Expand Down
44 changes: 37 additions & 7 deletions src/matches.js → src/matches.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
function assertNotNullOrUndefined(matcher) {
if (matcher == null) {
import {
Matcher,
NormalizerFn,
NormalizerOptions,
DefaultNormalizerOptions,
} from '../types'

type Nullish<T> = T | null | undefined

function assertNotNullOrUndefined<T>(
matcher: T,
): asserts matcher is NonNullable<T> {
if (matcher === null || matcher === undefined) {
throw new Error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`It looks like ${matcher} was passed instead of a matcher. Did you do something like getByText(${matcher})?`,
)
}
}

function fuzzyMatches(textToMatch, node, matcher, normalizer) {
function fuzzyMatches(
textToMatch: Nullish<string>,
node: Nullish<Element>,
matcher: Nullish<Matcher>,
normalizer: NormalizerFn,
) {
if (typeof textToMatch !== 'string') {
return false
}

assertNotNullOrUndefined(matcher)

const normalizedText = normalizer(textToMatch)

if (typeof matcher === 'string') {
return normalizedText.toLowerCase().includes(matcher.toLowerCase())
} else if (typeof matcher === 'function') {
Expand All @@ -23,7 +40,12 @@ function fuzzyMatches(textToMatch, node, matcher, normalizer) {
}
}

function matches(textToMatch, node, matcher, normalizer) {
function matches(
textToMatch: Nullish<string>,
node: Nullish<Element>,
matcher: Nullish<Matcher>,
normalizer: NormalizerFn,
) {
if (typeof textToMatch !== 'string') {
return false
}
Expand All @@ -40,7 +62,10 @@ function matches(textToMatch, node, matcher, normalizer) {
}
}

function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
function getDefaultNormalizer({
trim = true,
collapseWhitespace = true,
}: DefaultNormalizerOptions = {}): NormalizerFn {
return text => {
let normalizedText = text
normalizedText = trim ? normalizedText.trim() : normalizedText
Expand All @@ -60,7 +85,12 @@ function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
* @param {Function|undefined} normalizer The user-specified normalizer
* @returns {Function} A normalizer
*/
function makeNormalizer({trim, collapseWhitespace, normalizer}) {

function makeNormalizer({
trim,
collapseWhitespace,
normalizer,
}: NormalizerOptions) {
if (normalizer) {
// User has specified a custom normalizer
if (
Expand Down
4 changes: 2 additions & 2 deletions src/queries/all-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from '../matches'
export * from '../matches.ts'
export * from '../get-node-text'
export * from '../query-helpers'
export * from '../config'
export * from '../config.ts'
4 changes: 2 additions & 2 deletions src/queries/label-text.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {getConfig} from '../config'
import {getConfig} from '../config.ts'
import {checkContainerType} from '../helpers'
import {getLabels, getRealLabels, getLabelContent} from '../label-helpers'
import {getLabels, getRealLabels, getLabelContent} from '../label-helpers.ts'
import {
fuzzyMatches,
matches,
Expand Down
2 changes: 1 addition & 1 deletion src/queries/text.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {wrapAllByQueryWithSuggestion} from '../query-helpers'
import {checkContainerType} from '../helpers'
import {DEFAULT_IGNORE_TAGS} from '../config'
import {DEFAULT_IGNORE_TAGS} from '../config.ts'
import {
fuzzyMatches,
matches,
Expand Down
4 changes: 2 additions & 2 deletions src/query-helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {getSuggestedQuery} from './suggestions'
import {fuzzyMatches, matches, makeNormalizer} from './matches'
import {fuzzyMatches, matches, makeNormalizer} from './matches.ts'
import {waitFor} from './wait-for'
import {getConfig} from './config'
import {getConfig} from './config.ts'

function getElementError(message, container) {
return getConfig().getElementError(message, container)
Expand Down
2 changes: 1 addition & 1 deletion src/role-helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {elementRoles} from 'aria-query'
import {computeAccessibleName} from 'dom-accessibility-api'
import {prettyDOM} from './pretty-dom'
import {getConfig} from './config'
import {getConfig} from './config.ts'

const elementRoleList = buildElementRoleList(elementRoles)

Expand Down
6 changes: 3 additions & 3 deletions src/suggestions.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {computeAccessibleName} from 'dom-accessibility-api'
import {getDefaultNormalizer} from './matches'
import {getDefaultNormalizer} from './matches.ts'
import {getNodeText} from './get-node-text'
import {DEFAULT_IGNORE_TAGS, getConfig} from './config'
import {DEFAULT_IGNORE_TAGS, getConfig} from './config.ts'
import {getImplicitAriaRoles, isInaccessible} from './role-helpers'
import {getLabels} from './label-helpers'
import {getLabels} from './label-helpers.ts'

const normalize = getDefaultNormalizer()

Expand Down
2 changes: 1 addition & 1 deletion src/wait-for-dom-change.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
clearTimeout,
runWithRealTimers,
} from './helpers'
import {getConfig} from './config'
import {getConfig} from './config.ts'

let hasWarned = false

Expand Down
2 changes: 1 addition & 1 deletion src/wait-for.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
setTimeout,
clearTimeout,
} from './helpers'
import {getConfig, runWithExpensiveErrorDiagnosticsDisabled} from './config'
import {getConfig, runWithExpensiveErrorDiagnosticsDisabled} from './config.ts'

// This is so the stack trace the developer sees is one that's
// closer to their code (because async stack traces are hard to follow).
Expand Down
7 changes: 7 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./node_modules/kcd-scripts/shared-tsconfig.json",
"compilerOptions": {
"allowJs": true
},
"include": ["./src", "./types"]
}
Loading