Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Node.js minimum and dependencies; remove running-in-a-browser support #3542

Merged
merged 3 commits into from
May 2, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
"no-invalid-this": "off",
"require-unicode-regexp": "off",
"prefer-template": "off",
"prefer-object-has-own": "off", // remove this once Node v16 is the minimum
"logical-assignment-operators": "off", // remove this once Node v16 is the minimum
"new-cap": ["error", { "capIsNewExceptions": ["ByteString", "USVString", "DOMString"] }],

// Custom rules
Expand Down
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### Basic info:

- **Node.js version:** <!-- only v14 and above are supported -->
- **jsdom version:** <!-- only v21 and above are supported -->
- **Node.js version:** <!-- only v16 and above are supported -->
- **jsdom version:** <!-- only v22 and above are supported -->

### Minimal reproduction case

Expand Down
25 changes: 3 additions & 22 deletions .github/workflows/jsdom-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissions:

jobs:
lint:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -33,7 +33,7 @@ jobs:
build-with-canvas:
env:
TEST_SUITE: 'node-canvas'
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -50,31 +50,12 @@ jobs:
run: yarn --frozen-lockfile
- name: Run tests
run: yarn add canvas && yarn test --retries 1
build-with-browser:
env:
TEST_SUITE: 'browser'
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Run web browser tests
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Setup HOSTS file for Web Platform Test server
run: ./test/web-platform-tests/tests/wpt make-hosts-file | sudo tee -a /etc/hosts
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Run tests
run: yarn test-browser
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version:
- 14
- 16
- 18
architecture:
Expand Down
18 changes: 1 addition & 17 deletions Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,11 @@ To write such a test that, simply add a file in `test/api/`, following the surro

Although ideally you should not need to worry about this, there are some tests that are for legacy reasons not in the right format; they use Mocha, but really should be web platform tests. We're keeping them around for coverage until we can convert them. If you run `yarn test`, you will get the full test suite, including such old tests.

### Testing against the browser

jsdom has experimental support to run in directly in a browser, in both the main document and in a web worker! So we try run as many tests as possible in browsers too. Currently we only test in Chrome, since it has the same JavaScript features as the Node.js environment we usually develop in. So you'll need Chrome installed on your machine.

