-
-
Notifications
You must be signed in to change notification settings - Fork 31
feat(tape-to-node-test): first draft
#296
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
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: Bruno Rodrigues <swe@brunocroh.com>
ljharb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(btw the diff would read cleaner if it was "output" instead of "expected", because then "input" would be sorted before "output" instead of the reverse)
some of my comments apply on all the input/expected combos, so i'll post this now before being exhaustive
the input/expect structure isn't chose by us it's the design of the codemod tool |
Co-authored-by: Bruno Rodrigues <swe@brunocroh.com>
|
@ljharb i had resolved all of your concerns |
ljharb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a big issue with converting to assert - namely, that tape test suites very intentionally do not terminate at a failed assertion, but node's assert throws (which prevents running the rest of the assertions).
Does the test runner have a non-throwing assertion API?
recipes/tape-to-node-test/tests/advanced-assertions/expected.js
Outdated
Show resolved
Hide resolved
| const opts = { skip: false }; | ||
|
|
||
| test('timeout with variable opts', opts, async (t) => { | ||
| // TODO: Add timeout: `123` to test options manually; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does the test runner not have an imperative timeout API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't found it
i have updated the comment but the user have to found a way to change their code because I dunno if we have an equivalent |
|
If there's no equivalent, then probably it doesn't make sense to provide a codemod until there is? |
IMO we should warn it on readme and people will be aware and then they know that will change how does it work. |
| const ASSERTION_MAPPING: Record<string, string> = { | ||
| equal: 'strictEqual', | ||
| notEqual: 'notStrictEqual', | ||
| strictEqual: 'strictEqual', | ||
| notStrictEqual: 'notStrictEqual', | ||
| deepEqual: 'deepStrictEqual', | ||
| notDeepEqual: 'notDeepStrictEqual', | ||
| looseEqual: 'equal', | ||
| notLooseEqual: 'notEqual', | ||
| ok: 'ok', | ||
| ifError: 'ifError', | ||
| error: 'ifError', | ||
| throws: 'throws', | ||
| doesNotThrow: 'doesNotThrow', | ||
| match: 'match', | ||
| doesNotMatch: 'doesNotMatch', | ||
| fail: 'fail', | ||
| same: 'deepStrictEqual', | ||
| notSame: 'notDeepStrictEqual', | ||
| // Aliases | ||
| assert: 'ok', | ||
| ifErr: 'ifError', | ||
| iferror: 'ifError', | ||
| equals: 'strictEqual', | ||
| isEqual: 'strictEqual', | ||
| strictEquals: 'strictEqual', | ||
| is: 'strictEqual', | ||
| notEquals: 'notStrictEqual', | ||
| isNotEqual: 'notStrictEqual', | ||
| doesNotEqual: 'notStrictEqual', | ||
| isInequal: 'notStrictEqual', | ||
| notStrictEquals: 'notStrictEqual', | ||
| isNot: 'notStrictEqual', | ||
| not: 'notStrictEqual', | ||
| looseEquals: 'equal', | ||
| notLooseEquals: 'notEqual', | ||
| deepEquals: 'deepStrictEqual', | ||
| isEquivalent: 'deepStrictEqual', | ||
| notDeepEquals: 'notDeepStrictEqual', | ||
| notEquivalent: 'notDeepStrictEqual', | ||
| notDeeply: 'notDeepStrictEqual', | ||
| isNotDeepEqual: 'notDeepStrictEqual', | ||
| isNotDeeply: 'notDeepStrictEqual', | ||
| isNotEquivalent: 'notDeepStrictEqual', | ||
| isInequivalent: 'notDeepStrictEqual', | ||
| deepLooseEqual: 'deepEqual', | ||
| notDeepLooseEqual: 'notDeepEqual', | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const ASSERTION_MAPPING: Record<string, string> = { | |
| equal: 'strictEqual', | |
| notEqual: 'notStrictEqual', | |
| strictEqual: 'strictEqual', | |
| notStrictEqual: 'notStrictEqual', | |
| deepEqual: 'deepStrictEqual', | |
| notDeepEqual: 'notDeepStrictEqual', | |
| looseEqual: 'equal', | |
| notLooseEqual: 'notEqual', | |
| ok: 'ok', | |
| ifError: 'ifError', | |
| error: 'ifError', | |
| throws: 'throws', | |
| doesNotThrow: 'doesNotThrow', | |
| match: 'match', | |
| doesNotMatch: 'doesNotMatch', | |
| fail: 'fail', | |
| same: 'deepStrictEqual', | |
| notSame: 'notDeepStrictEqual', | |
| // Aliases | |
| assert: 'ok', | |
| ifErr: 'ifError', | |
| iferror: 'ifError', | |
| equals: 'strictEqual', | |
| isEqual: 'strictEqual', | |
| strictEquals: 'strictEqual', | |
| is: 'strictEqual', | |
| notEquals: 'notStrictEqual', | |
| isNotEqual: 'notStrictEqual', | |
| doesNotEqual: 'notStrictEqual', | |
| isInequal: 'notStrictEqual', | |
| notStrictEquals: 'notStrictEqual', | |
| isNot: 'notStrictEqual', | |
| not: 'notStrictEqual', | |
| looseEquals: 'equal', | |
| notLooseEquals: 'notEqual', | |
| deepEquals: 'deepStrictEqual', | |
| isEquivalent: 'deepStrictEqual', | |
| notDeepEquals: 'notDeepStrictEqual', | |
| notEquivalent: 'notDeepStrictEqual', | |
| notDeeply: 'notDeepStrictEqual', | |
| isNotDeepEqual: 'notDeepStrictEqual', | |
| isNotDeeply: 'notDeepStrictEqual', | |
| isNotEquivalent: 'notDeepStrictEqual', | |
| isInequivalent: 'notDeepStrictEqual', | |
| deepLooseEqual: 'deepEqual', | |
| notDeepLooseEqual: 'notDeepEqual', | |
| }; | |
| const ASSERTION_MAPPING = { | |
| equal: 'strictEqual', | |
| notEqual: 'notStrictEqual', | |
| strictEqual: 'strictEqual', | |
| notStrictEqual: 'notStrictEqual', | |
| deepEqual: 'deepStrictEqual', | |
| notDeepEqual: 'notDeepStrictEqual', | |
| looseEqual: 'equal', | |
| notLooseEqual: 'notEqual', | |
| ok: 'ok', | |
| ifError: 'ifError', | |
| error: 'ifError', | |
| throws: 'throws', | |
| doesNotThrow: 'doesNotThrow', | |
| match: 'match', | |
| doesNotMatch: 'doesNotMatch', | |
| fail: 'fail', | |
| same: 'deepStrictEqual', | |
| notSame: 'notDeepStrictEqual', | |
| // Aliases | |
| assert: 'ok', | |
| ifErr: 'ifError', | |
| iferror: 'ifError', | |
| equals: 'strictEqual', | |
| isEqual: 'strictEqual', | |
| strictEquals: 'strictEqual', | |
| is: 'strictEqual', | |
| notEquals: 'notStrictEqual', | |
| isNotEqual: 'notStrictEqual', | |
| doesNotEqual: 'notStrictEqual', | |
| isInequal: 'notStrictEqual', | |
| notStrictEquals: 'notStrictEqual', | |
| isNot: 'notStrictEqual', | |
| not: 'notStrictEqual', | |
| looseEquals: 'equal', | |
| notLooseEquals: 'notEqual', | |
| deepEquals: 'deepStrictEqual', | |
| isEquivalent: 'deepStrictEqual', | |
| notDeepEquals: 'notDeepStrictEqual', | |
| notEquivalent: 'notDeepStrictEqual', | |
| notDeeply: 'notDeepStrictEqual', | |
| isNotDeepEqual: 'notDeepStrictEqual', | |
| isNotDeeply: 'notDeepStrictEqual', | |
| isNotEquivalent: 'notDeepStrictEqual', | |
| isInequivalent: 'notDeepStrictEqual', | |
| deepLooseEqual: 'deepEqual', | |
| notDeepLooseEqual: 'notDeepEqual', | |
| }; |
The type here can be inferred
| console.warn( | ||
| `[Codemod] Warning: ${methodName} at ${fileName}:${line}:${column} has no direct equivalent in node:test. Please migrate manually.`, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @nodejs/test_runner a onFailure (perhaps afterFail, keeping the naming similar) handler is a pretty neat feature for us to include.
Also, onFinish -> after, right?, or does after not run on the completion of the test it's created in?
| case 'true': | ||
| if (func) edits.push(func.replace('assert.ok')); | ||
| break; | ||
| case 'false': | ||
| if (args) { | ||
| const val = args.child(1); | ||
| if (val) { | ||
| edits.push({ | ||
| startPos: val.range().start.index, | ||
| endPos: val.range().start.index, | ||
| insertedText: '!', | ||
| }); | ||
| if (func) edits.push(func.replace('assert.ok')); | ||
| } | ||
| } | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For aliases, just add another case statement to the previous part (see my other comments)
| } | ||
|
|
||
| switch (method.toLowerCase()) { | ||
| case 'notok': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| case 'notok': | |
| case 'notok': | |
| case 'false': |
| case 'pass': | ||
| if (args) { | ||
| // Insert 'true' as first arg | ||
| // args text is like "('msg')" or "()" | ||
| const openParen = args.child(0); | ||
| if (openParen) { | ||
| edits.push({ | ||
| startPos: openParen.range().end.index, | ||
| endPos: openParen.range().end.index, | ||
| insertedText: args.children().length > 2 ? 'true, ' : 'true', | ||
| }); | ||
| if (func) edits.push(func.replace('assert.ok')); | ||
| } | ||
| } | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our assertions don't work this way, as the message will not be the same. Therefore, this isn't an exact change. These statements should rather be removed entirely.
| case 'plan': | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put this at the end, for ease of reading
| for (const call of lifecycleCalls) { | ||
| const { line, column } = call.range().start; | ||
| const fileName = root.filename(); | ||
| const methodName = | ||
| call.field('function')?.field('property')?.text() || 'lifecycle method'; | ||
|
|
||
| console.warn( | ||
| `[Codemod] Warning: ${methodName} at ${fileName}:${line}:${column} has no direct equivalent in node:test. Please migrate manually.`, | ||
| ); | ||
|
|
||
| const lines = call.text().split(/\r?\n/); | ||
| const newText = lines | ||
| .map((line, i) => (i === 0 ? `// TODO: ${line}` : `// ${line}`)) | ||
| .join(EOL); | ||
| edits.push(call.replace(newText)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no reason to handle these separately from the rest in transformAssertions. Can't this logic go in the switch statement?
| * @param testCall the AST node of the test function call | ||
| * @param useDone whether to use the done callback for ending tests | ||
| */ | ||
| function transformAssertions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This transforms methods, not just assertions
Related issue
Close #260