Skip to content
This repository has been archived by the owner on Sep 9, 2019. It is now read-only.

Commit

Permalink
feat(ast): support asyncCallbackName option (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
azu authored Jan 18, 2019
1 parent 54ada0c commit 130ab2d
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 18 deletions.
67 changes: 61 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,49 @@
# comment-to-assert

Convert comment to `assert()` function.
Convert comment to `assert` function.

```js
var foo = 1;
const foo = 1;
foo;// => 1
```

Convert this to:

```js
var foo = 1;
assert.equal(foo, 1);
const foo = 1;
assert.strictEqual(foo, 1);
```

## Syntax

This library support following format.

```
expression; // => expected value
```

or

```
console.log(expression); // => expected value
```

**Special handling**:

Error:

```js
throw new Error("message"); // Error: "message"
```

Promise:

```js
Promise.resolve(1); // => Promise: 1
```

If you need to callback for promise, use `asyncCallbackName` option.

## Installation

npm install comment-to-assert
Expand All @@ -25,7 +55,7 @@ assert.equal(foo, 1);

## Usage

### toAssertFromSource(source : string): string
### toAssertFromSource(source : string, options: toAssertFromSourceOptions): string

Return string that transformed source string of arguments.

Expand All @@ -40,7 +70,21 @@ toAssertFromSource("1;// => 1");// => "assert.equal(1, 1)"
`toAssertFromSource` only support transform source code.
if want to source map, should use `toAssertFromAST` with own parser and generator.

### toAssertFromAST(AST : object): object
**Options:**

- `asyncCallbackName`: callback name when promise is resolved or rejected
- `babel`: [@babel/core](https://babeljs.io/docs/en/babel-core) option

```
interface toAssertFromSourceOptions {
asyncCallbackName?: string;
babel?: {
plugins: string[];
};
}
```

### toAssertFromAST(AST : object, options: toAssertFromASTOptions): object

Return AST object that transformed AST of arguments.

Expand All @@ -55,6 +99,17 @@ assert.deepEqual(a, [1]);
*/
```


**Options:**

- `asyncCallbackName`: callback name when promise is resolved or rejected

```
export interface toAssertFromASTOptions {
asyncCallbackName?: string;
}
```

### Example

See [example/](example/)
Expand Down
26 changes: 22 additions & 4 deletions src/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ function extractionBody(ast: any) {
export const ERROR_COMMENT_PATTERN = /^([a-zA-Z]*?Error)/;
export const PROMISE_COMMENT_PATTERN = /^Promise:\s*(.*?)\s*$/;

export function wrapAssert(actualNode: any, expectedNode: any): any {
export interface wrapAssertOptions {
asyncCallbackName?: string;
}

export function wrapAssert(actualNode: any, expectedNode: any, options: wrapAssertOptions = {}): any {
assert.notEqual(typeof expectedNode, "undefined");
const type = expectedNode.type || extractionBody(expectedNode).type;
const ACTUAL_NODE = actualNode;
Expand All @@ -48,12 +52,26 @@ export function wrapAssert(actualNode: any, expectedNode: any): any {
});
} else if (type === "Promise") {
const ARGS = isConsole(actualNode) ? actualNode.arguments[0] : actualNode;
return template`Promise.resolve(ARGS).then(v => {
// support callback
const asyncCallbackName = options.asyncCallbackName;
if (asyncCallbackName) {
return template`Promise.resolve(ARGS).then(v => {
${wrapAssert({ type: "Identifier", name: "v" }, expectedNode.node)}
${asyncCallbackName}(null, v);
return v;
}).catch(error => {
${asyncCallbackName}(error);
})`({
ARGS
});
} else {
return template`Promise.resolve(ARGS).then(v => {
${wrapAssert({ type: "Identifier", name: "v" }, expectedNode.node)}
return v;
});`({
ARGS
});
ARGS
});
}
} else if (isIdentifier(expectedNode) && expectedNode.name === "NaN") {
return template`assert.ok(isNaN(ACTUAL_NODE));`({
ACTUAL_NODE
Expand Down
21 changes: 16 additions & 5 deletions src/comment-to-assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ function getExpressionNodeFromCommentValue(string: string): { type: string } & {
}

export interface toAssertFromSourceOptions {
babel: {
asyncCallbackName?: string;
babel?: {
plugins: string[];
};
}
Expand All @@ -47,23 +48,33 @@ export function toAssertFromSource(code: string, options?: toAssertFromSourceOpt
const ast = parse(code, {
// parse in strict mode and allow module declarations
sourceType: "module",
plugins: (options && options.babel.plugins) || []
plugins: (options && options.babel && options.babel.plugins) || []
});
if (!ast) {
throw new Error("Can not parse the code");
}
const output = toAssertFromAST(ast);
const toAssertOptions =
options && options.asyncCallbackName !== undefined
? {
asyncCallbackName: options.asyncCallbackName
}
: {};
const output = toAssertFromAST(ast, toAssertOptions);
const babelFileResult = transformFromAstSync(output, code, { comments: true });
if (!babelFileResult) {
throw new Error("can not generate from ast: " + JSON.stringify(output));
}
return babelFileResult.code;
}

export interface toAssertFromASTOptions {
asyncCallbackName?: string;
}

/**
* transform AST to asserted AST.
*/
export function toAssertFromAST(ast: ParseResult) {
export function toAssertFromAST(ast: ParseResult, options: toAssertFromASTOptions = {}) {
const replaceSet = new Set();
traverse(ast, {
exit(path) {
Expand All @@ -72,7 +83,7 @@ export function toAssertFromAST(ast: ParseResult) {
if (commentExpression) {
const commentExpressionNode = getExpressionNodeFromCommentValue(commentExpression);
const actualNode = isExpressionStatement(path.node) ? path.node.expression : path.node;
const replacement = wrapAssert(actualNode, commentExpressionNode);
const replacement = wrapAssert(actualNode, commentExpressionNode, options);
path.replaceWith(replacement);
replaceSet.add(path.node);
}
Expand Down
20 changes: 17 additions & 3 deletions test/snapshot-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const fixturesDir = path.join(__dirname, "snapshots");
describe("Snapshot testing", () => {
fs.readdirSync(fixturesDir).map(caseName => {
const normalizedTestName = caseName.replace(/-/g, " ");
it(`Test ${normalizedTestName}`, function() {
it(`Test ${normalizedTestName}`, function(done) {
const fixtureDir = path.join(fixturesDir, caseName);
const actualFilePath = path.join(fixtureDir, "input.js");
const actualContent = fs.readFileSync(actualFilePath, "utf-8");
const actual = toAssertFromSource(actualContent);
const optionFilePath = path.join(fixtureDir, "options.js");
const options = fs.existsSync(optionFilePath) ? require(optionFilePath) : {};
const actual = toAssertFromSource(actualContent, options);
const expectedFilePath = path.join(fixtureDir, "output.js");
// UPDATE_SNAPSHOT=1 npm test
if (!fs.existsSync(expectedFilePath) || process.env.UPDATE_SNAPSHOT) {
Expand All @@ -29,13 +31,25 @@ ${fixtureDir}
${JSON.stringify(actual)}
`
);
if (typeof actual === "string") {
if (typeof actual !== "string") {
throw new Error("actual is not string");
}
if (options.asyncCallbackName) {
vm.runInContext(
actual,
vm.createContext({
assert,
done
})
);
} else {
vm.runInContext(
actual,
vm.createContext({
assert
})
);
done();
}
});
});
Expand Down
1 change: 1 addition & 0 deletions test/snapshots/Promise.resolve.asyncCallback/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Promise.resolve(1); // => Promise: 1
3 changes: 3 additions & 0 deletions test/snapshots/Promise.resolve.asyncCallback/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
asyncCallbackName: "done"
};
7 changes: 7 additions & 0 deletions test/snapshots/Promise.resolve.asyncCallback/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Promise.resolve(Promise.resolve(1)).then(v => {
assert.strictEqual(v, 1);
done(null, v);
return v;
}).catch(error => {
done(error);
}); // => Promise: 1

0 comments on commit 130ab2d

Please sign in to comment.