Skip to content

Commit 99b0992

Browse files
committed
Wire in jsdom to run browser-based code in the test runner
1 parent 7aafa6d commit 99b0992

File tree

5 files changed

+89
-16
lines changed

5 files changed

+89
-16
lines changed

Diff for: 12_browser.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ file containing the JavaScript program) to be fetched from a URL.
258258
----
259259
<!doctype html>
260260
<h1>Testing alert</h1>
261-
<script src="http://eloquentjavascript.net/js/hello.js">
261+
<script src="js/hello.js">
262262
</script>
263263
----
264264

Diff for: 13_dom.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ and use `document.getElementById` instead.
215215
----
216216
<!doctype html>
217217
<p>My ostrich Gertrude:</p>
218-
<p><img id="getrude" src="img/ostrich.png"></p>
218+
<p><img id="gertrude" src="img/ostrich.png"></p>
219219

220220
<script>
221221
var ostrich = document.getElementById("gertrude");
@@ -533,6 +533,8 @@ happen, and consequently run really slowly. This page demonstrates the
533533
difference. It contains two different programs that build up a list of
534534
“X” characters 2000 pixels wide, and measures the time they take.
535535

536+
// test: nonumbers
537+
536538
[source,text/html]
537539
----
538540
<!doctype html>

Diff for: bin/run_tests.js

+81-12
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ var fs = require("fs");
66
var acorn = require("acorn");
77

88
var file = process.argv[2];
9+
var chapNum = Number(file.match(/^\d*/)[0]);
910
var input = fs.readFileSync(file, "utf8");
1011

11-
var code = "var alert = function() {}, prompt = function() { return 'x'; }, confirm = function() { return true; }; window = this;\n";
12+
var baseCode = "var alert = function() {}, prompt = function() { return 'x'; }, confirm = function() { return true; }; window = this; requestAnimationFrame = setTimeout = clearTimeout = setInterval = clearInterval = Math.min;\n";
1213

1314
var include = /\n:load_files: (\[[^\]]+\])/.exec(input);
1415
if (include) JSON.parse(include[1]).forEach(function(fileName) {
1516
var text = fs.readFileSync("html/" + fileName);
1617
if (!/\/\/ test: no/.test(text))
17-
code += text;
18+
baseCode += text;
1819
});
1920

