Repository: skovhus/jest-codemods
Pull request #131 (2018-09-18 19:10:27)

Support skipping import detection
https://github.com/skovhus/jest-codemods/issues/119

This adds a new CLI question to figure out if the user is using `global` for their current assertion library. If they do we skip the conservative import/require detection that we currently always run.

<img width="601" alt="screenshot 2018-09-18 21 04 57" src="https://user-images.githubusercontent.com/1260305/45710558-3fca4600-bb87-11e8-960f-77bbbae11255.png">



Patch:
diff --git a/package.json b/package.json
index 2791d56..163de3e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "jest-codemods",
-  "version": "0.15.0",
+  "version": "0.16.0",
   "description": "Codemods for migrating test files to Jest",
   "license": "MIT",
   "repository": "skovhus/jest-codemods",
diff --git a/src/cli/index.js b/src/cli/index.js
index 3e3d9e7..cc2b1dd 100644
--- a/src/cli/index.js
+++ b/src/cli/index.js
@@ -106,7 +106,7 @@ const TRANSFORMER_INQUIRER_CHOICES = [
         value: TRANSFORMER_TAPE,
     },
     {
-        name: 'All of the above (by detecting usage)!',
+        name: 'All of the above (use with care)!',
         value: 'all',
     },
     {
@@ -131,6 +131,22 @@ inquirer
             pageSize: TRANSFORMER_INQUIRER_CHOICES.length,
             choices: TRANSFORMER_INQUIRER_CHOICES,
         },
