UNPKG

ember-freshdesk-codemods

Version:
223 lines (190 loc) 6.75 kB
// Playground // https://astexplorer.net/#/gist/b30b9a6dc8d67bf77d563518de19a32b/f9527ba69f224d390583e8571a5acc5affc6a0ea const { getParser } = require('codemod-cli').jscodeshift; const { getOptions } = require('codemod-cli'); const matcherTransformer = require('./matcher-transformer'); const { cleanupImports, setupHooksForTest, setupCallbackHooks } = require('../cleanup-imports'); const beautifyImports = require('../beautify-imports'); const { findExpect, renameIdentifiers, renameImports } = require('./utils'); // TODO We need to address the following // [ ] Take the list of expects and convert them to qunit asserts. // [X] Use hooks from module params and not as return value for setupTests. // [ ] Add more fixtures. // [ ] Take a sample batch of 50 and migrate them to qunit using some simple code-mods. // [X] Refactor to a mapper to determine what type of assertion and what type of transform. // [X] Clean up unused imports sych as context, findAll. // [X] Migrate context to module and add hooks. // [X] If a describe or a context is skipped then all the tests within the specifc module needs to be skipped and added as an import to qunit // [ ] Clean up any beforeEach and afterEach called directly form mocha. // // Known issues // [ ] There are some tests which have an expect as the return statements and also having await methods infront of it. // [ ] Mocha's done callback needs to be removed if present. // [ ] Faker is getting removed from the tests. need to analyse why. module.exports = function transformer(file, api) { const j = getParser(api); const options = getOptions(); const root = j(file.source); const lineTerminator = file.source.indexOf('\r\n') > -1 ? '\r\n' : '\n'; const renameIdentifierList = [ ['describe', 'module'], ['context', 'module'], ['it', 'test'], ['setupRenderingWithMirage', 'setupRenderingForModule'], ['setupAcceptance', 'setupApplicationForModule'] ]; const renameImportImports = [ ['mocha', 'qunit'], ['ember-mocha', 'ember-qunit'] ] const setupTestTypes = [ 'setupTest', 'setupRenderingTest', 'setupApplicationTest', 'setupRenderingForModule', 'setupApplicationForModule' ]; const callbackHooks = ['before', 'after', 'beforeEach', 'afterEach']; renameIdentifiers(renameIdentifierList, root, j); renameImports(renameImportImports, root, j); // transformer for moving it.skip, describe.skip, module.skip => skip and nested children to have skip transformSkippedTests(j, root); root.find(j.FunctionExpression) .filter((path) => path.parent.node.callee && ['test', 'skip'].includes(path.parent.node.callee.name)) .forEach(transformerTests); cleanupImports(j, root); setupHooksForTest(setupTestTypes, j, root); setupCallbackHooks(callbackHooks, 'module', j, root); return beautifyImports( root.toSource({ quote: 'single', lineTerminator, trailingComma: false }) ); function transformSkippedTests(j, root, skipName = 'skip') { let collection = root.find(j.CallExpression, { callee: { property: { name: skipName } } }).forEach((path) => { let { node } = path; let name = (node.callee.object || node.callee).name; tranformObjectToCallee(j, path, name); }).forEach((path) => { j(path.parent).find(j.CallExpression) .filter(({node}) => { let name = (node.callee.object || node.callee).name; return (name === 'test'); }) .forEach((path) => { tranformObjectToCallee(j, path, skipName); }); }); if(collection.length > 0) { importSkip(root, j, skipName); } } function importSkip(root, j, name) { root.find(j.ImportDeclaration, { source: { value: 'qunit' } }).forEach((path) => { let hasImport = (j(path).find(j.ImportSpecifier, { imported: { name } }).length > 0); if(!hasImport) { path.node.specifiers.push(j.importSpecifier(j.identifier(name))); } }); } function tranformObjectToCallee(j, path, toName) { let node = path.node; let callee = node.callee.object || node.callee; path.node.callee = j.identifier(toName); } function filterOnlyFunctions(path) { let hasArrowFunction = (j(path).find(j.ArrowFunctionExpression).length === 0); let hasFunction = (j(path).find(j.FunctionExpression).length === 0); return (hasArrowFunction && hasFunction); } function pathHasExpects(path) { return (findExpect(path, j).length > 0); } function removeDoneMethod(path) { j(path).find(j.Identifier, { name: 'done' }) .closest(j.CallExpression) .remove(); } function transformerTests(path) { if(pathHasExpects(path)) { path.node.params = ['assert']; removeDoneMethod(path); } filterExpressions(j(path).find(j.ExpressionStatement)) .forEach(transformExpect); filterExpressions(j(path).find(j.ReturnStatement)) .forEach(returnExtraction); } function filterExpressions(graphTree) { return graphTree .filter(pathHasExpects) .filter(filterOnlyFunctions) } function returnExtraction(path) { var node = path.node.argument; var expression = runMacherTranformer(path.node.argument, path); var newNode = j(expression).find(j.ExpressionStatement).get(); path.node.argument = newNode.value.expression; } function memberAndCallExtraction(path) { var expression = runMacherTranformer(path.node.expression, path); var newNode = j(expression).find(j.ExpressionStatement).get(); path.node.expression = newNode.value.expression; } function awaitExtraction(path) { var expression = runMacherTranformer(path.node.expression.argument, path); var newNode = j(expression).find(j.ExpressionStatement).get(); path.node.expression.argument = newNode.value.expression; } function transformExpect(path) { switch (path.node.expression.type) { case 'CallExpression': case 'MemberExpression': memberAndCallExtraction(path); return; case 'AwaitExpression': awaitExtraction(path); return; } } function runMacherTranformer(expression, path) { var matchedExpression = j(expression).toSource(); var BreakException = {}; try { matcherTransformer .forEach(({ name, matcher, transformer }) => { if (matcher(expression, path, j, root)) { matchedExpression = transformer(expression, path, j, root); throw BreakException; } }); } catch (e) { if (e !== BreakException) throw e; } return matchedExpression; } }