Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you can, then you've almost certainly found a bug in or missing feature of js

Almost all of our relevant functionality is covered in either the [DOM Living Standard](http://dom.spec.whatwg.org/) or the [HTML Living Standard](http://www.whatwg.org/specs/web-apps/current-work/). There are various obsolete W3C specs ("DOM Level 2" etc.) that were never really implemented in browsers, and there is also the "DOM Level 4" W3C fork of the WHATWG DOM Living Standard. But we try to stick to the two main WHATWG specs for jsdom these days.

Other specs might pop up from time to time, especially in regard to CSS stuff. In general Mozilla's Servo project provides [good guidance on relavant places to look](https://github.com/servo/servo/wiki/Relevant-spec-links).
Other specs might pop up from time to time, especially in regard to CSS stuff. In general Mozilla's Servo project provides [good guidance on relavant places to look](https://github.com/servo/servo/wiki/Relevant-spec-links). [platform.html5.org](https://platform.html5.org/) is also pretty comprehensive.

Once you have that nailed down, you'll want to ask:

Expand Down Expand Up @@ -68,13 +68,11 @@ So e.g. use `npm test -- -s console` to run the console-related tests.

To import a test from w3c/web-platform-tests, add the appropriate line to `test/web-platform-tests/index.js`. This framework is still in its early days, so feel free to open an issue if it's not working quite like you expect.

If you're writing a bunch of new tests for a feature, and those tests don't exist in w3c/web-platform-tests, you can do one of two things. The most noble course of action is to submit a pull request to web-platform-tests, get it accepted and merged, then update jsdom to run those tests. That way, all existing browsers will run the test too, improving interoperability for everyone!
If you're writing a bunch of new tests for a feature, and those tests don't exist in w3c/web-platform-tests, you'll need to write your own. The best way to do this is to add new web platform tests into the `test/web-platform-tests/to-upstream` directory. Follow the [web platform test contribution guidelines](http://testthewebforward.org/docs/writing-tests.html) for the format, or just try to match what you see nearby.

Alternately, you can write some tests just for jsdom. The older tests, being a mix of auto-generated and organically-grown, have gotten pretty hairy over time. But for new tests we try to follow a clean and uniform style, which you can see in files like [`test/living-dom/attributes.js`](https://github.com/tmpvar/jsdom/blob/master/test/living-dom/attributes.js) or [`test/window/history.js`](https://github.com/tmpvar/jsdom/blob/master/test/window/history.js). Do your best to follow that.
Alternately, you can write some tests just for jsdom. This should be avoided when possible, but is necessary for cases where e.g. you are testing a jsdom specific API. Try to follow the style from newer files, like [`test/jsdom/node-location.js`](https://github.com/tmpvar/jsdom/blob/master/test/jsdom/node-location.js).

If you're just adding a simple fix to existing functionality, you can add an appropriate test to the bottom of the relevant test file, e.g. in `test/level2/html.js`. This should generally be avoided, however, as these legacy imported tests from the old days are messy and hard to maintain.

Note for future reference: to update the submodules used for the web-platform-tests use the command `git submodule update --recursive --remote`.
(Note for future reference for the maintainers: to update the submodules used for the web-platform-tests use the command `git submodule update --recursive --remote`.)

### Running tests in the browser

Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@
"browserify": "^11.1.0",
"colors": "^1.0.3",
"eslint": "^1.5.1",
"eslint-plugin-html": "^1.0.4",
"fs-readdir-recursive": "^1.0.0",
"http-server": "^0.8.0",
"jscs": "^1.12.0",
"nodeunit": "0.9.1",
"optimist": "0.6.1",
"portfinder": "0.4.0",
"q": "^1.2.0",
"recursive-readdir": "^1.2.1",
"selenium-standalone": "^4.6.1",
"st": "^0.5.5",
"wd": "0.3.12",
Expand All @@ -61,7 +62,7 @@
"pretest": "npm run convert-idl && git submodule update --init --recursive",
"test": "node ./test/runner",
"test-browser": "node ./test/browser-runner",
"lint": "jscs lib/ && jscs test/ && eslint . && eslint --no-ignore lib/jsdom/living/generated/**/*-impl.js",
"lint": "jscs lib/ && jscs test/ && eslint . && eslint --no-ignore lib/jsdom/living/generated/**/*-impl.js && eslint test/web-platform-tests/to-upstream --ext .html",
"update-authors": "git log --format=\"%aN <%aE>\" | sort -f | uniq > AUTHORS.txt",
"benchmark": "node ./benchmark/runner",
"benchmark-browser": "node ./benchmark/runner --bundle",
Expand Down
8 changes: 6 additions & 2 deletions scripts/webidl/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const fs = require("fs");
const path = require("path");
const Q = require("q");
const recursive = require("recursive-readdir");
const readdirRecursive = require("fs-readdir-recursive");
const webidl2js = require("webidl2js");

const UTIL_PATH = "lib/jsdom/living/generated/util.js";
Expand Down Expand Up @@ -30,7 +30,7 @@ function doConversion(inputPath) {
.then(inputStat => {
isDir = inputStat.isDirectory();
if (isDir) {
return Q.nfcall(recursive, inputPath, ["*.js"]);
return readdirRecursive(inputPath, onlyIDL).map(relativePath => path.resolve(inputPath, relativePath));
}

return [inputPath]; // get dir name
Expand All @@ -42,4 +42,8 @@ function doConversion(inputPath) {
});
}

function onlyIDL(filePath) {
return path.extname(filePath) === ".idl";
}

doConversion("lib/jsdom/living/generated/events").done();
117 changes: 0 additions & 117 deletions test/living-dom/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,97 +2,6 @@
const load = require("../util").load(__dirname);
const jsdom = require("../..");

exports["setAttribute should change the first attribute, irrespective of namespace"] = t => {
const doc = load("minimal");
const body = doc.getElementsByTagName("body")[0];
t.ok(body, "body is not null");

// setAttribute changes the first attribute, irrespective of namespace.
body.setAttributeNS("foo", "x", "first");
body.setAttributeNS("foo2", "x", "second");
body.setAttribute("x", "changed");

// Check that the attribues are as we expect.
t.equal(body.attributes.length, 2, "two attributes");
t.equal(body.getAttribute("x"), "changed");
t.equal(body.getAttributeNS("foo", "x"), "changed");
t.equal(body.getAttributeNS("foo2", "x"), "second");

t.done();
};

exports["removeAttribute should remove the first attribute, irrespective of namespace when the first attribute is " +
"not in a namespace"] = t => {
const doc = load("minimal");
const body = doc.getElementsByTagName("body")[0];
t.ok(body, "body is not null");

// Set body with attributes for testing.
body.setAttribute("x", "first");
body.setAttributeNS("foo", "x", "second");

// Check that the attributes are as we expect.
t.equal(body.attributes.length, 2, "two attributes");
t.equal(body.getAttribute("x"), "first");
t.equal(body.getAttributeNS(null, "x"), "first");
t.equal(body.getAttributeNS("foo", "x"), "second");

// removeAttribute removes the first attribute with name "x" that
// we set on the element, irrespective of namespace.
body.removeAttribute("x");

// The only attribute remaining should be the second one.
t.equal(body.getAttribute("x"), "second");
t.equal(body.getAttributeNS(null, "x"), null);
t.equal(body.getAttributeNS("foo", "x"), "second");
t.equal(body.attributes.length, 1, "one attribute");

t.done();
};

exports["removeAttribute should remove the first attribute, irrespective of namespace when the first attribute is " +
"in a namespace"] = t => {
const doc = load("minimal");
const body = doc.getElementsByTagName("body")[0];
t.ok(body, "body is not null");

// Set body with attributes for testing.
body.setAttributeNS("foo", "x", "first");
body.setAttributeNS("foo2", "x", "second");

// Check that the attribues are as we expect.
t.equal(body.attributes.length, 2, "two attributes");
t.equal(body.getAttribute("x"), "first");
t.equal(body.getAttributeNS("foo", "x"), "first");
t.equal(body.getAttributeNS("foo2", "x"), "second");

// removeAttribute removes the first attribute with name "x" that
// we set on the element, irrespective of namespace.
body.removeAttribute("x");

// The only attribute remaining should be the second one.
t.equal(body.getAttribute("x"), "second");
t.equal(body.getAttributeNS("foo", "x"), null);
t.equal(body.getAttributeNS("foo2", "x"), "second");
t.equal(body.attributes.length, 1, "one attribute");

t.done();
};

exports["hasAttribute should check for attribute presence, irrespective of namespace"] = t => {
const doc = load("minimal");
const body = doc.getElementsByTagName("body")[0];
t.ok(body, "body is not null");

// Set body with attributes for testing.
body.setAttributeNS("foo", "x", "first");

// Checks for attribute presence, irrespective of namespace.
t.ok(body.hasAttribute("x"));

t.done();
};

exports["an attribute set by setAttributeNS should be accessible as a field on the `attributes` field of an Element"] =
t => {
const doc = load("minimal");
Expand Down Expand Up @@ -222,29 +131,3 @@ exports["setting an attribute should not overwrite the fields of an `NamedNodeMa
t.equal(body.attributes.length, 1, "one attribute");
t.done();
};

exports["hasAttribute should work with all attribute casings"] = t => {
const document = jsdom.jsdom("<span data-e2='2' data-F2='3' id='t'></span>");

t.ok(document.getElementById("t").hasAttribute("data-e2"));
t.ok(document.getElementById("t").hasAttribute("data-E2"));
t.ok(document.getElementById("t").hasAttribute("data-f2"));
t.ok(document.getElementById("t").hasAttribute("data-F2"));

t.done();
};

exports["setAttribute should lowercase before setting"] = t => {
// https://github.com/whatwg/dom/issues/31

const document = jsdom.jsdom();

document.body.setAttribute("FOO", "bar");

t.equal(document.body.getAttribute("foo"), "bar");
t.equal(document.body.getAttribute("FOO"), "bar");
t.equal(document.body.getAttributeNS("", "foo"), "bar");
t.equal(document.body.getAttributeNS("", "FOO"), null);

t.done();
};
1 change: 1 addition & 0 deletions test/runner
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var files = [
"browser/css.js",
"browser/index.js",
"web-platform-tests/index.js",
"web-platform-tests/to-upstream.js",
"misc/domparsing.js"
];

Expand Down
4 changes: 3 additions & 1 deletion test/web-platform-tests/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";
const runWebPlatformTest = require("./run-web-platform-test")(exports);
const path = require("path");
const runWebPlatformTest = require("./run-web-platform-test")(exports, path.resolve(__dirname, "tests"));

[
"dom/nodes/CharacterData-appendData.html",
Expand All @@ -19,6 +20,7 @@ const runWebPlatformTest = require("./run-web-platform-test")(exports);
"dom/nodes/DOMImplementation-hasFeature.html",
"dom/nodes/Element-classlist.html",
"dom/nodes/Element-getElementsByClassName.html",
// "dom/nodes/attributes.html", // TODO Attr: once we fix Attr from scratch, this can be enabled
"dom/nodes/getElementsByClassName-01.htm",
"dom/nodes/getElementsByClassName-02.htm",
"dom/nodes/getElementsByClassName-03.htm",
Expand Down
13 changes: 10 additions & 3 deletions test/web-platform-tests/run-web-platform-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const http = require("http");
const path = require("path");
const fs = require("fs");
const request = require("request");
const st = require("st");
const jsdom = require("../..");
Expand Down Expand Up @@ -55,8 +56,8 @@ function createJsdom(urlPrefix, testPath, t) {
});
}

module.exports = function (exports) {
const staticFileServer = st({ path: path.resolve(__dirname, "tests"), url: "/", passthrough: true });
module.exports = function (exports, testDir) {
const staticFileServer = st({ path: testDir, url: "/", passthrough: true });
const server = http.createServer((req, res) => {
staticFileServer(req, res, () => fallbackToHostedVersion(req, res));
}).listen();
Expand All @@ -69,7 +70,13 @@ module.exports = function (exports) {
};

function fallbackToHostedVersion(req, res) {
// Problem getting it from disk. Let's try the online version!
// If testDir does not contain resources/, we should get the one from web-platform-tests.
if (req.url.startsWith("/resources")) {
fs.createReadStream(path.resolve(__dirname, "tests", req.url.substring(1))).pipe(res);
return;
}

// Otherwise, this is a problem getting it from the disk. Let's try the online version!

const url = new URL(req.url, urlPrefix);
url.protocol = "https";
Expand Down
8 changes: 8 additions & 0 deletions test/web-platform-tests/to-upstream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";
const path = require("path");
const readdirRecursive = require("fs-readdir-recursive");

const testsPath = path.resolve(__dirname, "to-upstream");
const runWebPlatformTest = require("./run-web-platform-test")(exports, testsPath);

readdirRecursive(testsPath).forEach(runWebPlatformTest);
57 changes: 57 additions & 0 deletions test/web-platform-tests/to-upstream/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"plugins": [
"eslint-plugin-html"
],
"env": {
"node": false,
"browser": true
},
"rules": {
"padded-blocks": 0, // we like to add spaces around the main test block
"no-multiple-empty-lines": 0 // some bug with the eslint-plugin-html package makes this spuriously trigger
},
"globals": {
"EventWatcher": false,
"test": false,
"async_test": false,
"promise_test": false,
"promise_rejects": false,
"generate_tests": false,
"setup": false,
"done": false,
"on_event": false,
"step_timeout": false,
"format_value": false,
"assert_true": false,
"assert_false": false,
"assert_equals": false,
"assert_not_equals": false,
"assert_in_array": false,
"assert_object_equals": false,
"assert_array_equals": false,
"assert_approx_equals": false,
"assert_less_than": false,
"assert_greater_than": false,
"assert_between_exclusive": false,
"assert_less_than_equal": false,
"assert_greater_than_equal": false,
"assert_between_inclusive": false,
"assert_regexp_match": false,
"assert_class_string": false,
"assert_exists": false,
"assert_own_property": false,
"assert_not_exists": false,
"assert_inherits": false,
"assert_idl_attribute": false,
"assert_readonly": false,
"assert_throws": false,
"assert_unreached": false,
"assert_any": false,
"fetch_tests_from_worker": false,
"timeout": false,
"add_start_callback": false,
"add_test_state_callback": false,
"add_result_callback": false,
"add_completion_callback": false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Element.prototype.hasAttribute</title>
<link rel=help href="https://dom.spec.whatwg.org/#dom-element-hasattribute">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<span data-e2="2" data-F2="3" id="t"></span>

<script>
"use strict";

test(() => {

const el = document.createElement("p");
el.setAttributeNS("foo", "x", "first");

assert_true(el.hasAttribute("x"));

}, "hasAttribute should check for attribute presence, irrespective of namespace");

test(() => {

const el = document.getElementById("t");

assert_true(el.hasAttribute("data-e2"));
assert_true(el.hasAttribute("data-E2"));
assert_true(el.hasAttribute("data-f2"));
assert_true(el.hasAttribute("data-F2"));

}, "hasAttribute should work with all attribute casings");
</script>
Loading