+        {
+            name: 'skipImportDetection',
+            type: 'list',
+            message:
+                'Are you using the global object for assertions (i.e. without requiring them)',
+            choices: [
+                {
+                    name: `No, I use import/require statements for my current assertion library`,
+                    value: false,
+                },
+                {
+                    name: `Yes, and I'm not afraid of false positive transformations`,
+                    value: true,
+                },
+            ],
+        },
         {
             name: 'standaloneMode',
             type: 'list',
@@ -192,7 +208,13 @@ inquirer
         },
     ])
     .then(answers => {
-        const { files, transformer, mochaAssertion, standaloneMode } = answers;
+        const {
+            files,
+            transformer,
+            mochaAssertion,
+            skipImportDetection,
+            standaloneMode,
+        } = answers;
 
         if (transformer === 'other') {
             return supportFailure(
@@ -222,6 +244,15 @@ inquirer
             );
         }
 
+        if (skipImportDetection) {
+            transformerArgs.push('--skipImportDetection=true');
+            console.log(
+                chalk.yellow(
+                    '\nNOTICE: Skipping import detection, you might get false positives'
+                )
+            );
+        }
+
         return executeTransformations(
             filesExpanded,
             cli.flags,
diff --git a/src/transformers/ava.js b/src/transformers/ava.js
index 3f025cd..9394b54 100644
--- a/src/transformers/ava.js
+++ b/src/transformers/ava.js
@@ -60,7 +60,7 @@ export default function avaToJest(fileInfo, api, options) {
 
     const testFunctionName = removeRequireAndImport(j, ast, 'ava');
 
-    if (!testFunctionName) {
+    if (!testFunctionName && !options.skipImportDetection) {
         // No AVA require/import were found
         return fileInfo.source;
     }
diff --git a/src/transformers/chai-assert.js b/src/transformers/chai-assert.js
index 16365f3..4a0ee2f 100644
--- a/src/transformers/chai-assert.js
+++ b/src/transformers/chai-assert.js
@@ -179,7 +179,7 @@ export default function transformer(fileInfo, api, options) {
 
     let chaiAssertExpression;
 
-    const assertLocalName = removeRequireAndImport(j, ast, 'chai', 'assert');
+    let assertLocalName = removeRequireAndImport(j, ast, 'chai', 'assert');
     const defaultImportLocalName = removeDefaultImport(j, ast, 'chai');
     if (assertLocalName) {
         chaiAssertExpression = {
@@ -201,8 +201,15 @@ export default function transformer(fileInfo, api, options) {
     }
 
     if (!chaiAssertExpression) {
-        // No Chai require/import were found
-        return fileInfo.source;
+        if (!options.skipImportDetection) {
+            // No Chai require/import were found
+            return fileInfo.source;
+        }
+        assertLocalName = 'assert';
+        chaiAssertExpression = {
+            type: 'Identifier',
+            name: 'assert',
+        };
     }
 
     const logWarning = (msg, path) => logger(fileInfo, msg, path);
diff --git a/src/transformers/expect-js.js b/src/transformers/expect-js.js
index 523a2e2..3e69951 100644
--- a/src/transformers/expect-js.js
+++ b/src/transformers/expect-js.js
@@ -62,7 +62,7 @@ export default function expectJsTransfomer(fileInfo, api, options) {
     const expectImport = getRequireOrImportName(j, ast, EXPECT_JS);
     const logWarning = (msg, node) => logger(fileInfo, msg, node);
 
-    if (!expectImport) {
+    if (!expectImport && !options.skipImportDetection) {
         // No expect.js require/import were found
         return fileInfo.source;
     }
diff --git a/src/transformers/expect.js b/src/transformers/expect.js
index 30726a2..5935df2 100644
--- a/src/transformers/expect.js
+++ b/src/transformers/expect.js
@@ -101,7 +101,7 @@ export default function expectTransformer(fileInfo, api, options) {
     const ast = j(fileInfo.source);
     const { standaloneMode } = options;
 
-    if (!hasRequireOrImport(j, ast, EXPECT)) {
+    if (!hasRequireOrImport(j, ast, EXPECT) && !options.skipImportDetection) {
         // No expect require/import were found
         return fileInfo.source;
     }
diff --git a/src/transformers/tape.js b/src/transformers/tape.js
index b565ab6..486dacf 100644
--- a/src/transformers/tape.js
+++ b/src/transformers/tape.js
@@ -85,11 +85,15 @@ export default function tapeToJest(fileInfo, api, options) {
     const j = api.jscodeshift;
     const ast = j(fileInfo.source);
 
-    const testFunctionName = removeRequireAndImport(j, ast, 'tape');
+    let testFunctionName = removeRequireAndImport(j, ast, 'tape');
 
     if (!testFunctionName) {
         // No Tape require/import were found
-        return fileInfo.source;
+        if (!options.skipImportDetection) {
+            return fileInfo.source;
+        }
+
+        testFunctionName = 'tape';
     }
 
     const logWarning = (msg, node) => logger(fileInfo, msg, node);


Test patch:
diff --git a/src/transformers/ava.test.js b/src/transformers/ava.test.js
index b502222..bde3f4a 100644
--- a/src/transformers/ava.test.js
+++ b/src/transformers/ava.test.js
@@ -12,9 +12,9 @@ beforeEach(() => {
     console.warn = v => consoleWarnings.push(v);
 });
 
-function testChanged(msg, source, expectedOutput) {
+function testChanged(msg, source, expectedOutput, options = {}) {
     test(msg, () => {
-        const result = wrappedPlugin(source);
+        const result = wrappedPlugin(source, options);
         expect(result).toBe(expectedOutput);
         expect(consoleWarnings).toEqual([]);
     });
@@ -38,6 +38,23 @@ test(t => {
 `
 );
 
+testChanged(
+    'changes code without require/import if skipImportDetection is set',
+    `
+// @flow
+test(t => {
+    t.notOk(1);
+})
+`,
+    `
+// @flow
+test(t => {
+    expect(1).toBeFalsy();
+})
+`,
+    { skipImportDetection: true }
+);
+
 // TODO: jscodeshift adds semi colon when preserving first line comments :/
 testChanged(
     'maps assertions',
diff --git a/src/transformers/chai-assert.test.js b/src/transformers/chai-assert.test.js
index 01b61a8..832c0db 100644
--- a/src/transformers/chai-assert.test.js
+++ b/src/transformers/chai-assert.test.js
@@ -12,9 +12,9 @@ beforeEach(() => {
     console.warn = v => consoleWarnings.push(v);
 });
 
-function testChanged(msg, source, expectedOutput) {
+function testChanged(msg, source, expectedOutput, options) {
     test(msg, () => {
-        const result = wrappedPlugin(source);
+        const result = wrappedPlugin(source, options);
         expect(result).toBe(expectedOutput);
         expect(consoleWarnings).toEqual([]);
     });
@@ -151,6 +151,13 @@ import { assert } from 'chai';`,
 
 testChanged('mappings', mappingTest.input, mappingTest.output);
 
+testChanged(
+    'mapping without import if skipImportDetection is set',
+    mappingTest.input.replace("import { assert } from 'chai';\n", ''),
+    mappingTest.output,
+    { skipImportDetection: true }
+);
+
 test('not supported assertions', () => {
     const unsupportedAssertions = [
         'deepProperty',
diff --git a/src/transformers/expect-js.test.js b/src/transformers/expect-js.test.js
index 69e074e..a036f9e 100644
--- a/src/transformers/expect-js.test.js
+++ b/src/transformers/expect-js.test.js
@@ -39,6 +39,21 @@ testChanged(
     `
 );
 
+testChanged(
+    'changes code without expect require/import if skipImportDetection is set',
+    `
+    test(t => {
+      expect(stuff).to.be.ok();
+    })
+    `,
+    `
+    test(t => {
+      expect(stuff).toBeTruthy();
+    })
+    `,
+    { skipImportDetection: true }
+);
+
 testChanged(
     'maps expect matchers',
     `
diff --git a/src/transformers/expect.test.js b/src/transformers/expect.test.js
index 8ce2abb..6a4a351 100644
--- a/src/transformers/expect.test.js
+++ b/src/transformers/expect.test.js
@@ -39,6 +39,21 @@ testChanged(
     `
 );
 
+testChanged(
+    'changes code without expect require/import if skipImportDetection is set',
+    `
+    test(t => {
+      expect(stuff).toExist();
+    })
+    `,
+    `
+    test(t => {
+      expect(stuff).toBeTruthy();
+    })
+    `,
+    { skipImportDetection: true }
+);
+
 testChanged(
     'maps expect matchers',
     `
diff --git a/src/transformers/tape.test.js b/src/transformers/tape.test.js
index 2c0cd82..56a9c5a 100644
--- a/src/transformers/tape.test.js
+++ b/src/transformers/tape.test.js
@@ -12,9 +12,9 @@ beforeEach(() => {
     console.warn = v => consoleWarnings.push(v);
 });
 
-function testChanged(msg, source, expectedOutput) {
+function testChanged(msg, source, expectedOutput, options = {}) {
     test(msg, () => {
-        const result = wrappedPlugin(source);
+        const result = wrappedPlugin(source, options);
         expect(result).toBe(expectedOutput);
         expect(consoleWarnings).toEqual([]);
     });
@@ -34,6 +34,19 @@ test(t => {
 });`
 );
 
+testChanged(
+    'changes code without tape require/import if skipImportDetection is set',
+    `
+test(t => {
+    t.notOk(1);
+});`,
+    `
+test(t => {
+    expect(1).toBeFalsy();
+});`,
+    { skipImportDetection: true }
+);
+
 testChanged(
     'CommonJs requires',
     `