summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXadillaX <i@2333.moe>2022-01-14 20:17:30 +0800
committerXadillaX <i@2333.moe>2022-01-20 13:45:54 +0800
commit9759695545640381607bf1d3d73733a4a12aec85 (patch)
tree71bf0925d66be32f32ce8c7880b3600e7eaf827f
parent8e413ff374231924aaf96da39315995ffab02b71 (diff)
downloadnode-new-9759695545640381607bf1d3d73733a4a12aec85.tar.gz
lib: modify `DOMException` to pass WPT
PR-URL: https://github.com/nodejs/node/pull/41517 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
-rw-r--r--lib/internal/per_context/domexception.js24
-rw-r--r--test/fixtures/wpt/README.md1
-rw-r--r--test/fixtures/wpt/versions.json6
-rw-r--r--test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constants.any.js51
-rw-r--r--test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js32
-rw-r--r--test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js140
-rw-r--r--test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.js120
-rw-r--r--test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/exceptions.html78
-rw-r--r--test/wpt/status/webidl/ecmascript-binding/es-exceptions.json1
-rw-r--r--test/wpt/test-domexception.js19
10 files changed, 468 insertions, 4 deletions
diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js
index 1ea37f7062..5296e54193 100644
--- a/lib/internal/per_context/domexception.js
+++ b/lib/internal/per_context/domexception.js
@@ -1,11 +1,14 @@
'use strict';
const {
- Error,
+ ErrorCaptureStackTrace,
+ ErrorPrototype,
ObjectDefineProperties,
ObjectDefineProperty,
+ ObjectSetPrototypeOf,
SafeWeakMap,
SafeMap,
+ SafeSet,
SymbolToStringTag,
TypeError,
} = primordials;
@@ -33,6 +36,7 @@ function throwInvalidThisError(Base, type) {
throw err;
}
+let disusedNamesSet;
let internalsMap;
let nameToCodeMap;
let isInitialized = false;
@@ -49,13 +53,21 @@ function ensureInitialized() {
forEachCode((name, codeName, value) => {
nameToCodeMap.set(name, value);
});
+
+ // These were removed from the error names table.
+ // See https://github.com/heycam/webidl/pull/946.
+ disusedNamesSet = new SafeSet()
+ .add('DOMStringSizeError')
+ .add('NoDataAllowedError')
+ .add('ValidationError');
+
isInitialized = true;
}
-class DOMException extends Error {
+class DOMException {
constructor(message = '', name = 'Error') {
ensureInitialized();
- super();
+ ErrorCaptureStackTrace(this);
internalsMap.set(this, {
message: `${message}`,
name: `${name}`
@@ -86,11 +98,17 @@ class DOMException extends Error {
if (internals === undefined) {
throwInvalidThisError(TypeError, 'DOMException');
}
+
+ if (disusedNamesSet.has(internals.name)) {
+ return 0;
+ }
+
const code = nameToCodeMap.get(internals.name);
return code === undefined ? 0 : code;
}
}
+ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);
ObjectDefineProperties(DOMException.prototype, {
[SymbolToStringTag]: { configurable: true, value: 'DOMException' },
name: { enumerable: true, configurable: true },
diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md
index 8cd6c98684..f5d946eff1 100644
--- a/test/fixtures/wpt/README.md
+++ b/test/fixtures/wpt/README.md
@@ -27,6 +27,7 @@ Last update:
- url: https://github.com/web-platform-tests/wpt/tree/77d54aa9e0/url
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/cdd0f03df4/WebCryptoAPI
+- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt
diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json
index 4ad5f68a33..9f81c28c19 100644
--- a/test/fixtures/wpt/versions.json
+++ b/test/fixtures/wpt/versions.json
@@ -66,5 +66,9 @@
"WebCryptoAPI": {
"commit": "cdd0f03df41b222aed098fbbb11c6a3cc500a86b",
"path": "WebCryptoAPI"
+ },
+ "webidl/ecmascript-binding/es-exceptions": {
+ "commit": "a370aad338d6ed743abb4d2c6ae84a7f1058558c",
+ "path": "webidl/ecmascript-binding/es-exceptions"
}
-}
+} \ No newline at end of file
diff --git a/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constants.any.js b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constants.any.js
new file mode 100644
index 0000000000..bb846a494e
--- /dev/null
+++ b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constants.any.js
@@ -0,0 +1,51 @@
+'use strict';
+
+test(function() {
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27732
+ var constants = [
+ "INDEX_SIZE_ERR",
+ "DOMSTRING_SIZE_ERR",
+ "HIERARCHY_REQUEST_ERR",
+ "WRONG_DOCUMENT_ERR",
+ "INVALID_CHARACTER_ERR",
+ "NO_DATA_ALLOWED_ERR",
+ "NO_MODIFICATION_ALLOWED_ERR",
+ "NOT_FOUND_ERR",
+ "NOT_SUPPORTED_ERR",
+ "INUSE_ATTRIBUTE_ERR",
+ "INVALID_STATE_ERR",
+ "SYNTAX_ERR",
+ "INVALID_MODIFICATION_ERR",
+ "NAMESPACE_ERR",
+ "INVALID_ACCESS_ERR",
+ "VALIDATION_ERR",
+ "TYPE_MISMATCH_ERR",
+ "SECURITY_ERR",
+ "NETWORK_ERR",
+ "ABORT_ERR",
+ "URL_MISMATCH_ERR",
+ "QUOTA_EXCEEDED_ERR",
+ "TIMEOUT_ERR",
+ "INVALID_NODE_TYPE_ERR",
+ "DATA_CLONE_ERR"
+ ]
+ var objects = [
+ [DOMException, "DOMException constructor object"],
+ [DOMException.prototype, "DOMException prototype object"]
+ ]
+ constants.forEach(function(name, i) {
+ objects.forEach(function(o) {
+ var object = o[0], description = o[1];
+ test(function() {
+ assert_equals(object[name], i + 1, name)
+ assert_own_property(object, name)
+ var pd = Object.getOwnPropertyDescriptor(object, name)
+ assert_false("get" in pd, "get")
+ assert_false("set" in pd, "set")
+ assert_false(pd.writable, "writable")
+ assert_true(pd.enumerable, "enumerable")
+ assert_false(pd.configurable, "configurable")
+ }, "Constant " + name + " on " + description)
+ })
+ })
+})
diff --git a/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js
new file mode 100644
index 0000000000..a015470cad
--- /dev/null
+++ b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-and-prototype.any.js
@@ -0,0 +1,32 @@
+test(function() {
+ assert_own_property(self, "DOMException", "property of global");
+
+ var desc = Object.getOwnPropertyDescriptor(self, "DOMException");
+ assert_false("get" in desc, "get");
+ assert_false("set" in desc, "set");
+ assert_true(desc.writable, "writable");
+ assert_false(desc.enumerable, "enumerable");
+ assert_true(desc.configurable, "configurable");
+}, "existence and property descriptor of DOMException");
+
+test(function() {
+ assert_own_property(self.DOMException, "prototype", "prototype property");
+
+ var desc = Object.getOwnPropertyDescriptor(self.DOMException, "prototype");
+ assert_false("get" in desc, "get");
+ assert_false("set" in desc, "set");
+ assert_false(desc.writable, "writable");
+ assert_false(desc.enumerable, "enumerable");
+ assert_false(desc.configurable, "configurable");
+}, "existence and property descriptor of DOMException.prototype");
+
+test(function() {
+ assert_own_property(self.DOMException.prototype, "constructor", "property of prototype");
+ var desc = Object.getOwnPropertyDescriptor(self.DOMException.prototype, "constructor");
+ assert_false("get" in desc, "get");
+ assert_false("set" in desc, "set");
+ assert_true(desc.writable, "writable");
+ assert_false(desc.enumerable, "enumerable");
+ assert_true(desc.configurable, "configurable");
+ assert_equals(self.DOMException.prototype.constructor, self.DOMException, "equality with actual constructor");
+}, "existence and property descriptor of DOMException.prototype.constructor");
diff --git a/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js
new file mode 100644
index 0000000000..e9917af228
--- /dev/null
+++ b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.js
@@ -0,0 +1,140 @@
+'use strict';
+
+test(function() {
+ var ex = new DOMException();
+ assert_equals(ex.name, "Error",
+ "Not passing a name should end up with 'Error' as the name");
+ assert_equals(ex.message, "",
+ "Not passing a message should end up with empty string as the message");
+}, 'new DOMException()');
+
+test(function() {
+ var ex = new DOMException();
+ assert_false(ex.hasOwnProperty("name"),
+ "The name property should be inherited");
+ assert_false(ex.hasOwnProperty("message"),
+ "The message property should be inherited");
+}, 'new DOMException(): inherited-ness');
+
+test(function() {
+ var ex = new DOMException(null);
+ assert_equals(ex.name, "Error",
+ "Not passing a name should end up with 'Error' as the name");
+ assert_equals(ex.message, "null",
+ "Passing null as message should end up with stringified 'null' as the message");
+}, 'new DOMException(null)');
+
+test(function() {
+ var ex = new DOMException(undefined);
+ assert_equals(ex.name, "Error",
+ "Not passing a name should end up with 'Error' as the name");
+ assert_equals(ex.message, "",
+ "Not passing a message should end up with empty string as the message");
+}, 'new DOMException(undefined)');
+
+test(function() {
+ var ex = new DOMException(undefined);
+ assert_false(ex.hasOwnProperty("name"),
+ "The name property should be inherited");
+ assert_false(ex.hasOwnProperty("message"),
+ "The message property should be inherited");
+}, 'new DOMException(undefined): inherited-ness');
+
+test(function() {
+ var ex = new DOMException("foo");
+ assert_equals(ex.name, "Error",
+ "Not passing a name should still end up with 'Error' as the name");
+ assert_equals(ex.message, "foo", "Should be using passed-in message");
+}, 'new DOMException("foo")');
+
+test(function() {
+ var ex = new DOMException("foo");
+ assert_false(ex.hasOwnProperty("name"),
+ "The name property should be inherited");
+ assert_false(ex.hasOwnProperty("message"),
+ "The message property should be inherited");
+}, 'new DOMException("foo"): inherited-ness');
+
+test(function() {
+ var ex = new DOMException("bar", undefined);
+ assert_equals(ex.name, "Error",
+ "Passing undefined for name should end up with 'Error' as the name");
+ assert_equals(ex.message, "bar", "Should still be using passed-in message");
+}, 'new DOMException("bar", undefined)');
+
+test(function() {
+ var ex = new DOMException("bar", "NotSupportedError");
+ assert_equals(ex.name, "NotSupportedError", "Should be using the passed-in name");
+ assert_equals(ex.message, "bar", "Should still be using passed-in message");
+ assert_equals(ex.code, DOMException.NOT_SUPPORTED_ERR,
+ "Should have the right exception code");
+}, 'new DOMException("bar", "NotSupportedError")');
+
+test(function() {
+ var ex = new DOMException("bar", "NotSupportedError");
+ assert_false(ex.hasOwnProperty("name"),
+ "The name property should be inherited");
+ assert_false(ex.hasOwnProperty("message"),
+ "The message property should be inherited");
+}, 'new DOMException("bar", "NotSupportedError"): inherited-ness');
+
+test(function() {
+ var ex = new DOMException("bar", "foo");
+ assert_equals(ex.name, "foo", "Should be using the passed-in name");
+ assert_equals(ex.message, "bar", "Should still be using passed-in message");
+ assert_equals(ex.code, 0,
+ "Should have 0 for code for a name not in the exception names table");
+}, 'new DOMException("bar", "foo")');
+
+[
+ {name: "IndexSizeError", code: 1},
+ {name: "HierarchyRequestError", code: 3},
+ {name: "WrongDocumentError", code: 4},
+ {name: "InvalidCharacterError", code: 5},
+ {name: "NoModificationAllowedError", code: 7},
+ {name: "NotFoundError", code: 8},
+ {name: "NotSupportedError", code: 9},
+ {name: "InUseAttributeError", code: 10},
+ {name: "InvalidStateError", code: 11},
+ {name: "SyntaxError", code: 12},
+ {name: "InvalidModificationError", code: 13},
+ {name: "NamespaceError", code: 14},
+ {name: "InvalidAccessError", code: 15},
+ {name: "TypeMismatchError", code: 17},
+ {name: "SecurityError", code: 18},
+ {name: "NetworkError", code: 19},
+ {name: "AbortError", code: 20},
+ {name: "URLMismatchError", code: 21},
+ {name: "QuotaExceededError", code: 22},
+ {name: "TimeoutError", code: 23},
+ {name: "InvalidNodeTypeError", code: 24},
+ {name: "DataCloneError", code: 25},
+
+ // These were removed from the error names table.
+ // See https://github.com/heycam/webidl/pull/946.
+ {name: "DOMStringSizeError", code: 0},
+ {name: "NoDataAllowedError", code: 0},
+ {name: "ValidationError", code: 0},
+
+ // The error names which don't have legacy code values.
+ {name: "EncodingError", code: 0},
+ {name: "NotReadableError", code: 0},
+ {name: "UnknownError", code: 0},
+ {name: "ConstraintError", code: 0},
+ {name: "DataError", code: 0},
+ {name: "TransactionInactiveError", code: 0},
+ {name: "ReadOnlyError", code: 0},
+ {name: "VersionError", code: 0},
+ {name: "OperationError", code: 0},
+ {name: "NotAllowedError", code: 0}
+].forEach(function(test_case) {
+ test(function() {
+ var ex = new DOMException("msg", test_case.name);
+ assert_equals(ex.name, test_case.name,
+ "Should be using the passed-in name");
+ assert_equals(ex.message, "msg",
+ "Should be using the passed-in message");
+ assert_equals(ex.code, test_case.code,
+ "Should have matching legacy code from error names table");
+ },'new DOMexception("msg", "' + test_case.name + '")');
+});
diff --git a/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.js b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.js
new file mode 100644
index 0000000000..cd4e5b6341
--- /dev/null
+++ b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/DOMException-custom-bindings.any.js
@@ -0,0 +1,120 @@
+"use strict";
+
+test(() => {
+ assert_throws_js(TypeError, () => DOMException());
+}, "Cannot construct without new");
+
+test(() => {
+ assert_equals(Object.getPrototypeOf(DOMException.prototype), Error.prototype);
+}, "inherits from Error: prototype-side");
+
+test(() => {
+ assert_equals(Object.getPrototypeOf(DOMException), Function.prototype);
+}, "does not inherit from Error: class-side");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_false(e.hasOwnProperty("message"), "property is not own");
+
+ const propDesc = Object.getOwnPropertyDescriptor(DOMException.prototype, "message");
+ assert_equals(typeof propDesc.get, "function", "property descriptor is a getter");
+ assert_equals(propDesc.set, undefined, "property descriptor is not a setter");
+ assert_true(propDesc.enumerable, "property descriptor enumerable");
+ assert_true(propDesc.configurable, "property descriptor configurable");
+}, "message property descriptor");
+
+test(() => {
+ const getter = Object.getOwnPropertyDescriptor(DOMException.prototype, "message").get;
+
+ assert_throws_js(TypeError, () => getter.apply({}));
+}, "message getter performs brand checks (i.e. is not [LegacyLenientThis])");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_false(e.hasOwnProperty("name"), "property is not own");
+
+ const propDesc = Object.getOwnPropertyDescriptor(DOMException.prototype, "name");
+ assert_equals(typeof propDesc.get, "function", "property descriptor is a getter");
+ assert_equals(propDesc.set, undefined, "property descriptor is not a setter");
+ assert_true(propDesc.enumerable, "property descriptor enumerable");
+ assert_true(propDesc.configurable, "property descriptor configurable");
+}, "name property descriptor");
+
+test(() => {
+ const getter = Object.getOwnPropertyDescriptor(DOMException.prototype, "name").get;
+
+ assert_throws_js(TypeError, () => getter.apply({}));
+}, "name getter performs brand checks (i.e. is not [LegacyLenientThis])");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_false(e.hasOwnProperty("code"), "property is not own");
+
+ const propDesc = Object.getOwnPropertyDescriptor(DOMException.prototype, "code");
+ assert_equals(typeof propDesc.get, "function", "property descriptor is a getter");
+ assert_equals(propDesc.set, undefined, "property descriptor is not a setter");
+ assert_true(propDesc.enumerable, "property descriptor enumerable");
+ assert_true(propDesc.configurable, "property descriptor configurable");
+}, "code property descriptor");
+
+test(() => {
+ const getter = Object.getOwnPropertyDescriptor(DOMException.prototype, "code").get;
+
+ assert_throws_js(TypeError, () => getter.apply({}));
+}, "code getter performs brand checks (i.e. is not [LegacyLenientThis])");
+
+test(() => {
+ const e = new DOMException("message", "InvalidCharacterError");
+ assert_equals(e.code, 5, "Initially the code is set to 5");
+
+ Object.defineProperty(e, "name", {
+ value: "WrongDocumentError"
+ });
+
+ assert_equals(e.code, 5, "The code is still set to 5");
+}, "code property is not affected by shadowing the name property");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_equals(Object.prototype.toString.call(e), "[object DOMException]");
+}, "Object.prototype.toString behavior is like other interfaces");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_false(e.hasOwnProperty("toString"), "toString must not exist on the instance");
+ assert_false(DOMException.prototype.hasOwnProperty("toString"), "toString must not exist on DOMException.prototype");
+ assert_equals(typeof e.toString, "function", "toString must still exist (via Error.prototype)");
+}, "Inherits its toString() from Error.prototype");
+
+test(() => {
+ const e = new DOMException("message", "name");
+ assert_equals(e.toString(), "name: message",
+ "The default Error.prototype.toString() behavior must work on supplied name and message");
+
+ Object.defineProperty(e, "name", { value: "new name" });
+ Object.defineProperty(e, "message", { value: "new message" });
+ assert_equals(e.toString(), "new name: new message",
+ "The default Error.prototype.toString() behavior must work on shadowed names and messages");
+}, "toString() behavior from Error.prototype applies as expected");
+
+test(() => {
+ assert_throws_js(TypeError, () => DOMException.prototype.toString());
+}, "DOMException.prototype.toString() applied to DOMException.prototype throws because of name/message brand checks");
+
+test(() => {
+ let stackOnNormalErrors;
+ try {
+ throw new Error("normal error");
+ } catch (e) {
+ stackOnNormalErrors = e.stack;
+ }
+
+ let stackOnDOMException;
+ try {
+ throw new DOMException("message", "name");
+ } catch (e) {
+ stackOnDOMException = e.stack;
+ }
+
+ assert_equals(typeof stackOnDOMException, typeof stackOnNormalErrors, "The typeof values must match");
+}, "If the implementation has a stack property on normal errors, it also does on DOMExceptions");
diff --git a/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/exceptions.html b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/exceptions.html
new file mode 100644
index 0000000000..d26c662669
--- /dev/null
+++ b/test/fixtures/wpt/webidl/ecmascript-binding/es-exceptions/exceptions.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>DOMException-throwing tests</title>
+<link rel=author title="Aryeh Gregor" href=ayg@aryeh.name>
+<div id=log></div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+/**
+ * This file just picks one case where browsers are supposed to throw an
+ * exception, and tests the heck out of whether it meets the spec. In the
+ * future, all these checks should be in assert_throws_dom(), but we don't want
+ * every browser failing every assert_throws_dom() check until they fix every
+ * single bug in their exception-throwing.
+ *
+ * We don't go out of our way to test everything that's already tested by
+ * interfaces.html, like whether all constants are present on the object, but
+ * some things are duplicated.
+ */
+setup({explicit_done: true});
+
+function testException(exception, global, desc) {
+ test(function() {
+ assert_equals(global.Object.getPrototypeOf(exception),
+ global.DOMException.prototype);
+ }, desc + "Object.getPrototypeOf(exception) === DOMException.prototype");
+
+
+ test(function() {
+ assert_false(exception.hasOwnProperty("name"));
+ }, desc + "exception.hasOwnProperty(\"name\")");
+ test(function() {
+ assert_false(exception.hasOwnProperty("message"));
+ }, desc + "exception.hasOwnProperty(\"message\")");
+
+ test(function() {
+ assert_equals(exception.name, "HierarchyRequestError");
+ }, desc + "exception.name === \"HierarchyRequestError\"");
+
+ test(function() {
+ assert_equals(exception.code, global.DOMException.HIERARCHY_REQUEST_ERR);
+ }, desc + "exception.code === DOMException.HIERARCHY_REQUEST_ERR");
+
+ test(function() {
+ assert_equals(global.Object.prototype.toString.call(exception),
+ "[object DOMException]");
+ }, desc + "Object.prototype.toString.call(exception) === \"[object DOMException]\"");
+}
+
+
+// Test in current window
+var exception = null;
+try {
+ // This should throw a HierarchyRequestError in every browser since the
+ // Stone Age, so we're really only testing exception-throwing details.
+ document.documentElement.appendChild(document);
+} catch(e) {
+ exception = e;
+}
+testException(exception, window, "");
+
+// Test in iframe
+var iframe = document.createElement("iframe");
+iframe.src = "about:blank";
+iframe.onload = function() {
+ var exception = null;
+ try {
+ iframe.contentDocument.documentElement.appendChild(iframe.contentDocument);
+ } catch(e) {
+ exception = e;
+ }
+ testException(exception, iframe.contentWindow, "In iframe: ");
+
+ document.body.removeChild(iframe);
+ done();
+};
+document.body.appendChild(iframe);
+</script>
diff --git a/test/wpt/status/webidl/ecmascript-binding/es-exceptions.json b/test/wpt/status/webidl/ecmascript-binding/es-exceptions.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/test/wpt/status/webidl/ecmascript-binding/es-exceptions.json
@@ -0,0 +1 @@
+{}
diff --git a/test/wpt/test-domexception.js b/test/wpt/test-domexception.js
new file mode 100644
index 0000000000..09018a25ac
--- /dev/null
+++ b/test/wpt/test-domexception.js
@@ -0,0 +1,19 @@
+'use strict';
+
+require('../common');
+const { WPTRunner } = require('../common/wpt');
+
+const runner = new WPTRunner('webidl/ecmascript-binding/es-exceptions');
+
+runner.setFlags(['--expose-internals']);
+runner.setInitScript(`
+ const { internalBinding } = require('internal/test/binding');
+ const { DOMException } = internalBinding('messaging');
+ Object.defineProperty(global, 'DOMException', {
+ writable: true,
+ configurable: true,
+ value: DOMException,
+ });
+`);
+
+runner.runJsTests();