UNPKG

babel-plugin-gwt

Version:

Data Driven Testing babel plugin inspired by Groovy's Spock framework

183 lines (159 loc) 5.64 kB
module.exports = gwtPlugin; const getLeftFirstData = node => { let acc = []; if (node.left) { acc.push(...getLeftFirstData(node.left)); } if (node.right) { acc.push(...getLeftFirstData(node.right)); } if (!node.left && !node.right) { acc.push(node); } return acc; }; const hasBodyFunction = index => args => looksLike(args[index], { type: t => t === 'ArrowFunctionExpression' || t === 'FunctionExpression' }); function gwtPlugin({ types: t, transform }) { return { name: 'gwt', visitor: { ExpressionStatement(path) { const isTestBlock = looksLike(path, { node: { expression: { callee: { type: 'Identifier', name: n => n === 'it' || n === 'test' || n === 'fit' || n === 'ftest' || n === 'xit' || n === 'xtest' } } } }); const isOnlyBlock = looksLike(path, { node: { expression: { callee: { type: 'MemberExpression', object: { type: 'Identifier', name: n => n === 'it' || n === 'test' }, property: { type: 'Identifier', name: 'only' } }, arguments: hasBodyFunction(1) } } }); const isSkipBlock = looksLike(path, { node: { expression: { callee: { type: 'MemberExpression', object: { type: 'Identifier', name: n => n === 'it' || n === 'test' }, property: { type: 'Identifier', name: 'skip' } }, arguments: hasBodyFunction(1) } } }); console.log(transform('const a = 1')) if (!isTestBlock && !isOnlyBlock && !isSkipBlock) return; const testType = path.node.expression.callee; const getLabelTitle = (labels, name) => { const label = labels.find(stmt => looksLike(stmt.label, { type: 'Identifier', name })); // todo add maybe.fromNullable return label ? label.body.expression.value : ''; }; const getWhereRows = labels => { const label = labels.find(stmt => looksLike(stmt.label, { type: 'Identifier', name: 'where' })); // todo add maybe.fromNullable if (label) { return label.body.body.map(node => getLeftFirstData(node.expression)); } return []; }; const labels = path.node.expression.arguments[1].body.body.filter(stmt => t.isLabeledStatement(stmt)); const given = getLabelTitle(labels, 'given'); const when = getLabelTitle(labels, 'when'); const then = getLabelTitle(labels, 'then'); const whereRows = getWhereRows(labels); const whereTitles = whereRows.length > 0 ? whereRows[0].map(({ name }) => name) : []; if (!given && !when && !then && !whereTitles.length && !whereRows.length) return; let testTitle = path.node.expression.arguments[0].value; if (given) { testTitle = `${testTitle} given: ${given}`; } if (when) { testTitle = `${testTitle} when: ${when}`; } if (then) { testTitle = `${testTitle} then: ${then}`; } // t.expressionStatement(expression) // t.callExpression(callee, arguments) // t.memberExpression(object, property, computed, optional) // t.arrowFunctionExpression(params, body, async) // t.templateLiteral(quasis, expressions) const quasi = testTitle .split(/#\w+/g) .map((a, index) => t.templateElement({ raw: a }, index == testTitle.split(/\$\{\w+\}/g).length - 1)); const expressions = (testTitle.match(/#\w+/g) || []).map(s => t.identifier(s.substring(1))); const testTitleAst = t.templateLiteral(quasi, expressions); const testRows = whereRows.slice(1).map(row => t.objectExpression( whereTitles.map((title, index) => { return t.objectProperty(t.identifier(title), row[index]); }) ) ); const testDataTitles = whereTitles.map(title => t.objectProperty(t.identifier(title), t.identifier(title), false, true) ); const body = path.node.expression.arguments[1].body.body.filter(stmt => !t.isLabeledStatement(stmt)); const y = t.expressionStatement( t.callExpression(t.memberExpression(t.arrayExpression(testRows), t.identifier('forEach')), [ t.arrowFunctionExpression( [t.objectPattern(testDataTitles)], t.blockStatement([ t.expressionStatement( t.callExpression(testType, [testTitleAst, t.arrowFunctionExpression([], t.blockStatement(body))]) ) ]) ) ]) ); path.replaceWith(y); } } }; } function looksLike(a, b) { return ( a && b && Object.keys(b).every(bKey => { const bVal = b[bKey]; const aVal = a[bKey]; if (typeof bVal === 'function') { return bVal(aVal); } return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal); }) ); } function isPrimitive(val) { return val == null || /^[sbn]/.test(typeof val); }