Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Jun 17, 2017
1 parent 30cda13 commit b29d205
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
16 changes: 16 additions & 0 deletions tests/fixtures/parsers/enhanced-parser2.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions tests/fixtures/parsers/enhanced-parser3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use strict";

const assert = require("assert");
const ScopeManager = require("eslint-scope/lib/scope-manager");
const Referencer = require("eslint-scope/lib/referencer");
const Traverser = require("../../../lib/util/traverser");

class EnhancedReferencer extends Referencer {
visitClass(node) {

// Visit decorators.
if (node.experimentalDecorators) {
for (const decorator of node.experimentalDecorators) {
this.visit(decorator);
}
}

// Do default.
super.visitClass(node);
}

Decorator(node) {
if (node.expression) {
this.visit(node.expression);
}
}
}

function analyzeScope(ast) {
const options = {
optimistic: false,
directive: false,
nodejsScope: false,
impliedStrict: false,
sourceType: "script",
ecmaVersion: 6,
childVisitorKeys: null,
fallback: Traverser.getKeys
};
const scopeManager = new ScopeManager(options);
const referencer = new EnhancedReferencer(options, scopeManager);

referencer.visit(ast);

return scopeManager;
}

exports.parse = code => {
assert(code === "@foo class A {}");
const ast = { type: "Program", start: 0, end: 15, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 15 } }, comments: [], tokens: [{ type: "Punctuator", value: "@", start: 0, end: 1, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } }, range: [0, 1] }, { type: "Identifier", value: "foo", start: 1, end: 4, loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 } }, range: [1, 4] }, { type: "Keyword", value: "class", start: 5, end: 10, loc: { start: { line: 1, column: 5 }, end: { line: 1, column: 10 } }, range: [5, 10] }, { type: "Identifier", value: "A", start: 11, end: 12, loc: { start: { line: 1, column: 11 }, end: { line: 1, column: 12 } }, range: [11, 12] }, { type: "Punctuator", value: "{", start: 13, end: 14, loc: { start: { line: 1, column: 13 }, end: { line: 1, column: 14 } }, range: [13, 14] }, { type: "Punctuator", value: "}", start: 14, end: 15, loc: { start: { line: 1, column: 14 }, end: { line: 1, column: 15 } }, range: [14, 15] }], range: [5, 15], sourceType: "module", body: [{ type: "ClassDeclaration", start: 5, end: 15, loc: { start: { line: 1, column: 5 }, end: { line: 1, column: 15 } }, experimentalDecorators: [{ type: "Decorator", start: 0, end: 4, loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 4 } }, expression: { type: "Identifier", start: 1, end: 4, loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 }, identifierName: "foo" }, name: "foo", range: [1, 4], _babelType: "Identifier" }, range: [0, 4], _babelType: "Decorator" }], id: { type: "Identifier", start: 11, end: 12, loc: { start: { line: 1, column: 11 }, end: { line: 1, column: 12 }, identifierName: "A" }, name: "A", range: [11, 12], _babelType: "Identifier" }, superClass: null, body: { type: "ClassBody", start: 13, end: 15, loc: { start: { line: 1, column: 13 }, end: { line: 1, column: 15 } }, body: [], range: [13, 15], _babelType: "ClassBody" }, range: [5, 15], _babelType: "ClassDeclaration" }] };

return {
ast,
scope: analyzeScope(ast)
};
};
128 changes: 128 additions & 0 deletions tests/lib/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,30 @@ describe("eslint", () => {
assert.equal(messages.length, 1);
assert.equal(messages[0].message, "Hi!");
});

it("should use the same parserServices if source code object is reused", () => {
const parser = path.resolve(__dirname, "../fixtures/parsers/enhanced-parser.js");

linter.defineRule("test-service-rule", context => ({
Literal(node) {
context.report({
node,
message: context.parserServices.test.getMessage()
});
}
}));

const config = { rules: { "test-service-rule": 2 }, parser };
const messages = linter.verify("0", config, filename);

assert.equal(messages.length, 1);
assert.equal(messages[0].message, "Hi!");

const messages2 = linter.verify(linter.getSourceCode(), config, filename);

assert.equal(messages2.length, 1);
assert.equal(messages2[0].message, "Hi!");
});
}

