Switch from Jasmine to Mocha & Chai

The switch is mostly mechanical, with assertions translated 1:1. The
biggest non-mechanical part is rewriting Jasmine custom matchers as Chai
helpers. The matchers were streamlined and simplified in the process and
their messages were made more in line with messages produced by built-in
Chai helpers.

Fixes #409.
redux
David Majda 7 years ago
parent 266f17d11c
commit 73de2c9384

@ -9,7 +9,7 @@ let del = require("del");
let eslint = require("gulp-eslint");
let gulp = require("gulp");
let header = require("gulp-header");
let jasmine = require("gulp-jasmine");
let mocha = require("gulp-mocha");
let package_ = require("./package");
let peg = require("./lib/peg");
let rename = require("gulp-rename");
@ -65,8 +65,8 @@ gulp.task("lint", () =>
// Run specs.
gulp.task("spec", () =>
gulp.src(SPEC_FILES)
.pipe(jasmine())
gulp.src(SPEC_FILES, { read: false })
.pipe(mocha())
);
// Run benchmarks.

@ -47,6 +47,7 @@
"babel-preset-es2015": "6.14.0",
"babelify": "7.3.0",
"browserify": "13.1.0",
"chai": "3.5.0",
"del": "2.2.2",
"eslint-config-dmajda": "1.0.0",
"express": "4.14.0",
@ -54,12 +55,13 @@
"gulp": "3.9.1",
"gulp-eslint": "3.0.1",
"gulp-header": "1.8.8",
"gulp-jasmine": "0.2.0",
"gulp-mocha": "3.0.1",
"gulp-rename": "1.2.2",
"gulp-transform": "1.0.8",
"gulp-uglify": "2.0.0",
"morgan": "1.7.0",
"run-sequence": "^1.2.2",
"sinon": "1.17.6",
"vinyl-buffer": "1.0.0",
"vinyl-source-stream": "1.1.0"
},

@ -1,6 +1,6 @@
{
"env": {
"commonjs": true,
"jasmine": true
"mocha": true
}
}

@ -2,20 +2,24 @@
/* global console */
let chai = require("chai");
let peg = require("../../lib/peg");
let sinon = require("sinon");
let expect = chai.expect;
describe("generated parser API", function() {
describe("parse", function() {
it("parses input", function() {
let parser = peg.generate("start = 'a'");
expect(parser.parse("a")).toBe("a");
expect(parser.parse("a")).to.equal("a");
});
it("throws an exception on syntax error", function() {
let parser = peg.generate("start = 'a'");
expect(() => { parser.parse("b"); }).toThrow();
expect(() => { parser.parse("b"); }).to.throw();
});
describe("start rule", function() {
@ -27,20 +31,20 @@ describe("generated parser API", function() {
describe("when |startRule| is not set", function() {
it("starts parsing from the first allowed rule", function() {
expect(parser.parse("x")).toBe("b");
expect(parser.parse("x")).to.equal("b");
});
});
describe("when |startRule| is set to an allowed rule", function() {
it("starts parsing from specified rule", function() {
expect(parser.parse("x", { startRule: "b" })).toBe("b");
expect(parser.parse("x", { startRule: "c" })).toBe("c");
expect(parser.parse("x", { startRule: "b" })).to.equal("b");
expect(parser.parse("x", { startRule: "c" })).to.equal("c");
});
});
describe("when |startRule| is set to a disallowed start rule", function() {
it("throws an exception", function() {
expect(() => { parser.parse("x", { startRule: "a" }); }).toThrow();
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
});
});
});
@ -55,18 +59,24 @@ describe("generated parser API", function() {
describe("default tracer", function() {
it("traces using console.log (if console is defined)", function() {
if (typeof console === "object") {
spyOn(console, "log");
sinon.stub(console, "log");
}
try {
parser.parse("b");
if (typeof console === "object") {
expect(console.log).toHaveBeenCalledWith("1:1-1:1 rule.enter start");
expect(console.log).toHaveBeenCalledWith("1:1-1:1 rule.enter a");
expect(console.log).toHaveBeenCalledWith("1:1-1:1 rule.fail a");
expect(console.log).toHaveBeenCalledWith("1:1-1:1 rule.enter b");
expect(console.log).toHaveBeenCalledWith("1:1-1:2 rule.match b");
expect(console.log).toHaveBeenCalledWith("1:1-1:2 rule.match start");
expect(console.log.calledWithExactly("1:1-1:1 rule.enter start")).to.equal(true);
expect(console.log.calledWithExactly("1:1-1:1 rule.enter a")).to.equal(true);
expect(console.log.calledWithExactly("1:1-1:1 rule.fail a")).to.equal(true);
expect(console.log.calledWithExactly("1:1-1:1 rule.enter b")).to.equal(true);
expect(console.log.calledWithExactly("1:1-1:2 rule.match b")).to.equal(true);
expect(console.log.calledWithExactly("1:1-1:2 rule.match start")).to.equal(true);
}
} finally {
if (typeof console === "object") {
console.log.restore();
}
}
});
});
@ -74,43 +84,43 @@ describe("generated parser API", function() {
describe("custom tracers", function() {
describe("trace", function() {
it("receives tracing events", function() {
let tracer = jasmine.createSpyObj("tracer", ["trace"]);
let tracer = { trace: sinon.spy() };
parser.parse("b", { tracer: tracer });
expect(tracer.trace).toHaveBeenCalledWith({
expect(tracer.trace.calledWithExactly({
type: "rule.enter",
rule: "start",
location: {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 0, line: 1, column: 1 }
}
});
expect(tracer.trace).toHaveBeenCalledWith({
})).to.equal(true);
expect(tracer.trace.calledWithExactly({
type: "rule.enter",
rule: "a",
location: {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 0, line: 1, column: 1 }
}
});
expect(tracer.trace).toHaveBeenCalledWith({
})).to.equal(true);
expect(tracer.trace.calledWithExactly({
type: "rule.fail",
rule: "a",
location: {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 0, line: 1, column: 1 }
}
});
expect(tracer.trace).toHaveBeenCalledWith({
})).to.equal(true);
expect(tracer.trace.calledWithExactly({
type: "rule.enter",
rule: "b",
location: {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 0, line: 1, column: 1 }
}
});
expect(tracer.trace).toHaveBeenCalledWith({
})).to.equal(true);
expect(tracer.trace.calledWithExactly({
type: "rule.match",
rule: "b",
result: "b",
@ -118,8 +128,8 @@ describe("generated parser API", function() {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 1, line: 1, column: 2 }
}
});
expect(tracer.trace).toHaveBeenCalledWith({
})).to.equal(true);
expect(tracer.trace.calledWithExactly({
type: "rule.match",
rule: "start",
result: "b",
@ -127,7 +137,7 @@ describe("generated parser API", function() {
start: { offset: 0, line: 1, column: 1 },
end: { offset: 1, line: 1, column: 2 }
}
});
})).to.equal(true);
});
});
});

@ -1,22 +1,26 @@
"use strict";
let chai = require("chai");
let peg = require("../../lib/peg");
let sinon = require("sinon");
let expect = chai.expect;
describe("PEG.js API", function() {
describe("generate", function() {
it("generates a parser", function() {
let parser = peg.generate("start = 'a'");
expect(typeof parser).toBe("object");
expect(parser.parse("a")).toBe("a");
expect(typeof parser).to.equal("object");
expect(parser.parse("a")).to.equal("a");
});
it("throws an exception on syntax error", function() {
expect(() => { peg.generate("start = @"); }).toThrow();
expect(() => { peg.generate("start = @"); }).to.throw();
});
it("throws an exception on semantic error", function() {
expect(() => { peg.generate("start = undefined"); }).toThrow();
expect(() => { peg.generate("start = undefined"); }).to.throw();
});
describe("allowed start rules", function() {
@ -34,9 +38,9 @@ describe("PEG.js API", function() {
it("generated parser can start only from the first rule", function() {
let parser = peg.generate(grammar, { optimize: "speed" });
expect(parser.parse("x", { startRule: "a" })).toBe("x");
expect(() => { parser.parse("x", { startRule: "b" }); }).toThrow();
expect(() => { parser.parse("x", { startRule: "c" }); }).toThrow();
expect(parser.parse("x", { startRule: "a" })).to.equal("x");
expect(() => { parser.parse("x", { startRule: "b" }); }).to.throw();
expect(() => { parser.parse("x", { startRule: "c" }); }).to.throw();
});
});
@ -47,9 +51,9 @@ describe("PEG.js API", function() {
allowedStartRules: ["b", "c"]
});
expect(() => { parser.parse("x", { startRule: "a" }); }).toThrow();
expect(parser.parse("x", { startRule: "b" })).toBe("x");
expect(parser.parse("x", { startRule: "c" })).toBe("x");
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
expect(parser.parse("x", { startRule: "b" })).to.equal("x");
expect(parser.parse("x", { startRule: "c" })).to.equal("x");
});
});
});
@ -59,9 +63,9 @@ describe("PEG.js API", function() {
it("generated parser can start only from the first rule", function() {
let parser = peg.generate(grammar, { optimize: "size" });
expect(parser.parse("x", { startRule: "a" })).toBe("x");
expect(() => { parser.parse("x", { startRule: "b" }); }).toThrow();
expect(() => { parser.parse("x", { startRule: "c" }); }).toThrow();
expect(parser.parse("x", { startRule: "a" })).to.equal("x");
expect(() => { parser.parse("x", { startRule: "b" }); }).to.throw();
expect(() => { parser.parse("x", { startRule: "c" }); }).to.throw();
});
});
@ -72,9 +76,9 @@ describe("PEG.js API", function() {
allowedStartRules: ["b", "c"]
});
expect(() => { parser.parse("x", { startRule: "a" }); }).toThrow();
expect(parser.parse("x", { startRule: "b" })).toBe("x");
expect(parser.parse("x", { startRule: "c" })).toBe("x");
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
expect(parser.parse("x", { startRule: "b" })).to.equal("x");
expect(parser.parse("x", { startRule: "c" })).to.equal("x");
});
});
});
@ -91,7 +95,7 @@ describe("PEG.js API", function() {
it("generated parser doesn't cache intermediate parse results", function() {
let parser = peg.generate(grammar);
expect(parser.parse("ac")).toBe(2);
expect(parser.parse("ac")).to.equal(2);
});
});
@ -99,7 +103,7 @@ describe("PEG.js API", function() {
it("generated parser doesn't cache intermediate parse results", function() {
let parser = peg.generate(grammar, { cache: false });
expect(parser.parse("ac")).toBe(2);
expect(parser.parse("ac")).to.equal(2);
});
});
@ -107,7 +111,7 @@ describe("PEG.js API", function() {
it("generated parser caches intermediate parse results", function() {
let parser = peg.generate(grammar, { cache: true });
expect(parser.parse("ac")).toBe(1);
expect(parser.parse("ac")).to.equal(1);
});
});
});
@ -118,33 +122,33 @@ describe("PEG.js API", function() {
describe("when |trace| is not set", function() {
it("generated parser doesn't trace", function() {
let parser = peg.generate(grammar);
let tracer = jasmine.createSpyObj("tracer", ["trace"]);
let tracer = { trace: sinon.spy() };
parser.parse("a", { tracer: tracer });
expect(tracer.trace).not.toHaveBeenCalled();
expect(tracer.trace.called).to.equal(false);
});
});
describe("when |trace| is set to |false|", function() {
it("generated parser doesn't trace", function() {
let parser = peg.generate(grammar, { trace: false });
let tracer = jasmine.createSpyObj("tracer", ["trace"]);
let tracer = { trace: sinon.spy() };
parser.parse("a", { tracer: tracer });
expect(tracer.trace).not.toHaveBeenCalled();
expect(tracer.trace.called).to.equal(false);
});
});
describe("when |trace| is set to |true|", function() {
it("generated parser traces", function() {
let parser = peg.generate(grammar, { trace: true });
let tracer = jasmine.createSpyObj("tracer", ["trace"]);
let tracer = { trace: sinon.spy() };
parser.parse("a", { tracer: tracer });
expect(tracer.trace).toHaveBeenCalled();
expect(tracer.trace.called).to.equal(true);
});
});
});
@ -159,8 +163,8 @@ describe("PEG.js API", function() {
it("returns generated parser object", function() {
let parser = peg.generate(grammar);
expect(typeof parser).toBe("object");
expect(parser.parse("a")).toBe("a");
expect(typeof parser).to.equal("object");
expect(parser.parse("a")).to.equal("a");
});
});
@ -168,8 +172,8 @@ describe("PEG.js API", function() {
it("returns generated parser object", function() {
let parser = peg.generate(grammar, { output: "parser" });
expect(typeof parser).toBe("object");
expect(parser.parse("a")).toBe("a");
expect(typeof parser).to.equal("object");
expect(parser.parse("a")).to.equal("a");
});
});
@ -177,8 +181,8 @@ describe("PEG.js API", function() {
it("returns generated parser source code", function() {
let source = peg.generate(grammar, { output: "source" });
expect(typeof source).toBe("string");
expect(eval(source).parse("a")).toBe("a");
expect(typeof source).to.equal("string");
expect(eval(source).parse("a")).to.equal("a");
});
});
});

