Skip to content

Commit c8bb37d

Browse files
committed
Migrate ANSI strip to normalizer
1 parent 709cf62 commit c8bb37d

File tree

5 files changed

+39
-49
lines changed

5 files changed

+39
-49
lines changed

src/matches.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import stripAnsiFn from 'strip-ansi'
2+
13
function assertNotNullOrUndefined(matcher) {
24
if (matcher === null || matcher === undefined) {
35
throw new Error(
@@ -43,33 +45,38 @@ function matches(textToMatch, node, matcher, normalizer) {
4345
}
4446
}
4547

46-
function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
48+
function getDefaultNormalizer({trim = true, collapseWhitespace = true, stripAnsi = true} = {}) {
4749
return text => {
4850
let normalizedText = text
4951
normalizedText = trim ? normalizedText.trim() : normalizedText
5052
normalizedText = collapseWhitespace
5153
? normalizedText.replace(/\s+/g, ' ')
5254
: normalizedText
55+
normalizedText = stripAnsi ? stripAnsiFn(normalizedText) : normalizedText
5356
return normalizedText
5457
}
5558
}
5659

5760
/**
61+
* @param {Object} props
5862
* Constructs a normalizer to pass to functions in matches.js
59-
* @param {boolean|undefined} trim The user-specified value for `trim`, without
63+
* @param {boolean|undefined} props.trim The user-specified value for `trim`, without
64+
* any defaulting having been applied
65+
* @param {boolean|undefined} props.stripAnsi The user-specified value for `stripAnsi`, without
6066
* any defaulting having been applied
61-
* @param {boolean|undefined} collapseWhitespace The user-specified value for
67+
* @param {boolean|undefined} props.collapseWhitespace The user-specified value for
6268
* `collapseWhitespace`, without any defaulting having been applied
63-
* @param {Function|undefined} normalizer The user-specified normalizer
69+
* @param {Function|undefined} props.normalizer The user-specified normalizer
6470
* @returns {Function} A normalizer
6571
*/
6672

67-
function makeNormalizer({trim, collapseWhitespace, normalizer}) {
73+
function makeNormalizer({trim, stripAnsi, collapseWhitespace, normalizer}) {
6874
if (normalizer) {
6975
// User has specified a custom normalizer
7076
if (
7177
typeof trim !== 'undefined' ||
72-
typeof collapseWhitespace !== 'undefined'
78+
typeof collapseWhitespace !== 'undefined' ||
79+
typeof stripAnsi !== 'undefined'
7380
) {
7481
// They've also specified a value for trim or collapseWhitespace
7582
throw new Error(
@@ -82,7 +89,7 @@ function makeNormalizer({trim, collapseWhitespace, normalizer}) {
8289
return normalizer
8390
} else {
8491
// No custom normalizer specified. Just use default.
85-
return getDefaultNormalizer({trim, collapseWhitespace})
92+
return getDefaultNormalizer({trim, collapseWhitespace, stripAnsi})
8693
}
8794
}
8895

src/pure.ts

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,10 @@
66
*/
77
import childProcess from 'child_process'
88
import stripFinalNewline from 'strip-final-newline'
9-
import stripAnsi from 'strip-ansi'
109
import {RenderOptions, TestInstance} from '../types/pure'
1110
import {_runObservers} from './mutation-observer'
1211
import {getQueriesForElement} from './get-queries-for-instance'
1312

14-
class NotBoundToInstanceError extends Error {
15-
constructor(prop: string) {
16-
super(`
17-
You've attempted to read '${prop}' from a destructured value.
18-
Please do not destructure \`${prop}\` from \`render\`, instead do something like this:
19-
20-
const client = render( /* ... */ );
21-
expect(client.${prop}).toBe(["Hi"]);
22-
23-
Because ${prop} relies on mutation to function, you'll be left with stale data if this is not done
24-
`)
25-
}
26-
}
27-
2813
async function render(
2914
command: string,
3015
args: string[] = [],
@@ -40,6 +25,8 @@ async function render(
4025
let _readyPromiseInternals: null | {resolve: Function; reject: Function} =
4126
null
4227

28+
let _isReadyResolved = false;
29+
4330
const execOutputAPI = {
4431
_isOutputAPI: true,
4532
_isReady: new Promise(
@@ -51,42 +38,35 @@ async function render(
5138
},
5239
// An array of strings gathered from stdout when unable to do
5340
// `await stdout` because of inquirer interactive prompts
54-
_stdoutArr: [] as string[],
55-
get stdoutArr(): string[] {
56-
// TODO: This error throwing doesn't _actually_ work, because
57-
// when the value is initially destructured, `this`, _is_ defined
58-
// and later, when the user goes to run `console.log(stdoutArr`), it's no
59-
// longer referencing this getter - instead it's a new variable that's assigned
60-
// a non-getter string
61-
if (!(this as unknown) || !this._isOutputAPI) {
62-
throw new NotBoundToInstanceError('stdoutArr')
63-
}
64-
return this._stdoutArr
65-
},
66-
set stdoutArr(val: string[]) {},
41+
stdoutArr: [] as Array<string | Buffer>,
6742
get stdoutStr(): string {
68-
if (!(this as unknown) || !this._isOutputAPI) {
69-
throw new NotBoundToInstanceError('stdoutStr')
70-
}
7143
return this.stdoutArr.join('\n')
7244
},
7345
set stdoutStr(val: string) {},
7446
}
7547

7648
exec.stdout.on('data', (result: string | Buffer) => {
77-
// TODO: Move `strip-ansi` to `normalizer` within `queries-text` instead
78-
const resStr = stripAnsi(stripFinalNewline(result as string).toString())
49+
const resStr = stripFinalNewline(result as string);
7950
execOutputAPI.stdoutArr.push(resStr)
8051
_runObservers()
81-
if (_readyPromiseInternals) _readyPromiseInternals.resolve()
52+
if (_readyPromiseInternals && !_isReadyResolved) {
53+
_readyPromiseInternals.resolve()
54+
_isReadyResolved = true;
55+
}
8256
})
8357

8458
exec.stdout.on('error', result => {
85-
if (_readyPromiseInternals) _readyPromiseInternals.reject(result)
59+
if (_readyPromiseInternals && !_isReadyResolved) {
60+
_readyPromiseInternals.reject(result)
61+
_isReadyResolved = true;
62+
}
8663
})
8764

8865
exec.stderr.on('data', (result: string) => {
89-
if (_readyPromiseInternals) _readyPromiseInternals.reject(new Error(result))
66+
if (_readyPromiseInternals && !_isReadyResolved) {
67+
_readyPromiseInternals.reject(new Error(result))
68+
_isReadyResolved = true;
69+
}
9070
})
9171

9272
await execOutputAPI._isReady

src/queries/text.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ const queryByTextBase: QueryByText = (
1313
exact = false,
1414
collapseWhitespace,
1515
trim,
16-
normalizer
16+
normalizer,
17+
stripAnsi
1718
} = {},
1819
) => {
1920
const matcher = exact ? matches : fuzzyMatches
2021
const matchNormalizer = makeNormalizer({
22+
stripAnsi,
2123
collapseWhitespace,
2224
trim,
23-
normalizer,
24-
// Why TypeScript, why?
25-
} as unknown as true)
25+
normalizer
26+
})
2627
const str = instance.stdoutStr
2728
if (matcher(str, instance, text, matchNormalizer)) return instance
2829
else return null

types/matches.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface MatcherOptions {
1515
/** Use normalizer with getDefaultNormalizer instead */
1616
trim?: boolean
1717
/** Use normalizer with getDefaultNormalizer instead */
18+
stripAnsi?: boolean
19+
/** Use normalizer with getDefaultNormalizer instead */
1820
collapseWhitespace?: boolean
1921
normalizer?: NormalizerFn
2022
/** suppress suggestions for a specific query */
@@ -31,6 +33,7 @@ export type Match = (
3133
export interface DefaultNormalizerOptions {
3234
trim?: boolean
3335
collapseWhitespace?: boolean
36+
stripAnsi?: boolean
3437
}
3538

3639
export function getDefaultNormalizer(

types/pure.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ export interface RenderOptions {
1010
*/
1111
export interface TestInstance {
1212
cleanup(): void
13-
// Possibly switch to `stdout.on('data'` prop in the future
14-
stdoutArr: string[]
13+
stdoutArr: Array<string, Buffer>
1514
stdin: Writable
1615
stdout: Readable
1716
stderr: Readable

0 commit comments

Comments
 (0)