@travetto/test
Advanced tools
Comparing version 0.0.50 to 0.0.51
@@ -30,3 +30,3 @@ { | ||
}, | ||
"version": "0.0.50" | ||
"version": "0.0.51" | ||
} |
@@ -18,16 +18,16 @@ import { AppEnv, isPlainObject, isFunction, isPrimitive } from '@travetto/base'; | ||
const OP_MAPPING: { [key: string]: string } = { | ||
includes: '{expected} should include {actual}', | ||
test: '{expected} should match {actual}', | ||
throws: 'should throw {expected}', | ||
doesNotThrow: 'should not throw {expected}', | ||
equal: '{actual} should equal {expected}', | ||
notEqual: '{actual} should not equal {expected}', | ||
deepEqual: '{actual} should deep equal {expected}', | ||
notDeepEqual: '{actual} should not deep equal {expected}', | ||
strictEqual: '{actual} should strictly equal {expected}', | ||
notStrictEqual: '{actual} should strictly not equal {expected}', | ||
greaterThanEqual: '{actual} should be greater than or equal to {expected}', | ||
greaterThan: '{actual} should be greater than {expected}', | ||
lessThanEqual: '{actual} should be less than or equal to {expected}', | ||
lessThan: '{actual} should be less than {expected}' | ||
includes: '{expected} {state} include {actual}', | ||
test: '{expected} {state} match {actual}', | ||
throws: '{state} throw {expected}', | ||
doesNotThrow: '{state} not throw {expected}', | ||
equal: '{actual} {state} equal {expected}', | ||
notEqual: '{actual} {state} not equal {expected}', | ||
deepEqual: '{actual} {state} deep equal {expected}', | ||
notDeepEqual: '{actual} {state} not deep equal {expected}', | ||
strictEqual: '{actual} {state} strictly equal {expected}', | ||
notStrictEqual: '{actual} {state} strictly not equal {expected}', | ||
greaterThanEqual: '{actual} {state} be greater than or equal to {expected}', | ||
greaterThan: '{actual} {state} be greater than {expected}', | ||
lessThanEqual: '{actual} {state} be less than or equal to {expected}', | ||
lessThan: '{actual} {state} be less than {expected}' | ||
} | ||
@@ -82,3 +82,3 @@ | ||
static check(filename: string, text: string, name: string, ...args: any[]) { | ||
static check(filename: string, text: string, fn: string, positive: boolean, ...args: any[]) { | ||
const { file, line } = this.readFilePosition(new Error(), filename.replace(/[.][tj]s$/, '')); | ||
@@ -90,5 +90,12 @@ | ||
file, line, text, | ||
operator: ASSERT_FN_OPERATOR[name] | ||
operator: ASSERT_FN_OPERATOR[fn], | ||
}; | ||
if (name === 'fail') { | ||
const common: { [key: string]: string } = { | ||
state: positive ? 'should' : 'should not' | ||
}; | ||
const asrt = positive ? assert : (x: any, msg?: string) => assert(!x, msg); | ||
if (fn === 'fail') { | ||
if (args.length > 1) { | ||
@@ -102,3 +109,3 @@ assertion.actual = args[0]; | ||
} | ||
} else if (/throw/i.test(name)) { | ||
} else if (/throw/i.test(fn)) { | ||
assertion.operator = 'throw'; | ||
@@ -111,3 +118,3 @@ if (typeof args[1] !== 'string') { | ||
} | ||
} else if (name === 'ok' || name === 'assert') { | ||
} else if (fn === 'ok' || fn === 'assert') { | ||
assertion.actual = args[0]; | ||
@@ -118,3 +125,3 @@ assertion.message = args[1]; | ||
} else { | ||
assertion.operator = name || ''; | ||
assertion.operator = fn || ''; | ||
assertion.message = args[2]; | ||
@@ -134,15 +141,15 @@ assertion.expected = args[1]; | ||
switch (name) { | ||
case 'instanceOf': assert(args[0] instanceof args[1], args[2]); break; | ||
case 'lessThan': assert(args[0] < args[1], args[2]); break; | ||
case 'lessThanEqual': assert(args[0] <= args[1], args[2]); break; | ||
case 'greaterThan': assert(args[0] > args[1], args[2]); break; | ||
case 'greaterThanEqual': assert(args[0] >= args[1], args[2]); break; | ||
switch (fn) { | ||
case 'instanceOf': asrt(args[0] instanceof args[1], args[2]); break; | ||
case 'lessThan': asrt(args[0] < args[1], args[2]); break; | ||
case 'lessThanEqual': asrt(args[0] <= args[1], args[2]); break; | ||
case 'greaterThan': asrt(args[0] > args[1], args[2]); break; | ||
case 'greaterThanEqual': asrt(args[0] >= args[1], args[2]); break; | ||
default: | ||
if (name && (assert as any)[name]) { // Assert call | ||
(assert as any)[name].apply(null, args); | ||
} else if (args[1] && name && args[1][name]) { // Method call | ||
assert(args[1][name](args[0])); | ||
if (fn && (assert as any)[fn]) { // Assert call | ||
(assert as any)[fn].apply(null, args); | ||
} else if (args[1] && fn && args[1][fn]) { // Method call | ||
asrt(args[1][fn](args[0])); | ||
} else { | ||
assert.apply(null, args); // Do normal | ||
asrt.apply(null, args); // Do normal | ||
} | ||
@@ -156,5 +163,7 @@ } | ||
if (!assertion.message) { | ||
assertion.message = (OP_MAPPING[name] || `should be {expected}`); | ||
assertion.message = (OP_MAPPING[fn] || `{state} be {expected}`); | ||
} | ||
assertion.message = assertion.message.replace(/[{]([A-Za-z]+)[}]/g, (a, k) => (assertion as any)[k]); | ||
assertion.message = assertion.message | ||
.replace(/[{]([A-Za-z]+)[}]/g, (a, k) => common[k] || (assertion as any)[k]) | ||
.replace(/not not/g, 'not'); // Handle double negatives | ||
assertion.error = e; | ||
@@ -161,0 +170,0 @@ this.add(assertion); |
@@ -43,2 +43,8 @@ import * as ts from 'typescript'; | ||
interface Command { | ||
fn: string; | ||
args: ts.Expression[]; | ||
negate?: boolean; | ||
} | ||
function isDeepLiteral(node: ts.Expression) { | ||
@@ -49,3 +55,3 @@ return ts.isArrayLiteralExpression(node) || | ||
function doAssert<T extends ts.CallExpression>(state: AssertState, node: T, name: string, args: ts.Expression[]): T { | ||
function doAssert<T extends ts.CallExpression>(state: AssertState, node: T, cmd: Command): T { | ||
prepAssert(state); | ||
@@ -56,19 +62,12 @@ | ||
// Handle METHOD | ||
if (METHOD_REGEX.test(firstText)) { | ||
if (first && ts.isCallExpression(first) && ts.isPropertyAccessExpression(first.expression)) { | ||
name = METHODS[first.expression.name.text!]; | ||
args = [first.arguments[0], first.expression.expression]; | ||
} | ||
} | ||
args = args.filter(x => x !== undefined && x !== null); | ||
cmd.args = cmd.args.filter(x => x !== undefined && x !== null); | ||
const check = ts.createCall(state.assertCheck, undefined, ts.createNodeArray([ | ||
ts.createLiteral('__filename'), | ||
ts.createIdentifier('__filename'), | ||
ts.createLiteral(firstText), | ||
ts.createLiteral(name), | ||
...args | ||
ts.createLiteral(cmd.fn), | ||
ts.createLiteral(!cmd.negate), | ||
...cmd.args | ||
])); | ||
for (const arg of args) { | ||
for (const arg of cmd.args) { | ||
arg.parent = check; | ||
@@ -94,36 +93,53 @@ } | ||
function visitNode<T extends ts.Node>(context: ts.TransformationContext, node: T, state: AssertState): T { | ||
let replaced = false; | ||
function getCommand(args: ts.Expression[] | ts.NodeArray<ts.Expression>): Command | undefined { | ||
if (ts.isCallExpression(node)) { | ||
const exp: ts.Expression = node.expression; | ||
if (ts.isIdentifier(exp) && exp.getText() === ASSERT_CMD) { | ||
replaced = true; | ||
const comp = args[0]!; | ||
const message = args.length === 2 ? args[1] : undefined; | ||
const comp = node.arguments[0]!; | ||
const message = node.arguments.length === 2 ? node.arguments[1] : undefined; | ||
if (ts.isBinaryExpression(comp)) { | ||
let opFn = OPTOKEN_ASSERT_FN[comp.operatorToken.kind]; | ||
if (ts.isBinaryExpression(comp)) { | ||
let opFn = OPTOKEN_ASSERT_FN[comp.operatorToken.kind]; | ||
if (opFn) { | ||
const literal = isDeepLiteral(comp.left) ? comp.left : isDeepLiteral(comp.right) ? comp.right : undefined; | ||
if (/equal/i.test(opFn) && literal) { | ||
opFn = EQUALS_MAPPING[opFn] || opFn; | ||
} | ||
return { fn: opFn, args: [comp.left, comp.right, message!] }; | ||
} else { | ||
return { fn: ASSERT_CMD, args: [...args] }; | ||
} | ||
if (opFn) { | ||
const literal = isDeepLiteral(comp.left) ? comp.left : isDeepLiteral(comp.right) ? comp.right : undefined; | ||
if (/equal/i.test(opFn) && literal) { | ||
opFn = EQUALS_MAPPING[opFn] || opFn; | ||
} | ||
} else if (ts.isPrefixUnaryExpression(comp) && comp.operator === ts.SyntaxKind.ExclamationToken) { | ||
if (ts.isPrefixUnaryExpression(comp.operand)) { | ||
const inner = comp.operand.operand; | ||
return { fn: 'ok', args: [inner, message!] }; | ||
} else { | ||
const inner = comp.operand; | ||
return { ...getCommand([inner])!, negate: true }; | ||
} | ||
} else { | ||
// Handle METHOD | ||
const firstText = comp.getText(); | ||
if (METHOD_REGEX.test(firstText) && ts.isCallExpression(comp) && ts.isPropertyAccessExpression(comp.expression)) { | ||
return { | ||
fn: METHODS[comp.expression.name.text!], | ||
args: [comp.arguments[0], comp.expression.expression] | ||
}; | ||
} else { | ||
return { fn: ASSERT_CMD, args: [...args] }; | ||
} | ||
} | ||
} | ||
node = doAssert(state, node, opFn, [comp.left, comp.right, message!]); | ||
} else { | ||
node = doAssert(state, node, ASSERT_CMD, [...node.arguments]); | ||
} | ||
function visitNode<T extends ts.Node>(context: ts.TransformationContext, node: T, state: AssertState): T { | ||
} else if (ts.isPrefixUnaryExpression(comp) && comp.operator === ts.SyntaxKind.ExclamationToken) { | ||
if (ts.isPrefixUnaryExpression(comp.operand)) { | ||
const inner = comp.operand.operand; | ||
node = doAssert(state, node, 'ok', [inner, message!]); // !!v | ||
} else { | ||
node = doAssert(state, node, ASSERT_CMD, [comp, message!]); // !v | ||
} | ||
} else { | ||
node = doAssert(state, node, ASSERT_CMD, [...node.arguments]); | ||
let replaced = false; | ||
if (ts.isCallExpression(node)) { | ||
const exp = node.expression; | ||
if (ts.isIdentifier(exp) && exp.getText() === ASSERT_CMD) { | ||
const cmd = getCommand(node.arguments); | ||
if (cmd) { | ||
node = doAssert(state, node, cmd); | ||
replaced = true; | ||
} | ||
@@ -133,6 +149,4 @@ } else if (ts.isPropertyAccessExpression(exp) && ts.isIdentifier(exp.expression)) { | ||
if (ident.escapedText === ASSERT_CMD) { | ||
node = doAssert(state, node, { fn: exp.name.escapedText as string, args: [...node.arguments] }); | ||
replaced = true; | ||
node = doAssert(state, node, exp.name.escapedText as string, [...node.arguments]); | ||
// Already in near, final form, just rewrite to intermediate | ||
} | ||
@@ -139,0 +153,0 @@ } |
49007
1384