it("should pass parser as parserPath to all rules when default parser is used", () => {
Expand Down Expand Up @@ -3854,6 +3878,8 @@ describe("eslint", () => {

// only test in Node.js, not browser
if (typeof window === "undefined") {
const escope = require("eslint-scope");
const Traverser = require("../../lib/util/traverser");

describe("Custom parser", () => {

Expand Down Expand Up @@ -3905,6 +3931,108 @@ describe("eslint", () => {
assert.equal(messages[0].message, errorPrefix + require(parser).expectedError);
});

describe("if a parser provides 'visitorKeys'", () => {
const types = [];
let scopeAnalyzeStub = null;
let sourceCode = null;

before(() => {
scopeAnalyzeStub = sinon.spy(escope, "analyze");

const parser = path.join(parserFixtures, "enhanced-parser2.js");

linter.defineRule("collect-node-types", () => ({
"*"(node) {
types.push(node.type);
}
}));
linter.verify("@foo class A {}", {
parser,
rules: {
"collect-node-types": "error"
}
});

sourceCode = linter.getSourceCode();
});
after(() => {
scopeAnalyzeStub.restore();
});

it("Traverser should use the visitorKeys (so 'types' includes 'Decorator')", () => {
assert.deepEqual(
types,
["Program", "ClassDeclaration", "Identifier", "ClassBody", "Decorator", "Identifier"]
);
});

it("eslint-scope should use the visitorKeys (so 'childVisitorKeys.ClassDeclaration' includes 'experimentalDecorators')", () => {
assert(scopeAnalyzeStub.calledOnce);
assert.deepEqual(
scopeAnalyzeStub.firstCall.args[1].childVisitorKeys.ClassDeclaration,
Traverser.DEFAULT_VISITOR_KEYS.ClassDeclaration.concat(["experimentalDecorators"])
);
});

it("should use the same visitorKeys if the source code object is reused", () => {
const types2 = [];

linter.defineRule("collect-node-types", () => ({
"*"(node) {
types2.push(node.type);
}
}));
linter.verify(sourceCode, {
rules: {
"collect-node-types": "error"
}
});

assert.deepEqual(
types2,
["Program", "ClassDeclaration", "Identifier", "ClassBody", "Decorator", "Identifier"]
);
});
});

describe("if a parser provides 'scope'", () => {
let scopeAnalyzeStub = null;
let scope = null;
let sourceCode = null;

before(() => {
scopeAnalyzeStub = sinon.spy(escope, "analyze");

const parser = path.join(parserFixtures, "enhanced-parser3.js");

linter.verify("@foo class A {}", { parser });

scope = linter.getScope();
sourceCode = linter.getSourceCode();
});
after(() => {
scopeAnalyzeStub.restore();
});

it("should not use eslint-scope analyzer", () => {
assert(scopeAnalyzeStub.notCalled);
});

it("should use the scope (so the global scope has the reference of '@foo')", () => {
assert.equal(scope.references.length, 1);
assert.deepEqual(
scope.references[0].identifier.name,
"foo"
);
});

it("should use the same scope if the source code object is reused", () => {
linter.verify(sourceCode, {});

assert.equal(linter.getScope(), scope);
});
});

});
}

Expand Down
40 changes: 40 additions & 0 deletions tests/lib/util/source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const fs = require("fs"),
leche = require("leche"),
Linter = require("../../../lib/linter"),
SourceCode = require("../../../lib/util/source-code"),
Traverser = require("../../../lib/util/traverser"),
astUtils = require("../../../lib/ast-utils");

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -51,6 +52,21 @@ describe("SourceCode", () => {
assert.equal(sourceCode.ast, ast);
});

it("should create a new instance when called with valid optional data", () => {
const parserServices = {};
const scopeManager = {};
const visitorKeys = {};
const ast = { comments: [], tokens: [], loc: {}, range: [] };
const sourceCode = new SourceCode("foo;", ast, parserServices, scopeManager, visitorKeys);

assert.isObject(sourceCode);
assert.equal(sourceCode.text, "foo;");
assert.equal(sourceCode.ast, ast);
assert.equal(sourceCode.parserServices, parserServices);
assert.equal(sourceCode.scopeManager, scopeManager);
assert.equal(sourceCode.visitorKeys, visitorKeys);
});

it("should split text into lines when called with valid data", () => {
const ast = { comments: [], tokens: [], loc: {}, range: [] };
const sourceCode = new SourceCode("foo;\nbar;", ast);
Expand Down Expand Up @@ -1719,6 +1735,30 @@ describe("SourceCode", () => {
assert.notProperty(node.parent, "parent");
});

it("should use visitorKeys", () => {
const text = "a + b";
const ast = espree.parse(text, DEFAULT_CONFIG);
let node;

// default
sourceCode = new SourceCode(text, ast);
node = sourceCode.getNodeByRangeIndex(0);
assert.equal(node.type, "Identifier");

// no traverse BinaryExpression#left
sourceCode = new SourceCode(
text,
ast,
null,
null,
Object.assign({}, Traverser.DEFAULT_VISITOR_KEYS, {
BinaryExpression: ["right"]
})
);
node = sourceCode.getNodeByRangeIndex(0);
assert.equal(node.type, "BinaryExpression");
});

});

describe("isSpaceBetweenTokens()", () => {
Expand Down
47 changes: 47 additions & 0 deletions tests/lib/util/traverser.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,51 @@ describe("Traverser", () => {
assert.deepEqual(enteredNodes, [fakeAst, fakeAst.body[0], fakeAst.body[1], fakeAst.body[1].foo]);
assert.deepEqual(exitedNodes, [fakeAst.body[0], fakeAst.body[1].foo, fakeAst.body[1], fakeAst]);
});

it("traverses AST as using 'keys' option if given", () => {
const traverser = new Traverser();
const fakeAst = {
type: "Program",
body: [
{
type: "ClassDeclaration",
id: {
type: "Identifier"
},
superClass: null,
body: {
type: "ClassBody",
body: []
},
experimentalDecorators: [
{
type: "Decorator",
expression: {}
}
]
}
]
};

fakeAst.body[0].parent = fakeAst;

const visited = [];

// default.
traverser.traverse(fakeAst, {
enter: node => visited.push(node.type)
});
assert.deepEqual(visited, ["Program", "ClassDeclaration", "Identifier", "ClassBody"]);

visited.splice(0, visited.length);

// with keys option.
traverser.traverse(fakeAst, {
enter: node => visited.push(node.type),
keys: Object.assign({}, Traverser.DEFAULT_VISITOR_KEYS, {
ClassDeclaration: Traverser.DEFAULT_VISITOR_KEYS.ClassDeclaration.concat(["experimentalDecorators"])
})
});
assert.deepEqual(visited, ["Program", "ClassDeclaration", "Identifier", "ClassBody", "Decorator"]);
});
});

0 comments on commit b29d205

Please sign in to comment.