diff --git a/README.md b/README.md index 1a95c30..94427f1 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ practices.

[![Code of Conduct](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](./CODE_OF_CONDUCT.md) + [![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-) + [![Watch on GitHub](https://img.shields.io/github/watchers/crutchcorn/cli-testing-library.svg?style=social)](https://github.com/crutchcorn/cli-testing-library/watchers) diff --git a/docs/api.md b/docs/api.md index f229f92..b2ce96e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -185,7 +185,7 @@ exit code it closed with. ```javascript const instance = render('command') -await waitFor(() => expect(instance.hasExit()).toMatchObject({ exitCode: 1 })) +await waitFor(() => expect(instance.hasExit()).toMatchObject({exitCode: 1})) ``` This method returns `null` if still running, but `{exitCode: number}` if it has @@ -206,8 +206,8 @@ They're defined as: ```typescript interface TestInstance { - stdoutArr: Array<{contents: Buffer | string, timestamp: number}> - stderrArr: Array<{contents: Buffer | string, timestamp: number}> + stdoutArr: Array<{contents: Buffer | string; timestamp: number}> + stderrArr: Array<{contents: Buffer | string; timestamp: number}> } ``` diff --git a/src/__tests__/execute-scripts/log-err.js b/src/__tests__/execute-scripts/log-err.js index 4fe2bf0..de42973 100644 --- a/src/__tests__/execute-scripts/log-err.js +++ b/src/__tests__/execute-scripts/log-err.js @@ -1,3 +1,3 @@ -console.log("Log here"); -console.warn("Warn here"); -console.error("Error here"); +console.log('Log here') +console.warn('Warn here') +console.error('Error here') diff --git a/src/config.ts b/src/config.ts index 86a2ae0..1337b5c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -27,17 +27,15 @@ let config: InternalConfig = { // called when getBy* queries fail. (message, container) => Error getInstanceError(message, testInstance: TestInstance | undefined) { - let instanceWarning: string = ""; + let instanceWarning: string = '' if (testInstance) { - const stdallArrStr = testInstance.getStdallStr(); + const stdallArrStr = testInstance.getStdallStr() instanceWarning = `\n${stdallArrStr}` } else { - instanceWarning = ""; + instanceWarning = '' } const error = new Error( - [message, instanceWarning] - .filter(Boolean) - .join('\n\n'), + [message, instanceWarning].filter(Boolean).join('\n\n'), ) error.name = 'TestingLibraryElementError' return error diff --git a/src/index.js b/src/index.js index 5f73193..d0e0cd1 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,10 @@ import {cleanup} from './pure' // or teardown then we'll automatically run cleanup afterEach test // this ensures that tests run in isolation from each other // if you don't like this then set the CTL_SKIP_AUTO_CLEANUP env variable to 'true'. -if (typeof process === 'undefined' || !(process.env && process.env.CTL_SKIP_AUTO_CLEANUP)) { +if ( + typeof process === 'undefined' || + !(process.env && process.env.CTL_SKIP_AUTO_CLEANUP) +) { // ignore teardown() in code coverage because Jest does not support it /* istanbul ignore else */ if (typeof afterEach === 'function') { diff --git a/src/matchers/to-be-in-the-console.js b/src/matchers/to-be-in-the-console.js index ae7b4ad..56d9f37 100644 --- a/src/matchers/to-be-in-the-console.js +++ b/src/matchers/to-be-in-the-console.js @@ -8,7 +8,9 @@ export function toBeInTheConsole(instance) { } const errormessage = instance - ? getDefaultNormalizer()(instance.stdoutArr.map(obj => obj.contents).join('\n')) + ? getDefaultNormalizer()( + instance.stdoutArr.map(obj => obj.contents).join('\n'), + ) : null return { diff --git a/src/matchers/to-have-errormessage.js b/src/matchers/to-have-errormessage.js index d596d72..4288c10 100644 --- a/src/matchers/to-have-errormessage.js +++ b/src/matchers/to-have-errormessage.js @@ -11,7 +11,9 @@ export function toHaveErrorMessage(testInstance, checkWith) { const expectsErrorMessage = checkWith !== undefined - const errormessage = getDefaultNormalizer()(testInstance.stderrArr.map(obj => obj.contents).join('\n')) + const errormessage = getDefaultNormalizer()( + testInstance.stderrArr.map(obj => obj.contents).join('\n'), + ) return { pass: expectsErrorMessage diff --git a/src/pretty-cli.js b/src/pretty-cli.js index 150c9bc..699b134 100644 --- a/src/pretty-cli.js +++ b/src/pretty-cli.js @@ -15,7 +15,7 @@ function prettyCLI(testInstance, maxLength) { throw new TypeError(`Expected an instance but got ${testInstance}`) } - const outStr = testInstance.getStdallStr(); + const outStr = testInstance.getStdallStr() // eslint-disable-next-line no-negated-condition return maxLength !== undefined && outStr.length > maxLength diff --git a/src/pure.ts b/src/pure.ts index a1ccace..d33fba0 100644 --- a/src/pure.ts +++ b/src/pure.ts @@ -1,4 +1,5 @@ import childProcess from 'child_process' +import {performance} from 'perf_hooks' import stripFinalNewline from 'strip-final-newline' import {RenderOptions, RenderResult, TestInstance} from '../types/pure' import {_runObservers} from './mutation-observer' @@ -8,7 +9,6 @@ import {bindObjectFnsToInstance, setCurrentInstance} from './helpers' import {fireEvent} from './events' import {getConfig} from './config' import {logCLI} from './pretty-cli' -import {performance} from 'perf_hooks' const mountedInstances = new Set() @@ -48,8 +48,8 @@ async function render( }, // An array of strings gathered from stdout when unable to do // `await stdout` because of inquirer interactive prompts - stdoutArr: [] as Array<{contents: Buffer | string, timestamp: number}>, - stderrArr: [] as Array<{contents: Buffer | string, timestamp: number}>, + stdoutArr: [] as Array<{contents: Buffer | string; timestamp: number}>, + stderrArr: [] as Array<{contents: Buffer | string; timestamp: number}>, hasExit() { return this.__exitCode === null ? null : {exitCode: this.__exitCode} }, @@ -66,7 +66,10 @@ async function render( } const resStr = stripFinalNewline(result as string) - execOutputAPI.stdoutArr.push({contents: resStr, timestamp: performance.now()}) + execOutputAPI.stdoutArr.push({ + contents: resStr, + timestamp: performance.now(), + }) _runObservers() }) @@ -77,7 +80,10 @@ async function render( } const resStr = stripFinalNewline(result as string) - execOutputAPI.stderrArr.push({contents: resStr, timestamp: performance.now()}) + execOutputAPI.stderrArr.push({ + contents: resStr, + timestamp: performance.now(), + }) _runObservers() }) @@ -104,12 +110,12 @@ async function render( await execOutputAPI._isReady - function getStdallStr(this: Omit) { - return this.stderrArr.concat(this.stdoutArr) - .sort((a,b) => a.timestamp < b.timestamp ? -1 : 1) - .map(obj => obj.contents) - .join('\n'); + return this.stderrArr + .concat(this.stdoutArr) + .sort((a, b) => (a.timestamp < b.timestamp ? -1 : 1)) + .map(obj => obj.contents) + .join('\n') } return Object.assign( diff --git a/src/suggestions.js b/src/suggestions.js index 115b012..9b1be8b 100644 --- a/src/suggestions.js +++ b/src/suggestions.js @@ -61,7 +61,9 @@ function canSuggest(currentMethod, requestedMethod, data) { } export function getSuggestedQuery(instance, variant = 'get', method) { - const textContent = normalize(instance.stdoutArr.map(obj => obj.contents).join('\n')) + const textContent = normalize( + instance.stdoutArr.map(obj => obj.contents).join('\n'), + ) if (canSuggest('Text', method, textContent)) { return makeSuggestion('Text', instance, textContent, {variant}) } diff --git a/tests/setup-env.js b/tests/setup-env.js index df6577e..6a5e3be 100644 --- a/tests/setup-env.js +++ b/tests/setup-env.js @@ -1,7 +1,7 @@ import '../src/extend-expect' -import {configure, getConfig} from '../src/config' import hasAnsi from 'has-ansi' import stripAnsi from 'strip-ansi' +import {configure, getConfig} from '../src/config' /** * We have instances where we need to disable this serializer to test for ANSI codes diff --git a/types/__tests__/type-tests.ts b/types/__tests__/type-tests.ts index 1de5a10..8bbce98 100644 --- a/types/__tests__/type-tests.ts +++ b/types/__tests__/type-tests.ts @@ -42,8 +42,8 @@ export async function eventTest() { export async function keyboardTest() { const instance = await render('command', []) - userEvent.keyboard(instance, 'Test') - instance.userEvent.keyboard('Test') + await userEvent.keyboard(instance, 'Test') + await instance.userEvent.keyboard('Test') await instance.userEvent.keyboard('Test', {delay: 0}) fireEvent.write(instance, {value: 'test'}) diff --git a/types/event-map.d.ts b/types/event-map.d.ts new file mode 100644 index 0000000..612e43f --- /dev/null +++ b/types/event-map.d.ts @@ -0,0 +1,12 @@ +import {TestInstance} from '../types' +declare const eventMap: { + sigterm: (instance: TestInstance) => Promise + sigkill: (instance: TestInstance) => Promise + write: ( + instance: TestInstance, + props: { + value: string + }, + ) => boolean +} +export {eventMap} diff --git a/types/events.d.ts b/types/events.d.ts index 79584b2..c1cf924 100644 --- a/types/events.d.ts +++ b/types/events.d.ts @@ -1,4 +1,4 @@ -import type {eventMap} from '../src/event-map' +import type {eventMap} from './event-map' import {TestInstance} from './pure' type EventMap = typeof eventMap diff --git a/types/index.d.ts b/types/index.d.ts index 88a1ce9..f63bca0 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,4 +14,4 @@ export * from './queries' export * from './query-helpers' export * from './suggestions' export * from './wait-for' -export * from '../src/user-event/index' +export * from './user-event/index' diff --git a/types/pure.d.ts b/types/pure.d.ts index 9e41810..c967d8f 100644 --- a/types/pure.d.ts +++ b/types/pure.d.ts @@ -1,14 +1,14 @@ import type {SpawnOptionsWithoutStdio} from 'child_process' import {ChildProcessWithoutNullStreams} from 'child_process' -import type userEvent from '../src/user-event/index' +import type userEvent from './user-event/index' import * as queries from './queries' import type {BoundFunction} from './get-queries-for-instance' export interface TestInstance { clear(): void process: ChildProcessWithoutNullStreams - stdoutArr: Array<{contents: Buffer | string, timestamp: number}> - stderrArr: Array<{contents: Buffer | string, timestamp: number}> + stdoutArr: Array<{contents: Buffer | string; timestamp: number}> + stderrArr: Array<{contents: Buffer | string; timestamp: number}> getStdallStr(): string hasExit(): null | {exitCode: number} debug(maxLength?: number): void diff --git a/types/user-event/index.d.ts b/types/user-event/index.d.ts new file mode 100644 index 0000000..b574d96 --- /dev/null +++ b/types/user-event/index.d.ts @@ -0,0 +1,6 @@ +import {keyboard} from './keyboard/index' +declare const userEvent: { + keyboard: typeof keyboard +} +export default userEvent +export type {keyboardKey} from './keyboard/index' diff --git a/types/user-event/keyboard/getNextKeyDef.d.ts b/types/user-event/keyboard/getNextKeyDef.d.ts new file mode 100644 index 0000000..1422058 --- /dev/null +++ b/types/user-event/keyboard/getNextKeyDef.d.ts @@ -0,0 +1,15 @@ +import {keyboardKey, keyboardOptions} from './types' +/** + * Get the next key from keyMap + * + * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`. + * Everything else will be interpreted as a typed character - e.g. `a`. + * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`. + */ +export declare function getNextKeyDef( + text: string, + options: keyboardOptions, +): { + keyDef: keyboardKey + consumedLength: number +} diff --git a/types/user-event/keyboard/index.d.ts b/types/user-event/keyboard/index.d.ts new file mode 100644 index 0000000..a386a03 --- /dev/null +++ b/types/user-event/keyboard/index.d.ts @@ -0,0 +1,19 @@ +import {TestInstance} from '../../../types' +import {keyboardOptions, keyboardKey} from './types' +export type {keyboardOptions, keyboardKey} +export declare function keyboard( + instance: TestInstance, + text: string, + options?: Partial< + keyboardOptions & { + delay: number + } + >, +): void | Promise +export declare function keyboardImplementationWrapper( + instance: TestInstance, + text: string, + config?: Partial, +): { + promise: Promise +} diff --git a/types/user-event/keyboard/keyMap.d.ts b/types/user-event/keyboard/keyMap.d.ts new file mode 100644 index 0000000..f513916 --- /dev/null +++ b/types/user-event/keyboard/keyMap.d.ts @@ -0,0 +1,10 @@ +import {keyboardKey} from './types' +/** + * Mapping for a default US-104-QWERTY keyboard + * + * These use ANSI-C quoting, which seems to work for Linux, macOS, and Windows alike + * @see https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#ANSI_002dC-Quoting + * @see https://stackoverflow.com/questions/35429671/detecting-key-press-within-bash-scripts + * @see https://gist.github.com/crutchcorn/2811db78a7b924cf54f4507198427fd2 + */ +export declare const defaultKeyMap: keyboardKey[] diff --git a/types/user-event/keyboard/keyboardImplementation.d.ts b/types/user-event/keyboard/keyboardImplementation.d.ts new file mode 100644 index 0000000..c758c02 --- /dev/null +++ b/types/user-event/keyboard/keyboardImplementation.d.ts @@ -0,0 +1,7 @@ +import {TestInstance} from '../../../types' +import {keyboardOptions} from './types' +export declare function keyboardImplementation( + instance: TestInstance, + text: string, + options: keyboardOptions, +): Promise diff --git a/types/user-event/keyboard/types.d.ts b/types/user-event/keyboard/types.d.ts new file mode 100644 index 0000000..b256b45 --- /dev/null +++ b/types/user-event/keyboard/types.d.ts @@ -0,0 +1,12 @@ +export type keyboardOptions = { + /** Delay between keystrokes */ + delay: number + /** Keyboard layout to use */ + keyboardMap: keyboardKey[] +} +export interface keyboardKey { + /** Physical location on a keyboard */ + code?: string + /** Character or functional key hex code */ + hex?: string +} diff --git a/types/user-event/utils.d.ts b/types/user-event/utils.d.ts new file mode 100644 index 0000000..298919a --- /dev/null +++ b/types/user-event/utils.d.ts @@ -0,0 +1 @@ +export declare function wait(time?: number): Promise