describe("jsdom/cancel-requests", { skipIfBrowser: true }, () => { let server; let host; before(() => { return createServer((req, res) => { setTimeout(() => { res.writeHead(200, { "Content-Length": routes[req.url].length }); res.end(routes[req.url]); }, 200); }) .then(s => { server = s; host = `http://127.0.0.1:${s.address().port}`; }); }); after(() => { return server.destroy(); }); specify("aborting env request should stop window creation", { async: true }, t => { const req = jsdom.env({ url: host + "/html", created(err, window) { assert.ok(err, "There should be an error"); assert.notOk(window, "the window should not have been created"); t.done(); }, features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] } }); req.abort(); }); specify("closing window should close requests", { async: true }, t => { jsdom.env({ url: host + "/html", created(err, window) { assert.ifError(err, "There should be no errors"); process.nextTick(() => { const script = window.document.getElementsByTagName("script")[1]; script.onload = () => { assert.ok(false, "the external script onload should not be executed (old)"); }; script.addEventListener("load", () => { assert.ok(false, "the external script onload should not be executed"); }, false); window.close(); setTimeout(() => { assert.notOk(window.testJs, "the external script should not execute"); t.done(); }, 1000); }); }, features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] } }); }); specify("closing window should close XHR", { async: true }, t => { jsdom.env({ url: host + "/html", created(err, window) { assert.ifError(err, "There should be no errors"); process.nextTick(() => { const xhr = new window.XMLHttpRequest(); xhr.open("GET", "/xhr", true); xhr.onload = () => { assert.ok(false, "xhr should not trigger load (old)"); t.done(); }; xhr.addEventListener("load", () => { assert.ok(false, "xhr should not trigger load"); }, false); xhr.send(); window.close(); setTimeout(() => { t.done(); }, 1000); }); } }); }); specify("stopping window should close requests and error event should be triggered", { async: true }, t => { jsdom.env({ url: host + "/html", created(err, window) { assert.ifError(err, "There should be no errors"); process.nextTick(() => { const script = window.document.getElementsByTagName("script")[1]; script.onerror = () => { assert.ok(false, "the external script onerror should not be executed (old)"); }; script.addEventListener("error", () => { assert.ok(false, "the external script onerror should not be executed"); }); window.stop(); setTimeout(() => { assert.ok(!window.testJs, "the external script should not execute"); t.done(); }, 1000); }); }, features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] } }); }); specify("stopping window should close xhr and abort event should be triggered", { async: true }, t => { jsdom.env({ url: host + "/html", created(err, window) { assert.ifError(err, "There should be no errors"); process.nextTick(() => { const xhr = new window.XMLHttpRequest(); xhr.open("GET", "/xhr", true); xhr.onreadystatechange = () => { assert.ok(true, "xhr should trigger change state (old)"); }; xhr.addEventListener("readystatechange", () => { assert.ok(true, "xhr should trigger change state"); }, false); xhr.onload = () => { assert.ok(false, "xhr should not trigger load (old)"); t.done(); }; xhr.addEventListener("load", () => { assert.ok(false, "xhr should not trigger load"); t.done(); }, false); xhr.onabort = () => { assert.ok(true, "xhr should trigger abort (old)"); }; xhr.addEventListener("abort", () => { assert.ok(true, "xhr should trigger abort"); }, false); xhr.send(); window.stop(); setTimeout(() => { t.done(); }, 1000); }); } }); }); });
describe("API: constructor options", () => { describe("(general tests)", () => { it("should not mutate the passed-in options object", () => { const options = {}; // eslint-disable-next-line no-new new JSDOM(``, options); assert.strictEqual(Object.getOwnPropertyNames(options).length, 0); }); }); describe("referrer", () => { it("should allow customizing document.referrer via the referrer option", () => { const { document } = (new JSDOM(``, { referrer: "http://example.com/" })).window; assert.strictEqual(document.referrer, "http://example.com/"); }); it("should throw an error when passing an invalid absolute URL for referrer", () => { assert.throws(() => new JSDOM(``, { referrer: "asdf" }), TypeError); }); it("should canonicalize referrer URLs", () => { const { document } = (new JSDOM(``, { referrer: "http:example.com" })).window; assert.strictEqual(document.referrer, "http://example.com/"); }); it("should have a default referrer URL of the empty string", () => { const { document } = (new JSDOM()).window; assert.strictEqual(document.referrer, ""); }); }); describe("url", () => { it("should allow customizing document URL via the url option", () => { const { window } = new JSDOM(``, { url: "http://example.com/" }); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); }); it("should throw an error when passing an invalid absolute URL for url", () => { assert.throws(() => new JSDOM(``, { url: "asdf" }), TypeError); assert.throws(() => new JSDOM(``, { url: "/" }), TypeError); }); it("should canonicalize document URLs", () => { const { window } = new JSDOM(``, { url: "http:example.com" }); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); }); it("should have a default document URL of about:blank", () => { const { window } = new JSDOM(); assert.strictEqual(window.location.href, "about:blank"); assert.strictEqual(window.document.URL, "about:blank"); assert.strictEqual(window.document.documentURI, "about:blank"); }); }); describe("contentType", () => { it("should have a default content type of text/html", () => { const { document } = (new JSDOM()).window; assert.strictEqual(document.contentType, "text/html"); }); it("should allow customizing document content type via the contentType option", () => { const { document } = (new JSDOM(``, { contentType: "application/funstuff+xml" })).window; assert.strictEqual(document.contentType, "application/funstuff+xml"); }); it("should not show content type parameters in document.contentType (HTML)", () => { const { document } = (new JSDOM(``, { contentType: "text/html; charset=utf8" })).window; assert.strictEqual(document.contentType, "text/html"); }); it("should not show content type parameters in document.contentType (XML)", () => { const { document } = (new JSDOM(``, { contentType: "application/xhtml+xml; charset=utf8" })).window; assert.strictEqual(document.contentType, "application/xhtml+xml"); }); it("should disallow content types that are unparseable", () => { assert.throws(() => new JSDOM(``, { contentType: "" }), Error); assert.throws(() => new JSDOM(``, { contentType: "html" }), Error); assert.throws(() => new JSDOM(``, { contentType: "text/html/xml" }), Error); }); it("should disallow content types that are not XML or HTML", () => { assert.throws(() => new JSDOM(``, { contentType: "text/sgml" }), RangeError); assert.throws(() => new JSDOM(``, { contentType: "application/javascript" }), RangeError); assert.throws(() => new JSDOM(``, { contentType: "text/plain" }), RangeError); }); }); describe("userAgent", () => { it("should have a default user agent following the correct pattern", () => { const expected = `Mozilla/5.0 (${process.platform}) AppleWebKit/537.36 ` + `(KHTML, like Gecko) jsdom/${packageVersion}`; const dom = new JSDOM(); assert.strictEqual(dom.window.navigator.userAgent, expected); }); it("should set the user agent to the given value", () => { const dom = new JSDOM(``, { userAgent: "test user agent" }); assert.strictEqual(dom.window.navigator.userAgent, "test user agent"); }); }); describe("includeNodeLocations", () => { it("should throw when set to true alongside an XML content type", () => { assert.throws(() => new JSDOM(``, { includeNodeLocations: true, contentType: "application/xhtml+xml" })); }); // mostly tested by nodeLocation() tests in ./methods.js }); describe("cookieJar", () => { it("should use the passed cookie jar", () => { const cookieJar = new jsdom.CookieJar(); const dom = new JSDOM(``, { cookieJar }); assert.strictEqual(dom.cookieJar, cookieJar); }); it("should reflect changes to the cookie jar in document.cookie", () => { const cookieJar = new jsdom.CookieJar(); const { document } = (new JSDOM(``, { cookieJar })).window; cookieJar.setCookieSync("foo=bar", document.URL); assert.strictEqual(document.cookie, "foo=bar"); }); it("should have loose behavior by default when using the CookieJar constructor", () => { const cookieJar = new jsdom.CookieJar(); const { document } = (new JSDOM(``, { cookieJar })).window; cookieJar.setCookieSync("foo", document.URL); assert.strictEqual(document.cookie, "foo"); }); it("should have a loose-by-default cookie jar even if none is passed", () => { const dom = new JSDOM(); const { document } = dom.window; dom.cookieJar.setCookieSync("foo", document.URL); assert.instanceOf(dom.cookieJar, jsdom.CookieJar); assert.strictEqual(document.cookie, "foo"); }); }); describe("virtualConsole", () => { it("should use the passed virtual console", () => { const virtualConsole = new jsdom.VirtualConsole(); const dom = new JSDOM(``, { virtualConsole }); assert.strictEqual(dom.virtualConsole, virtualConsole); }); it("should have a virtual console even if none is passed", () => { const dom = new JSDOM(); assert.instanceOf(dom.virtualConsole, jsdom.VirtualConsole); }); }); describe("beforeParse", () => { it("should execute with a window and document but no nodes", () => { let windowPassed; const dom = new JSDOM(``, { beforeParse(window) { assert.instanceOf(window, window.Window); assert.instanceOf(window.document, window.Document); assert.strictEqual(window.document.doctype, null); assert.strictEqual(window.document.documentElement, null); assert.strictEqual(window.document.childNodes.length, 0); windowPassed = window; } }); assert.strictEqual(windowPassed, dom.window); }); it("should not have built-ins on the window by default", () => { let windowPassed; const dom = new JSDOM(``, { beforeParse(window) { assert.strictEqual(window.Array, undefined); windowPassed = window; } }); assert.strictEqual(windowPassed, dom.window); }); it("should have built-ins on the window when running scripts outside-only", () => { let windowPassed; const dom = new JSDOM(``, { runScripts: "outside-only", beforeParse(window) { assert.typeOf(window.Array, "function"); windowPassed = window; } }); assert.strictEqual(windowPassed, dom.window); }); it("should have built-ins on the window when running scripts dangerously", () => { let windowPassed; const dom = new JSDOM(``, { runScripts: "dangerously", beforeParse(window) { assert.typeOf(window.Array, "function"); windowPassed = window; } }); assert.strictEqual(windowPassed, dom.window); }); }); describe("pretendToBeVisual", () => { describe("not set", () => { it("document should be hidden and in prerender", () => { const { document } = (new JSDOM(``)).window; assert.strictEqual(document.hidden, true); assert.strictEqual(document.visibilityState, "prerender"); }); it("document should not have rAF", () => { const { window } = new JSDOM(``); assert.isUndefined(window.requestAnimationFrame); assert.isUndefined(window.cancelAnimationFrame); }); }); describe("set to true", () => { it("document should be not be hidden and be visible", () => { const { document } = (new JSDOM(``, { pretendToBeVisual: true })).window; assert.strictEqual(document.hidden, false); assert.strictEqual(document.visibilityState, "visible"); }); it("document should call rAF", { async: true }, context => { const { window } = new JSDOM(``, { pretendToBeVisual: true }); window.requestAnimationFrame(() => { context.done(); }); // Further functionality tests are in web platform tests }); }); }); });
describe("browser/index", () => { specify("notfound_getelementsbyclassname", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); p.className = "unknown"; body.appendChild(p); const elements = doc.getElementsByClassName("first-p"); assert.equal(elements.length, 0, "no results"); }); specify("basic_getelementsbyclassname", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); p.className = "first-p"; body.appendChild(p); const elements = doc.getElementsByClassName("first-p"); assert.equal(elements.item(0), p, "p and first-p"); }); specify("multiple_getelementsbyclassname", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); p.className = "first-p second third"; body.appendChild(p); const first = doc.getElementsByClassName("first-p").item(0); const second = doc.getElementsByClassName("second").item(0); const third = doc.getElementsByClassName("third").item(0); assert.equal(first, p, "p and first-p"); assert.equal(second, p, "p and second"); assert.equal(third, p, "p and third"); }); specify("testclassnameworksasexpected", () => { const doc = (new JSDOM()).window.document; const p = doc.createElement("p"); p.setAttribute("class", "first-p"); assert.equal(p.className, "first-p", "class attribute is same as className"); p.className += " second"; assert.equal(p.className, "first-p second", "className getter/setter"); }); specify("basic_getelementbyid", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); p.id = "theid"; body.appendChild(p); const element = doc.getElementById("theid"); assert.equal(element, p, "p and #theid"); }); specify("nonexistant_getelementbyid", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); p.id = "theid"; body.appendChild(p); const element = doc.getElementById("non-existant-id"); assert.equal(element, null, "p and #theid"); }); specify("remove_nonexistantattribute", () => { const doc = (new JSDOM()).window.document; const { body } = doc; assert.doesNotThrow(() => body.removeAttribute("non-existant"), "setValue_throws_NO_MODIFICATION_ERR"); }); specify("render_singletag", () => { const doc = (new JSDOM()).window.document; const p = doc.createElement("p"); const img = doc.createElement("img"); p.appendChild(img); const out = p.outerHTML; assert.equal(out.match(/<\/img>/), null, "end tag not included in output"); }); specify("render_specialchars", () => { const doc = (new JSDOM()).window.document; const p = doc.createElement("p"); const specials = "\"<>&\xA0"; const escapedSpecials = "\"<>& "; p.setAttribute("specials", specials); p.innerHTML = escapedSpecials; const pp = doc.createElement("p"); pp.appendChild(p); assert.equal(pp.innerHTML, `<p specials=""<>& ">"<>& </p>`); }); specify("parse_scripttags", () => { const doc = (new JSDOM()).window.document; const { head } = doc; const scriptHtml = `<script>alert("hello world")</script>`; head.innerHTML = scriptHtml; assert.equal(scriptHtml, head.innerHTML, "original and processed"); }); specify("parse_styletags", () => { const doc = (new JSDOM()).window.document; const { head } = doc; const styleHtml = `<style>body: {color: #fff;}</style>`; head.innerHTML = styleHtml; assert.equal(styleHtml, head.innerHTML, "original and processed"); }); specify("parse_doublespacetags", () => { const doc = (new JSDOM()).window.document; const html = `<html><body class="testing" /></html>`; assert.doesNotThrow(() => doc.write(html), "setValue_throws_INVALID_CHARACTER_ERR"); }); specify("serialize_styleattribute", () => { const doc = (new JSDOM()).window.document; doc.documentElement.style.color = "black"; doc.documentElement.style.backgroundColor = "white"; assert.equal( doc.documentElement.outerHTML, `<html style="color: black; background-color: white;"><head></head><body></body></html>` ); }); specify("innerhtml_removeallchildren", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.appendChild(doc.createElement("p")); body.innerHTML = ""; assert.equal(body.childNodes.length, 0, "still has children"); }); specify("innerhtml_null", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.appendChild(doc.createElement("p")); body.innerHTML = null; assert.equal(body.childNodes.length, 0, "still has children"); }); specify("parse_doctype_containing_newline", () => { const html = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html></html>`; const doc = (new JSDOM(html)).window.document; assert.ok(doc.doctype, "doctype should not be falsy"); }); specify("basic_nodelist_indexOf", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); body.appendChild(p); const div = doc.createElement("div"); body.appendChild(div); const span = doc.createElement("span"); body.appendChild(span); const index = Array.prototype.indexOf.call(body.childNodes, span); assert.equal(index, 2, "indexOf 'span' in childNodes"); }); specify("nonexistant_nodelist_indexOf", () => { const doc = (new JSDOM()).window.document; const { body } = doc; const p = doc.createElement("p"); body.appendChild(p); const div = doc.createElement("div"); p.appendChild(div); const index = Array.prototype.indexOf.call(body.childNodes, div); assert.equal(index, -1, "indexOf 'div' in childNodes"); }); specify("input_fires_click_event", () => { const doc = (new JSDOM(` <html><head></head><body> <input type="checkbox" id="check" value="check" /> </body> `)).window.document; const checkbox = doc.getElementById("check"); checkbox.addEventListener("click", event => { assert.equal(event.type, "click", "event type"); assert.equal(event.target, checkbox, "event type"); }); checkbox.click(); }); specify("basic_radio_selected", () => { const doc = (new JSDOM(`<html><head></head><body> <input type="radio" id="rad0" value="rad0" name="radioGroup0" /> <input type="radio" id="rad1" value="rad1" name="radioGroup0" checked="checked" /> <input type="radio" id="rad2" value="rad2" name="radioGroup1" /> </body> `)).window.document; const radio0 = doc.getElementById("rad0"); const radio1 = doc.getElementById("rad1"); const radio2 = doc.getElementById("rad2"); assert.ok(!radio0.checked, "radio not checked"); assert.ok(radio1.checked, "radio checked"); assert.ok(!radio2.checked, "radio not checked"); radio2.click(); radio0.click(); assert.ok(radio0.checked, "radio checked"); assert.ok(!radio1.checked, "radio not checked"); assert.ok(radio2.checked, "radio checked"); radio1.click(); assert.ok(!radio0.checked, "radio not checked"); assert.ok(radio1.checked, "radio checked"); assert.ok(radio2.checked, "radio checked"); }); specify("radio_no_click_deselected", () => { const doc = (new JSDOM(` <html><head></head><body> <input type="radio" id="rad0" value="rad0" name="radioGroup0" /> </body> `)).window.document; const radio0 = doc.getElementById("rad0"); radio0.click(); assert.ok(radio0.checked, "radio checked"); radio0.click(); assert.ok(radio0.checked, "radio checked"); }); specify("select_set_value_updates_value", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option value="x">x</option><option value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); select.value = "x"; assert.equal(select.value, "x", "select element value"); select.value = "y"; assert.equal(select.value, "y", "select element selectedIndex"); }); specify("select_set_value_updates_selectedIndex", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> + <option value="x">x</option><option value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); select.value = "x"; assert.equal(select.selectedIndex, 0, "select element selectedIndex"); select.value = "y"; assert.equal(select.selectedIndex, 1, "select element selectedIndex"); }); specify("select_set_value_updates_option_selected", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option id="optX" value="x">x</option><option id="optY" value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); const option0 = doc.getElementById("optX"); const option1 = doc.getElementById("optY"); select.value = "x"; assert.ok(option0.selected, "option element selected"); select.value = "y"; assert.ok(option1.selected, "option element selected"); }); specify("select_set_selectedIndex_updates_value", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option value="x">x</option><option value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); select.selectedIndex = 0; assert.equal(select.value, "x", "select element selectedIndex"); select.selectedIndex = 1; assert.equal(select.value, "y", "select element selectedIndex"); }); specify("select_set_selectedIndex_updates_selectedIndex", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option value="x">x</option><option value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); select.selectedIndex = 0; assert.equal(select.selectedIndex, 0, "select element selectedIndex"); select.selectedIndex = 1; assert.equal(select.selectedIndex, 1, "select element selectedIndex"); }); specify("select_set_selectedIndex_updates_option_selected", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option id="optX" value="x">x</option><option id="optY" value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); const option0 = doc.getElementById("optX"); const option1 = doc.getElementById("optY"); select.selectedIndex = 0; assert.ok(option0.selected, "option element selected"); assert.ok(!option1.selected, "option element selected"); select.selectedIndex = 1; assert.ok(option1.selected, "option element selected"); assert.ok(!option0.selected, "option element selected"); }); specify("select_set_option_selected_updates_value", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option id="optX" value="x">x</option><option id="optY" value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); const option0 = doc.getElementById("optX"); const option1 = doc.getElementById("optY"); select.selectedIndex = 0; option0.selected = true; assert.equal(select.value, "x", "select element value"); option1.selected = true; assert.equal(select.value, "y", "select element value"); }); specify("select_set_option_selected_updates_selectedIndex", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option id="optX" value="x">x</option><option id="optY" value="y">y</option> </select> `; const select = doc.getElementById("selectElement"); const option0 = doc.getElementById("optX"); const option1 = doc.getElementById("optY"); option0.selected = true; assert.equal(select.selectedIndex, 0, "select element selectedIndex"); option1.selected = true; assert.equal(select.selectedIndex, 1, "select element selectedIndex"); }); specify("select_set_option_selected_updates_option_selected", () => { const doc = (new JSDOM()).window.document; const { body } = doc; body.innerHTML = ` <select id="selectElement"> <option id="optX" value="x">x</option><option id="optY" value="y">y</option> </select> `; const option0 = doc.getElementById("optX"); const option1 = doc.getElementById("optY"); option0.selected = true; assert.ok(option0.selected, "option element selected"); assert.ok(!option1.selected, "option element selected"); option1.selected = true; assert.ok(option1.selected, "option element selected"); assert.ok(!option0.selected, "option element selected"); }); });
describe("xhr-file-urls", { skipIfBrowser: true }, () => { specify("Getting a file URL should work (from the same file URL)", t => { // From https://github.com/tmpvar/jsdom/pull/1180 const { window } = new JSDOM(``, { url: toFileUrl(__filename) }); const xhr = new window.XMLHttpRequest(); xhr.onload = () => { assert.strictEqual(xhr.responseText, fs.readFileSync(__filename, { encoding: "utf-8" })); t.done(); }; xhr.open("GET", toFileUrl(__filename), true); xhr.send(); }, { async: true }); specify( "Getting a file URL should have valid default response without setting responseType", t => { // From https://github.com/tmpvar/jsdom/pull/1183 const { window } = new JSDOM(``, { url: toFileUrl(__filename) }); const xhr = new window.XMLHttpRequest(); xhr.onload = () => { assert.strictEqual(xhr.response, fs.readFileSync(__filename, { encoding: "utf-8" })); t.done(); }; xhr.open("GET", toFileUrl(__filename), true); xhr.send(); }, { async: true } ); specify("Getting a file URL should not throw for getResponseHeader", t => { // From https://github.com/tmpvar/jsdom/pull/1180 const { window } = new JSDOM(``, { url: toFileUrl(__filename) }); const xhr = new window.XMLHttpRequest(); xhr.onload = () => { assert.doesNotThrow(() => { assert.strictEqual(xhr.getResponseHeader("Blahblahblah"), null); }); t.done(); }; xhr.open("GET", toFileUrl(__filename), true); xhr.send(); }, { async: true }); specify("Getting a file URL should not throw for getAllResponseHeaders", t => { // From https://github.com/tmpvar/jsdom/pull/1183 const { window } = new JSDOM(``, { url: toFileUrl(__filename) }); const xhr = new window.XMLHttpRequest(); xhr.onload = () => { assert.doesNotThrow(() => { assert.strictEqual(xhr.getAllResponseHeaders(), ""); }); t.done(); }; xhr.open("GET", toFileUrl(__filename), true); xhr.send(); }, { async: true }); });
describe("Local tests in Web Platform Test format (to-upstream)", () => { [ "console/methods.html", "dom/attributes-are-not-nodes.html", "dom/collections/HTMLCollection-iterator.html", "dom/events/AddEventListenerOptions-once.html", "dom/events/EventTarget-add-remove-listener.html", "dom/events/EventTarget-prototype-constructor.html", "dom/events/EventTarget-this-of-listener.html", "dom/nodes/Document-createCDATASection.html", "dom/nodes/Document-createCDATASection.xhtml", "dom/nodes/Element-hasAttribute.html", "dom/nodes/Element-removeAttribute.html", "dom/nodes/Element-setAttribute.html", "dom/nodes/Element-tagName.html", "dom/nodes/attributes-namednodemap.html", "dom/nodes/getElementsByClassName-32.html", "dom/nodes/getElementsByClassName-empty-set.html", "dom/nodes/getElementsByClassName-whitespace-class-names.html", "dom/nodes/Node-cloneNode-input.html", "dom/nodes/Node-cloneNode-svg.html", "dom/nodes/Node-isEqualNode.html", "dom/nodes/Node-mutation-adoptNode.html", "dom/nodes/ParentNode-querySelector-escapes.html", "dom/nodes/Text-wholeText.html", "domparsing/DOMParser-dont-upstream.html", "domparsing/insert-adjacent.html", "FileAPI/blob/Blob-isClosed.html", "FileAPI/file/File-lastModified.html", "encoding/meta/meta-charset-no-quotes.html", "encoding/meta/meta-charset-simple-quotes.html", "encoding/meta/meta-charset.html", "encoding/meta/meta-http-equiv-no-quotes.html", "encoding/meta/meta-http-equiv-reverse.html", "encoding/meta/meta-http-equiv-simple-quotes.html", "encoding/meta/meta-http-equiv.html", "encoding/meta/no-meta.html", "html/browsers/windows/nested-browsing-contexts/iframe-referrer.html", "html/dom/elements/elements-in-the-dom/click-in-progress-flag.html", "html/editing/activation/click-bail-on-disabled.html", "html/editing/focus/focus-management/active-element.html", "html/editing/focus/focus-management/focus-on-all-elements.html", "html/named-access-on-window/basics.html", "html/named-access-on-window/changing.html", "html/named-access-on-window/doc-no-window.html", "html/named-access-on-window/existing-prop.html", "html/named-access-on-window/multi-match.html", "html/named-access-on-window/nested-context.html", "html/named-access-on-window/only-name.html", "html/named-access-on-window/removing.html", "html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/applets.html", "html/semantics/document-metadata/the-link-element/stylesheet-appropriate-time-to-obtain.html", "html/semantics/forms/resetting-a-form/reset-form-2.html", "html/semantics/forms/the-button-element/button-click-submits.html", "html/semantics/forms/the-button-element/button-type.html", "html/semantics/forms/the-form-element/form-action.html", "html/semantics/forms/the-input-element/checkbox-click-events.html", "html/semantics/forms/the-input-element/disabled-checkbox.html", "html/semantics/forms/the-input-element/radio-input-cancel.html", "html/semantics/forms/the-label-element/proxy-click-to-associated-element.html", "html/semantics/forms/the-option-element/option-index.html", "html/semantics/forms/the-select-element/select-multiple.html", "html/semantics/forms/the-textarea-element/select.html", "html/semantics/forms/the-textarea-element/set-value-reset-selection.html", "html/semantics/forms/the-textarea-element/setRangeText.html", "html/semantics/forms/the-textarea-element/setSelectionRange.html", "html/semantics/forms/the-textarea-element/value-defaultValue-textContent.html", "html/semantics/links/links-created-by-a-and-area-elements/html-hyperlink-element-utils-href.html", "html/semantics/scripting-1/the-script-element/script-languages-dont-upstream.html", "html/semantics/scripting-1/the-script-element/changing-src.html", "html/semantics/tabular-data/the-table-element/insertRow-method-03.html", "html/semantics/tabular-data/the-table-element/parentless-props.html", "html/syntax/parsing/foreign_content_dom_properties.html", "html/webappapis/events/event-handler-processing-algorithm-non-booleans.html", "html/webappapis/timers/arguments.html", "html/webappapis/timers/errors.html", "html/webappapis/timers/settimeout-setinterval-handles.html", "XMLHttpRequest/formdata-constructor.html", "XMLHttpRequest/thrown-error-in-events.html", "XMLHttpRequest/send-authentication-cors-post.htm" ] .forEach(runWebPlatformTest); });
describe('recorder reducer', () => { describe('recorderStart actions', () => { it('changes "active" to true and "command" to "start"', () => { const newState = {active: true, command: 'start', stream: null, blobs: []} expect(reducer(undefined, recorderStart())).to.eql(newState) }) }) describe('recorderStop actions', () => { it('changes "active" to false and "command" to "stop"', () => { const origState = {active: true, command: 'start', stream: null, blobs: []} const newState = {active: false, command: 'stop', stream: null, blobs: []} expect(reducer(origState, recorderStop())).to.eql(newState) }) }) describe('recorderPause actions', () => { it('changes "active" to false and "command" to "pause"', () => { const origState = {active: true, command: 'start', stream: null, blobs: []} const newState = {active: false, command: 'pause', stream: null, blobs: []} expect(reducer(origState, recorderPause())).to.eql(newState) }) }) describe('recorderResume actions', () => { it('changes "active" to true and "command" to "resume"', () => { const origState = {active: false, command: 'pause', stream: null, blobs: []} const newState = {active: true, command: 'resume', stream: null, blobs: []} expect(reducer(origState, recorderResume())).to.eql(newState) }) }) describe('recorderOnStop actions', () => { it('adds a new blob object to the "blobs" array', () => { expect(reducer(undefined, recorderOnStop('abcd')).blobs).to.eql(['abcd']) }) }) describe('recorderGotStream actions', () => { it('defines the "stream" object', () => { expect(reducer(undefined, recorderGotStream('abcd')).stream).to.equal('abcd') }) }) describe('recorderUnmount', () => { it('resets the state', () => { const origState = {active: true, command: 'resume', stream: 'stream', blobs: [1, 2, 3]} expect(reducer(origState, recorderUnmount())).to.eql({active: false, command: 'none', stream: null, blobs: []}) }) }) })
describe("jsdom/miscellaneous", () => { specify("build_window", () => { const window = jsdom.jsdom().defaultView; assert.notEqual(window, null, "window should not be null"); assert.notEqual(window.document, null, "window.document should not be null"); }); specify("jsdom_takes_html", () => { const document = jsdom.jsdom(`<a id="test" href="#test">`); assert.equal( document.getElementById("test").getAttribute("href"), "#test", "Passing html into jsdom() should populate the resulting doc" ); }); specify("jsdom_empty_html", () => { const emptyDoc = jsdom.jsdom(""); const blankDoc = jsdom.jsdom(" "); assert.equal( emptyDoc.innerHTML, blankDoc.innerHTML, "Passing blank and empty strings into jsdom() result in the same html" ); }); specify("jsdom_method_creates_default_document", () => { const doc = jsdom.jsdom(); assert.equal(doc.documentElement.nodeName, "HTML", "Calling jsdom.jsdom() should automatically populate the doc"); }); specify("jsdom_method_works_with_referrer_under_document", () => { const doc = jsdom.jsdom(undefined, { document: { referrer: "http://example.com" } }); assert.equal(doc.referrer, "http://example.com"); }); specify("jquerify_url", { async: true }, t => { const jQueryUrl = "http://code.jquery.com/jquery-1.4.4.min.js"; jsdom.jQueryify(tmpWindow(), jQueryUrl, (window, jQuery) => { testFunction(assert, window, jQuery, true); t.done(); }); }); specify("jquerify_invalid", { async: true }, t => { jsdom.jQueryify(jsdom.jsdom("", { url: "http://www.example.org" }).defaultView, 1, (window, jQuery) => { assert.strictEqual(window.jQuery, undefined); assert.strictEqual(jQuery, undefined); t.done(); }); }); // This is in response to issue # 280 - scripts don't load over https. // See: https://github.com/tmpvar/jsdom/issues/280 // // When a transfer is done, HTTPS servers in the wild might emit "close", or // might emit "end". Node"s HTTPS server always emits "end", so we need to // fake a "close" to test this fix. specify("env_with_https", { async: true }, t => { // Save the real https.request so we can restore it later. const oldRequest = https.request; // Mock response object const res = Object.create(EventEmitter.prototype); res.setEncoding = () => {}; res.headers = {}; // Monkey patch https.request so it emits "close" instead of "end. https.request = () => { // Mock the request object. const req = Object.create(EventEmitter.prototype); req.setHeader = () => {}; req.end = () => {}; process.nextTick(() => { req.emit("response", res); process.nextTick(() => { res.emit("data", "window.attachedHere = 123"); res.emit("close"); }); }); return req; }; jsdom.env({ html: `<a href="/path/to/hello">World</a>`, // The script url doesn"t matter as long as its https, since our mocked // request doens"t actually fetch anything. scripts: "https://doesntmatter.com/script.js", strictSSL: false, done(errors, window) { if (errors) { assert.ok(false, errors.message); } else { assert.notEqual(window.location, null, "window.location should not be null"); assert.equal(window.attachedHere, 123, "script should execute on our window"); assert.equal(window.document.getElementsByTagName("a").item(0).innerHTML, "World", "anchor text"); } https.request = oldRequest; t.done(); } }); }); specify("appendChild_to_document_with_existing_documentElement", () => { function t() { try { const doc = jsdom.jsdom(); doc.appendChild(doc.createElement("html")); } catch (e) { assert.equal(e.code, 3, "Should throw HIERARCHY_ERR"); throw e; } } assert.throws(t); }); specify("importNode", () => { assert.doesNotThrow(() => { const doc1 = jsdom.jsdom(`<html><body><h1 id="headline">Hello <span id="world">World</span></h1></body></html>`); const doc2 = jsdom.jsdom(); doc2.body.appendChild(doc2.importNode(doc1.getElementById("headline"), true)); doc2.getElementById("world").className = "foo"; }); }); specify("window_is_augmented_with_dom_features", () => { const document = jsdom.jsdom(); const window = document.defaultView; assert.notEqual(window.Element, null, "window.Element should not be null"); }); specify("url_resolution", () => { const html = ` <html> <head></head> <body> <a href="http://example.com" id="link1">link1</a> <a href="/local.html" id="link2">link2</a> <a href="local.html" id="link3">link3</a> <a href="../../local.html" id="link4">link4</a> <a href="#here" id="link5">link5</a> <a href="//example.com/protocol/avoidance.html" id="link6">protocol</a> </body>\ </html>`; function testLocal() { const url = "file:///path/to/docroot/index.html"; const doc = jsdom.jsdom(html, { url }); assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "file:///local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "file:///path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "file:///path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "file:///path/to/docroot/index.html#here", "Relative URL should be resolved" ); // test.equal( // doc.getElementById("link6").href, // "//prototol/avoidance.html", // "Protocol-less URL should be resolved" // ); } function testRemote() { const url = "http://example.com/path/to/docroot/index.html"; const doc = jsdom.jsdom(html, { url }); assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "http://example.com/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "http://example.com/path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "http://example.com/path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "http://example.com/path/to/docroot/index.html#here", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link6").href, "http://example.com/protocol/avoidance.html", "Relative URL should be resolved" ); } function testBase() { const url = "about:blank"; const doc = jsdom.jsdom(html, { url }); const base = doc.createElement("base"); base.href = "http://example.com/path/to/docroot/index.html"; doc.getElementsByTagName("head").item(0).appendChild(base); assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "http://example.com/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "http://example.com/path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "http://example.com/path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "http://example.com/path/to/docroot/index.html#here", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link6").href, "http://example.com/protocol/avoidance.html", "Relative URL should be resolved" ); } testLocal(); testRemote(); testBase(); }); specify("numeric_values", () => { const html = `<html><body><td data-year="2011" data-month="0" data-day="9"> <a href="#" class=" ">9</a> </td></body></html>`; const document = jsdom.jsdom(html); const a = document.body.children.item(0); a.innerHTML = 9; a.setAttribute("id", 123); assert.strictEqual(a.innerHTML, "9", "Element stringify"); assert.strictEqual(a.getAttributeNode("id").nodeValue, "123", "Attribute stringify"); }); specify("mutation_events", () => { const document = jsdom.jsdom(); let created = ""; let removed = ""; document.addEventListener("DOMNodeInserted", ev => { created += ev.target.tagName; }); document.addEventListener("DOMNodeRemoved", ev => { removed += ev.target.tagName; }); const h1 = document.createElement("h1"); const h2 = document.createElement("h2"); const h3 = document.createElement("h3"); document.body.appendChild(h2); document.body.insertBefore(h1, h2); document.body.insertBefore(h3, null); assert.strictEqual("H2H1H3", created, "an event should be dispatched for each created element"); document.body.removeChild(h1); document.body.insertBefore(h3, h2); assert.strictEqual("H1H3", removed, "an event should be dispatched for each removed element"); let text = h2.innerHTML = "foo"; h2.addEventListener("DOMCharacterDataModified", ev => { text = ev.target.nodeValue; }); h2.firstChild.nodeValue = "bar"; assert.equal(h2.innerHTML, text, "ChactaterData changes should be captured"); let event; h2.setAttribute("class", "foo"); document.addEventListener("DOMAttrModified", ev => { event = ev; }); h2.setAttribute("class", "bar"); assert.ok(event, "Changing an attribute should trigger DOMAttrModified"); assert.equal(event.attrName, "class", "attrName should be class"); assert.equal(event.prevValue, "foo", "prevValue should be foo"); assert.equal(event.newValue, "bar", "newValue should be bar"); event = false; h2.setAttribute("class", "bar"); assert.ok(!event, "Setting the same value again should not trigger an event"); h2.removeAttribute("class"); assert.ok(event, "Removing an attribute should trigger DOMAttrModified"); assert.equal(event.attrName, "class", "attrName should be class"); assert.equal(event.prevValue, "bar", "prevValue should be bar"); }); specify("DomSubtreeModifiedEvents", () => { const document = jsdom.jsdom(); let firedAfterAddedChild = false; let firedAfterAddedTextNode = false; let firedAfterAddingAttr = false; let firedAfterChangingAttr = false; let firedAfterRemovedAttr = false; document.addEventListener("DOMSubtreeModified", () => { firedAfterAddedChild = true; }); const div = document.createElement("div"); document.body.appendChild(div); assert.ok(firedAfterAddedChild, "DOMSubtreeModified event should be fired for each created element"); document.addEventListener("DOMSubtreeModified", () => { firedAfterAddedTextNode = true; }); const textNode = document.createTextNode("text node test"); document.getElementsByTagName("div")[0].appendChild(textNode); assert.ok(firedAfterAddedTextNode, "DOMSubtreeModified event should be fired when texnode value changed"); document.addEventListener("DOMSubtreeModified", () => { firedAfterAddingAttr = true; }); document.getElementsByTagName("div")[0].setAttribute("class", "test-class"); assert.ok(firedAfterAddingAttr, "DOMSubtreeModified event should be fired when attribute added"); document.addEventListener("DOMSubtreeModified", () => { firedAfterChangingAttr = true; }); document.getElementsByTagName("div")[0].setAttribute("class", "test-class-2"); assert.ok(firedAfterChangingAttr, "DOMSubtreeModified event should be fired when attribute value changed"); firedAfterChangingAttr = false; document.getElementsByTagName("div")[0].setAttribute("class", "test-class-2"); assert.ok( firedAfterChangingAttr === false, "DOMSubtreeModified not be fired when new attribute value same as old one" ); document.addEventListener("DOMSubtreeModified", () => { firedAfterRemovedAttr = true; }); document.getElementsByTagName("div")[0].removeAttribute("class"); assert.ok(firedAfterRemovedAttr, "DOMSubtreeModified event should be fired when attribute removed"); firedAfterRemovedAttr = false; document.getElementsByTagName("div")[0].removeAttribute("class"); assert.ok( firedAfterRemovedAttr === false, "DOMSubtreeModified not be fired when try to remove attribute does not exists" ); }); specify("childNodes_updates_on_insertChild", () => { const window = jsdom.jsdom("").defaultView; const div = window.document.createElement("div"); let text = window.document.createTextNode("bar"); div.appendChild(text); assert.strictEqual(text, div.childNodes[0], "childNodes NodeList should update after appendChild"); text = window.document.createTextNode("bar"); div.insertBefore(text, null); assert.strictEqual(text, div.childNodes[1], "childNodes NodeList should update after insertBefore"); }); specify("option_set_selected", () => { const window = jsdom.jsdom("").defaultView; const select = window.document.createElement("select"); const option0 = window.document.createElement("option"); select.appendChild(option0); option0.setAttribute("selected", "selected"); const optgroup = window.document.createElement("optgroup"); select.appendChild(optgroup); const option1 = window.document.createElement("option"); optgroup.appendChild(option1); assert.strictEqual(true, option0.selected, "initially selected"); assert.strictEqual(false, option1.selected, "initially not selected"); assert.strictEqual(option1, select.options[1], "options should include options inside optgroup"); option1.defaultSelected = true; assert.strictEqual(false, option0.selected, "selecting other option should deselect this"); assert.strictEqual(true, option0.defaultSelected, "default should not change"); assert.strictEqual(true, option1.selected, "selected changes when defaultSelected changes"); assert.strictEqual(true, option1.defaultSelected, "I just set this"); option0.defaultSelected = false; option0.selected = true; assert.strictEqual(true, option0.selected, "I just set this"); assert.strictEqual(false, option0.defaultSelected, "selected does not set default"); assert.strictEqual(false, option1.selected, "should deselect others"); assert.strictEqual(true, option1.defaultSelected, "unchanged"); }); specify("children_should_be_available_right_after_document_creation", () => { const doc = jsdom.jsdom("<html><body><div></div></body></html>"); assert.ok((doc.body.children[0] !== undefined), "there should be a body, and it should have a child"); }); specify("children_should_be_available_right_after_document_creation_scripts", () => { const html = `<html><body> <script type="text/javascript"> const h = document.createElement("div"); h.innerHTML = '<div style="opacity:0.8"></div>'; window.myNode = h.childNodes[0]; </script> </body></html>`; const window = jsdom.jsdom(html).defaultView; assert.ok(window.myNode.nodeType); }); specify("fix_for_issue_221", () => { const html = "<html><head></head><body></body></html>"; const document = jsdom.jsdom(html); const div = document.createElement("div"); document.body.appendChild(div); div.appendChild(document.createTextNode("hello world")); assert.strictEqual(div.childNodes[0].nodeValue, "hello world", "Nodelist children should be populated immediately"); }); specify("parsing_and_serializing_entities", () => { const html = `<html><body><a href="http://example.com/?a=b&c=d"><æ☺foo</a>`; const document = jsdom.jsdom(html); const anchor = document.getElementsByTagName("a")[0]; assert.strictEqual(anchor.getAttribute("href"), "http://example.com/?a=b&c=d", "href attribute value should be deentitified"); assert.strictEqual(anchor.firstChild.nodeValue, "<æ☺foo", "nodeValue of text node should be deentitified"); assert.ok(anchor.outerHTML.indexOf("http://example.com/?a=b&c=d") !== -1, "outerHTML of anchor href should be entitified"); assert.ok(anchor.innerHTML.indexOf("<") === 0, "innerHTML of anchor should begin with <"); }); specify("parsing_and_serializing_unknown_entities", () => { const html = "<html><body>&nowayjose;☺lah;	q;</body></html>"; const document = jsdom.jsdom(html); assert.strictEqual(document.body.firstChild.nodeValue, "&nowayjose;☺lah; q;", "Unknown and unparsable entities should be handled like a browser would"); assert.strictEqual(document.body.innerHTML, "&nowayjose;☺lah; q;", "Unknown and unparsable entities should be handled like a browser would"); }); specify("entities_in_script_should_be_left_alone", () => { const html = `<!DOCTYPE html><html><head></head><body><script>alert(""");</script></body></html>`; const document = jsdom.jsdom(html); assert.strictEqual(document.body.innerHTML, `<script>alert(""");</script>`); assert.strictEqual(document.body.firstChild.innerHTML, `alert(""");`); }); specify("document_title_and_entities", () => { const html = "<html><head><title><b>Hello</b></title></head><body></body></html>"; const document = jsdom.jsdom(html); assert.strictEqual(document.title, "<b>Hello</b>", `document.title should be the deentitified version of what was in the original HTML` ); document.title = "<b>World</b>"; assert.strictEqual(document.title, "<b>World</b>", `When document.title is set programmatically to something looking like HTML tags, then read again, it should have the exact same value, no entification should take place` ); document.title = "<b>World</b>"; assert.strictEqual(document.title, "<b>World</b>", `When document.title is set programmatically to something looking like HTML entities, then read again, it should have the exact same value, no deentification should take place` ); }); specify("setting_and_getting_textContent", () => { const html = `<html><head>\n<title><foo></title></head> <body>Hello<span><span>, </span>world</span>!</body></html>`; const document = jsdom.jsdom(html); assert.strictEqual(document.textContent, null, "textContent of document should be null" ); assert.strictEqual(document.head.textContent, "\n<foo>", "textContent of document.head should be the initial whitespace plus the textContent of the document title" ); assert.strictEqual( document.body.textContent, "Hello, world!", "textContent of document.body should be the concatenation of the textContent values of its child nodes" ); assert.strictEqual( document.createTextNode("<b>World</b>").textContent, "<b>World</b>", "textContent of programmatically created text node should be identical to its nodeValue" ); assert.strictEqual( document.createComment("<b>World</b>").textContent, "<b>World</b>", "textContent of programmatically created comment node should be identical to its nodeValue" ); const frag = document.createDocumentFragment(); frag.appendChild(document.createTextNode("<foo><b></b>")); frag.appendChild(document.createElement("div")).appendChild(document.createTextNode("<foo><b></b>")); assert.strictEqual( frag.textContent, "<foo><b></b><foo><b></b>", "textContent of programmatically created document fragment should be the concatenation " + "of the textContent values of its child nodes" ); const div = document.createElement("div"); div.innerHTML = "&lt;b&gt;\nWorld&lt;/b&gt;<span></span><span>" + "<span></span></span><span>&lt;b&gt;World&lt;/b&gt;</span>"; assert.strictEqual(div.textContent, "<b>\nWorld</b><b>W\orld</b>", `textContent of complex programmatically created <div> should be the concatenation of the textContent values of its child nodes` ); }); specify("issues_230_259", () => { const instr = `<html><body style="color: #ffffff; foo: bar"></body></html>`; const doc = jsdom.jsdom(instr); assert.ok(jsdom.serializeDocument(doc).match(/0: *color/) === null); }); // see: https://github.com/tmpvar/jsdom/issues/262 specify("issue_262", () => { const document = jsdom.jsdom("<html><body></body></html>"); const a = document.createElement("a"); a.setAttribute("style", "color:blue"); a.style.setProperty("color", "red"); assert.equal(a.outerHTML.match(/style="/g).length, 1, "style attribute must not be serialized twice"); }); // see: https://github.com/tmpvar/jsdom/issues/267 specify("issue_267", () => { const document = jsdom.jsdom("<html><body></body></html>"); const a = document.createElement("a"); a.style.width = "100%"; assert.ok(a.getAttribute("style").match(/^\s*width\s*:\s*100%\s*;?\s*$/), "style attribute must contain width"); }); // Test inline event handlers set on the body. specify("test_body_event_handler_inline", { skipIfBrowser: true, async: true }, t => { // currently skipped in browsers because of an issue: // TODO: https://github.com/tmpvar/jsdom/issues/1379 const html = ` <html> <head> <script> function loader () { window.loader_called = true; } </script> </head> <body onload="loader()"></body> </html>`; const doc = jsdom.jsdom(html, { deferClose: true }); const window = doc.defaultView; // In JSDOM, listeners registered with addEventListener are called before // "traditional" listeners, so listening for "load" will fire before our // inline listener. This means we have to check the value on the next // tick. window.addEventListener("load", () => { process.nextTick(() => { assert.equal(window.loader_called, true); t.done(); }); }); doc.close(); }); // Make sure traditional handlers on the body element set via script are // forwarded to the window. specify("test_body_event_handler_script", { async: true }, t => { const doc = jsdom.jsdom("<html><head></head><body></body></html>", { deferClose: true }); const window = doc.defaultView; assert.equal(window.onload, undefined); doc.body.onload = () => { t.done(); }; assert.notEqual(window.onload, undefined); doc.close(); }); // Test inline event handlers on a regular element. specify("test_element_inline_event_handler", () => { const doc = jsdom.jsdom(` <html> <head></head> <body> <div onclick="window.divClicked = true;" onmouseover="window.divMousedOver = true;" onmouseout="window.divCalledFrom = this.tagName;"> <a></a> </div> </body> </html>`); const window = doc.defaultView; const div = doc.getElementsByTagName("div")[0]; assert.equal(window.divClicked, undefined); assert.equal(window.divMousedOver, undefined); const click = doc.createEvent("MouseEvents"); click.initEvent("click", false, false); div.dispatchEvent(click); assert.equal(window.divClicked, true); const mouseOver = doc.createEvent("MouseEvents"); mouseOver.initEvent("mouseover", false, false); div.dispatchEvent(mouseOver); assert.equal(window.divMousedOver, true); const mouseOut = doc.createEvent("MouseEvents"); mouseOut.initEvent("mouseout", false, false); div.dispatchEvent(mouseOut); assert.equal(window.divCalledFrom, "DIV"); }); // Test for issue 287 - element.onevent check doesn"t work // See: https://github.com/tmpvar/jsdom/issues/287 specify("issue_287", () => { const doc = jsdom.jsdom(); const elem = doc.createElement("form"); elem.setAttribute("onsubmit", ";"); assert.equal(typeof elem.onsubmit, "function"); }); specify("get_element_by_id", () => { const doc = jsdom.jsdom(); const el = doc.createElement("div"); el.setAttribute("id", "foo"); assert.equal(doc.getElementById("foo"), null, "Element must not be found until it has been added to the DOM"); doc.body.appendChild(el); assert.equal(doc.getElementById("foo"), el, "Element must be found after being added"); el.id = "bar"; assert.equal(doc.getElementById("foo"), null, "Element must not be found by its previous id"); assert.equal(doc.getElementById("bar"), el, "Element must be found by its new id"); el.setAttribute("id", "baz"); assert.equal(doc.getElementById("bar"), null, "Element must not be found by its previous id"); assert.equal(doc.getElementById("baz"), el, "Element must be found by its new id"); el.getAttributeNode("id").nodeValue = "boo"; assert.equal(doc.getElementById("boo"), el, "Element must be found by its new id"); doc.body.removeChild(el); assert.equal(doc.getElementById(el.id), null, "Element must not be found after it has been removed"); }); specify("get_element_by_id_multi_id", () => { const doc = jsdom.jsdom(); const div = doc.createElement("div"); div.setAttribute("id", "foo"); doc.body.appendChild(div); const span = doc.createElement("span"); span.setAttribute("id", "foo"); doc.body.appendChild(span); // now if we remove the second element, we should still find the first doc.body.removeChild(span); assert.equal(doc.getElementById("foo"), div, "Original div#foo must be found after removing invalid span#foo"); }); specify("issue_335_inline_event_handlers", () => { const doc = jsdom.jsdom(`<a onclick="somefunction()">call some function</a>`); const a = doc.getElementsByTagName("a").item(0); const onclick = a.getAttribute("onclick"); assert.notEqual(onclick, null); assert.equal(onclick, "somefunction()"); assert.ok(jsdom.serializeDocument(doc).indexOf("onclick") > -1); }); specify("issue_338_internal_nodelist_props", () => { const doc = jsdom.jsdom(); const props = Object.keys(doc.body.childNodes); assert.equal(props.length, 0, "Internal properties must not be enumerable"); }); specify("setting_and_getting_script_element_text", () => { const doc = jsdom.jsdom("<script></script>"); const script = doc.getElementsByTagName("script")[0]; assert.equal(script.text, ""); script.text = "const x = 3;"; assert.equal(script.text, "const x = 3;"); script.text = "const y = 2;"; assert.equal(script.text, "const y = 2;"); }); specify("issue_239_replace_causes_script_execution", { async: true }, t => { jsdom.env({ html: `<script type="text/javascript">window.a = 1;/* remove me */ console.log("executed?")</script>`, done(errors, window) { window.document.write(jsdom.serializeDocument(window.document).replace("/* remove me */", "")); window.document.close(); assert.equal(typeof window.a, "undefined"); t.done(); } }); }); specify("issue_355_on_events_should_not_execute_js_when_disabled", { async: true }, t => { const html = `<html><body onload="undefined()">something</body></html>`; jsdom.env(html, e => { assert.equal(e, null); t.done(); }); }); specify("issue_361_textarea_value_property", () => { const doc = jsdom.jsdom(`<html><body><textarea id="mytextarea"></textarea></body></html>`); doc.getElementById("mytextarea").value = "<foo>"; assert.equal(doc.getElementById("mytextarea").value, "<foo>"); }); specify("on_events_should_be_called_in_bubbling_phase", () => { const doc = jsdom.jsdom(` <html> <head></head> <body> <div onclick="window.divClicked = true;" onmouseover="window.divMousedOver = true;"> <a></a> </div> </body> </html>`); const window = doc.defaultView; const a = doc.getElementsByTagName("a")[0]; assert.equal(window.divClicked, undefined); assert.equal(window.divMousedOver, undefined); const click = doc.createEvent("MouseEvents"); click.initEvent("click", true, false); a.dispatchEvent(click); assert.equal(window.divClicked, true); const mouseOver = doc.createEvent("MouseEvents"); mouseOver.initEvent("mouseover", true, false); a.dispatchEvent(mouseOver); assert.equal(window.divMousedOver, true); }); specify("css_classes_should_be_attached_to_dom", () => { const dom = jsdom.jsdom().defaultView; assert.notEqual(dom.StyleSheet, undefined); assert.notEqual(dom.MediaList, undefined); assert.notEqual(dom.CSSStyleSheet, undefined); assert.notEqual(dom.CSSRule, undefined); assert.notEqual(dom.CSSStyleRule, undefined); assert.notEqual(dom.CSSMediaRule, undefined); assert.notEqual(dom.CSSImportRule, undefined); assert.notEqual(dom.CSSStyleDeclaration, undefined); }); specify("issue_530_async_load_events", { async: true }, t => { const doc = jsdom.jsdom("<html><head></head><body></body></html>"); const window = doc.defaultView; // Add the load event after the document is already created; it shouldn"t // fire until nextTick. The test will fail (with a timeout) if it has // already fired. window.addEventListener("load", () => { assert.ok(true); t.done(); }); }); specify("iframe_contents", () => { const document = jsdom.jsdom("<iframe></iframe>"); const iframeDocument = document.querySelector("iframe").contentWindow.document; assert.equal(jsdom.serializeDocument(iframeDocument), "<html><head></head><body></body></html>"); assert.ok(iframeDocument.documentElement); assert.ok(iframeDocument.head); assert.ok(iframeDocument.body); }); specify("issue_935_document_tostring_returns_null", () => { const document = jsdom.jsdom(); assert.equal(document.toString(), "[object HTMLDocument]"); }); specify("addmetatohead", () => { const window = jsdom.jsdom().defaultView; const meta = window.document.createElement("meta"); window.document.getElementsByTagName("head").item(0).appendChild(meta); const elements = window.document.getElementsByTagName("head").item(0).childNodes; assert.strictEqual(elements.item(elements.length - 1), meta, "last element should be the new meta tag"); assert.ok(jsdom.serializeDocument(window.document).indexOf("<meta>") > -1, "meta should have open tag"); assert.strictEqual( jsdom.serializeDocument(window.document).indexOf("</meta>"), -1, "meta should not be stringified with a closing tag" ); }); specify("no global leak when using window.location.reload", () => { // https://github.com/tmpvar/jsdom/pull/1032 assert.equal("errors" in global, false, "there should be no errors global before the call"); const window = jsdom.jsdom().defaultView; window.location.reload(); assert.equal("errors" in global, false, "there should be no errors global after the call"); }); specify("custom userAgent inherits to iframes", () => { // https://github.com/tmpvar/jsdom/issues/1344#issuecomment-175272389 const window = jsdom.jsdom("<!DOCTYPE html><iframe></iframe>", { userAgent: "custom user agent" }).defaultView; assert.strictEqual(window.navigator.userAgent, "custom user agent"); assert.strictEqual(window.frames[0].navigator.userAgent, "custom user agent"); }); // these tests require file system access or they start a http server describe("node specific tests", { skipIfBrowser: true }, () => { specify("fix_for_issue_172", () => { jsdom.env(`<html><body><script type="text/javascript"></script></body></html>`, [ "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.6.2.js") ], () => { // ensure the callback gets called! }); }); specify("jquerify_file", { async: true }, t => { const jQueryFile = path.resolve(__dirname, "../jquery-fixtures/jquery-1.4.4.js"); jsdom.jQueryify(tmpWindow(), toFileUrl(jQueryFile), (window, jQuery) => { testFunction(assert, window, jQuery, true); t.done(); }); }); specify("jquerify_attribute_selector_gh_400", { async: true }, t => { const window = jsdom.jsdom().defaultView; jsdom.jQueryify(window, "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"), () => { assert.doesNotThrow(() => { window.$("body").append(`<html><body><div data-foo="bar"/><div data-baz="foo"/></body></html>`); }); assert.equal(window.$("*[data-foo]").length, 1); t.done(); }); }); specify("env_with_compression", { async: true }, t => { const server = http.createServer((req, res) => { switch (req.url) { case "/": const text = "window.attachedHere = 123"; const buf = new Buffer(text, "utf-8"); zlib.gzip(buf, (_, result) => { res.writeHead(200, { "Content-Length": result.length, "Content-Encoding": "gzip" }); res.emit("data", result); res.end(result); }); break; } }); server.listen(8001, "127.0.0.1", () => { jsdom.env({ html: `<a href="/path/to/hello">World</a>`, scripts: "http://127.0.0.1:8001", done(errors, window) { server.close(); if (errors) { assert.ok(false, errors.message); } else { assert.notEqual(window.location, null, "window.location should not be null"); assert.equal(window.attachedHere, 123, "script should execute on our window"); assert.equal(window.document.getElementsByTagName("a").item(0).innerHTML, "World", "anchor text"); } t.done(); } }); }); }); specify("env_with_features_and_external_resources", { async: true }, t => { jsdom.env( "http://backbonejs.org/examples/todos/index.html", { features: { FetchExternalResources: ["script", "frame", "link"], ProcessExternalResources: ["script", "frame", "link"], MutationEvents: "2.0", QuerySelector: false } }, (error, window) => { assert.ifError(error); assert.equal(typeof window._, "function", "Underscore loaded"); assert.equal(typeof window.$, "function", "jQuery loaded"); t.done(); } ); }); specify("ensure_scripts_can_be_disabled_via_options_features", { async: true }, t => { const html = `<html><head><script src="./files/hello.js"></script></head> <body><span id="test">hello from html</span></body></html>`; const doc2 = jsdom.jsdom(html, { url: toFileUrl(__filename), features: { FetchExternalResources: ["script"], ProcessExternalResources: false } }); setTimeout(() => { assert.equal(doc2.getElementById("test").innerHTML, "hello from html", "js should not be executed (doc2)"); t.done(); }, 100); }); specify("ensure_scripts_can_be_executed_via_options_features", { async: true }, t => { const html = `<html><head><script src="./files/hello.js"></script></head> <body><span id="test">hello from html</span></body></html>`; const doc = jsdom.jsdom(html, { url: toFileUrl(__filename), features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] } }); doc.defaultView.doCheck = () => { assert.equal(doc.getElementById("test").innerHTML, "hello from javascript"); t.done(); }; }); specify("ensure_resolution_is_not_thrown_off_by_hrefless_base_tag", { async: true }, t => { const html = `<html><head><base target="whatever"> <script src="./files/hello.js"></script></head><body> <span id="test">hello from html</span></body></html>`; const doc = jsdom.jsdom(html, { url: toFileUrl(__filename), features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] } }); doc.defaultView.doCheck = () => { assert.equal(doc.getElementById("test").innerHTML, "hello from javascript"); t.done(); }; }); specify("ensure_resources_can_be_skipped_via_options_features", { async: true }, t => { const html = `<html><head><script src="./files/hello.js"></script> <script src="./files/nyan.js"></script></head> <body><span id="test">hello from html</span><span id="cat"> hello from cat</body></html>`; const doc2 = jsdom.jsdom(html, { url: toFileUrl(__filename), features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"], SkipExternalResources: new RegExp(".*/files/h") } }); doc2.defaultView.onload = () => { assert.equal(doc2.getElementById("test").innerHTML, "hello from html", "js should not be executed (doc2)"); assert.equal(doc2.getElementById("cat").innerHTML, "hello from nyan cat", "js should be executed (doc2)"); t.done(); }; }); specify("understand_file_protocol", { async: true }, t => { const html = ` <html> <head> <script type="text/javascript" src="` + toFileUrl("files/hello.js") + `"></script> </head> <body> <span id="test">hello from html</span> </body> </html>`; const doc = jsdom.jsdom(html); doc.onload = () => { assert.equal( doc.getElementById("test").innerHTML, "hello from javascript", "resource with file protocol should work" ); t.done(); }; }); specify("auto_tostring", () => { const buffer = fs.readFileSync(path.resolve(__dirname, "files/env.html")); let dom; assert.doesNotThrow(() => { dom = jsdom.jsdom(buffer); }, "buffers should automatically be stringified"); assert.equal(dom.documentElement.getElementsByTagName("*").length, 3, "should parse as per usual"); }); specify("allow_ender_to_run", { async: true }, t => { jsdom.env("<a />", ["file:" + path.resolve(__dirname, "files/ender-qwery.js")], (e, w) => { assert.ok(!e, "no errors"); assert.ok(w.ender, "ender exists"); assert.ok(w.$, "window contains $"); t.done(); }); }); specify("issue_509_out_of_memory", () => { const html = fs.readFileSync(path.resolve(__dirname, "files/reddit.html")); jsdom.jsdom(html.toString()); }); specify("jquery_val_on_selects", { async: true }, t => { const window = jsdom.jsdom().defaultView; jsdom.jQueryify(window, "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"), () => { window.$("body").append(`<html><body><select id="foo"><option value="first">f</option> <option value="last">l</option></select></body></html>`); assert.equal( window.document.querySelector("[value='first']").selected, true, "`selected` property should be `true` for first" ); assert.equal( window.document.querySelector("[value='last']").selected, false, "`selected` property should be `false` for last" ); assert.equal(window.$("[value='first']").val(), "first", "`val()` on first <option> should return its value"); assert.equal(window.$("[value='last']").val(), "last", "`val()` on last <option> should return its value"); const f = window.$("#foo"); assert.equal(f.val(), "first", "`val()` on <select> should return first <option>'s value"); window.$("#foo").val("last"); assert.equal( window.document.querySelector("[value='first']").selected, false, "`selected` property should be `false` for first" ); assert.equal( window.document.querySelector("[value='last']").selected, true, "`selected` property should be `true` for last" ); assert.equal(window.$("#foo").val(), "last", "`val()` should return last <option>'s value"); t.done(); }); }); specify("jquery_attr_mixed_case", { async: true }, t => { const window = jsdom.jsdom().defaultView; jsdom.jQueryify(window, "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"), () => { const $el = window.$(`<div mixedcase="blah"></div>`); assert.equal($el.attr("mixedCase"), "blah"); t.done(); }); }); specify("Calling show() method in jQuery 1.11.0 (GH-709)", { async: true }, t => { const window = jsdom.jsdom("<!DOCTYPE html><html><head></head><body></body></html>").defaultView; jsdom.jQueryify(window, "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"), () => { const $el = window.$("<div></div>"); assert.doesNotThrow(() => { $el.show(); }); t.done(); }); }); specify("Calling show() method in jQuery 1.11.0, second case (GH-709)", { async: true }, t => { const window = jsdom.jsdom("<!DOCTYPE html><html><head></head><body></body></html>").defaultView; jsdom.jQueryify(window, "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"), () => { const $el1 = window.$("<div></div>"); const $el2 = window.$("<span></span>"); assert.doesNotThrow(() => { $el1.show(); $el2.show(); }); t.done(); }); }); specify("redirected_url_equal_to_location_href", { async: true }, t => { const html = "<p>Redirect</p>"; const server = http.createServer((req, res) => { switch (req.url) { case "/": res.writeHead(302, { Location: "/redir" }); res.end(); break; case "/redir": res.writeHead(200, { "Content-Length": html.length }); res.end(html); break; } }); server.listen(8001, "127.0.0.1", () => { jsdom.env({ url: "http://127.0.0.1:8001", done(errors, window) { server.close(); if (errors) { assert.ok(false, errors.message); } else { assert.equal(window.document.body.innerHTML, html, "root page should be redirected"); assert.equal(window.location.href, "http://127.0.0.1:8001/redir", "window.location.href should equal to redirected url"); } t.done(); } }); }); }); specify("script_with_cookie", { async: true }, t => { const html = `<!DOCTYPE html><html><head><script src="/foo.js"></script></head><body>foo</body></html>`; const server = http.createServer((req, res) => { switch (req.url) { case "/": res.writeHead(200, { "Content-Length": html.length }); res.end(html); break; case "/foo.js": const cookie = req.headers.cookie; const name = cookie ? cookie.split("=")[1] : "no cookie"; const text = "document.body.innerHTML = 'Hello " + name + "'; window.doCheck();"; res.writeHead(200, { "Content-Length": text.length }); res.end(text); break; } }); server.listen(8001, "127.0.0.1", () => { jsdom.env({ url: "http://127.0.0.1:8001", document: { cookie: "name=world" }, features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] }, created(err, window) { window.doCheck = () => { server.close(); assert.ifError(err); assert.equal(window.document.body.innerHTML, "Hello world"); t.done(); }; } }); }); }); specify("xhr_with_cookie", { async: true }, t => { const html = `<!DOCTYPE html><html><head><script> const xhr = new XMLHttpRequest(); xhr.onload = function () { document.body.innerHTML = xhr.responseText; window.doCheck(); }; xhr.open("GET", "/foo.txt", true); xhr.send(); </script></head><body>foo</body></html>`; const server = http.createServer((req, res) => { switch (req.url) { case "/": res.writeHead(200, { "Content-Length": html.length }); res.end(html); break; case "/foo.txt": const cookie = req.headers.cookie; const name = cookie ? cookie.split("=")[1] : "no cookie"; const text = "Hello " + name; res.writeHead(200, { "Content-Length": text.length }); res.end(text); break; } }); server.listen(8001, "127.0.0.1", () => { jsdom.env({ url: "http://127.0.0.1:8001", document: { cookie: "name=world" }, features: { FetchExternalResources: ["script"], ProcessExternalResources: ["script"] }, done(err, window) { window.doCheck = () => { server.close(); assert.ifError(err); assert.equal(window.document.body.innerHTML, "Hello world"); t.done(); }; } }); }); }); }); // describe("node specific tests") });
describe("Web Platform Tests", () => { // jscs:disable maximumLineLength [ "dom/nodes/CharacterData-appendData.html", "dom/nodes/CharacterData-deleteData.html", "dom/nodes/CharacterData-insertData.html", "dom/nodes/CharacterData-remove.html", "dom/nodes/CharacterData-replaceData.html", "dom/nodes/Document-adoptNode.html", "dom/nodes/Document-contentType/contentType/createHTMLDocument.html", "dom/nodes/Document-createComment.html", "dom/nodes/Document-createProcessingInstruction.html", "dom/nodes/Document-createProcessingInstruction-xhtml.xhtml", "dom/nodes/Document-createTextNode.html", "dom/nodes/Document-implementation.html", "dom/nodes/DocumentType-literal.html", "dom/nodes/DocumentType-literal-xhtml.xhtml", "dom/nodes/DocumentType-remove.html", "dom/nodes/DOMImplementation-createDocumentType.html", "dom/nodes/DOMImplementation-createHTMLDocument.html", "dom/nodes/DOMImplementation-hasFeature.html", "dom/nodes/Element-classlist.html", "dom/nodes/Element-getElementsByClassName.html", "dom/nodes/Element-remove.html", "dom/nodes/attributes.html", "dom/nodes/getElementsByClassName-01.htm", "dom/nodes/getElementsByClassName-02.htm", "dom/nodes/getElementsByClassName-03.htm", "dom/nodes/getElementsByClassName-04.htm", "dom/nodes/getElementsByClassName-05.htm", "dom/nodes/getElementsByClassName-06.htm", "dom/nodes/getElementsByClassName-07.htm", "dom/nodes/getElementsByClassName-08.htm", "dom/nodes/getElementsByClassName-09.htm", "dom/nodes/getElementsByClassName-10.xml", // "dom/nodes/getElementsByClassName-11.xml", // XML class attribute and localName and namespaces don't work well "dom/nodes/getElementsByClassName-12.htm", "dom/nodes/getElementsByClassName-13.htm", "dom/nodes/getElementsByClassName-14.htm", "dom/nodes/getElementsByClassName-15.htm", "dom/nodes/getElementsByClassName-16.htm", "dom/nodes/getElementsByClassName-17.htm", "dom/nodes/getElementsByClassName-18.htm", "dom/nodes/getElementsByClassName-19.htm", "dom/nodes/getElementsByClassName-20.htm", "dom/nodes/getElementsByClassName-21.htm", "dom/nodes/getElementsByClassName-22.htm", "dom/nodes/getElementsByClassName-23.htm", "dom/nodes/getElementsByClassName-24.htm", "dom/nodes/getElementsByClassName-25.htm", "dom/nodes/getElementsByClassName-26.htm", "dom/nodes/getElementsByClassName-27.htm", "dom/nodes/getElementsByClassName-28.htm", "dom/nodes/getElementsByClassName-29.htm", "dom/nodes/getElementsByClassName-30.htm", "dom/nodes/getElementsByClassName-31.htm", "dom/nodes/Node-baseURI.html", "dom/nodes/Node-cloneNode.html", "dom/traversal/NodeFilter-constants.html", "dom/traversal/NodeIterator.html", "domparsing/insert-adjacent.html", "html/browsers/browsing-the-web/history-traversal/PopStateEvent.html", "html/browsers/browsing-the-web/history-traversal/hashchange_event.html", "html/browsers/browsing-the-web/history-traversal/popstate_event.html", // "html/browsers/history/the-history-interface/001.html", // complicated navigation stuff and structured cloning // "html/browsers/history/the-history-interface/002.html", // complicated navigation stuff and structured cloning // "html/browsers/history/the-history-interface/004.html", // subtle timing issues that I can't quite figure out; see comment in History-impl.js "html/browsers/history/the-history-interface/005.html", "html/browsers/history/the-history-interface/006.html", // "html/browsers/history/the-history-interface/007.html", // depends on the load event being delayed properly "html/browsers/history/the-history-interface/008.html", // "html/browsers/history/the-history-interface/009.html", // complicated navigation stuff for iframes // "html/browsers/history/the-history-interface/010.html", // complicated navigation stuff for iframes "html/browsers/history/the-history-interface/011.html", "html/browsers/history/the-history-interface/012.html", "html/browsers/history/the-location-interface/document_location.html", "html/browsers/history/the-location-interface/location-stringifier.html", "html/browsers/history/the-location-interface/location_hash.html", "html/browsers/history/the-location-interface/location_host.html", "html/browsers/history/the-location-interface/location_hostname.html", "html/browsers/history/the-location-interface/location_href.html", "html/browsers/history/the-location-interface/location_pathname.html", "html/browsers/history/the-location-interface/location_port.html", "html/browsers/history/the-location-interface/location_protocol.html", "html/browsers/history/the-location-interface/location_search.html", "html/dom/dynamic-markup-insertion/document-writeln/document.writeln-02.html", "html/dom/dynamic-markup-insertion/document-writeln/document.writeln-03.html", "html/dom/elements/global-attributes/classlist-nonstring.html", // "html/infrastructure/urls/terminology-0/document-base-url.html", // we don't support srcdoc <base> correctly "html/semantics/forms/the-input-element/selection.html", "html/semantics/scripting-1/the-script-element/script-language-type.html", "html/semantics/scripting-1/the-script-element/script-languages-01.html", // "html/semantics/scripting-1/the-script-element/script-languages-02.html", // our script execution timing is off; see discussion in https://github.com/tmpvar/jsdom/pull/1406 "html/semantics/scripting-1/the-script-element/script-noembed-noframes-iframe.xhtml", // "html/semantics/scripting-1/the-script-element/script-text-xhtml.xhtml", // not sure; XHTML problems? "html/semantics/scripting-1/the-script-element/script-text.html", // "html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/node-document.html", // templates in XHTML are totally messed up // "html/semantics/scripting-1/the-template-element/additions-to-parsing-xhtml-documents/template-child-nodes.html", // templates in XHTML are totally messed up // "html/semantics/scripting-1/the-template-element/additions-to-serializing-xhtml-documents/outerhtml.html", // templates in XHTML are totally messed up "html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/template-clone-children.html", "html/semantics/scripting-1/the-template-element/additions-to-the-steps-to-clone-a-node/templates-copy-document-owner.html", // "html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-document-type.html", // requires @@toStringTag // "html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-001.html", // template content owner document semantics not yet implemented // "html/semantics/scripting-1/the-template-element/definitions/template-contents-owner-test-002.html", // template content owner document semantics not yet implemented // "html/semantics/scripting-1/the-template-element/definitions/template-contents.html", // requires @@toStringTag "html/semantics/scripting-1/the-template-element/innerhtml-on-templates/innerhtml.html", "html/semantics/scripting-1/the-template-element/serializing-html-templates/outerhtml.html", "html/semantics/scripting-1/the-template-element/template-element/content-attribute.html", // "html/semantics/scripting-1/the-template-element/template-element/node-document-changes.html", // template content owner document semantics not yet implemented // "html/semantics/scripting-1/the-template-element/template-element/template-as-a-descendant.html", // template parsing not quite perfect yet "html/semantics/scripting-1/the-template-element/template-element/template-content-node-document.html", "html/semantics/scripting-1/the-template-element/template-element/template-content.html", "html/semantics/scripting-1/the-template-element/template-element/template-descendant-body.html", "html/semantics/scripting-1/the-template-element/template-element/template-descendant-frameset.html", "html/semantics/scripting-1/the-template-element/template-element/template-descendant-head.html", // "html/semantics/tabular-data/the-table-element/caption-methods.html", // "html/semantics/tabular-data/the-table-element/createTBody.html", "html/semantics/tabular-data/the-table-element/delete-caption.html", "html/semantics/tabular-data/the-table-element/insertRow-method-01.html", "html/semantics/tabular-data/the-table-element/insertRow-method-02.html", // "html/semantics/tabular-data/the-table-element/tBodies.html", // "html/semantics/tabular-data/the-table-element/table-insertRow.html", // "html/semantics/tabular-data/the-table-element/table-rows.html", "html/syntax/serializing-html-fragments/outerHTML.html", // "html/syntax/parsing/html5lib_template.html", // no idea what's going on here "html/syntax/parsing/template/additions-to-foster-parenting/template-is-a-foster-parent-element.html", "html/syntax/parsing/template/additions-to-foster-parenting/template-is-not-a-foster-parent-element.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/generating-of-implied-end-tags.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-body-token.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-frameset-token.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-head-token.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/ignore-html-token.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-body.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/start-tag-html.html", "html/syntax/parsing/template/additions-to-the-in-body-insertion-mode/template-end-tag-without-start-one.html", // "html/syntax/parsing/template/additions-to-the-in-frameset-insertion-mode/end-tag-frameset.html", // template parsing not quite perfect yet "html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/generating-of-implied-end-tags.html", "html/syntax/parsing/template/additions-to-the-in-head-insertion-mode/template-end-tag-without-start-one.html", "html/syntax/parsing/template/additions-to-the-in-table-insertion-mode/end-tag-table.html", "html/syntax/parsing/template/appending-to-a-template/template-child-nodes.html", "html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context.html", "html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context.html", "html/syntax/parsing/template/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context.html", // "html/syntax/parsing/template/creating-an-element-for-the-token/template-owner-document.html", // template content owner document semantics not yet implemented "html/webappapis/atob/base64.html", "html/webappapis/timers/evil-spec-example.html", "dom/events/Event-constants.html", "dom/events/Event-defaultPrevented.html", "dom/events/Event-dispatch-bubbles-false.html", "dom/events/Event-dispatch-handlers-changed.html", "dom/events/Event-dispatch-omitted-capture.html", "dom/events/Event-dispatch-propagation-stopped.html", "dom/events/Event-dispatch-reenter.html", "dom/events/Event-dispatch-target-moved.html", "dom/events/Event-dispatch-target-removed.html", "dom/events/Event-initEvent.html", "dom/events/Event-propagation.html", "dom/events/Event-type.html", "dom/events/Event-type-empty.html", "dom/events/EventTarget-addEventListener.html", "dom/events/EventTarget-dispatchEvent-returnvalue.html", "dom/events/EventTarget-dispatchEvent.html", "dom/events/EventTarget-removeEventListener.html", "dom/events/ProgressEvent.html", "DOMEvents/ClickFakeEvent.nondocument.html", "DOMEvents/event-phases-order.html", "DOMEvents/throwing-in-listener-and-window-error-event.html", "DOMEvents/throwing-in-listener-when-all-have-not-run-yet.html", "FileAPI/fileReader.html", "FileAPI/blob/Blob-slice.html", "FileAPI/file/File-constructor.html", "FileAPI/FileReader/Progress_event_bubbles_cancelable.html", "FileAPI/reading-data-section/FileReader-event-handler-attributes.html", "FileAPI/reading-data-section/filereader_abort.html", "FileAPI/reading-data-section/filereader_error.html", "FileAPI/reading-data-section/filereader_readAsArrayBuffer.html", "FileAPI/reading-data-section/filereader_readAsDataURL.html", "FileAPI/reading-data-section/filereader_readAsText.html", "FileAPI/reading-data-section/filereader_readystate.html", "FileAPI/reading-data-section/filereader_result.html", "XMLHttpRequest/FormData-append.html", "XMLHttpRequest/abort-after-receive.htm", "XMLHttpRequest/abort-after-send.htm", "XMLHttpRequest/abort-after-stop.htm", "XMLHttpRequest/abort-after-timeout.htm", "XMLHttpRequest/abort-during-done.htm", "XMLHttpRequest/abort-during-open.htm", "XMLHttpRequest/abort-during-unsent.htm", "XMLHttpRequest/abort-during-upload.htm", "XMLHttpRequest/abort-event-abort.htm", "XMLHttpRequest/abort-event-listeners.htm", "XMLHttpRequest/abort-event-loadend.htm", "XMLHttpRequest/abort-event-order.htm", "XMLHttpRequest/abort-upload-event-abort.htm", "XMLHttpRequest/abort-upload-event-loadend.htm", "XMLHttpRequest/anonymous-mode-unsupported.htm", "XMLHttpRequest/data-uri.htm", "XMLHttpRequest/event-abort.htm", "XMLHttpRequest/event-error.html", "XMLHttpRequest/event-load.htm", "XMLHttpRequest/event-loadend.htm", "XMLHttpRequest/event-loadstart.htm", "XMLHttpRequest/event-progress.htm", "XMLHttpRequest/event-readystate-sync-open.htm", "XMLHttpRequest/event-readystatechange-loaded.htm", "XMLHttpRequest/event-timeout.htm", "XMLHttpRequest/event-upload-progress-crossorigin.sub.htm", "XMLHttpRequest/event-upload-progress.htm", "XMLHttpRequest/formdata-blob.htm", "XMLHttpRequest/formdata-delete.htm", "XMLHttpRequest/formdata-get.htm", "XMLHttpRequest/formdata-has.htm", "XMLHttpRequest/formdata-set.htm", "XMLHttpRequest/formdata.htm", "XMLHttpRequest/getallresponseheaders-cookies.htm", "XMLHttpRequest/getallresponseheaders-status.htm", "XMLHttpRequest/getresponseheader-case-insensitive.htm", "XMLHttpRequest/getresponseheader-chunked-trailer.htm", "XMLHttpRequest/getresponseheader-cookies-and-more.htm", "XMLHttpRequest/getresponseheader-error-state.htm", "XMLHttpRequest/getresponseheader-server-date.htm", "XMLHttpRequest/getresponseheader-special-characters.htm", "XMLHttpRequest/getresponseheader-unsent-opened-state.htm", // "XMLHttpRequest/interface.html", // needs this PR https://github.com/tmpvar/jsdom/pull/1406 "XMLHttpRequest/open-after-abort.htm", "XMLHttpRequest/open-after-setrequestheader.htm", "XMLHttpRequest/open-during-abort.htm", "XMLHttpRequest/open-method-case-insensitive.htm", // "XMLHttpRequest/open-method-case-sensitive.htm", // request module forces upper case "XMLHttpRequest/open-method-bogus.htm", "XMLHttpRequest/open-method-insecure.htm", "XMLHttpRequest/open-method-responsetype-set-sync.htm", "XMLHttpRequest/open-open-send.htm", "XMLHttpRequest/open-open-sync-send.htm", "XMLHttpRequest/open-referer.htm", "XMLHttpRequest/open-send-open.htm", "XMLHttpRequest/open-sync-open-send.htm", "XMLHttpRequest/open-url-about-blank-window.htm", "XMLHttpRequest/open-url-base.htm", "XMLHttpRequest/open-url-base-inserted.htm", "XMLHttpRequest/open-url-base-inserted-after-open.htm", // "XMLHttpRequest/open-url-bogus.htm", // I don't understand this one "XMLHttpRequest/open-url-encoding.htm", "XMLHttpRequest/open-url-fragment.htm", "XMLHttpRequest/open-url-javascript-window-2.htm", "XMLHttpRequest/open-url-javascript-window.htm", "XMLHttpRequest/open-url-multi-window.htm", "XMLHttpRequest/open-url-multi-window-2.htm", "XMLHttpRequest/open-url-multi-window-3.htm", "XMLHttpRequest/open-url-multi-window-4.htm", // "XMLHttpRequest/open-url-multi-window-5.htm", // location.reload is not implemented // "XMLHttpRequest/open-url-worker-origin.htm", // needs Worker implementation // "XMLHttpRequest/open-url-worker-simple.htm", // needs Worker implementation "XMLHttpRequest/open-user-password-non-same-origin.htm", "XMLHttpRequest/overridemimetype-done-state.htm", // "XMLHttpRequest/overridemimetype-headers-received-state-force-shiftjis.htm", // needs proper encoding handling "XMLHttpRequest/overridemimetype-invalid-mime-type.htm", "XMLHttpRequest/overridemimetype-loading-state.htm", // "XMLHttpRequest/overridemimetype-open-state-force-utf-8.htm", // needs proper encoding handling "XMLHttpRequest/overridemimetype-open-state-force-xml.htm", // "XMLHttpRequest/overridemimetype-unsent-state-force-shiftjis.htm", // needs proper encoding handling "XMLHttpRequest/preserve-ua-header-on-redirect.htm", "XMLHttpRequest/progress-events-response-data-gzip.htm", "XMLHttpRequest/response-data-arraybuffer.htm", "XMLHttpRequest/response-data-blob.htm", // "XMLHttpRequest/response-data-deflate.htm", // request module does not support deflate "XMLHttpRequest/response-data-gzip.htm", "XMLHttpRequest/response-data-progress.htm", "XMLHttpRequest/response-invalid-responsetype.htm", "XMLHttpRequest/response-json.htm", "XMLHttpRequest/response-method.htm", "XMLHttpRequest/responseText-status.html", "XMLHttpRequest/responsetype.html", // "XMLHttpRequest/responsexml-basic.htm", // xml namespace issue with getElementById // "XMLHttpRequest/responsexml-document-properties.htm", see https://github.com/w3c/web-platform-tests/issues/2668 "XMLHttpRequest/responsexml-media-type.htm", "XMLHttpRequest/responsexml-non-document-types.htm", // "XMLHttpRequest/responsexml-non-well-formed.htm", // xml parsing is not strict "XMLHttpRequest/security-consideration.sub.html", "XMLHttpRequest/send-accept-language.htm", "XMLHttpRequest/send-accept.htm", "XMLHttpRequest/send-authentication-basic-cors-not-enabled.htm", "XMLHttpRequest/send-authentication-basic-cors.htm", "XMLHttpRequest/send-authentication-basic-repeat-no-args.htm", "XMLHttpRequest/send-authentication-basic-setrequestheader-existing-session.htm", "XMLHttpRequest/send-authentication-basic-setrequestheader.htm", "XMLHttpRequest/send-authentication-basic.htm", "XMLHttpRequest/send-authentication-competing-names-passwords.htm", // "XMLHttpRequest/send-authentication-cors-basic-setrequestheader.htm", // seems wrong ? "XMLHttpRequest/send-conditional.htm", "XMLHttpRequest/send-content-type-charset.htm", "XMLHttpRequest/send-content-type-string.htm", "XMLHttpRequest/send-data-arraybuffer.htm", "XMLHttpRequest/send-data-blob.htm", "XMLHttpRequest/send-data-es-object.htm", "XMLHttpRequest/send-data-formdata.htm", "XMLHttpRequest/send-data-unexpected-tostring.htm", "XMLHttpRequest/send-entity-body-basic.htm", "XMLHttpRequest/send-entity-body-document-bogus.htm", // "XMLHttpRequest/send-entity-body-document.htm", // needs proper encoding handling "XMLHttpRequest/send-entity-body-empty.htm", "XMLHttpRequest/send-entity-body-get-head-async.htm", "XMLHttpRequest/send-entity-body-get-head.htm", "XMLHttpRequest/send-entity-body-none.htm", "XMLHttpRequest/send-network-error-async-events.sub.htm", "XMLHttpRequest/send-network-error-sync-events.sub.htm", "XMLHttpRequest/send-no-response-event-loadend.htm", "XMLHttpRequest/send-no-response-event-loadstart.htm", "XMLHttpRequest/send-no-response-event-order.htm", "XMLHttpRequest/send-non-same-origin.sub.htm", // "XMLHttpRequest/send-receive-utf16.htm", // needs proper encoding handling "XMLHttpRequest/send-redirect-bogus-sync.htm", "XMLHttpRequest/send-redirect-bogus.htm", // "XMLHttpRequest/send-redirect-infinite-sync.htm", // the test seems broken locally // "XMLHttpRequest/send-redirect-infinite.htm", // the test seems broken locally "XMLHttpRequest/send-redirect-no-location.htm", // "XMLHttpRequest/send-redirect-to-cors.htm", // request module remove content-type header on redirect "XMLHttpRequest/send-redirect-to-non-cors.htm", // "XMLHttpRequest/send-redirect.htm", // request module remove content-type header on redirect "XMLHttpRequest/send-response-event-order.htm", "XMLHttpRequest/send-response-upload-event-loadend.htm", "XMLHttpRequest/send-response-upload-event-loadstart.htm", "XMLHttpRequest/send-response-upload-event-progress.htm", "XMLHttpRequest/send-send.htm", "XMLHttpRequest/send-sync-blocks-async.htm", "XMLHttpRequest/send-sync-no-response-event-load.htm", "XMLHttpRequest/send-sync-no-response-event-loadend.htm", "XMLHttpRequest/send-sync-no-response-event-order.htm", "XMLHttpRequest/send-sync-response-event-order.htm", "XMLHttpRequest/send-sync-timeout.htm", "XMLHttpRequest/send-timeout-events.htm", // "XMLHttpRequest/send-usp.html", // needs URLSearchParams implementation "XMLHttpRequest/setrequestheader-after-send.htm", "XMLHttpRequest/setrequestheader-allow-empty-value.htm", "XMLHttpRequest/setrequestheader-allow-whitespace-in-value.htm", "XMLHttpRequest/setrequestheader-before-open.htm", "XMLHttpRequest/setrequestheader-bogus-name.htm", "XMLHttpRequest/setrequestheader-bogus-value.htm", "XMLHttpRequest/setrequestheader-case-insensitive.htm", "XMLHttpRequest/setrequestheader-content-type.htm", "XMLHttpRequest/setrequestheader-header-allowed.htm", "XMLHttpRequest/setrequestheader-header-forbidden.htm", "XMLHttpRequest/setrequestheader-open-setrequestheader.htm", "XMLHttpRequest/status-async.htm", "XMLHttpRequest/status-basic.htm", "XMLHttpRequest/status-error.htm", "XMLHttpRequest/timeout-cors-async.htm", "XMLHttpRequest/timeout-sync.htm", "XMLHttpRequest/xmlhttprequest-basic.htm", "XMLHttpRequest/xmlhttprequest-eventtarget.htm", // "XMLHttpRequest/xmlhttprequest-network-error-sync.htm", // the test seems broken locally // "XMLHttpRequest/xmlhttprequest-network-error.htm", // the test seems broken locally // "XMLHttpRequest/xmlhttprequest-timeout-aborted.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-abortedonmain.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-overrides.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-overridesexpires.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-simple.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-synconmain.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-twice.html", // self instanceof Window fails // "XMLHttpRequest/xmlhttprequest-timeout-worker-aborted.html", // needs worker implementation // "XMLHttpRequest/xmlhttprequest-timeout-worker-overrides.html", // needs worker implementation // "XMLHttpRequest/xmlhttprequest-timeout-worker-overridesexpires.html", // needs worker implementation // "XMLHttpRequest/xmlhttprequest-timeout-worker-simple.html", // needs worker implementation // "XMLHttpRequest/xmlhttprequest-timeout-worker-synconworker.html", // needs worker implementation // "XMLHttpRequest/xmlhttprequest-timeout-worker-twice.html", // needs worker implementation "XMLHttpRequest/xmlhttprequest-unsent.htm", "XMLHttpRequest/XMLHttpRequest-withCredentials.html", "cors/allow-headers.htm", "cors/basic.htm", "cors/credentials-flag.htm", // "cors/late-upload-events.htm", // I don't know how to fix this one "cors/origin.htm", // "cors/preflight-cache.htm", // cache should probably be implemented for simple requests before "cors/redirect-origin.htm", "cors/redirect-preflight.htm", // "cors/redirect-preflight-2.htm", // preflight should also be done before redirected requests // but request module redirects cannot be paused while doing preflight "cors/redirect-userinfo.htm", // "cors/remote-origin.htm", // postMessage event does not contain source "cors/request-headers.htm", // "cors/response-headers.htm", // I don't find a spec about combining same value response headers // and slow synchronous requests cause a timeout on an asynchronous test // "cors/simple-requests.htm", // slow synchronous requests cause a timeout on an asynchronous test too "cors/status-async.htm", "cors/status-preflight.htm", "cors/status.htm" ] .forEach(runWebPlatformTest); });
describe("API: JSDOM class's methods", () => { describe("serialize", () => { it("should serialize the default document correctly", () => { const dom = new JSDOM(); assert.strictEqual(dom.serialize(), `<html><head></head><body></body></html>`); }); it("should serialize a text-only document correctly", () => { const dom = new JSDOM(`hello`); assert.strictEqual(dom.serialize(), `<html><head></head><body>hello</body></html>`); }); it("should serialize a document with HTML correctly", () => { const dom = new JSDOM(`<!DOCTYPE html><html><head></head><body><p>hello world!</p></body></html>`); assert.strictEqual(dom.serialize(), `<!DOCTYPE html><html><head></head><body><p>hello world!</p></body></html>`); }); it("should serialize documents with omitted and varying-case html or body tags correctly", () => { const inputs = [ "<HTML><BODY></BODY></HTML>", "<html><BODY></Body></HTML>", "<html><body></body></html>", "<body></body>", "" ]; const outputs = inputs.map(input => (new JSDOM(input)).serialize()); for (const output of outputs) { assert.strictEqual(output, `<html><head></head><body></body></html>`); } }); }); describe("nodeLocation", () => { it("should throw when includeNodeLocations is left as the default (false)", () => { const dom = new JSDOM(`<p>Hello</p>`); const node = dom.window.document.querySelector("p"); assert.throws(() => dom.nodeLocation(node)); }); it("should throw when includeNodeLocations is set explicitly to false", () => { const dom = new JSDOM(`<p>Hello</p>`, { includeNodeLocations: false }); const node = dom.window.document.querySelector("p"); assert.throws(() => dom.nodeLocation(node)); }); it("should give the correct location for an element", () => { const dom = new JSDOM(`<p>Hello</p>`, { includeNodeLocations: true }); const node = dom.window.document.querySelector("p"); assert.deepEqual(dom.nodeLocation(node), { line: 1, col: 1, startOffset: 0, endOffset: 12, startTag: { line: 1, col: 1, startOffset: 0, endOffset: 3 }, endTag: { line: 1, col: 9, startOffset: 8, endOffset: 12 } }); }); it("should give the correct location for a text node", () => { const dom = new JSDOM(`<p>Hello</p>`, { includeNodeLocations: true }); const node = dom.window.document.querySelector("p").firstChild; assert.deepEqual(dom.nodeLocation(node), { line: 1, col: 4, startOffset: 3, endOffset: 8 }); }); it("should give the correct location for a void element", () => { const dom = new JSDOM(`<p>Hello <img src="foo.jpg"> </p>`, { includeNodeLocations: true }); const node = dom.window.document.querySelector("img"); assert.deepEqual(dom.nodeLocation(node), { attrs: { src: { line: 2, col: 14, startOffset: 22, endOffset: 35 } }, line: 2, col: 9, startOffset: 17, endOffset: 36 }); }); }); describe("runVMScript", () => { it("should throw when runScripts is left as the default", () => { const dom = new JSDOM(); const script = new vm.Script("this.ran = true;"); assert.throws(() => dom.runVMScript(script), TypeError); assert.strictEqual(dom.window.ran, undefined); }); it("should work when runScripts is set to \"outside-only\"", () => { const dom = new JSDOM(``, { runScripts: "outside-only" }); const script = new vm.Script("this.ran = true;"); dom.runVMScript(script); assert.strictEqual(dom.window.ran, true); }); it("should work when runScripts is set to \"dangerously\"", () => { const dom = new JSDOM(``, { runScripts: "dangerously" }); const script = new vm.Script("this.ran = true;"); dom.runVMScript(script); assert.strictEqual(dom.window.ran, true); }); it("should return the result of the evaluation", () => { const dom = new JSDOM(``, { runScripts: "outside-only" }); const script = new vm.Script("5;"); const result = dom.runVMScript(script); assert.strictEqual(result, 5); }); it("should work with the same script multiple times", () => { const dom = new JSDOM(``, { runScripts: "outside-only" }); const script = new vm.Script("if (!this.ran) { this.ran = 0; } ++this.ran;"); dom.runVMScript(script); dom.runVMScript(script); dom.runVMScript(script); assert.strictEqual(dom.window.ran, 3); }); }); describe("reconfigure", () => { describe("windowTop", () => { it("should reconfigure the window.top property (tested from the outside)", () => { const dom = new JSDOM(); const newTop = { is: "top" }; dom.reconfigure({ windowTop: newTop }); assert.strictEqual(dom.window.top, newTop); }); it("should reconfigure the window.top property (tested from the inside)", () => { const dom = new JSDOM(``, { runScripts: "dangerously" }); const newTop = { is: "top" }; dom.reconfigure({ windowTop: newTop }); dom.window.document.body.innerHTML = `<script> window.topResult = top.is; </script>`; assert.strictEqual(dom.window.topResult, "top"); }); it("should do nothing when no options are passed", () => { const dom = new JSDOM(); dom.reconfigure({ }); assert.strictEqual(dom.window.top, dom.window); }); it("should change window.top to undefined if passing undefined", () => { const dom = new JSDOM(); dom.reconfigure({ windowTop: undefined }); assert.strictEqual(dom.window.top, undefined); }); }); describe("url", () => { it("should successfully change the URL", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); function testPass(urlString, expected = urlString) { dom.reconfigure({ url: urlString }); assert.strictEqual(window.location.href, expected); assert.strictEqual(window.document.URL, expected); assert.strictEqual(window.document.documentURI, expected); } testPass("http://localhost", "http://localhost/"); testPass("http://www.localhost", "http://www.localhost/"); testPass("http://www.localhost.com", "http://www.localhost.com/"); testPass("https://localhost/"); testPass("file://path/to/my/location/"); testPass("http://localhost.subdomain.subdomain/"); testPass("http://localhost:3000/"); testPass("http://localhost/"); }); it("should throw and not impact the URL when trying to change to an unparseable URL", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); function testFail(url) { assert.throws(() => dom.reconfigure({ url }), TypeError); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); } testFail("fail"); testFail("/fail"); testFail("fail.com"); testFail(undefined); }); it("should not throw and not impact the URL when no url option is given", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); assert.doesNotThrow(() => dom.reconfigure({ })); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); }); }); }); });
describe("reconfigure", () => { describe("windowTop", () => { it("should reconfigure the window.top property (tested from the outside)", () => { const dom = new JSDOM(); const newTop = { is: "top" }; dom.reconfigure({ windowTop: newTop }); assert.strictEqual(dom.window.top, newTop); }); it("should reconfigure the window.top property (tested from the inside)", () => { const dom = new JSDOM(``, { runScripts: "dangerously" }); const newTop = { is: "top" }; dom.reconfigure({ windowTop: newTop }); dom.window.document.body.innerHTML = `<script> window.topResult = top.is; </script>`; assert.strictEqual(dom.window.topResult, "top"); }); it("should do nothing when no options are passed", () => { const dom = new JSDOM(); dom.reconfigure({ }); assert.strictEqual(dom.window.top, dom.window); }); it("should change window.top to undefined if passing undefined", () => { const dom = new JSDOM(); dom.reconfigure({ windowTop: undefined }); assert.strictEqual(dom.window.top, undefined); }); }); describe("url", () => { it("should successfully change the URL", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); function testPass(urlString, expected = urlString) { dom.reconfigure({ url: urlString }); assert.strictEqual(window.location.href, expected); assert.strictEqual(window.document.URL, expected); assert.strictEqual(window.document.documentURI, expected); } testPass("http://localhost", "http://localhost/"); testPass("http://www.localhost", "http://www.localhost/"); testPass("http://www.localhost.com", "http://www.localhost.com/"); testPass("https://localhost/"); testPass("file://path/to/my/location/"); testPass("http://localhost.subdomain.subdomain/"); testPass("http://localhost:3000/"); testPass("http://localhost/"); }); it("should throw and not impact the URL when trying to change to an unparseable URL", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); function testFail(url) { assert.throws(() => dom.reconfigure({ url }), TypeError); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); } testFail("fail"); testFail("/fail"); testFail("fail.com"); testFail(undefined); }); it("should not throw and not impact the URL when no url option is given", () => { const dom = new JSDOM(``, { url: "http://example.com/" }); const window = dom.window; assert.strictEqual(window.document.URL, "http://example.com/"); assert.doesNotThrow(() => dom.reconfigure({ })); assert.strictEqual(window.location.href, "http://example.com/"); assert.strictEqual(window.document.URL, "http://example.com/"); assert.strictEqual(window.document.documentURI, "http://example.com/"); }); }); });
describe("jsdom/miscellaneous", () => { specify("DOMContentLoaded should not be fired after window.close() (GH-1479)", () => { const { window } = new JSDOM(`<html><head> <script> window.a = 0; document.addEventListener("DOMContentLoaded", () => window.a++, false); </script> </head><body></body></html>`, { runScripts: "dangerously" }); window.close(); assert.equal(window.a, 0); }); specify("appendChild_to_document_with_existing_documentElement", () => { function t() { try { const doc = (new JSDOM()).window.document; doc.appendChild(doc.createElement("html")); } catch (e) { assert.equal(e.code, 3, "Should throw HIERARCHY_ERR"); throw e; } } assert.throws(t); }); specify("importNode", () => { assert.doesNotThrow(() => { const html1 = `<html><body><h1 id="headline">Hello <span id="world">World</span></h1></body></html>`; const doc1 = (new JSDOM(html1)).window.document; const doc2 = (new JSDOM()).window.document; doc2.body.appendChild(doc2.importNode(doc1.getElementById("headline"), true)); doc2.getElementById("world").className = "foo"; }); }); specify("window_is_augmented_with_dom_features", () => { const { window } = new JSDOM(); assert.notEqual(window.Element, null, "window.Element should not be null"); }); specify("url_resolution", () => { const html = ` <html> <head></head> <body> <a href="http://example.com" id="link1">link1</a> <a href="/local.html" id="link2">link2</a> <a href="local.html" id="link3">link3</a> <a href="../../local.html" id="link4">link4</a> <a href="#here" id="link5">link5</a> <a href="//example.com/protocol/avoidance.html" id="link6">protocol</a> </body>\ </html>`; function testLocal() { const url = "file:///path/to/docroot/index.html"; const doc = (new JSDOM(html, { url })).window.document; assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "file:///local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "file:///path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "file:///path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "file:///path/to/docroot/index.html#here", "Relative URL should be resolved" ); // test.equal( // doc.getElementById("link6").href, // "//prototol/avoidance.html", // "Protocol-less URL should be resolved" // ); } function testRemote() { const url = "http://example.com/path/to/docroot/index.html"; const doc = (new JSDOM(html, { url })).window.document; assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "http://example.com/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "http://example.com/path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "http://example.com/path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "http://example.com/path/to/docroot/index.html#here", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link6").href, "http://example.com/protocol/avoidance.html", "Relative URL should be resolved" ); } function testBase() { const url = "about:blank"; const doc = (new JSDOM(html, { url })).window.document; const base = doc.createElement("base"); base.href = "http://example.com/path/to/docroot/index.html"; doc.getElementsByTagName("head").item(0).appendChild(base); assert.equal( doc.getElementById("link1").href, "http://example.com/", "Absolute URL should be left alone except for possible trailing slash" ); assert.equal( doc.getElementById("link2").href, "http://example.com/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link3").href, "http://example.com/path/to/docroot/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link4").href, "http://example.com/path/local.html", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link5").href, "http://example.com/path/to/docroot/index.html#here", "Relative URL should be resolved" ); assert.equal( doc.getElementById("link6").href, "http://example.com/protocol/avoidance.html", "Relative URL should be resolved" ); } testLocal(); testRemote(); testBase(); }); specify("numeric_values", () => { const html = `<html><body><td data-year="2011" data-month="0" data-day="9"> <a href="#" class=" ">9</a> </td></body></html>`; const { document } = (new JSDOM(html)).window; const a = document.body.children.item(0); a.innerHTML = 9; a.setAttribute("id", 123); assert.strictEqual(a.innerHTML, "9", "Element stringify"); assert.strictEqual(a.getAttributeNode("id").nodeValue, "123", "Attribute stringify"); }); specify("childNodes_updates_on_insertChild", () => { const { window } = new JSDOM(); const div = window.document.createElement("div"); let text = window.document.createTextNode("bar"); div.appendChild(text); assert.strictEqual( text, div.childNodes[0], "childNodes NodeList should update after appendChild" ); text = window.document.createTextNode("bar"); div.insertBefore(text, null); assert.strictEqual( text, div.childNodes[1], "childNodes NodeList should update after insertBefore" ); }); specify("option_set_selected", () => { const { window } = new JSDOM(); const select = window.document.createElement("select"); const option0 = window.document.createElement("option"); select.appendChild(option0); option0.setAttribute("selected", "selected"); const optgroup = window.document.createElement("optgroup"); select.appendChild(optgroup); const option1 = window.document.createElement("option"); optgroup.appendChild(option1); assert.strictEqual(true, option0.selected, "initially selected"); assert.strictEqual(false, option1.selected, "initially not selected"); assert.strictEqual(option1, select.options[1], "options should include options inside optgroup"); option1.defaultSelected = true; assert.strictEqual(false, option0.selected, "selecting other option should deselect this"); assert.strictEqual(true, option0.defaultSelected, "default should not change"); assert.strictEqual(true, option1.selected, "selected changes when defaultSelected changes"); assert.strictEqual(true, option1.defaultSelected, "I just set this"); option0.defaultSelected = false; option0.selected = true; assert.strictEqual(true, option0.selected, "I just set this"); assert.strictEqual(false, option0.defaultSelected, "selected does not set default"); assert.strictEqual(false, option1.selected, "should deselect others"); assert.strictEqual(true, option1.defaultSelected, "unchanged"); }); specify("fix_for_issue_221", () => { const html = "<html><head></head><body></body></html>"; const { document } = (new JSDOM(html)).window; const div = document.createElement("div"); document.body.appendChild(div); div.appendChild(document.createTextNode("hello world")); assert.strictEqual( div.childNodes[0].nodeValue, "hello world", "Nodelist children should be populated immediately" ); }); specify("parsing_and_serializing_entities", () => { const html = `<html><body><a href="http://example.com/?a=b&c=d"><æ☺foo</a>`; const { document } = (new JSDOM(html)).window; const anchor = document.getElementsByTagName("a")[0]; assert.strictEqual( anchor.getAttribute("href"), "http://example.com/?a=b&c=d", "href attribute value should be deentitified" ); assert.strictEqual( anchor.firstChild.nodeValue, "<æ☺foo", "nodeValue of text node should be deentitified" ); assert.ok( anchor.outerHTML.indexOf("http://example.com/?a=b&c=d") !== -1, "outerHTML of anchor href should be entitified" ); assert.ok( anchor.innerHTML.indexOf("<") === 0, "innerHTML of anchor should begin with <" ); }); specify("parsing_and_serializing_unknown_entities", () => { const html = "<html><body>&nowayjose;☺lah;	q;</body></html>"; const { document } = (new JSDOM(html)).window; assert.strictEqual( document.body.firstChild.nodeValue, "&nowayjose;☺lah;\tq;", "Unknown and unparsable entities should be handled like a browser would" ); assert.strictEqual( document.body.innerHTML, "&nowayjose;☺lah;\tq;", "Unknown and unparsable entities should be handled like a browser would" ); }); specify("entities_in_script_should_be_left_alone", () => { const html = `<!DOCTYPE html><html><head></head><body><script>alert(""");</script></body></html>`; const { document } = (new JSDOM(html)).window; assert.strictEqual(document.body.innerHTML, `<script>alert(""");</script>`); assert.strictEqual(document.body.firstChild.innerHTML, `alert(""");`); }); specify("document_title_and_entities", () => { const html = "<html><head><title><b>Hello</b></title></head><body></body></html>"; const { document } = (new JSDOM(html)).window; assert.strictEqual( document.title, "<b>Hello</b>", `document.title should be the deentitified version of what was in the original HTML` ); document.title = "<b>World</b>"; assert.strictEqual( document.title, "<b>World</b>", `When document.title is set programmatically to something looking like HTML tags, then read again, it should have the exact same value, no entification should take place` ); document.title = "<b>World</b>"; assert.strictEqual( document.title, "<b>World</b>", `When document.title is set programmatically to something looking like HTML entities, then read again, it should have the exact same value, no deentification should take place` ); }); specify("setting_and_getting_textContent", () => { const html = `<html><head>\n<title><foo></title></head> <body>Hello<span><span>, </span>world</span>!</body></html>`; const { document } = (new JSDOM(html)).window; assert.strictEqual( document.textContent, null, "textContent of document should be null" ); assert.strictEqual( document.head.textContent, "\n<foo>", "textContent of document.head should be the initial whitespace plus the textContent " + "of the document title" ); assert.strictEqual( document.body.textContent, "Hello, world!", "textContent of document.body should be the concatenation of the textContent values of its child nodes" ); assert.strictEqual( document.createTextNode("<b>World</b>").textContent, "<b>World</b>", "textContent of programmatically created text node should be identical to its nodeValue" ); assert.strictEqual( document.createComment("<b>World</b>").textContent, "<b>World</b>", "textContent of programmatically created comment node should be identical to its nodeValue" ); const frag = document.createDocumentFragment(); frag.appendChild(document.createTextNode("<foo><b></b>")); frag.appendChild(document.createElement("div")).appendChild(document.createTextNode("<foo><b></b>")); assert.strictEqual( frag.textContent, "<foo><b></b><foo><b></b>", "textContent of programmatically created document fragment should be the concatenation " + "of the textContent values of its child nodes" ); const div = document.createElement("div"); div.innerHTML = "&lt;b&gt;\nWorld&lt;/b&gt;<span></span><span>" + "<span></span></span><span>&lt;b&gt;World&lt;/b&gt;</span>"; assert.strictEqual( div.textContent, "<b>\nWorld</b><b>World</b>", `textContent of complex programmatically created <div> should be the concatenation of the textContent values of its child nodes` ); }); specify("issues_230_259", () => { const instr = `<html><body style="color: #ffffff; foo: bar"></body></html>`; const dom = new JSDOM(instr); assert.ok(dom.serialize().match(/0: *color/) === null); }); // see: https://github.com/tmpvar/jsdom/issues/262 specify("issue_262", () => { const { document } = (new JSDOM()).window; const a = document.createElement("a"); a.setAttribute("style", "color:blue"); a.style.setProperty("color", "red"); assert.equal(a.outerHTML.match(/style="/g).length, 1, "style attribute must not be serialized twice"); }); // see: https://github.com/tmpvar/jsdom/issues/267 specify("issue_267", () => { const { document } = (new JSDOM()).window; const a = document.createElement("a"); a.style.width = "100%"; assert.ok(a.getAttribute("style").match(/^\s*width\s*:\s*100%\s*;?\s*$/), "style attribute must contain width"); }); // Test inline event handlers set on the body. // TODO this currently fails!? specify.skip("test_body_event_handler_inline", { skipIfBrowser: true, async: true }, t => { // currently skipped in browsers because of an issue: // TODO: https://github.com/tmpvar/jsdom/issues/1379 const html = ` <html> <head> <script> function loader () { window.loader_called = true; } </script> </head> <body onload="loader()"></body> </html>`; const doc = (new JSDOM(html, { runScripts: "dangerously" })).window.document; const window = doc.defaultView; // In JSDOM, listeners registered with addEventListener are called before // "traditional" listeners, so listening for "load" will fire before our // inline listener. This means we have to check the value on the next // tick. window.addEventListener("load", () => { process.nextTick(() => { assert.equal(window.loader_called, true); t.done(); }); }); doc.close(); }); specify("get_element_by_id", () => { const doc = (new JSDOM()).window.document; const el = doc.createElement("div"); el.setAttribute("id", "foo"); assert.equal(doc.getElementById("foo"), null, "Element must not be found until it has been added to the DOM"); doc.body.appendChild(el); assert.equal(doc.getElementById("foo"), el, "Element must be found after being added"); el.id = "bar"; assert.equal(doc.getElementById("foo"), null, "Element must not be found by its previous id"); assert.equal(doc.getElementById("bar"), el, "Element must be found by its new id"); el.setAttribute("id", "baz"); assert.equal(doc.getElementById("bar"), null, "Element must not be found by its previous id"); assert.equal(doc.getElementById("baz"), el, "Element must be found by its new id"); el.getAttributeNode("id").nodeValue = "boo"; assert.equal(doc.getElementById("boo"), el, "Element must be found by its new id"); doc.body.removeChild(el); assert.equal(doc.getElementById(el.id), null, "Element must not be found after it has been removed"); }); specify("get_element_by_id_multi_id", () => { const doc = (new JSDOM()).window.document; const div = doc.createElement("div"); div.setAttribute("id", "foo"); doc.body.appendChild(div); const span = doc.createElement("span"); span.setAttribute("id", "foo"); doc.body.appendChild(span); // now if we remove the second element, we should still find the first doc.body.removeChild(span); assert.equal(doc.getElementById("foo"), div, "Original div#foo must be found after removing invalid span#foo"); }); specify("issue_335_inline_event_handlers", () => { const dom = new JSDOM(`<a onclick="somefunction()">call some function</a>`); const a = dom.window.document.getElementsByTagName("a").item(0); const onclick = a.getAttribute("onclick"); assert.notEqual(onclick, null); assert.equal(onclick, "somefunction()"); assert.ok(dom.serialize().indexOf("onclick") > -1); }); specify("issue_338_internal_nodelist_props", () => { const doc = (new JSDOM()).window.document; const props = Object.keys(doc.body.childNodes); assert.equal(props.length, 0, "Internal properties must not be enumerable"); }); specify("setting_and_getting_script_element_text", () => { const doc = (new JSDOM("<script></script>")).window.document; const script = doc.getElementsByTagName("script")[0]; assert.equal(script.text, ""); script.text = "const x = 3;"; assert.equal(script.text, "const x = 3;"); script.text = "const y = 2;"; assert.equal(script.text, "const y = 2;"); }); specify("issue_361_textarea_value_property", () => { const doc = (new JSDOM(`<html><body><textarea id="mytextarea"></textarea></body></html>`)).window.document; doc.getElementById("mytextarea").value = "<foo>"; assert.equal(doc.getElementById("mytextarea").value, "<foo>"); }); specify("css_classes_should_be_attached_to_dom", () => { const dom = (new JSDOM()).window; assert.notEqual(dom.StyleSheet, undefined); assert.notEqual(dom.MediaList, undefined); assert.notEqual(dom.CSSStyleSheet, undefined); assert.notEqual(dom.CSSRule, undefined); assert.notEqual(dom.CSSStyleRule, undefined); assert.notEqual(dom.CSSMediaRule, undefined); assert.notEqual(dom.CSSImportRule, undefined); assert.notEqual(dom.CSSStyleDeclaration, undefined); }); specify("issue_530_async_load_events", { async: true }, t => { const doc = (new JSDOM("<html><head></head><body></body></html>")).window.document; const window = doc.defaultView; // Add the load event after the document is already created; it shouldn"t // fire until nextTick. The test will fail (with a timeout) if it has // already fired. window.addEventListener("load", () => { assert.ok(true); t.done(); }); }); specify("iframe_contents", () => { const { document } = (new JSDOM("<iframe></iframe>")).window; const iframeDocument = document.querySelector("iframe").contentWindow.document; assert.equal(iframeDocument.documentElement.outerHTML, "<html><head></head><body></body></html>"); assert.ok(iframeDocument.documentElement); assert.ok(iframeDocument.head); assert.ok(iframeDocument.body); }); specify("addmetatohead", () => { const dom = new JSDOM(); const { window } = dom; const meta = window.document.createElement("meta"); window.document.getElementsByTagName("head").item(0).appendChild(meta); const elements = window.document.getElementsByTagName("head").item(0).childNodes; assert.strictEqual(elements.item(elements.length - 1), meta, "last element should be the new meta tag"); assert.ok(dom.serialize().indexOf("<meta>") > -1, "meta should have open tag"); assert.strictEqual( dom.serialize().indexOf("</meta>"), -1, "meta should not be stringified with a closing tag" ); }); // these tests require file system access or they start a http server describe("node specific tests", { skipIfBrowser: true }, () => { specify("ensure_resolution_is_not_thrown_off_by_hrefless_base_tag", { async: true }, t => { const html = `<html><head><base target="whatever"></head><body> <span id="test">hello from html</span> <script src="./files/hello.js"></script></body></html>`; const { window } = new JSDOM(html, { url: toFileUrl(__filename), runScripts: "dangerously", resources: "usable" }); window.doCheck = () => { assert.equal(window.document.getElementById("test").innerHTML, "hello from javascript"); t.done(); }; }); specify("understand_file_protocol", { async: true }, t => { const html = ` <html> <head> </head> <body> <span id="test">hello from html</span> <script type="text/javascript" src="` + toFileUrl("files/hello.js") + `"></script> </body> </html>`; const { window } = new JSDOM(html, { resources: "usable", runScripts: "dangerously" }); window.doCheck = () => { assert.equal( window.document.getElementById("test").innerHTML, "hello from javascript", "resource with file protocol should work" ); t.done(); }; }); specify("jquery_val_on_selects", { async: true }, t => { const { window } = new JSDOM(``, { resources: "usable", runScripts: "dangerously" }); const script = window.document.createElement("script"); script.src = "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"); script.onload = () => { window.$("body").append(`<html><body><select id="foo"><option value="first">f</option> <option value="last">l</option></select></body></html>`); assert.equal( window.document.querySelector("[value='first']").selected, true, "`selected` property should be `true` for first" ); assert.equal( window.document.querySelector("[value='last']").selected, false, "`selected` property should be `false` for last" ); assert.equal(window.$("[value='first']").val(), "first", "`val()` on first <option> should return its value"); assert.equal(window.$("[value='last']").val(), "last", "`val()` on last <option> should return its value"); const f = window.$("#foo"); assert.equal(f.val(), "first", "`val()` on <select> should return first <option>'s value"); window.$("#foo").val("last"); assert.equal( window.document.querySelector("[value='first']").selected, false, "`selected` property should be `false` for first" ); assert.equal( window.document.querySelector("[value='last']").selected, true, "`selected` property should be `true` for last" ); assert.equal(window.$("#foo").val(), "last", "`val()` should return last <option>'s value"); t.done(); }; window.document.body.appendChild(script); }); specify("jquery_attr_mixed_case", { async: true }, t => { const { window } = new JSDOM(``, { resources: "usable", runScripts: "dangerously" }); const script = window.document.createElement("script"); script.src = "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"); script.onload = () => { const $el = window.$(`<div mixedcase="blah"></div>`); assert.equal($el.attr("mixedCase"), "blah"); t.done(); }; window.document.body.appendChild(script); }); specify("Calling show() method in jQuery 1.11.0 (GH-709)", { async: true }, t => { const { window } = new JSDOM(``, { resources: "usable", runScripts: "dangerously" }); const script = window.document.createElement("script"); script.src = "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"); script.onload = () => { const $el = window.$("<div></div>"); assert.doesNotThrow(() => { $el.show(); }); t.done(); }; window.document.body.appendChild(script); }); specify("Calling show() method in jQuery 1.11.0, second case (GH-709)", { async: true }, t => { const { window } = new JSDOM(``, { resources: "usable", runScripts: "dangerously" }); const script = window.document.createElement("script"); script.src = "file:" + path.resolve(__dirname, "../jquery-fixtures/jquery-1.11.0.js"); script.onload = () => { const $el1 = window.$("<div></div>"); const $el2 = window.$("<span></span>"); assert.doesNotThrow(() => { $el1.show(); $el2.show(); }); t.done(); }; window.document.body.appendChild(script); }); }); // describe("node specific tests") });