@ -1,39 +1,11 @@
"use strict";
let chai = require("chai");
let peg = require("../../lib/peg");
describe("plugin API", function() {
beforeEach(function() {
this.addMatchers({
toBeObject() {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " "
+ (this.isNot ? "not " : "")
+ "to be an object.";
return this.actual !== null && typeof this.actual === "object";
},
toBeArray() {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " "
+ (this.isNot ? "not " : "")
+ "to be an array.";
return Object.prototype.toString.apply(this.actual) === "[object Array]";
},
toBeFunction() {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " "
+ (this.isNot ? "not " : "")
+ "to be a function.";
return typeof this.actual === "function";
}
});
});
let expect = chai.expect;
describe("plugin API", function() {
describe("use", function() {
let grammar = "start = 'a'";
@ -47,32 +19,32 @@ describe("plugin API", function() {
peg.generate(grammar, { plugins: plugins });
expect(pluginsUsed).toEqual([true, true, true]);
expect(pluginsUsed).to.deep.equal([true, true, true]);
});
it("receives configuration", function() {
let plugin = {
use(config) {
expect(config).toBeObject();
expect(config).to.be.an("object");
expect(config.parser).toBeObject();
expect(config.parser.parse("start = 'a'")).toBeObject();
expect(config.parser).to.be.an("object");
expect(config.parser.parse("start = 'a'")).to.be.an("object");
expect(config.passes).toBeObject();
expect(config.passes).to.be.an("object");
expect(config.passes.check).toBeArray();
expect(config.passes.check).to.be.an("array");
config.passes.check.forEach(pass => {
expect(pass).toBeFunction();
expect(pass).to.be.a("function");
});
expect(config.passes.transform).toBeArray();
expect(config.passes.transform).to.be.an("array");
config.passes.transform.forEach(pass => {
expect(pass).toBeFunction();
expect(pass).to.be.a("function");
});
expect(config.passes.generate).toBeArray();
expect(config.passes.generate).to.be.an("array");
config.passes.generate.forEach(pass => {
expect(pass).toBeFunction();
expect(pass).to.be.a("function");
});
}
};
@ -83,7 +55,7 @@ describe("plugin API", function() {
it("receives options", function() {
let plugin = {
use(config, options) {
expect(options).toEqual(generateOptions);
expect(options).to.equal(generateOptions);
}
};
let generateOptions = { plugins: [plugin], foo: 42 };
@ -114,7 +86,7 @@ describe("plugin API", function() {
};
let parser = peg.generate("a", { plugins: [plugin] });
expect(parser.parse("a")).toBe("a");
expect(parser.parse("a")).to.equal("a");
});
it("can change compiler passes", function() {
@ -129,7 +101,7 @@ describe("plugin API", function() {
};
let parser = peg.generate(grammar, { plugins: [plugin] });
expect(parser.parse("a")).toBe(42);
expect(parser.parse("a")).to.equal(42);
});
it("can change options", function() {
@ -148,9 +120,9 @@ describe("plugin API", function() {
plugins: [plugin]
});
expect(() => { parser.parse("x", { startRule: "a" }); }).toThrow();
expect(parser.parse("x", { startRule: "b" })).toBe("x");
expect(parser.parse("x", { startRule: "c" })).toBe("x");
expect(() => { parser.parse("x", { startRule: "a" }); }).to.throw();
expect(parser.parse("x", { startRule: "b" })).to.equal("x");
expect(parser.parse("x", { startRule: "c" })).to.equal("x");
});
});
});

File diff suppressed because it is too large Load Diff

@ -3,22 +3,17 @@
<head>
<meta charset="utf-8">
<title>PEG.js Spec Suite</title>
<link rel="stylesheet" href="vendor/jasmine/jasmine.css">
<script src="vendor/jasmine/jasmine.js"></script>
<script src="vendor/jasmine/jasmine-html.js"></script>
<link rel="stylesheet" href="vendor/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="vendor/mocha/mocha.js"></script>
<script>
mocha.setup('bdd');
</script>
<script src="bundle.js"></script>
<script>
(function() {
var env = jasmine.getEnv(),
reporter = new jasmine.HtmlReporter();
env.addReporter(reporter);
env.specFilter = reporter.specFilter;
window.onload = function() { env.execute(); };
})();
mocha.run();
</script>
</head>
<body>
</body>
</html>

@ -1,7 +1,13 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |generateBytecode|", function() {
let pass = peg.compiler.passes.generate.generateBytecode;
@ -15,7 +21,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for grammar", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST([
expect(pass).to.changeAST([
"a = 'a'",
"b = 'b'",
"c = 'c'"
@ -29,7 +35,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST([
expect(pass).to.changeAST([
"a = 'a'",
"b = 'b'",
"c = 'c'"
@ -46,7 +52,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for rule", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = 'a'", bytecodeDetails([
expect(pass).to.changeAST("start = 'a'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression>
]));
});
@ -56,7 +62,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start 'start' = 'a'";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
28, // SILENT_FAILS_ON
18, 1, 2, 2, 22, 1, 23, 2, // <expression>
29, // SILENT_FAILS_OFF
@ -66,7 +72,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"peg$otherExpectation(\"start\")",
"\"a\"",
"peg$literalExpectation(\"a\", false)"
@ -76,7 +82,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for choice", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = 'a' / 'b' / 'c'", bytecodeDetails([
expect(pass).to.changeAST("start = 'a' / 'b' / 'c'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1, // <alternatives[0]>
14, 21, 0, // IF_ERROR
6, // * POP
@ -93,7 +99,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a' { code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
15, 6, 0, // IF_NOT_ERROR
@ -104,7 +110,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"function() { code }"
@ -116,7 +122,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = a:'a' { code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
15, 7, 0, // IF_NOT_ERROR
@ -127,7 +133,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"function(a) { code }"
@ -139,7 +145,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = a:'a' b:'b' c:'c' { code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <elements[0]>
15, 40, 3, // IF_NOT_ERROR
@ -163,7 +169,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"\"b\"",
@ -180,7 +186,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a' 'b' 'c'";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <elements[0]>
15, 33, 3, // IF_NOT_ERROR
@ -203,7 +209,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"\"b\"",
@ -216,7 +222,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for labeled", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = a:'a'", bytecodeDetails([
expect(pass).to.changeAST("start = a:'a'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression>
]));
});
@ -224,7 +230,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for text", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = $'a'", bytecodeDetails([
expect(pass).to.changeAST("start = $'a'", bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
15, 2, 1, // IF_NOT_ERROR
@ -239,7 +245,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = &'a'";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
28, // SILENT_FAILS_ON
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
@ -255,7 +261,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -266,7 +272,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = !'a'";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
28, // SILENT_FAILS_ON
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
@ -282,7 +288,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -293,7 +299,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a'?";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
14, 2, 0, // IF_ERROR
6, // * POP
@ -302,7 +308,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -313,7 +319,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a'*";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
4, // PUSH_EMPTY_ARRAY
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
16, 9, // WHILE_NOT_ERROR
@ -324,7 +330,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -335,7 +341,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a'+";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
4, // PUSH_EMPTY_ARRAY
18, 0, 2, 2, 22, 0, 23, 1, // <expression>
15, 12, 3, // IF_NOT_ERROR
@ -350,7 +356,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -359,7 +365,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for group", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = ('a')", bytecodeDetails([
expect(pass).to.changeAST("start = ('a')", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression>
]));
});
@ -370,7 +376,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = &{ code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
25, // UPDATE_SAVED_POS
26, 0, 0, 0, // CALL
13, 2, 2, // IF
@ -382,7 +388,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(
expect(pass).to.changeAST(
grammar,
constsDetails(["function() { code }"])
);
@ -393,7 +399,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = a:'a' b:'b' c:'c' &{ code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <elements[0]>
15, 55, 3, // IF_NOT_ERROR
@ -427,7 +433,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"\"b\"",
@ -445,7 +451,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = !{ code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
25, // UPDATE_SAVED_POS
26, 0, 0, 0, // CALL
13, 2, 2, // IF
@ -457,7 +463,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(
expect(pass).to.changeAST(
grammar,
constsDetails(["function() { code }"])
);
@ -468,7 +474,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = a:'a' b:'b' c:'c' !{ code }";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <elements[0]>
15, 55, 3, // IF_NOT_ERROR
@ -502,7 +508,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)",
"\"b\"",
@ -517,7 +523,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for rule_ref", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST([
expect(pass).to.changeAST([
"start = other",
"other = 'other'"
].join("\n"), {
@ -536,13 +542,13 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = ''";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
0, 0 // PUSH
]));
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails(["\"\""]));
expect(pass).to.changeAST(grammar, constsDetails(["\"\""]));
});
});
@ -550,7 +556,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'a'";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
18, 0, 2, 2, // MATCH_STRING
22, 0, // * ACCEPT_STRING
23, 1 // * FAIL
@ -558,7 +564,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"a\", false)"
]));
@ -569,7 +575,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = 'A'i";
it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
19, 0, 2, 2, // MATCH_STRING_IC
21, 1, // * ACCEPT_N
23, 1 // * FAIL
@ -577,7 +583,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([
expect(pass).to.changeAST(grammar, constsDetails([
"\"a\"",
"peg$literalExpectation(\"A\", true)"
]));
@ -587,7 +593,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for class", function() {
it("generates correct bytecode", function() {
expect(pass).toChangeAST("start = [a]", bytecodeDetails([
expect(pass).to.changeAST("start = [a]", bytecodeDetails([
20, 0, 2, 2, // MATCH_REGEXP
21, 1, // * ACCEPT_N
23, 1 // * FAIL
@ -596,7 +602,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("non-inverted case-sensitive", function() {
it("defines correct constants", function() {
expect(pass).toChangeAST("start = [a]", constsDetails([
expect(pass).to.changeAST("start = [a]", constsDetails([
"/^[a]/",
"peg$classExpectation([\"a\"], false, false)"
]));
@ -605,7 +611,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("inverted case-sensitive", function() {
it("defines correct constants", function() {
expect(pass).toChangeAST("start = [^a]", constsDetails([
expect(pass).to.changeAST("start = [^a]", constsDetails([
"/^[^a]/",
"peg$classExpectation([\"a\"], true, false)"
]));
@ -614,7 +620,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("non-inverted case-insensitive", function() {
it("defines correct constants", function() {
expect(pass).toChangeAST("start = [a]i", constsDetails([
expect(pass).to.changeAST("start = [a]i", constsDetails([
"/^[a]/i",
"peg$classExpectation([\"a\"], false, true)"
]));
@ -623,7 +629,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("complex", function() {
it("defines correct constants", function() {
expect(pass).toChangeAST("start = [ab-def-hij-l]", constsDetails([
expect(pass).to.changeAST("start = [ab-def-hij-l]", constsDetails([
"/^[ab-def-hij-l]/",
"peg$classExpectation([\"a\", [\"b\", \"d\"], \"e\", [\"f\", \"h\"], \"i\", [\"j\", \"l\"]], false, false)"
]));
@ -635,7 +641,7 @@ describe("compiler pass |generateBytecode|", function() {
let grammar = "start = .";
it("generates bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([
expect(pass).to.changeAST(grammar, bytecodeDetails([
17, 2, 2, // MATCH_ANY
21, 1, // * ACCEPT_N
23, 0 // * FAIL
@ -643,7 +649,7 @@ describe("compiler pass |generateBytecode|", function() {
});
it("defines correct constants", function() {
expect(pass).toChangeAST(
expect(pass).to.changeAST(
grammar,
constsDetails(["peg$anyExpectation()"])
);

@ -2,12 +2,13 @@
let peg = require("../../../../lib/peg");
beforeEach(function() {
this.addMatchers({
toChangeAST(grammar, details, options) {
module.exports = function(chai, utils) {
let Assertion = chai.Assertion;
Assertion.addMethod("changeAST", function(grammar, props, options) {
options = options !== undefined ? options : {};
function matchDetails(value, details) {
function matchProps(value, props) {
function isArray(value) {
return Object.prototype.toString.apply(value) === "[object Array]";
}
@ -16,89 +17,72 @@ beforeEach(function() {
return value !== null && typeof value === "object";
}
if (isArray(details)) {
if (isArray(props)) {
if (!isArray(value)) { return false; }
if (value.length !== details.length) { return false; }
for (let i = 0; i < details.length; i++) {
if (!matchDetails(value[i], details[i])) { return false; }
if (value.length !== props.length) { return false; }
for (let i = 0; i < props.length; i++) {
if (!matchProps(value[i], props[i])) { return false; }
}
return true;
} else if (isObject(details)) {
} else if (isObject(props)) {
if (!isObject(value)) { return false; }
let keys = Object.keys(details);
let keys = Object.keys(props);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (!(key in value)) { return false; }
if (!matchDetails(value[key], details[key])) { return false; }
if (!matchProps(value[key], props[key])) { return false; }
}
return true;
} else {
return value === details;
return value === props;
}
}
let ast = peg.parser.parse(grammar);
this.actual(ast, options);
this.message = () =>
"Expected the pass "
+ "with options " + jasmine.pp(options) + " "
+ (this.isNot ? "not " : "")
+ "to change the AST " + jasmine.pp(ast) + " "
+ "to match " + jasmine.pp(details) + ", "
+ "but it " + (this.isNot ? "did" : "didn't") + ".";
utils.flag(this, "object")(ast, options);
return matchDetails(ast, details);
},
this.assert(
matchProps(ast, props),
"expected #{this} to change the AST to match #{exp}",
"expected #{this} to not change the AST to match #{exp}",
props,
ast
);
});
toReportError(grammar, details) {
Assertion.addMethod("reportError", function(grammar, props) {
let ast = peg.parser.parse(grammar);
let passed, result;
try {
this.actual(ast);
utils.flag(this, "object")(ast);
passed = true;
} catch (e) {
if (this.isNot) {
this.message = () =>
"Expected the pass not to report an error "
+ "for grammar " + jasmine.pp(grammar) + ", "
+ "but it did.";
} else {
if (details) {
let keys = Object.keys(details);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (!this.env.equals_(e[key], details[key])) {
this.message = () =>
"Expected the pass to report an error "
+ "with details " + jasmine.pp(details) + " "
+ "for grammar " + jasmine.pp(grammar) + ", "
+ "but " + jasmine.pp(key) + " "
+ "is " + jasmine.pp(e[key]) + ".";
return false;
}
}
}
}
return true;
result = e;
passed = false;
}
this.message = () =>
"Expected the pass to report an error "
+ (details ? "with details " + jasmine.pp(details) + " " : "")
+ "for grammar " + jasmine.pp(grammar) + ", "
+ "but it didn't.";
return false;
this.assert(
!passed,
"expected #{this} to report an error but it didn't",
"expected #{this} to not report an error but #{act} was reported",
null,
result
);
if (!passed && props !== undefined) {
Object.keys(props).forEach(key => {
new Assertion(result).to.have.property(key)
.that.is.deep.equal(props[key]);
});
}
});
});
};

@ -1,13 +1,19 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |removeProxyRules|", function() {
let pass = peg.compiler.passes.transform.removeProxyRules;
describe("when a proxy rule isn't listed in |allowedStartRules|", function() {
it("updates references and removes it", function() {
expect(pass).toChangeAST(
expect(pass).to.changeAST(
[
"start = proxy",
"proxy = proxied",
@ -29,7 +35,7 @@ describe("compiler pass |removeProxyRules|", function() {
describe("when a proxy rule is listed in |allowedStartRules|", function() {
it("updates references but doesn't remove it", function() {
expect(pass).toChangeAST(
expect(pass).to.changeAST(
[
"start = proxy",
"proxy = proxied",

@ -1,13 +1,19 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |reportDuplicateLabels|", function() {
let pass = peg.compiler.passes.check.reportDuplicateLabels;
describe("in a sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError("start = a:'a' a:'a'", {
expect(pass).to.reportError("start = a:'a' a:'a'", {
message: "Label \"a\" is already defined at line 1, column 9.",
location: {
start: { offset: 14, line: 1, column: 15 },
@ -17,29 +23,29 @@ describe("compiler pass |reportDuplicateLabels|", function() {
});
it("doesn't report labels duplicate with labels in subexpressions", function() {
expect(pass).not.toReportError("start = ('a' / a:'a' / 'a') a:'a'");
expect(pass).not.toReportError("start = (a:'a' { }) a:'a'");
expect(pass).not.toReportError("start = ('a' a:'a' 'a') a:'a'");
expect(pass).not.toReportError("start = b:(a:'a') a:'a'");
expect(pass).not.toReportError("start = $(a:'a') a:'a'");
expect(pass).not.toReportError("start = &(a:'a') a:'a'");
expect(pass).not.toReportError("start = !(a:'a') a:'a'");
expect(pass).not.toReportError("start = (a:'a')? a:'a'");
expect(pass).not.toReportError("start = (a:'a')* a:'a'");
expect(pass).not.toReportError("start = (a:'a')+ a:'a'");
expect(pass).not.toReportError("start = (a:'a') a:'a'");
expect(pass).to.not.reportError("start = ('a' / a:'a' / 'a') a:'a'");
expect(pass).to.not.reportError("start = (a:'a' { }) a:'a'");
expect(pass).to.not.reportError("start = ('a' a:'a' 'a') a:'a'");
expect(pass).to.not.reportError("start = b:(a:'a') a:'a'");
expect(pass).to.not.reportError("start = $(a:'a') a:'a'");
expect(pass).to.not.reportError("start = &(a:'a') a:'a'");
expect(pass).to.not.reportError("start = !(a:'a') a:'a'");
expect(pass).to.not.reportError("start = (a:'a')? a:'a'");
expect(pass).to.not.reportError("start = (a:'a')* a:'a'");
expect(pass).to.not.reportError("start = (a:'a')+ a:'a'");
expect(pass).to.not.reportError("start = (a:'a') a:'a'");
});
});
describe("in a choice", function() {
it("doesn't report labels duplicate with labels of preceding alternatives", function() {
expect(pass).not.toReportError("start = a:'a' / a:'a'");
expect(pass).to.not.reportError("start = a:'a' / a:'a'");
});
});
describe("in outer sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError("start = a:'a' (a:'a')", {
expect(pass).to.reportError("start = a:'a' (a:'a')", {
message: "Label \"a\" is already defined at line 1, column 9.",
location: {
start: { offset: 15, line: 1, column: 16 },
@ -49,11 +55,11 @@ describe("compiler pass |reportDuplicateLabels|", function() {
});
it("doesn't report labels duplicate with the label of the current element", function() {
expect(pass).not.toReportError("start = a:(a:'a')");
expect(pass).to.not.reportError("start = a:(a:'a')");
});
it("doesn't report labels duplicate with labels of following elements", function() {
expect(pass).not.toReportError("start = (a:'a') a:'a'");
expect(pass).to.not.reportError("start = (a:'a') a:'a'");
});
});
});

@ -1,12 +1,18 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |reportDuplicateRules|", function() {
let pass = peg.compiler.passes.check.reportDuplicateRules;
it("reports duplicate rules", function() {
expect(pass).toReportError([
expect(pass).to.reportError([
"start = 'a'",
"start = 'b'"
].join("\n"), {

@ -1,12 +1,18 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |reportInfiniteRecursion|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRecursion;
it("reports direct left recursion", function() {
expect(pass).toReportError("start = start", {
expect(pass).to.reportError("start = start", {
message: "Possible infinite loop when parsing (left recursion: start -> start).",
location: {
start: { offset: 8, line: 1, column: 9 },
@ -16,7 +22,7 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
});
it("reports indirect left recursion", function() {
expect(pass).toReportError([
expect(pass).to.reportError([
"start = stop",
"stop = start"
].join("\n"), {
@ -30,86 +36,86 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
describe("in sequences", function() {
it("reports left recursion if all preceding elements match empty string", function() {
expect(pass).toReportError("start = '' '' '' start");
expect(pass).to.reportError("start = '' '' '' start");
});
it("doesn't report left recursion if some preceding element doesn't match empty string", function() {
expect(pass).not.toReportError("start = 'a' '' '' start");
expect(pass).not.toReportError("start = '' 'a' '' start");
expect(pass).not.toReportError("start = '' '' 'a' start");
expect(pass).to.not.reportError("start = 'a' '' '' start");
expect(pass).to.not.reportError("start = '' 'a' '' start");
expect(pass).to.not.reportError("start = '' '' 'a' start");
});
// Regression test for #359.
it("reports left recursion when rule reference is wrapped in an expression", function() {
expect(pass).toReportError("start = '' start?");
expect(pass).to.reportError("start = '' start?");
});
it("computes expressions that always consume input on success correctly", function() {
expect(pass).toReportError([
expect(pass).to.reportError([
"start = a start",
"a 'a' = ''"
].join("\n"));
expect(pass).not.toReportError([
expect(pass).to.not.reportError([
"start = a start",
"a 'a' = 'a'"
].join("\n"));
expect(pass).toReportError("start = ('' / 'a' / 'b') start");
expect(pass).toReportError("start = ('a' / '' / 'b') start");
expect(pass).toReportError("start = ('a' / 'b' / '') start");
expect(pass).not.toReportError("start = ('a' / 'b' / 'c') start");
expect(pass).to.reportError("start = ('' / 'a' / 'b') start");
expect(pass).to.reportError("start = ('a' / '' / 'b') start");
expect(pass).to.reportError("start = ('a' / 'b' / '') start");
expect(pass).to.not.reportError("start = ('a' / 'b' / 'c') start");
expect(pass).toReportError("start = ('' { }) start");
expect(pass).not.toReportError("start = ('a' { }) start");
expect(pass).to.reportError("start = ('' { }) start");
expect(pass).to.not.reportError("start = ('a' { }) start");
expect(pass).toReportError("start = ('' '' '') start");
expect(pass).not.toReportError("start = ('a' '' '') start");
expect(pass).not.toReportError("start = ('' 'a' '') start");
expect(pass).not.toReportError("start = ('' '' 'a') start");
expect(pass).to.reportError("start = ('' '' '') start");
expect(pass).to.not.reportError("start = ('a' '' '') start");
expect(pass).to.not.reportError("start = ('' 'a' '') start");
expect(pass).to.not.reportError("start = ('' '' 'a') start");
expect(pass).toReportError("start = a:'' start");
expect(pass).not.toReportError("start = a:'a' start");
expect(pass).to.reportError("start = a:'' start");
expect(pass).to.not.reportError("start = a:'a' start");
expect(pass).toReportError("start = $'' start");
expect(pass).not.toReportError("start = $'a' start");
expect(pass).to.reportError("start = $'' start");
expect(pass).to.not.reportError("start = $'a' start");
expect(pass).toReportError("start = &'' start");
expect(pass).toReportError("start = &'a' start");
expect(pass).to.reportError("start = &'' start");
expect(pass).to.reportError("start = &'a' start");
expect(pass).toReportError("start = !'' start");
expect(pass).toReportError("start = !'a' start");
expect(pass).to.reportError("start = !'' start");
expect(pass).to.reportError("start = !'a' start");
expect(pass).toReportError("start = ''? start");
expect(pass).toReportError("start = 'a'? start");
expect(pass).to.reportError("start = ''? start");
expect(pass).to.reportError("start = 'a'? start");
expect(pass).toReportError("start = ''* start");
expect(pass).toReportError("start = 'a'* start");
expect(pass).to.reportError("start = ''* start");
expect(pass).to.reportError("start = 'a'* start");
expect(pass).toReportError("start = ''+ start");
expect(pass).not.toReportError("start = 'a'+ start");
expect(pass).to.reportError("start = ''+ start");
expect(pass).to.not.reportError("start = 'a'+ start");
expect(pass).toReportError("start = ('') start");
expect(pass).not.toReportError("start = ('a') start");
expect(pass).to.reportError("start = ('') start");
expect(pass).to.not.reportError("start = ('a') start");
expect(pass).toReportError("start = &{ } start");
expect(pass).to.reportError("start = &{ } start");
expect(pass).toReportError("start = !{ } start");
expect(pass).to.reportError("start = !{ } start");
expect(pass).toReportError([
expect(pass).to.reportError([
"start = a start",
"a = ''"
].join("\n"));
expect(pass).not.toReportError([
expect(pass).to.not.reportError([
"start = a start",
"a = 'a'"
].join("\n"));
expect(pass).toReportError("start = '' start");
expect(pass).not.toReportError("start = 'a' start");
expect(pass).to.reportError("start = '' start");
expect(pass).to.not.reportError("start = 'a' start");
expect(pass).not.toReportError("start = [a-d] start");
expect(pass).to.not.reportError("start = [a-d] start");
expect(pass).not.toReportError("start = . start");
expect(pass).to.not.reportError("start = . start");
});
});
});

@ -1,12 +1,18 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |reportInfiniteRepetition|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRepetition;
it("reports infinite loops for zero_or_more", function() {
expect(pass).toReportError("start = ('')*", {
expect(pass).to.reportError("start = ('')*", {
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
location: {
start: { offset: 8, line: 1, column: 9 },
@ -16,7 +22,7 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
});
it("reports infinite loops for one_or_more", function() {
expect(pass).toReportError("start = ('')+", {
expect(pass).to.reportError("start = ('')+", {
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
location: {
start: { offset: 8, line: 1, column: 9 },
@ -26,70 +32,70 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
});
it("computes expressions that always consume input on success correctly", function() {
expect(pass).toReportError([
expect(pass).to.reportError([
"start = a*",
"a 'a' = ''"
].join("\n"));
expect(pass).not.toReportError([
expect(pass).to.not.reportError([
"start = a*",
"a 'a' = 'a'"
].join("\n"));
expect(pass).toReportError("start = ('' / 'a' / 'b')*");
expect(pass).toReportError("start = ('a' / '' / 'b')*");
expect(pass).toReportError("start = ('a' / 'b' / '')*");
expect(pass).not.toReportError("start = ('a' / 'b' / 'c')*");
expect(pass).to.reportError("start = ('' / 'a' / 'b')*");
expect(pass).to.reportError("start = ('a' / '' / 'b')*");
expect(pass).to.reportError("start = ('a' / 'b' / '')*");
expect(pass).to.not.reportError("start = ('a' / 'b' / 'c')*");
expect(pass).toReportError("start = ('' { })*");
expect(pass).not.toReportError("start = ('a' { })*");
expect(pass).to.reportError("start = ('' { })*");
expect(pass).to.not.reportError("start = ('a' { })*");
expect(pass).toReportError("start = ('' '' '')*");
expect(pass).not.toReportError("start = ('a' '' '')*");
expect(pass).not.toReportError("start = ('' 'a' '')*");
expect(pass).not.toReportError("start = ('' '' 'a')*");
expect(pass).to.reportError("start = ('' '' '')*");
expect(pass).to.not.reportError("start = ('a' '' '')*");
expect(pass).to.not.reportError("start = ('' 'a' '')*");
expect(pass).to.not.reportError("start = ('' '' 'a')*");
expect(pass).toReportError("start = (a:'')*");
expect(pass).not.toReportError("start = (a:'a')*");
expect(pass).to.reportError("start = (a:'')*");
expect(pass).to.not.reportError("start = (a:'a')*");
expect(pass).toReportError("start = ($'')*");
expect(pass).not.toReportError("start = ($'a')*");
expect(pass).to.reportError("start = ($'')*");
expect(pass).to.not.reportError("start = ($'a')*");
expect(pass).toReportError("start = (&'')*");
expect(pass).toReportError("start = (&'a')*");
expect(pass).to.reportError("start = (&'')*");
expect(pass).to.reportError("start = (&'a')*");
expect(pass).toReportError("start = (!'')*");
expect(pass).toReportError("start = (!'a')*");
expect(pass).to.reportError("start = (!'')*");
expect(pass).to.reportError("start = (!'a')*");
expect(pass).toReportError("start = (''?)*");
expect(pass).toReportError("start = ('a'?)*");
expect(pass).to.reportError("start = (''?)*");
expect(pass).to.reportError("start = ('a'?)*");
expect(pass).toReportError("start = (''*)*");
expect(pass).toReportError("start = ('a'*)*");
expect(pass).to.reportError("start = (''*)*");
expect(pass).to.reportError("start = ('a'*)*");
expect(pass).toReportError("start = (''+)*");
expect(pass).not.toReportError("start = ('a'+)*");
expect(pass).to.reportError("start = (''+)*");
expect(pass).to.not.reportError("start = ('a'+)*");
expect(pass).toReportError("start = ('')*");
expect(pass).not.toReportError("start = ('a')*");
expect(pass).to.reportError("start = ('')*");
expect(pass).to.not.reportError("start = ('a')*");
expect(pass).toReportError("start = (&{ })*");
expect(pass).to.reportError("start = (&{ })*");
expect(pass).toReportError("start = (!{ })*");
expect(pass).to.reportError("start = (!{ })*");
expect(pass).toReportError([
expect(pass).to.reportError([
"start = a*",
"a = ''"
].join("\n"));
expect(pass).not.toReportError([
expect(pass).to.not.reportError([
"start = a*",
"a = 'a'"
].join("\n"));
expect(pass).toReportError("start = ''*");
expect(pass).not.toReportError("start = 'a'*");
expect(pass).to.reportError("start = ''*");
expect(pass).to.not.reportError("start = 'a'*");
expect(pass).not.toReportError("start = [a-d]*");
expect(pass).to.not.reportError("start = [a-d]*");
expect(pass).not.toReportError("start = .*");
expect(pass).to.not.reportError("start = .*");
});
});

@ -1,12 +1,18 @@
"use strict";
let chai = require("chai");
let helpers = require("./helpers");
let peg = require("../../../../lib/peg");
chai.use(helpers);
let expect = chai.expect;
describe("compiler pass |reportUndefinedRules|", function() {
let pass = peg.compiler.passes.check.reportUndefinedRules;
it("reports undefined rules", function() {
expect(pass).toReportError("start = undefined", {
expect(pass).to.reportError("start = undefined", {
message: "Rule \"undefined\" is not defined.",
location: {
start: { offset: 8, line: 1, column: 9 },

@ -1,7 +1,10 @@
"use strict";
let chai = require("chai");
let peg = require("../../lib/peg");
let expect = chai.expect;
describe("PEG.js grammar parser", function() {
let literalAbcd = { type: "literal", value: "abcd", ignoreCase: false };
let literalEfgh = { type: "literal", value: "efgh", ignoreCase: false };
@ -163,441 +166,425 @@ describe("PEG.js grammar parser", function() {
return strip;
})();
beforeEach(function() {
this.addMatchers({
toParseAs(expected) {
let result;
try {
result = peg.parser.parse(this.actual);
} catch (e) {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " "
+ "to parse as " + jasmine.pp(expected) + ", "
+ "but it failed to parse with message "
+ jasmine.pp(e.message) + ".";
function helpers(chai, utils) {
let Assertion = chai.Assertion;
return false;
}
Assertion.addMethod("parseAs", function(expected) {
let result = peg.parser.parse(utils.flag(this, "object"));
stripLocation(result);
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " "
+ (this.isNot ? "not " : "")
+ "to parse as " + jasmine.pp(expected) + ", "
+ "but it parsed as " + jasmine.pp(result) + ".";
return this.env.equals_(result, expected);
},
this.assert(
utils.eql(result, expected),
"expected #{this} to parse as #{exp} but got #{act}",
"expected #{this} to not parse as #{exp}",
expected,
result,
!utils.flag(this, "negate")
);
});
toFailToParse(details) {
let result;
Assertion.addMethod("failToParse", function(props) {
let passed, result;
try {
result = peg.parser.parse(this.actual);
result = peg.parser.parse(utils.flag(this, "object"));
passed = true;
} catch (e) {
if (this.isNot) {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " to parse, "
+ "but it failed with message "
+ jasmine.pp(e.message) + ".";
} else {
if (details) {
let keys = Object.keys(details);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (!this.env.equals_(e[key], details[key])) {
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " to fail to parse"
+ (details ? " with details " + jasmine.pp(details) : "") + ", "
+ "but " + jasmine.pp(key) + " "
+ "is " + jasmine.pp(e[key]) + ".";
return false;
}
}
}
}
return true;
result = e;
passed = false;
}
if (passed) {
stripLocation(result);
}
this.message = () =>
"Expected " + jasmine.pp(this.actual) + " to fail to parse"
+ (details ? " with details " + jasmine.pp(details) : "") + ", "
+ "but it parsed as " + jasmine.pp(result) + ".";
this.assert(
!passed,
"expected #{this} to fail to parse but got #{act}",
"expected #{this} to not fail to parse but it failed with #{act}",
null,
result
);
return false;
if (!passed && props !== undefined) {
Object.keys(props).forEach(key => {
new Assertion(result).to.have.property(key)
.that.is.deep.equal(props[key]);
});
}
});
}
// Helper activation needs to put inside a |beforeEach| block because the
// helpers conflict with the ones in
// spec/behavior/generated-parser-behavior.spec.js.
beforeEach(function() {
chai.use(helpers);
});
// Canonical Grammar is "a = 'abcd'; b = 'efgh'; c = 'ijkl';".
it("parses Grammar", function() {
expect("\na = 'abcd';\n").toParseAs(
expect("\na = 'abcd';\n").to.parseAs(
{ type: "grammar", initializer: null, rules: [ruleA] }
);
expect("\na = 'abcd';\nb = 'efgh';\nc = 'ijkl';\n").toParseAs(
expect("\na = 'abcd';\nb = 'efgh';\nc = 'ijkl';\n").to.parseAs(
{ type: "grammar", initializer: null, rules: [ruleA, ruleB, ruleC] }
);
expect("\n{ code };\na = 'abcd';\n").toParseAs(
expect("\n{ code };\na = 'abcd';\n").to.parseAs(
{ type: "grammar", initializer: initializer, rules: [ruleA] }
);
});
// Canonical Initializer is "{ code }".
it("parses Initializer", function() {
expect("{ code };start = 'abcd'").toParseAs(
expect("{ code };start = 'abcd'").to.parseAs(
{ type: "grammar", initializer: initializer, rules: [ruleStart] }
);
});
// Canonical Rule is "a = 'abcd';".
it("parses Rule", function() {
expect("start\n=\n'abcd';").toParseAs(
expect("start\n=\n'abcd';").to.parseAs(
oneRuleGrammar(literalAbcd)
);
expect("start\n'start rule'\n=\n'abcd';").toParseAs(
expect("start\n'start rule'\n=\n'abcd';").to.parseAs(
oneRuleGrammar(named)
);
});
// Canonical Expression is "'abcd'".
it("parses Expression", function() {
expect("start = 'abcd' / 'efgh' / 'ijkl'").toParseAs(
expect("start = 'abcd' / 'efgh' / 'ijkl'").to.parseAs(
oneRuleGrammar(choice)
);
});
// Canonical ChoiceExpression is "'abcd' / 'efgh' / 'ijkl'".
it("parses ChoiceExpression", function() {
expect("start = 'abcd' { code }").toParseAs(
expect("start = 'abcd' { code }").to.parseAs(
oneRuleGrammar(actionAbcd)
);
expect("start = 'abcd' { code }\n/\n'efgh' { code }").toParseAs(
expect("start = 'abcd' { code }\n/\n'efgh' { code }").to.parseAs(
oneRuleGrammar(choice2)
);
expect(
"start = 'abcd' { code }\n/\n'efgh' { code }\n/\n'ijkl' { code }\n/\n'mnop' { code }"
).toParseAs(
).to.parseAs(
oneRuleGrammar(choice4)
);
});
// Canonical ActionExpression is "'abcd' { code }".
it("parses ActionExpression", function() {
expect("start = 'abcd' 'efgh' 'ijkl'").toParseAs(
expect("start = 'abcd' 'efgh' 'ijkl'").to.parseAs(
oneRuleGrammar(sequence)
);
expect("start = 'abcd' 'efgh' 'ijkl'\n{ code }").toParseAs(
expect("start = 'abcd' 'efgh' 'ijkl'\n{ code }").to.parseAs(
oneRuleGrammar(actionSequence)
);
});
// Canonical SequenceExpression is "'abcd' 'efgh' 'ijkl'".
it("parses SequenceExpression", function() {
expect("start = a:'abcd'").toParseAs(
expect("start = a:'abcd'").to.parseAs(
oneRuleGrammar(labeledAbcd)
);
expect("start = a:'abcd'\nb:'efgh'").toParseAs(
expect("start = a:'abcd'\nb:'efgh'").to.parseAs(
oneRuleGrammar(sequence2)
);
expect("start = a:'abcd'\nb:'efgh'\nc:'ijkl'\nd:'mnop'").toParseAs(
expect("start = a:'abcd'\nb:'efgh'\nc:'ijkl'\nd:'mnop'").to.parseAs(
oneRuleGrammar(sequence4)
);
});
// Canonical LabeledExpression is "a:'abcd'".
it("parses LabeledExpression", function() {
expect("start = a\n:\n!'abcd'").toParseAs(oneRuleGrammar(labeledSimpleNot));
expect("start = !'abcd'").toParseAs(oneRuleGrammar(simpleNotAbcd));
expect("start = a\n:\n!'abcd'").to.parseAs(oneRuleGrammar(labeledSimpleNot));
expect("start = !'abcd'").to.parseAs(oneRuleGrammar(simpleNotAbcd));
});
// Canonical PrefixedExpression is "!'abcd'".
it("parses PrefixedExpression", function() {
expect("start = !\n'abcd'?").toParseAs(oneRuleGrammar(simpleNotOptional));
expect("start = 'abcd'?").toParseAs(oneRuleGrammar(optional));
expect("start = !\n'abcd'?").to.parseAs(oneRuleGrammar(simpleNotOptional));
expect("start = 'abcd'?").to.parseAs(oneRuleGrammar(optional));
});
// Canonical PrefixedOperator is "!".
it("parses PrefixedOperator", function() {
expect("start = $'abcd'?").toParseAs(oneRuleGrammar(textOptional));
expect("start = &'abcd'?").toParseAs(oneRuleGrammar(simpleAndOptional));
expect("start = !'abcd'?").toParseAs(oneRuleGrammar(simpleNotOptional));
expect("start = $'abcd'?").to.parseAs(oneRuleGrammar(textOptional));
expect("start = &'abcd'?").to.parseAs(oneRuleGrammar(simpleAndOptional));
expect("start = !'abcd'?").to.parseAs(oneRuleGrammar(simpleNotOptional));
});
// Canonical SuffixedExpression is "'abcd'?".
it("parses SuffixedExpression", function() {
expect("start = 'abcd'\n?").toParseAs(oneRuleGrammar(optional));
expect("start = 'abcd'").toParseAs(oneRuleGrammar(literalAbcd));
expect("start = 'abcd'\n?").to.parseAs(oneRuleGrammar(optional));
expect("start = 'abcd'").to.parseAs(oneRuleGrammar(literalAbcd));
});
// Canonical SuffixedOperator is "?".
it("parses SuffixedOperator", function() {
expect("start = 'abcd'?").toParseAs(oneRuleGrammar(optional));
expect("start = 'abcd'*").toParseAs(oneRuleGrammar(zeroOrMore));
expect("start = 'abcd'+").toParseAs(oneRuleGrammar(oneOrMore));
expect("start = 'abcd'?").to.parseAs(oneRuleGrammar(optional));
expect("start = 'abcd'*").to.parseAs(oneRuleGrammar(zeroOrMore));
expect("start = 'abcd'+").to.parseAs(oneRuleGrammar(oneOrMore));
});
// Canonical PrimaryExpression is "'abcd'".
it("parses PrimaryExpression", function() {
expect("start = 'abcd'").toParseAs(trivialGrammar);
expect("start = [a-d]").toParseAs(classGrammar([["a", "d"]], false, false));
expect("start = .").toParseAs(anyGrammar());
expect("start = a").toParseAs(ruleRefGrammar("a"));
expect("start = &{ code }").toParseAs(oneRuleGrammar(semanticAnd));
expect("start = 'abcd'").to.parseAs(trivialGrammar);
expect("start = [a-d]").to.parseAs(classGrammar([["a", "d"]], false, false));
expect("start = .").to.parseAs(anyGrammar());
expect("start = a").to.parseAs(ruleRefGrammar("a"));
expect("start = &{ code }").to.parseAs(oneRuleGrammar(semanticAnd));
expect("start = (\na:'abcd'\n)").toParseAs(oneRuleGrammar(groupLabeled));
expect("start = (\n'abcd' 'efgh' 'ijkl'\n)").toParseAs(oneRuleGrammar(groupSequence));
expect("start = (\n'abcd'\n)").toParseAs(trivialGrammar);
expect("start = (\na:'abcd'\n)").to.parseAs(oneRuleGrammar(groupLabeled));
expect("start = (\n'abcd' 'efgh' 'ijkl'\n)").to.parseAs(oneRuleGrammar(groupSequence));
expect("start = (\n'abcd'\n)").to.parseAs(trivialGrammar);
});
// Canonical RuleReferenceExpression is "a".
it("parses RuleReferenceExpression", function() {
expect("start = a").toParseAs(ruleRefGrammar("a"));
expect("start = a").to.parseAs(ruleRefGrammar("a"));
expect("start = a\n=").toFailToParse();
expect("start = a\n'abcd'\n=").toFailToParse();
expect("start = a\n=").to.failToParse();
expect("start = a\n'abcd'\n=").to.failToParse();
});
// Canonical SemanticPredicateExpression is "!{ code }".
it("parses SemanticPredicateExpression", function() {
expect("start = !\n{ code }").toParseAs(oneRuleGrammar(semanticNot));
expect("start = !\n{ code }").to.parseAs(oneRuleGrammar(semanticNot));
});
// Canonical SemanticPredicateOperator is "!".
it("parses SemanticPredicateOperator", function() {
expect("start = &{ code }").toParseAs(oneRuleGrammar(semanticAnd));
expect("start = !{ code }").toParseAs(oneRuleGrammar(semanticNot));
expect("start = &{ code }").to.parseAs(oneRuleGrammar(semanticAnd));
expect("start = !{ code }").to.parseAs(oneRuleGrammar(semanticNot));
});
// The SourceCharacter rule is not tested.
// Canonical WhiteSpace is " ".
it("parses WhiteSpace", function() {
expect("start =\t'abcd'").toParseAs(trivialGrammar);
expect("start =\v'abcd'").toParseAs(trivialGrammar);
expect("start =\f'abcd'").toParseAs(trivialGrammar);
expect("start = 'abcd'").toParseAs(trivialGrammar);
expect("start =\u00A0'abcd'").toParseAs(trivialGrammar);
expect("start =\uFEFF'abcd'").toParseAs(trivialGrammar);
expect("start =\u1680'abcd'").toParseAs(trivialGrammar);
expect("start =\t'abcd'").to.parseAs(trivialGrammar);
expect("start =\v'abcd'").to.parseAs(trivialGrammar);
expect("start =\f'abcd'").to.parseAs(trivialGrammar);
expect("start = 'abcd'").to.parseAs(trivialGrammar);
expect("start =\u00A0'abcd'").to.parseAs(trivialGrammar);
expect("start =\uFEFF'abcd'").to.parseAs(trivialGrammar);
expect("start =\u1680'abcd'").to.parseAs(trivialGrammar);
});
// Canonical LineTerminator is "\n".
it("parses LineTerminator", function() {
expect("start = '\n'").toFailToParse();
expect("start = '\r'").toFailToParse();
expect("start = '\u2028'").toFailToParse();
expect("start = '\u2029'").toFailToParse();
expect("start = '\n'").to.failToParse();
expect("start = '\r'").to.failToParse();
expect("start = '\u2028'").to.failToParse();
expect("start = '\u2029'").to.failToParse();
});
// Canonical LineTerminatorSequence is "\r\n".
it("parses LineTerminatorSequence", function() {
expect("start =\n'abcd'").toParseAs(trivialGrammar);
expect("start =\r\n'abcd'").toParseAs(trivialGrammar);
expect("start =\r'abcd'").toParseAs(trivialGrammar);
expect("start =\u2028'abcd'").toParseAs(trivialGrammar);
expect("start =\u2029'abcd'").toParseAs(trivialGrammar);
expect("start =\n'abcd'").to.parseAs(trivialGrammar);
expect("start =\r\n'abcd'").to.parseAs(trivialGrammar);
expect("start =\r'abcd'").to.parseAs(trivialGrammar);
expect("start =\u2028'abcd'").to.parseAs(trivialGrammar);
expect("start =\u2029'abcd'").to.parseAs(trivialGrammar);
});
// Canonical Comment is "/* comment */".
it("parses Comment", function() {
expect("start =// comment\n'abcd'").toParseAs(trivialGrammar);
expect("start =/* comment */'abcd'").toParseAs(trivialGrammar);
expect("start =// comment\n'abcd'").to.parseAs(trivialGrammar);
expect("start =/* comment */'abcd'").to.parseAs(trivialGrammar);
});
// Canonical MultiLineComment is "/* comment */".
it("parses MultiLineComment", function() {
expect("start =/**/'abcd'").toParseAs(trivialGrammar);
expect("start =/*a*/'abcd'").toParseAs(trivialGrammar);
expect("start =/*abc*/'abcd'").toParseAs(trivialGrammar);
expect("start =/**/'abcd'").to.parseAs(trivialGrammar);
expect("start =/*a*/'abcd'").to.parseAs(trivialGrammar);
expect("start =/*abc*/'abcd'").to.parseAs(trivialGrammar);
expect("start =/**/*/'abcd'").toFailToParse();
expect("start =/**/*/'abcd'").to.failToParse();
});
// Canonical MultiLineCommentNoLineTerminator is "/* comment */".
it("parses MultiLineCommentNoLineTerminator", function() {
expect("a = 'abcd'/**/\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'/*a*/\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'/*abc*/\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'/**/\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd'/*a*/\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd'/*abc*/\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd'/**/*/\r\nb = 'efgh'").toFailToParse();
expect("a = 'abcd'/*\n*/\r\nb = 'efgh'").toFailToParse();
expect("a = 'abcd'/**/*/\r\nb = 'efgh'").to.failToParse();
expect("a = 'abcd'/*\n*/\r\nb = 'efgh'").to.failToParse();
});
// Canonical SingleLineComment is "// comment".
it("parses SingleLineComment", function() {
expect("start =//\n'abcd'").toParseAs(trivialGrammar);
expect("start =//a\n'abcd'").toParseAs(trivialGrammar);
expect("start =//abc\n'abcd'").toParseAs(trivialGrammar);
expect("start =//\n'abcd'").to.parseAs(trivialGrammar);
expect("start =//a\n'abcd'").to.parseAs(trivialGrammar);
expect("start =//abc\n'abcd'").to.parseAs(trivialGrammar);
expect("start =//\n@\n'abcd'").toFailToParse();
expect("start =//\n@\n'abcd'").to.failToParse();
});
// Canonical Identifier is "a".
it("parses Identifier", function() {
expect("start = a:'abcd'").toParseAs(oneRuleGrammar(labeledAbcd));
expect("start = a:'abcd'").to.parseAs(oneRuleGrammar(labeledAbcd));
});
// Canonical IdentifierName is "a".
it("parses IdentifierName", function() {
expect("start = a").toParseAs(ruleRefGrammar("a"));
expect("start = ab").toParseAs(ruleRefGrammar("ab"));
expect("start = abcd").toParseAs(ruleRefGrammar("abcd"));
expect("start = a").to.parseAs(ruleRefGrammar("a"));
expect("start = ab").to.parseAs(ruleRefGrammar("ab"));
expect("start = abcd").to.parseAs(ruleRefGrammar("abcd"));
});
// Canonical IdentifierStart is "a".
it("parses IdentifierStart", function() {
expect("start = a").toParseAs(ruleRefGrammar("a"));
expect("start = $").toParseAs(ruleRefGrammar("$"));
expect("start = _").toParseAs(ruleRefGrammar("_"));
expect("start = \\u0061").toParseAs(ruleRefGrammar("a"));
expect("start = a").to.parseAs(ruleRefGrammar("a"));
expect("start = $").to.parseAs(ruleRefGrammar("$"));
expect("start = _").to.parseAs(ruleRefGrammar("_"));
expect("start = \\u0061").to.parseAs(ruleRefGrammar("a"));
});
// Canonical IdentifierPart is "a".
it("parses IdentifierPart", function() {
expect("start = aa").toParseAs(ruleRefGrammar("aa"));
expect("start = a\u0300").toParseAs(ruleRefGrammar("a\u0300"));
expect("start = a0").toParseAs(ruleRefGrammar("a0"));
expect("start = a\u203F").toParseAs(ruleRefGrammar("a\u203F"));
expect("start = a\u200C").toParseAs(ruleRefGrammar("a\u200C"));
expect("start = a\u200D").toParseAs(ruleRefGrammar("a\u200D"));
expect("start = aa").to.parseAs(ruleRefGrammar("aa"));
expect("start = a\u0300").to.parseAs(ruleRefGrammar("a\u0300"));
expect("start = a0").to.parseAs(ruleRefGrammar("a0"));
expect("start = a\u203F").to.parseAs(ruleRefGrammar("a\u203F"));
expect("start = a\u200C").to.parseAs(ruleRefGrammar("a\u200C"));
expect("start = a\u200D").to.parseAs(ruleRefGrammar("a\u200D"));
});
// Unicode rules and reserved word rules are not tested.
// Canonical LiteralMatcher is "'abcd'".
it("parses LiteralMatcher", function() {
expect("start = 'abcd'").toParseAs(literalGrammar("abcd", false));
expect("start = 'abcd'i").toParseAs(literalGrammar("abcd", true));
expect("start = 'abcd'").to.parseAs(literalGrammar("abcd", false));
expect("start = 'abcd'i").to.parseAs(literalGrammar("abcd", true));
});
// Canonical StringLiteral is "'abcd'".
it("parses StringLiteral", function() {
expect("start = \"\"").toParseAs(literalGrammar("", false));
expect("start = \"a\"").toParseAs(literalGrammar("a", false));
expect("start = \"abc\"").toParseAs(literalGrammar("abc", false));
expect("start = \"\"").to.parseAs(literalGrammar("", false));
expect("start = \"a\"").to.parseAs(literalGrammar("a", false));
expect("start = \"abc\"").to.parseAs(literalGrammar("abc", false));
expect("start = ''").toParseAs(literalGrammar("", false));
expect("start = 'a'").toParseAs(literalGrammar("a", false));
expect("start = 'abc'").toParseAs(literalGrammar("abc", false));
expect("start = ''").to.parseAs(literalGrammar("", false));
expect("start = 'a'").to.parseAs(literalGrammar("a", false));
expect("start = 'abc'").to.parseAs(literalGrammar("abc", false));
});
// Canonical DoubleStringCharacter is "a".
it("parses DoubleStringCharacter", function() {
expect("start = \"a\"").toParseAs(literalGrammar("a", false));
expect("start = \"\\n\"").toParseAs(literalGrammar("\n", false));
expect("start = \"\\\n\"").toParseAs(literalGrammar("", false));
expect("start = \"a\"").to.parseAs(literalGrammar("a", false));
expect("start = \"\\n\"").to.parseAs(literalGrammar("\n", false));
expect("start = \"\\\n\"").to.parseAs(literalGrammar("", false));
expect("start = \"\"\"").toFailToParse();
expect("start = \"\\\"").toFailToParse();
expect("start = \"\n\"").toFailToParse();
expect("start = \"\"\"").to.failToParse();
expect("start = \"\\\"").to.failToParse();
expect("start = \"\n\"").to.failToParse();
});
// Canonical SingleStringCharacter is "a".
it("parses SingleStringCharacter", function() {
expect("start = 'a'").toParseAs(literalGrammar("a", false));
expect("start = '\\n'").toParseAs(literalGrammar("\n", false));
expect("start = '\\\n'").toParseAs(literalGrammar("", false));
expect("start = 'a'").to.parseAs(literalGrammar("a", false));
expect("start = '\\n'").to.parseAs(literalGrammar("\n", false));
expect("start = '\\\n'").to.parseAs(literalGrammar("", false));
expect("start = '''").toFailToParse();
expect("start = '\\'").toFailToParse();
expect("start = '\n'").toFailToParse();
expect("start = '''").to.failToParse();
expect("start = '\\'").to.failToParse();
expect("start = '\n'").to.failToParse();
});
// Canonical CharacterClassMatcher is "[a-d]".
it("parses CharacterClassMatcher", function() {
expect("start = []").toParseAs(
expect("start = []").to.parseAs(
classGrammar([], false, false)
);
expect("start = [a-d]").toParseAs(
expect("start = [a-d]").to.parseAs(
classGrammar([["a", "d"]], false, false)
);
expect("start = [a]").toParseAs(
expect("start = [a]").to.parseAs(
classGrammar(["a"], false, false)
);
expect("start = [a-de-hi-l]").toParseAs(
expect("start = [a-de-hi-l]").to.parseAs(
classGrammar(
[["a", "d"], ["e", "h"], ["i", "l"]],
false,
false
)
);
expect("start = [^a-d]").toParseAs(
expect("start = [^a-d]").to.parseAs(
classGrammar([["a", "d"]], true, false)
);
expect("start = [a-d]i").toParseAs(
expect("start = [a-d]i").to.parseAs(
classGrammar([["a", "d"]], false, true)
);
expect("start = [\\\n]").toParseAs(
expect("start = [\\\n]").to.parseAs(
classGrammar([], false, false)
);
});
// Canonical ClassCharacterRange is "a-d".
it("parses ClassCharacterRange", function() {
expect("start = [a-d]").toParseAs(classGrammar([["a", "d"]], false, false));
expect("start = [a-d]").to.parseAs(classGrammar([["a", "d"]], false, false));
expect("start = [a-a]").toParseAs(classGrammar([["a", "a"]], false, false));
expect("start = [b-a]").toFailToParse({
expect("start = [a-a]").to.parseAs(classGrammar([["a", "a"]], false, false));
expect("start = [b-a]").to.failToParse({
message: "Invalid character range: b-a."
});
});
// Canonical ClassCharacter is "a".
it("parses ClassCharacter", function() {
expect("start = [a]").toParseAs(classGrammar(["a"], false, false));
expect("start = [\\n]").toParseAs(classGrammar(["\n"], false, false));
expect("start = [\\\n]").toParseAs(classGrammar([], false, false));
expect("start = [a]").to.parseAs(classGrammar(["a"], false, false));
expect("start = [\\n]").to.parseAs(classGrammar(["\n"], false, false));
expect("start = [\\\n]").to.parseAs(classGrammar([], false, false));
expect("start = []]").toFailToParse();
expect("start = [\\]").toFailToParse();
expect("start = [\n]").toFailToParse();
expect("start = []]").to.failToParse();
expect("start = [\\]").to.failToParse();
expect("start = [\n]").to.failToParse();
});
// Canonical LineContinuation is "\\\n".
it("parses LineContinuation", function() {
expect("start = '\\\r\n'").toParseAs(literalGrammar("", false));
expect("start = '\\\r\n'").to.parseAs(literalGrammar("", false));
});
// Canonical EscapeSequence is "n".
it("parses EscapeSequence", function() {
expect("start = '\\n'").toParseAs(literalGrammar("\n", false));
expect("start = '\\0'").toParseAs(literalGrammar("\x00", false));
expect("start = '\\xFF'").toParseAs(literalGrammar("\xFF", false));
expect("start = '\\uFFFF'").toParseAs(literalGrammar("\uFFFF", false));
expect("start = '\\n'").to.parseAs(literalGrammar("\n", false));
expect("start = '\\0'").to.parseAs(literalGrammar("\x00", false));
expect("start = '\\xFF'").to.parseAs(literalGrammar("\xFF", false));
expect("start = '\\uFFFF'").to.parseAs(literalGrammar("\uFFFF", false));
expect("start = '\\09'").toFailToParse();
expect("start = '\\09'").to.failToParse();
});
// Canonical CharacterEscapeSequence is "n".
it("parses CharacterEscapeSequence", function() {
expect("start = '\\n'").toParseAs(literalGrammar("\n", false));
expect("start = '\\a'").toParseAs(literalGrammar("a", false));
expect("start = '\\n'").to.parseAs(literalGrammar("\n", false));
expect("start = '\\a'").to.parseAs(literalGrammar("a", false));
});
// Canonical SingleEscapeCharacter is "n".
it("parses SingleEscapeCharacter", function() {
expect("start = '\\''").toParseAs(literalGrammar("'", false));
expect("start = '\\\"'").toParseAs(literalGrammar("\"", false));
expect("start = '\\\\'").toParseAs(literalGrammar("\\", false));
expect("start = '\\b'").toParseAs(literalGrammar("\b", false));
expect("start = '\\f'").toParseAs(literalGrammar("\f", false));
expect("start = '\\n'").toParseAs(literalGrammar("\n", false));
expect("start = '\\r'").toParseAs(literalGrammar("\r", false));
expect("start = '\\t'").toParseAs(literalGrammar("\t", false));
expect("start = '\\v'").toParseAs(literalGrammar("\v", false));
expect("start = '\\''").to.parseAs(literalGrammar("'", false));
expect("start = '\\\"'").to.parseAs(literalGrammar("\"", false));
expect("start = '\\\\'").to.parseAs(literalGrammar("\\", false));
expect("start = '\\b'").to.parseAs(literalGrammar("\b", false));
expect("start = '\\f'").to.parseAs(literalGrammar("\f", false));
expect("start = '\\n'").to.parseAs(literalGrammar("\n", false));
expect("start = '\\r'").to.parseAs(literalGrammar("\r", false));
expect("start = '\\t'").to.parseAs(literalGrammar("\t", false));
expect("start = '\\v'").to.parseAs(literalGrammar("\v", false));
});
// Canonical NonEscapeCharacter is "a".
it("parses NonEscapeCharacter", function() {
expect("start = '\\a'").toParseAs(literalGrammar("a", false));
expect("start = '\\a'").to.parseAs(literalGrammar("a", false));
// The negative predicate is impossible to test with PEG.js grammar
// structure.
@ -608,66 +595,66 @@ describe("PEG.js grammar parser", function() {
// Canonical HexEscapeSequence is "xFF".
it("parses HexEscapeSequence", function() {
expect("start = '\\xFF'").toParseAs(literalGrammar("\xFF", false));
expect("start = '\\xFF'").to.parseAs(literalGrammar("\xFF", false));
});
// Canonical UnicodeEscapeSequence is "uFFFF".
it("parses UnicodeEscapeSequence", function() {
expect("start = '\\uFFFF'").toParseAs(literalGrammar("\uFFFF", false));
expect("start = '\\uFFFF'").to.parseAs(literalGrammar("\uFFFF", false));
});
// Digit rules are not tested.
// Canonical AnyMatcher is ".".
it("parses AnyMatcher", function() {
expect("start = .").toParseAs(anyGrammar());
expect("start = .").to.parseAs(anyGrammar());
});
// Canonical CodeBlock is "{ code }".
it("parses CodeBlock", function() {
expect("start = 'abcd' { code }").toParseAs(actionGrammar(" code "));
expect("start = 'abcd' { code }").to.parseAs(actionGrammar(" code "));
});
// Canonical Code is " code ".
it("parses Code", function() {
expect("start = 'abcd' {a}").toParseAs(actionGrammar("a"));
expect("start = 'abcd' {abc}").toParseAs(actionGrammar("abc"));
expect("start = 'abcd' {{a}}").toParseAs(actionGrammar("{a}"));
expect("start = 'abcd' {{a}{b}{c}}").toParseAs(actionGrammar("{a}{b}{c}"));
expect("start = 'abcd' {a}").to.parseAs(actionGrammar("a"));
expect("start = 'abcd' {abc}").to.parseAs(actionGrammar("abc"));
expect("start = 'abcd' {{a}}").to.parseAs(actionGrammar("{a}"));
expect("start = 'abcd' {{a}{b}{c}}").to.parseAs(actionGrammar("{a}{b}{c}"));
expect("start = 'abcd' {{}").toFailToParse();
expect("start = 'abcd' {}}").toFailToParse();
expect("start = 'abcd' {{}").to.failToParse();
expect("start = 'abcd' {}}").to.failToParse();
});
// Unicode character category rules and token rules are not tested.
// Canonical __ is "\n".
it("parses __", function() {
expect("start ='abcd'").toParseAs(trivialGrammar);
expect("start = 'abcd'").toParseAs(trivialGrammar);
expect("start =\r\n'abcd'").toParseAs(trivialGrammar);
expect("start =/* comment */'abcd'").toParseAs(trivialGrammar);
expect("start = 'abcd'").toParseAs(trivialGrammar);
expect("start ='abcd'").to.parseAs(trivialGrammar);
expect("start = 'abcd'").to.parseAs(trivialGrammar);
expect("start =\r\n'abcd'").to.parseAs(trivialGrammar);
expect("start =/* comment */'abcd'").to.parseAs(trivialGrammar);
expect("start = 'abcd'").to.parseAs(trivialGrammar);
});
// Canonical _ is " ".
it("parses _", function() {
expect("a = 'abcd'\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'/* comment */\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd'/* comment */\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
});
// Canonical EOS is ";".
it("parses EOS", function() {
expect("a = 'abcd'\n;b = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd' // comment\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect("a = 'abcd'\n;b = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd' \r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd' // comment\r\nb = 'efgh'").to.parseAs(twoRuleGrammar);
expect("a = 'abcd'\nb = 'efgh'").to.parseAs(twoRuleGrammar);
});
// Canonical EOF is the end of input.
it("parses EOF", function() {
expect("start = 'abcd'\n").toParseAs(trivialGrammar);
expect("start = 'abcd'\n").to.parseAs(trivialGrammar);
});
});

@ -1,681 +0,0 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'},
self.createDom('span', { className: 'exceptions' },
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
function noTryCatch() {
return window.location.search.match(/catch=false/);
}
function searchWithCatch() {
var params = jasmine.HtmlReporter.parameters(window.document);
var removed = false;
var i = 0;
while (!removed && i < params.length) {
if (params[i].match(/catch=/)) {
params.splice(i, 1);
removed = true;
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
return params.join("&");
}
function setExceptionHandling() {
var chxCatch = document.getElementById('no_try_catch');
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
};
}
};
jasmine.HtmlReporter.parameters = function(doc) {
var paramStr = doc.location.search.substring(1);
var params = [];
if (paramStr.length > 0) {
params = paramStr.split('&');
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
if (params.length > 0) {
link += params.join("&");
}
return link;
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

@ -1,82 +0,0 @@
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }

File diff suppressed because it is too large Load Diff

@ -1,8 +1,10 @@
Copyright (c) 2008-2011 Pivotal Labs
(The MIT License)
Copyright (c) 2016 JS Foundation and contributors, https://js.foundation
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
@ -11,10 +13,10 @@ the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,326 @@
@charset "utf-8";
body {
margin:0;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 60px 50px;
}
#mocha ul,
#mocha li {
margin: 0;
padding: 0;
}
#mocha ul {
list-style: none;
}
#mocha h1,
#mocha h2 {
margin: 0;
}
#mocha h1 {
margin-top: 15px;
font-size: 1em;
font-weight: 200;
}
#mocha h1 a {
text-decoration: none;
color: inherit;
}
#mocha h1 a:hover {
text-decoration: underline;
}
#mocha .suite .suite h1 {
margin-top: 0;
font-size: .8em;
}
#mocha .hidden {
display: none;
}
#mocha h2 {
font-size: 12px;
font-weight: normal;
cursor: pointer;
}
#mocha .suite {
margin-left: 15px;
}
#mocha .test {
margin-left: 15px;
overflow: hidden;
}
#mocha .test.pending:hover h2::after {
content: '(pending)';
font-family: arial, sans-serif;
}
#mocha .test.pass.medium .duration {
background: #c09853;
}
#mocha .test.pass.slow .duration {
background: #b94a48;
}
#mocha .test.pass::before {
content: '✓';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #00d6b2;
}
#mocha .test.pass .duration {
font-size: 9px;
margin-left: 5px;
padding: 2px 5px;
color: #fff;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;
}
#mocha .test.pass.fast .duration {
display: none;
}
#mocha .test.pending {
color: #0b97c4;
}
#mocha .test.pending::before {
content: '◦';
color: #0b97c4;
}
#mocha .test.fail {
color: #c00;
}
#mocha .test.fail pre {
color: black;
}
#mocha .test.fail::before {
content: '✖';
font-size: 12px;
display: block;
float: left;
margin-right: 5px;
color: #c00;
}
#mocha .test pre.error {
color: #c00;
max-height: 300px;
overflow: auto;
}
#mocha .test .html-error {
overflow: auto;
color: black;
line-height: 1.5;
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
max-width: 85%; /*(1)*/
max-width: -webkit-calc(100% - 42px);
max-width: -moz-calc(100% - 42px);
max-width: calc(100% - 42px); /*(2)*/
max-height: 300px;
word-wrap: break-word;
border-bottom-color: #ddd;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-box-shadow: 0 1px 3px #eee;
box-shadow: 0 1px 3px #eee;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
#mocha .test .html-error pre.error {
border: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: 0;
-moz-box-shadow: 0;
box-shadow: 0;
padding: 0;
margin: 0;
margin-top: 18px;
max-height: none;
}
/**
* (1): approximate for browsers not supporting calc
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
* ^^ seriously
*/
#mocha .test pre {
display: block;
float: left;
clear: left;
font: 12px/1.5 monaco, monospace;
margin: 5px;
padding: 15px;
border: 1px solid #eee;
max-width: 85%; /*(1)*/
max-width: -webkit-calc(100% - 42px);
max-width: -moz-calc(100% - 42px);
max-width: calc(100% - 42px); /*(2)*/
word-wrap: break-word;
border-bottom-color: #ddd;
-webkit-box-shadow: 0 1px 3px #eee;
-moz-box-shadow: 0 1px 3px #eee;
box-shadow: 0 1px 3px #eee;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
#mocha .test h2 {
position: relative;
}
#mocha .test a.replay {
position: absolute;
top: 3px;
right: 0;
text-decoration: none;
vertical-align: middle;
display: block;
width: 15px;
height: 15px;
line-height: 15px;
text-align: center;
background: #eee;
font-size: 15px;
-webkit-border-radius: 15px;
-moz-border-radius: 15px;
border-radius: 15px;
-webkit-transition:opacity 200ms;
-moz-transition:opacity 200ms;
-o-transition:opacity 200ms;
transition: opacity 200ms;
opacity: 0.3;
color: #888;
}
#mocha .test:hover a.replay {
opacity: 1;
}
#mocha-report.pass .test.fail {
display: none;
}
#mocha-report.fail .test.pass {
display: none;
}
#mocha-report.pending .test.pass,
#mocha-report.pending .test.fail {
display: none;
}
#mocha-report.pending .test.pass.pending {
display: block;
}
#mocha-error {
color: #c00;
font-size: 1.5em;
font-weight: 100;
letter-spacing: 1px;
}
#mocha-stats {
position: fixed;
top: 15px;
right: 10px;
font-size: 12px;
margin: 0;
color: #888;
z-index: 1;
}
#mocha-stats .progress {
float: right;
padding-top: 0;
/**
* Set safe initial values, so mochas .progress does not inherit these
* properties from Bootstrap .progress (which causes .progress height to
* equal line height set in Bootstrap).
*/
height: auto;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background-color: initial;
}
#mocha-stats em {
color: black;
}
#mocha-stats a {
text-decoration: none;
color: inherit;
}
#mocha-stats a:hover {
border-bottom: 1px solid #eee;
}
#mocha-stats li {
display: inline-block;
margin: 0 5px;
list-style: none;
padding-top: 11px;
}
#mocha-stats canvas {
width: 40px;
height: 40px;
}
#mocha code .comment { color: #ddd; }
#mocha code .init { color: #2f6fad; }
#mocha code .string { color: #5890ad; }
#mocha code .keyword { color: #8a6343; }
#mocha code .number { color: #2f6fad; }
@media screen and (max-device-width: 480px) {
#mocha {
margin: 60px 0px;
}
#mocha #stats {
position: absolute;
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save