2021
function wrapTestOutput(snippet, config) {
@@ -36,12 +37,27 @@ function pos(index) {
3637
return "line " + (input.slice(0, index).split("\n").length + 1);
3738
}
3839

39-
var re = /((?:\/\/.*\n|\s)*)(?:[sandbox=.*\n)?\[source,javascript\]\n----\n([\s\S]*?\n)----/g, m;
40+
var sandboxes = {}, anonId = 0;
41+
42+
var re = /((?:\/\/.*\n|\s)*)(?:\[sandbox="([^"]*)"\]\n)?\[source,([^\]]+)\]\n----\n([\s\S]*?\n)----/g, m;
4043
while (m = re.exec(input)) {
41-
var snippet = m[2], hasConf = m[1].match(/\/\/ test: (.*)/), config = hasConf ? hasConf[1] : "";
44+
var snippet = m[4], hasConf = m[1].match(/\/\/ test: (.*)/);
45+
var sandbox = m[2] || "null", type = m[3], config = hasConf ? hasConf[1] : "";
4246
var where = pos(m.index);
47+
48+
if (type != "javascript" && type != "text/html") continue;
49+
50+
var boxId = m[2] || (type == "javascript" ? "null" : "box" + (++anonId));
51+
var sandbox = sandboxes[boxId];
52+
if (!sandbox)
53+
sandbox = sandboxes[boxId] = {code: ""};
54+
55+
if (type == "text/html") {
56+
var stripped = stripHTML(snippet);
57+
snippet = stripped.javascript;
58+
}
4359
try {
44-
acorn.parse(snippet, {strictSemicolons: !/01_value/.test(file)});
60+
acorn.parse(snippet, {strictSemicolons: chapNum != 1});
4561
} catch(e) {
4662
console.log("parse error at " + where + ": " + e.toString());
4763
}
@@ -50,8 +66,28 @@ while (m = re.exec(input)) {
5066
else if (/\/\/ /.test(snippet)) snippet = wrapTestOutput(snippet, config);
5167
if (/\bwrap\b/.test(config)) snippet = "(function(){\n" + snippet + "}());\n";
5268

53-
code += "console.pos = " + JSON.stringify(where) + ";\n";
54-
code += snippet;
69+
if (type == "text/html") {
70+
if (sandbox.html) console.log("Double HTML for box " + boxId);
71+
sandbox.html = stripped.html;
72+
sandbox.code = stripped.included + "console.pos = " + JSON.stringify(where) + ";\n" + snippet + sandbox.code;
73+
} else {
74+
sandbox.code += "console.pos = " + JSON.stringify(where) + ";\n";
75+
sandbox.code += snippet;
76+
}
77+
}
78+
79+
function stripHTML(code) {
80+
var included = "", script = "";
81+
code = code.replace(/<script\b[^>]*?(?:\bsrc\s*=\s*('[^']+'|"[^"]+"|[^\s>]+)[^>]*)?>([\s\S]*?)<\/script>/, function(m, src, content) {
82+
if (src) {
83+
if (/["']/.test(src.charAt(0))) src = src.slice(1, src.length - 1);
84+
included += fs.readFileSync("html/" + src, "utf8");
85+
} else {
86+
script += content;
87+
}
88+
return "";
89+
});
90+
return {html: code, included: included, javascript: script};
5591
}
5692

5793
function represent(val) {
@@ -136,6 +172,7 @@ var accum = "", _console = {
136172
var clip = string.indexOf("…"), ok = false;
137173
if (/\btrailing\b/.test(config)) accum = accum.replace(/\s+(\n|$)/g, "$1");
138174
if (/\btrim\b/.test(config)) { accum = accum.trim(); string = string.trim(); }
175+
if (/\bnonumbers\b/.test(config)) { accum = accum.replace(/\d/g, ""); string = string.replace(/\d/g, ""); }
139176
if (/\bclip\b/.test(config)) ok = compareClipped(string, accum);
140177
else if (/\bjoin\b/.test(config)) ok = compareJoined(string, accum);
141178
else if (clip > -1) ok = string.slice(0, clip) == accum.slice(0, clip);
@@ -145,7 +182,6 @@ var accum = "", _console = {
145182
},
146183
missingErr: function() {
147184
console.log("expected error not raised at " + this.pos);
148-
console.log(code);
149185
},
150186
compareErr: function(err, string) {
151187
if (err.toString() != string)
@@ -154,8 +190,41 @@ var accum = "", _console = {
154190
pos: null
155191
};
156192

157-
try {
158-
(new Function("console", code))(_console);
159-
} catch(e) {
160-
console.log("error raised (" + _console.pos + "): " + e.toString());
193+
function report(err) {
194+
var msg = err.toString();
195+
if (/^\[object/.test(msg) && err.message) msg = err.message;
196+
console.log("error raised (" + _console.pos + "): " + msg, err.stack);
197+
}
198+
199+
var i = 0, boxes = Object.keys(sandboxes).map(function(k) { return sandboxes[k]; });;
200+
function nextSandbox() {
201+
if (i == boxes.length) return;
202+
var sandbox = boxes[i];
203+
i++;
204+
if (chapNum < 12) { // Language-only
205+
try {
206+
(new Function("console", baseCode + sandbox.code))(_console);
207+
nextSandbox();
208+
} catch(e) {
209+
report(e);
210+
}
211+
} else {
212+
require("jsdom").env({
213+
url: file + "/" + i,
214+
html: sandbox.html || "<!doctype html><body></body>",
215+
src: [baseCode],
216+
done: function(err, window) {
217+
if (err) report(err[0]);
218+
window.console = _console;
219+
window.Element.prototype.innerText = "abc";
220+
try {
221+
window.run(sandbox.code, file + "/" + i);
222+
} catch (e) {
223+
report(e);
224+
}
225+
nextSandbox();
226+
}
227+
});
228+
}
161229
}
230+
nextSandbox();

Diff for: html/js/sandbox.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
if (/["']/.test(src.charAt(0))) src = src.slice(1, src.length - 1);
8585
tag.src = src;
8686
} else {
87-
tag.innerHTML = preprocess(content, sandbox);
87+
tag.text = preprocess(content, sandbox);
8888
}
8989
scriptTags.push(tag);
9090
return "";

Diff for: package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
},
1111
"dependencies": {
1212
"acorn": "0.3",
13-
"codemirror": "3.18"
13+
"codemirror": "3.18",
14+
"kkoopa/contextify": "0.1",
15+
"jsdom": "0.10"
1416
}
1517
}

0 commit comments

Comments
 (0)