Skip to content

Commit

Permalink
feat: staticTruthCheck option
Browse files Browse the repository at this point in the history
  • Loading branch information
jeysal committed Mar 30, 2018
1 parent 8ecb9ee commit 5a2da53
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 1 deletion.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,19 @@ in your test files wherever you use assertion blocks.

type: `boolean | string`
default: `true` (`'power-assert'`)

**`staticTruthCheck`**

The plugin can try to statically evaluate your assertion expressions at compile-time
and throw an error if they can be inferred to always be truthy or to always be falsy.
Such expressions sometimes indicate a test that does not provide any value.

Here's an example of an assertion expression that can be inferred to always be truthy:

```javascript
const x = 1;
expect: x === 1;
```

type: `boolean`
default: `false`
6 changes: 6 additions & 0 deletions src/assertify-statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NodePath } from '@babel/traverse';
import * as BabelTypes from '@babel/types';
import createEspowerVisitor from 'babel-plugin-espower/create';

import checkStatically from './check-statically';
import { InternalConfig } from './config';
import generateAssertIdentifier from './generate-assert-identifier';

Expand All @@ -14,6 +15,11 @@ export default (
const { scope, node: statement } = statementPath;

if (t.isExpressionStatement(statement)) {
const expressionPath = statementPath.get('expression') as NodePath<
BabelTypes.Expression
>;
checkStatically(expressionPath, config);

const origExpr = statement.expression;

const assertIdentifier = generateAssertIdentifier(t, config)(scope);
Expand Down
21 changes: 21 additions & 0 deletions src/check-statically.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NodePath } from 'babel-traverse';
import { Expression } from 'babel-types';

import { InternalConfig } from './config';

export default (
expressionPath: NodePath<Expression>,
{ staticTruthCheck }: InternalConfig,
) => {
if (!staticTruthCheck) return;

const truthy: boolean | undefined = expressionPath.evaluateTruthy();

if (truthy !== undefined) {
throw expressionPath.buildCodeFrameError(
`Expression statement in assertion is always ${
truthy ? 'truthy' : 'falsy'
}`,
);
}
};
11 changes: 10 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export interface InternalConfig {
powerAssert: boolean;
autoImport: string; // empty => no auto import
staticTruthCheck: boolean;
}

/**
Expand All @@ -13,16 +14,18 @@ export interface InternalConfig {
export interface Config {
powerAssert?: boolean;
autoImport?: boolean | string; // false => '', true => 'power-assert'
staticTruthCheck?: boolean;
}

export const defaultConfig = {
powerAssert: true,
autoImport: true,
staticTruthCheck: false,
};

// tslint:disable no-parameter-reassignment
export const extractConfigFromState = ({
opts: { powerAssert, autoImport },
opts: { powerAssert, autoImport, staticTruthCheck },
}: {
opts: Config;
}): InternalConfig => {
Expand All @@ -42,8 +45,14 @@ export const extractConfigFromState = ({
autoImport = 'power-assert';
}

// staticTruthCheck
if (staticTruthCheck === undefined) {
({ staticTruthCheck } = defaultConfig);
}

return {
powerAssert,
autoImport,
staticTruthCheck,
};
};
17 changes: 17 additions & 0 deletions test/__snapshots__/static-truth-check.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`throws if an expression can be inferred to always be falsy 1`] = `
"undefined: Expression statement in assertion is always falsy
1 | import assert from 'power-assert';
2 | const x = 0;
> 3 | expect: x === 1;
| ^"
`;

exports[`throws if an expression can be inferred to always be truthy 1`] = `
"undefined: Expression statement in assertion is always truthy
1 | import assert from 'power-assert';
2 | const x = 1;
> 3 | expect: x === 1;
| ^"
`;
72 changes: 72 additions & 0 deletions test/static-truth-check.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { transform } from '@babel/core';

import plugin from '../src';
import { Config } from '../src/config';

test('throws if an expression can be inferred to always be truthy', () => {
expect(() =>
transform(
`import assert from 'power-assert';
const x = 1;
expect: x === 1;`,
{
plugins: [
[
plugin,
{
powerAssert: false,
autoImport: false,
staticTruthCheck: true,
} as Config,
],
],
highlightCode: false,
},
),
).toThrowErrorMatchingSnapshot();
});

test('throws if an expression can be inferred to always be falsy', () => {
expect(() =>
transform(
`import assert from 'power-assert';
const x = 0;
expect: x === 1;`,
{
plugins: [
[
plugin,
{
powerAssert: false,
autoImport: false,
staticTruthCheck: true,
} as Config,
],
],
highlightCode: false,
},
),
).toThrowErrorMatchingSnapshot();
});

test('does not throw if an expression may or may not be truthy', () => {
expect(() =>
transform(
`import assert from 'power-assert';
import sut from './sut';
expect: sut() === 42;`,
{
plugins: [
[
plugin,
{
powerAssert: false,
autoImport: false,
staticTruthCheck: true,
} as Config,
],
],
},
),
).not.toThrowError();
});

0 comments on commit 5a2da53

Please sign in to comment.