Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase test coverage #4742

Merged
merged 7 commits into from Oct 23, 2016
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/babel-traverse/src/path/inference/inferers.js
Expand Up @@ -137,7 +137,13 @@ function Func() {
return t.genericTypeAnnotation(t.identifier("Function"));
}

export { Func as Function, Func as Class };
export {
Func as FunctionExpression,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be breaking if we remove Func as Function, Func as Class? (although maybe it's not used at all)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I interpreted it to be an implementation detail, where getTypeAnnotation(), baseTypeStrictlyMatches() etc are the actual API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And IIRC this was actually broken the old way - i.e. whoever wrote this was clearly expecting some code to expand Function and Class a la visitor method names, and it wasn't happening. (But not tested so it only came up when I tackled coverage)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 yeah we have a lot that might not be used anywhere

Func as ArrowFunctionExpression,
Func as FunctionDeclaration,
Func as ClassExpression,
Func as ClassDeclaration
};

export function CallExpression() {
return resolveCall(this.get("callee"));
Expand Down
163 changes: 142 additions & 21 deletions packages/babel-traverse/test/inference.js
@@ -1,9 +1,10 @@
let traverse = require("../lib").default;
let assert = require("assert");
let parse = require("babylon").parse;
import traverse from "../lib";
import assert from "assert";
import { parse } from "babylon";
import * as t from "babel-types";

function getPath(code) {
let ast = parse(code);
const ast = parse(code, {plugins: ["flow", "asyncGenerators"]});
let path;
traverse(ast, {
Program: function (_path) {
Expand All @@ -17,40 +18,160 @@ function getPath(code) {
describe("inference", function () {
describe("baseTypeStrictlyMatches", function () {
it("it should work with null", function () {
let path = getPath("var x = null; x === null").get("body")[1].get("expression");
let left = path.get("left");
let right = path.get("right");
let strictMatch = left.baseTypeStrictlyMatches(right);
const path = getPath("var x = null; x === null").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);

assert.ok(strictMatch, "null should be equal to null");
});

it("it should work with numbers", function () {
let path = getPath("var x = 1; x === 2").get("body")[1].get("expression");
let left = path.get("left");
let right = path.get("right");
let strictMatch = left.baseTypeStrictlyMatches(right);
const path = getPath("var x = 1; x === 2").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);

assert.ok(strictMatch, "null should be equal to null");
assert.ok(strictMatch, "number should be equal to number");
});

it("it should bail when type changes", function () {
let path = getPath("var x = 1; if (foo) x = null;else x = 3; x === 2").get("body")[2].get("expression");
let left = path.get("left");
let right = path.get("right");
const path = getPath("var x = 1; if (foo) x = null;else x = 3; x === 2").get("body")[2].get("expression");
const left = path.get("left");
const right = path.get("right");

let strictMatch = left.baseTypeStrictlyMatches(right);
const strictMatch = left.baseTypeStrictlyMatches(right);

assert.ok(!strictMatch, "type might change in if statement");
});

it("it should differentiate between null and undefined", function () {
let path = getPath("var x; x === null").get("body")[1].get("expression");
let left = path.get("left");
let right = path.get("right");
let strictMatch = left.baseTypeStrictlyMatches(right);
const path = getPath("var x; x === null").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);

assert.ok(!strictMatch, "null should not match undefined");
});
});
describe("getTypeAnnotation", function () {
it("should infer from type cast", function () {
const path = getPath("(x: number)").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");

});
it("should infer string from template literal", function () {
const path = getPath("`hey`").get("body")[0].get("expression");
assert.ok(t.isStringTypeAnnotation(path.getTypeAnnotation()), "should be string");
});
it("should infer number from +x", function () {
const path = getPath("+x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer T from new T", function () {
const path = getPath("new T").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "T", "should be T");
});
it("should infer number from ++x", function () {
const path = getPath("++x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer number from --x", function () {
const path = getPath("--x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer void from void x", function () {
const path = getPath("void x").get("body")[0].get("expression");
assert.ok(t.isVoidTypeAnnotation(path.getTypeAnnotation()), "should be void");
});
it("should infer string from typeof x", function () {
const path = getPath("typeof x").get("body")[0].get("expression");
assert.ok(t.isStringTypeAnnotation(path.getTypeAnnotation()), "should be string");
});
it("should infer boolean from !x", function () {
const path = getPath("!x").get("body")[0].get("expression");
assert.ok(t.isBooleanTypeAnnotation(path.getTypeAnnotation()), "should be boolean");
});
it("should infer type of sequence expression", function () {
const path = getPath("a,1").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer type of logical expression", function () {
const path = getPath("'a' && 1").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isStringTypeAnnotation(type.types[0]), "first type in union should be string");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer type of conditional expression", function () {
const path = getPath("q ? true : 0").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isBooleanTypeAnnotation(type.types[0]), "first type in union should be boolean");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer RegExp from RegExp literal", function () {
const path = getPath("/.+/").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "RegExp", "should be RegExp");
});
it("should infer Object from object expression", function () {
const path = getPath("({ a: 5 })").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Object", "should be Object");
});
it("should infer Array from array expression", function () {
const path = getPath("[ 5 ]").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Array", "should be Array");
});
it("should infer Function from function", function () {
const path = getPath("(function (): string {})").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Function", "should be Function");
});
it("should infer call return type using function", function () {
const path = getPath("(function (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isStringTypeAnnotation(type), "should be string");
});
it("should infer call return type using async function", function () {
const path = getPath("(async function (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Promise", "should be Promise");
});
it("should infer call return type using async generator function", function () {
const path = getPath("(async function * (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "AsyncIterator", "should be AsyncIterator");
});
it("should infer number from x/y", function () {
const path = getPath("x/y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isNumberTypeAnnotation(type), "should be number");
});
it("should infer boolean from x instanceof y", function () {
const path = getPath("x instanceof y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isBooleanTypeAnnotation(type), "should be boolean");
});
it("should infer number from 1 + 2", function () {
const path = getPath("1 + 2").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isNumberTypeAnnotation(type), "should be number");
});
it("should infer string|number from x + y", function () {
const path = getPath("x + y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isStringTypeAnnotation(type.types[0]), "first type in union should be string");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer type of tagged template literal", function () {
const path = getPath("(function (): RegExp {}) `hey`").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "RegExp", "should be RegExp");
});
});
});
147 changes: 147 additions & 0 deletions packages/babel-types/test/converters.js
@@ -0,0 +1,147 @@
import * as t from "../lib";
import { assert } from "chai";

describe("converters", function () {
describe("valueToNode", function () {
it("number", function () {
assert.deepEqual(t.valueToNode(Math.PI), t.numericLiteral(Math.PI));
assert.deepEqual(t.valueToNode(-Infinity), t.numericLiteral(-Infinity));
assert.deepEqual(t.valueToNode(NaN), t.numericLiteral(NaN));
});
it("string", function () {
assert.deepEqual(t.valueToNode("This is a \"string\""), t.stringLiteral("This is a \"string\""));
});
it("boolean", function () {
assert.deepEqual(t.valueToNode(true), t.booleanLiteral(true));
assert.deepEqual(t.valueToNode(false), t.booleanLiteral(false));
});
it("null", function () {
assert.deepEqual(t.valueToNode(null), t.nullLiteral());
});
it("undefined", function () {
assert.deepEqual(t.valueToNode(undefined), t.identifier("undefined"));
});
it("RegExp", function () {
assert.deepEqual(t.valueToNode(/abc.+/gm), t.regExpLiteral("abc.+", "gm"));
});
it("array", function () {
assert.deepEqual(t.valueToNode([1, "a"]), t.arrayExpression([t.numericLiteral(1), t.stringLiteral("a")]));
});
it("object", function () {
assert.deepEqual(t.valueToNode({
a: 1,
"b c": 2
}), t.objectExpression([
t.objectProperty(t.identifier("a"), t.numericLiteral(1)),
t.objectProperty(t.stringLiteral("b c"), t.numericLiteral(2))
]));
});
it("throws if cannot convert", function () {
assert.throws(function () {
t.valueToNode(Object);
});
assert.throws(function () {
t.valueToNode(Symbol());
});
});
});
describe("toKeyAlias", function () {
beforeEach(function () {
// make tests deterministic
t.toKeyAlias.uid = 0;
});
it("doesn't change string literals", function () {
assert.equal(t.toKeyAlias(t.objectProperty(t.stringLiteral("a"), t.nullLiteral())), "\"a\"");
});
it("wraps around at Number.MAX_SAFE_INTEGER", function () {
assert.equal(t.toKeyAlias(t.objectMethod("method", t.identifier("a"), [], t.blockStatement([]))), "0");
});
});
describe("toStatement", function () {
it("noop on statements", function () {
const node = t.emptyStatement();
assert.equal(t.toStatement(node), node);
t.assertEmptyStatement(node);
});
it("mutate class expression to declaration", function () {
const node = t.classExpression(t.identifier("A"), null, t.classBody([]), []);
t.toStatement(node);
t.assertClassDeclaration(node);
});
it("fail if class expression has no id", function () {
const node = t.classExpression(null, null, t.classBody([]), []);
assert.throws(function() {
t.toStatement(node);
});
assert.strictEqual(t.toStatement(node, /* ignore = */ true), false);
t.assertClassExpression(node);
});
it("mutate function expression to declaration", function () {
const node = t.functionExpression(t.identifier("A"), [], t.blockStatement([]));
t.toStatement(node);
t.assertFunctionDeclaration(node);
});
it("fail if function expression has no id", function () {
const node = t.functionExpression(null, [], t.blockStatement([]));
assert.throws(function() {
t.toStatement(node);
});
assert.strictEqual(t.toStatement(node, /* ignore = */ true), false);
t.assertFunctionExpression(node);
});
it("assignment expression", function () {
const node = t.assignmentExpression("+=", t.identifier("x"), t.numericLiteral(1));
t.assertExpressionStatement(t.toStatement(node));
t.assertAssignmentExpression(node);
});
it("fail if cannot convert node type", function () {
const node = t.yieldExpression(t.identifier("foo"));
assert.throws(function() {
t.toStatement(node);
});
assert.strictEqual(t.toStatement(node, /* ignore = */ true), false);
t.assertYieldExpression(node);
});
});
describe("toExpression", function () {
it("noop on expressions", function () {
const node = t.identifier("a");
assert.equal(t.toExpression(node), node);
t.assertIdentifier(node);
});
it("mutate class declaration to expression", function () {
const node = t.classDeclaration(t.identifier("A"), null, t.classBody([]), []);
t.toExpression(node);
t.assertClassExpression(node);
});
it("mutate function declaration to expression", function () {
const node = t.functionDeclaration(t.identifier("A"), [], t.blockStatement([]));
t.toExpression(node);
t.assertFunctionExpression(node);
});
it("mutate object method to expression", function () {
const node = t.objectMethod("method", t.identifier("A"), [], t.blockStatement([]));
t.toExpression(node);
t.assertFunctionExpression(node);
});
it("mutate class method to expression", function () {
const node = t.classMethod("constructor", t.identifier("A"), [], t.blockStatement([]));
t.toExpression(node);
t.assertFunctionExpression(node);
});
it("expression statement", function () {
const inner = t.yieldExpression(t.identifier("foo"));
const node = t.expressionStatement(inner);
t.assertYieldExpression(t.toExpression(node));
assert.equal(t.toExpression(node), inner);
t.assertExpressionStatement(node);
});
it("fail if cannot convert node type", function () {
const node = t.program([]);
assert.throws(function() {
t.toExpression(node);
});
t.assertProgram(node);
});
});
});