The mocha test cases are executed in Chrome using [karma](https://karma-runner.github.io/). Currently, web platform tests are not executed in the browser yet.

**To run all browser tests:** `yarn test-browser`

**To run the karma tests in an iframe:** `yarn test-browser-iframe`

**To run the karma tests in a web worker:** `yarn test-browser-worker`

## Benchmarks

This project cares about performance. There are a number of benchmarks that you can run. If you suspect your contribution has an impact on the performance of existing functionality, make sure you run the benchmarks before and after your change so that you can compare.

You can also run the benchmarks using the native DOM implementation of Chrome. A comparison with jsdom will automatically be made for you. If your new feature is much slower than the alternative DOM implementation, there might be an unexpected bottleneck somewhere in your change.

**To run benchmarks in Node.js:** `yarn benchmark`

**To run benchmarks in the browser:** `yarn benchmark-browser`, then open `benchmark/browser-runner.html` in Chrome (or Chromium) and use the developer console to execute the `run()` function.
**To run benchmarks:** `yarn benchmark`

## Issues

Expand Down
12 changes: 1 addition & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

jsdom is a pure-JavaScript implementation of many web standards, notably the WHATWG [DOM](https://dom.spec.whatwg.org/) and [HTML](https://html.spec.whatwg.org/multipage/) Standards, for use with Node.js. In general, the goal of the project is to emulate enough of a subset of a web browser to be useful for testing and scraping real-world web applications.

The latest versions of jsdom require Node.js v14 or newer. (Versions of jsdom below v20 still work with previous Node.js versions, but are unsupported.)
The latest versions of jsdom require Node.js v16 or newer. (Versions of jsdom below v22 still work with previous Node.js versions, but are unsupported.)

## Basic usage

Expand Down Expand Up @@ -446,16 +446,6 @@ Timers in the jsdom (set by `window.setTimeout()` or `window.setInterval()`) wil

If you want to be sure to shut down a jsdom window, use `window.close()`, which will terminate all running timers (and also remove any event listeners on the window and document).

### Running jsdom inside a web browser

jsdom has some support for being run inside a web browser, using [browserify](https://browserify.org/). That is, inside a web browser, you can use a browserified jsdom to create an entirely self-contained set of plain JavaScript objects which look and act much like the browser's existing DOM objects, while being entirely independent of them. "Virtual DOM", indeed!

jsdom's primary target is still Node.js, and so we use language features that are only present in recent Node.js versions. Thus, older browsers will likely not work. (Even transpilation will not help: we use `Proxy`s extensively throughout the jsdom codebase.)

Notably, jsdom works well inside a web worker. The original contributor, [@lawnsea](https://github.com/lawnsea/), who made this possible, has [published a paper](https://pdfs.semanticscholar.org/47f0/6bb6607a975500a30e9e52d7c9fbc0034e27.pdf) about his project which uses this capability.

Not everything works perfectly when running jsdom inside a web browser. Sometimes that is because of fundamental limitations (such as not having filesystem access), but sometimes it is simply because we haven't spent enough time making the appropriate small tweaks. Bug reports are certainly welcome.

### Debugging the DOM using Chrome DevTools

In Node.js you can debug programs using Chrome DevTools. See the [official documentation](https://nodejs.org/en/docs/inspector/) for how to get started.
Expand Down
18 changes: 0 additions & 18 deletions benchmark/browser-runner.html

This file was deleted.

47 changes: 0 additions & 47 deletions benchmark/browser-runner.js

This file was deleted.

25 changes: 10 additions & 15 deletions benchmark/document-suite.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
"use strict";
const Benchmark = require("benchmark");
const jsdomBenchmark = require("./jsdom-benchmark");

// jsdom might be an empty object if omitted by browserify
const jsdom = require("..");

const nativeDoc = global.document;
const jsdomDoc = jsdom.JSDOM && (new jsdom.JSDOM()).window.document;
const jsdomDoc = (new jsdom.JSDOM()).window.document;

function noop() {
// intentional no-op function
Expand Down Expand Up @@ -71,19 +69,16 @@ module.exports = function documentSuite(optionsArg) {
addBenchmark(suite, benchmark);
}

if (jsdomDoc) {
const newOptions = {

...options,
...benchmarkFunctions(jsdomDoc, options),
jsdomDocumentImplementation: "jsdom"
};
const benchmark = jsdomBenchmark(newOptions);
const newOptions = {
...options,
...benchmarkFunctions(jsdomDoc, options),
jsdomDocumentImplementation: "jsdom"
};
const benchmark = jsdomBenchmark(newOptions);

// extra space in "jsdom " so that it aligns with "native"
benchmark.name = benchmark.name ? benchmark.name + " :: jsdom " : "jsdom ";
addBenchmark(suite, benchmark);
}
// extra space in "jsdom " so that it aligns with "native"
benchmark.name = benchmark.name ? benchmark.name + " :: jsdom " : "jsdom ";
addBenchmark(suite, benchmark);

return suite;
};
2 changes: 1 addition & 1 deletion benchmark/path-to-suites.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = function pathToSuites(benchmarks, paths) {
let suites = benchmarks;

for (const part of parts) {
suites = Object.prototype.hasOwnProperty.call(suites, part) && suites[part];
suites = Object.hasOwn(suites, part) && suites[part];
if (!suites) {
throw Error("Invalid suite: '" + path + "'");
}
Expand Down
23 changes: 0 additions & 23 deletions benchmark/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
const consoleReporter = require("./console-reporter");
const pathToSuites = require("./path-to-suites");
const benchmarks = require(".");
const fs = require("fs");
const path = require("path");
const { toFileUrl } = require("../lib/jsdom/utils");
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");

Expand All @@ -18,28 +15,8 @@ const { argv } = yargs(hideBin(process.argv))
alias: "s",
describe: "suites that you want to run, e.g.: dom/construction/createElement dom/foo"
})
.option("bundle", {
type: "boolean",
describe: "generate the JavaScript bundle required to run benchmarks in a browser"
})
.help();

if (argv.bundle) {
const bundle = require("browserify")({ debug: true });
bundle.require(path.resolve(__dirname, ".."), { expose: "jsdom" });
bundle.require(path.resolve(__dirname, "browser-runner.js"), { expose: "jsdom-browser-runner" });

bundle.bundle()
.pipe(fs.createWriteStream(path.resolve(__dirname, "browser-bundle.js")))
.on("finish", () => {
console.info(
"Open the following page in Chrome to begin benchmarking:",
toFileUrl(path.resolve(__dirname, "browser-runner.html"))
);
});
return;
}

let suitesToRun;
if (argv.suites) {
suitesToRun = pathToSuites(benchmarks, argv.suites);
Expand Down
2 changes: 1 addition & 1 deletion lib/jsdom/browser/Window.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const webIDLConversions = require("webidl-conversions");
const { CSSStyleDeclaration } = require("cssstyle");
const notImplemented = require("./not-implemented");
const { installInterfaces } = require("../living/interfaces");
const { define, mixin, performance } = require("../utils");
const { define, mixin } = require("../utils");
const Element = require("../living/generated/Element");
const EventTarget = require("../living/generated/EventTarget");
const EventHandlerNonNull = require("../living/generated/EventHandlerNonNull");
Expand Down
1 change: 0 additions & 1 deletion lib/jsdom/living/hr-time/Performance-impl.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use strict";

const EventTargetImpl = require("../events/EventTarget-impl").implementation;
const { performance } = require("../../utils");

class PerformanceImpl extends EventTargetImpl {
constructor(globalObject, args, privateData) {
Expand Down
2 changes: 1 addition & 1 deletion lib/jsdom/living/nodes/HTMLInputElement-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ class HTMLInputElementImpl extends HTMLElementImpl {

get files() {
if (this.type === "file") {
this[filesSymbol] = this[filesSymbol] || FileList.createImpl(this._globalObject);
this[filesSymbol] ||= FileList.createImpl(this._globalObject);
} else {
this[filesSymbol] = null;
}
Expand Down