@amadeus-it-group/protractor-to-playwright
Advanced tools
Comparing version 1.2.2 to 1.3.0
155
dist/cli.js
@@ -94,3 +94,3 @@ 'use strict'; | ||
const convertionErrors = []; | ||
const { src, dst, tsconfig, file: filePatterns, exclude, logfile, ambientFiles } = config; | ||
const { src, dst, tsconfig, file: filePatterns, exclude, logfile, ambientFiles, mergeContext, isContextToBeSaved } = config; | ||
const filebuffer = fs__namespace.createWriteStream(logfile); | ||
@@ -117,4 +117,5 @@ function log(msg, screen = true) { | ||
const filePatternsResolved = filePatterns.map(pattern => path__namespace.join(src, pattern)).concat(exclude.map(pattern => `!${path__namespace.join(src, pattern)}`)); | ||
const sourceFiles = project.addSourceFilesAtPaths(filePatternsResolved); | ||
const nbFiles = sourceFiles.length; | ||
const sourceFiles = new Set(project.addSourceFilesAtPaths(filePatternsResolved)); | ||
const nbFiles = sourceFiles.size; | ||
const savedContexts = new WeakMap(); | ||
// Log context information | ||
@@ -152,12 +153,22 @@ log(``); | ||
} | ||
/** | ||
* | ||
* @param node | ||
* @param parentTree Used to store information about the nodes | ||
*/ | ||
function processNode(node) { | ||
// Context filled by the children | ||
const context = {}; | ||
if (isContextToBeSaved(node)) { | ||
const existingContext = savedContexts.get(node); | ||
if (existingContext) { | ||
return existingContext; | ||
} | ||
savedContexts.set(node, context); | ||
} | ||
context.processing = true; | ||
node.forEachChild((childNode) => { | ||
Object.assign(context, processNode(childNode)); | ||
const subContext = processNode(childNode); | ||
if (subContext.processing) { | ||
const filetrace = getFileTrace(node); | ||
log(`Recursion error in ${filetrace}`); | ||
convertionErrors.push({ filetrace, error: new Error('Recursion error') }); | ||
return; | ||
} | ||
mergeContext(context, subContext, node, childNode); | ||
}); | ||
@@ -172,4 +183,11 @@ try { | ||
} | ||
delete context.processing; | ||
return context; | ||
} | ||
function publicProcessNode(node) { | ||
if (!isContextToBeSaved(node) || !sourceFiles.has(node.getSourceFile())) { | ||
return null; | ||
} | ||
return processNode(node); | ||
} | ||
const queue = []; | ||
@@ -211,3 +229,3 @@ function queueTransform(fn, node) { | ||
} | ||
const api = { srcPath, dstPath, transformFiles, queueTransform, log }; | ||
const api = { srcPath, dstPath, transformFiles, queueTransform, log, processNode: publicProcessNode }; | ||
return api; | ||
@@ -318,3 +336,4 @@ } | ||
const importFromProtractorRegExp = /^import\(".*?node_modules\/protractor\/.*?"\)/; | ||
const promiseRegExp = /^import\(".*?node_modules\/@types\/selenium-webdriver\/index"\)\.promise\.Promise<.*>$/; | ||
const protractorPromiseRegExp = /^import\(".*?node_modules\/@types\/selenium-webdriver\/index"\)\.promise\.Promise<.*>$/; | ||
const standardPromiseRegExp = /^Promise<.*>$/; | ||
const jasmineMatcherRegExp = /^jasmine\.(ArrayLike)?Matchers<.*>$/; | ||
@@ -358,2 +377,17 @@ const elementFinderRegExp = /(ElementFinder|ElementArrayFinder)$/; | ||
} | ||
const isFunction = (node) => tsMorph.Node.isFunctionDeclaration(node) || tsMorph.Node.isFunctionExpression(node) || tsMorph.Node.isArrowFunction(node) || tsMorph.Node.isMethodDeclaration(node); | ||
const isPromiseReturningFunction = (node) => { | ||
if (node.isAsync()) { | ||
return true; | ||
} | ||
const type = node.getReturnType().getText(); | ||
return standardPromiseRegExp.test(type) || protractorPromiseRegExp.test(type); | ||
}; | ||
function mergeContext(parentContext, childContext, parentNode, childNode) { | ||
if (isFunction(childNode)) { | ||
childContext = Object.assign({}, childContext); // avoid changing the child context (which is saved) | ||
delete childContext.addingAwait; // addingAwait must not pass through function boundaries | ||
} | ||
Object.assign(parentContext, childContext); | ||
} | ||
function getTransformNode({ stepStrategy }) { | ||
@@ -387,2 +421,13 @@ let isUtilsFileCreated = false; | ||
} | ||
else if (isFunction(node)) { | ||
if (context.addingAwait) { | ||
// the "await" keyword was added in the function | ||
if (!node.isAsync()) { | ||
// make the function async: | ||
queueTransform(function () { | ||
node.setIsAsync(true); | ||
}, node); | ||
} | ||
} | ||
} | ||
else if (tsMorph.Node.isImportDeclaration(node)) { | ||
@@ -421,3 +466,4 @@ if (protractorModuleregExp.test(node.getModuleSpecifierValue())) { | ||
const expression = node.getExpression(); | ||
if (promiseRegExp.test(expression.getType().getText())) { | ||
if (!tsMorph.Node.isBinaryExpression(expression) && protractorPromiseRegExp.test(expression.getType().getText())) { | ||
context.addingAwait = true; | ||
queueTransform(function () { | ||
@@ -505,3 +551,50 @@ expression.replaceWithText(`await ${expression.getText()}`); | ||
} | ||
function addAwaitWithNeededParentheses(node) { | ||
let text = `await ${node.getText()}`; | ||
const parentNode = node.getParent(); | ||
const dontNeedParentheses = tsMorph.Node.isExpressionStatement(parentNode) || tsMorph.Node.isVariableDeclaration(parentNode) || tsMorph.Node.isBinaryExpression(parentNode); | ||
if (!dontNeedParentheses) { | ||
text = `(${text})`; | ||
} | ||
node.replaceWithText(text); | ||
} | ||
function checkDefinitionsAndAddAwaitIfNeeded(node, definitions, context, project) { | ||
const { queueTransform, processNode } = project; | ||
let needAwait = false; | ||
const processingContexts = []; | ||
definitions = [...definitions]; | ||
for (let defNode of definitions) { | ||
if (tsMorph.Node.isVariableDeclaration(defNode) || tsMorph.Node.isPropertyAssignment(defNode) || tsMorph.Node.isShorthandPropertyAssignment(defNode)) { | ||
defNode = defNode.getInitializer(); | ||
} | ||
if (isFunction(defNode) && !isPromiseReturningFunction(defNode)) { | ||
// check if the function will become async after transforms: | ||
const defContext = processNode(defNode); | ||
if (defContext) { | ||
if (defContext.addingAwait) { | ||
needAwait = true; | ||
} | ||
else if (defContext.processing) { | ||
processingContexts.push(defContext); | ||
} | ||
} | ||
} | ||
else if (tsMorph.Node.isIdentifier(defNode)) { | ||
definitions.push(...defNode.getDefinitionNodes()); | ||
} | ||
} | ||
if (needAwait) { | ||
context.addingAwait = true; | ||
queueTransform(() => addAwaitWithNeededParentheses(node), node); | ||
} | ||
else if (processingContexts.length > 0) { | ||
queueTransform(function () { | ||
if (processingContexts.some(value => value.addingAwait)) { | ||
addAwaitWithNeededParentheses(node); | ||
} | ||
}, node); | ||
} | ||
} | ||
function transformCallExpression(node, context, project) { | ||
var _a; | ||
const { queueTransform, log } = project; | ||
@@ -560,2 +653,3 @@ function addPageParameter(fn, comment = '') { | ||
else if (stepStrategy === 'step') { | ||
context.addingAwait = true; | ||
queueTransform(function () { | ||
@@ -618,3 +712,4 @@ expression.replaceWithText('test.step'); | ||
const firstArg = node.getArguments()[0]; | ||
if (firstArg && promiseRegExp.test(firstArg.getType().getText())) { | ||
if (firstArg && protractorPromiseRegExp.test(firstArg.getType().getText())) { | ||
context.addingAwait = true; | ||
queueTransform(function () { | ||
@@ -645,2 +740,5 @@ firstArg.replaceWithText(`await ${firstArg.getText()}`); | ||
} | ||
default: | ||
checkDefinitionsAndAddAwaitIfNeeded(node, expression.getDefinitionNodes(), context, project); | ||
break; | ||
} | ||
@@ -727,2 +825,8 @@ } | ||
break; | ||
case 'takeScreenshot': { | ||
queueTransform(function () { | ||
node.replaceWithText(`${expression.getExpression().getText()}.screenshot().then(buffer => buffer.toString('base64'))`); | ||
}, node); | ||
break; | ||
} | ||
default: | ||
@@ -767,2 +871,3 @@ log(color__default["default"].yellow(`${getFileTrace(node)} : missing transform for ElementFinder.${expressionName}`)); | ||
context.useLocatorArray = true; | ||
context.addingAwait = true; | ||
queueTransform(function () { | ||
@@ -827,2 +932,9 @@ expression.replaceWithText(`(await makeLocatorArray(${expression.getExpression().getText()}))./* FIXME: filter must be managed specifically */filter`); | ||
} | ||
case 'getTitle': { | ||
const newText = `${getPageText(node, context)}.title`; | ||
queueTransform(function () { | ||
expression.replaceWithText(newText); | ||
}, expression); | ||
break; | ||
} | ||
case 'executeScript': { | ||
@@ -832,2 +944,9 @@ transformExecuteScript(node, context, project); | ||
} | ||
case 'takeScreenshot': { | ||
const newText = `${getPageText(node, context)}.screenshot().then(buffer => buffer.toString('base64'))`; | ||
queueTransform(function () { | ||
node.replaceWithText(newText); | ||
}, node); | ||
break; | ||
} | ||
default: | ||
@@ -959,2 +1078,8 @@ log(color__default["default"].yellow(`${getFileTrace(node)} : missing transform for ProtractorBrowser.${expressionName}`)); | ||
} | ||
else { | ||
const declarations = (_a = left.getType().getProperty(expression.getName())) === null || _a === void 0 ? void 0 : _a.getDeclarations(); | ||
if (declarations) { | ||
checkDefinitionsAndAddAwaitIfNeeded(node, declarations, context, project); | ||
} | ||
} | ||
} | ||
@@ -1151,3 +1276,5 @@ } | ||
logfile: path__default["default"].resolve(cwd, logfile), | ||
ambientFiles: [require.resolve("../global.d.ts")] | ||
ambientFiles: [require.resolve("../global.d.ts")], | ||
mergeContext, | ||
isContextToBeSaved: isFunction | ||
}, getTransformNode({ stepStrategy: test })); | ||
@@ -1154,0 +1281,0 @@ project.transformFiles(); |
{ | ||
"name": "@amadeus-it-group/protractor-to-playwright", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "Command line tool that automatically migrates tests from protractor to playwright.", | ||
@@ -35,21 +35,21 @@ "author": "Fabrice Basso <fabrice.basso@amadeus.com>", | ||
"devDependencies": { | ||
"@babel/core": "7.18.2", | ||
"@babel/plugin-syntax-typescript": "7.17.12", | ||
"@playwright/test": "^1.22.2", | ||
"@rollup/plugin-typescript": "^8.3.2", | ||
"@babel/core": "7.18.13", | ||
"@babel/plugin-syntax-typescript": "7.18.6", | ||
"@playwright/test": "^1.25.1", | ||
"@rollup/plugin-typescript": "^8.4.0", | ||
"@types/cli-progress": "^3.11.0", | ||
"@types/glob": "^7.2.0", | ||
"@types/jasmine": "^4.0.3", | ||
"@types/glob": "^8.0.0", | ||
"@types/jasmine": "^4.3.0", | ||
"@types/jasminewd2": "^2.0.10", | ||
"@types/yargs": "^17.0.10", | ||
"@typescript-eslint/eslint-plugin": "^5.27.0", | ||
"@typescript-eslint/parser": "^5.27.0", | ||
"@types/yargs": "^17.0.12", | ||
"@typescript-eslint/eslint-plugin": "^5.36.1", | ||
"@typescript-eslint/parser": "^5.36.1", | ||
"babel-plugin-istanbul": "^6.1.1", | ||
"eslint": "^8.16.0", | ||
"eslint": "^8.23.0", | ||
"glob": "^8.0.3", | ||
"nyc": "^15.1.0", | ||
"rollup": "^2.75.4", | ||
"ts-node": "^10.8.0", | ||
"rollup": "^2.79.0", | ||
"ts-node": "^10.9.1", | ||
"tslib": "^2.4.0", | ||
"typescript": "^4.7.2" | ||
"typescript": "^4.8.2" | ||
}, | ||
@@ -59,4 +59,4 @@ "dependencies": { | ||
"protractor": "^7.0.0", | ||
"cli-progress": "^3.11.1", | ||
"ts-morph": "^15.0.0", | ||
"cli-progress": "^3.11.2", | ||
"ts-morph": "^15.1.0", | ||
"yargs": "^17.5.1" | ||
@@ -63,0 +63,0 @@ }, |
75991
1439
Updatedcli-progress@^3.11.2
Updatedts-morph@^15.1.0