Merge pull request #523 from stack3mpty/fix/missing-event-constructors-521-522
fix(js): add global PromiseRejectionEvent and StorageEvent constructors
This commit is contained in:
2 files changed
+138
-1
No files matched your search
Vendored
+43
-1
@@ -2613,6 +2613,13 @@ class Document extends Node {
|
|||||||
// returned a generic Event for every type, which broke libraries that call
|
// returned a generic Event for every type, which broke libraries that call
|
||||||
// createEvent('CustomEvent').initCustomEvent(...) — see issue #41.
|
// createEvent('CustomEvent').initCustomEvent(...) — see issue #41.
|
||||||
createEvent(type) {
|
createEvent(type) {
|
||||||
|
const normalized = String(type || '').toLowerCase();
|
||||||
|
if (normalized === 'promiserejectionevent') {
|
||||||
|
throw new DOMException(
|
||||||
|
"The provided event type ('PromiseRejectionEvent') is invalid",
|
||||||
|
'NotSupportedError'
|
||||||
|
);
|
||||||
|
}
|
||||||
const map = {
|
const map = {
|
||||||
'customevent': CustomEvent, 'customevents': CustomEvent,
|
'customevent': CustomEvent, 'customevents': CustomEvent,
|
||||||
'mouseevent': MouseEvent, 'mouseevents': MouseEvent,
|
'mouseevent': MouseEvent, 'mouseevents': MouseEvent,
|
||||||
@@ -2627,8 +2634,9 @@ class Document extends Node {
|
|||||||
'popstateevent': PopStateEvent,
|
'popstateevent': PopStateEvent,
|
||||||
'animationevent': AnimationEvent,
|
'animationevent': AnimationEvent,
|
||||||
'transitionevent': TransitionEvent,
|
'transitionevent': TransitionEvent,
|
||||||
|
'storageevent': StorageEvent,
|
||||||
};
|
};
|
||||||
const Cls = map[String(type || '').toLowerCase()] || Event;
|
const Cls = map[normalized] || Event;
|
||||||
return new Cls('');
|
return new Cls('');
|
||||||
}
|
}
|
||||||
createRange() { return new Range(); }
|
createRange() { return new Range(); }
|
||||||
@@ -5007,6 +5015,40 @@ globalThis.ToggleEvent = class ToggleEvent extends Event {
|
|||||||
};
|
};
|
||||||
_markNative(globalThis.ToggleEvent);
|
_markNative(globalThis.ToggleEvent);
|
||||||
|
|
||||||
|
globalThis.PromiseRejectionEvent = class PromiseRejectionEvent extends Event {
|
||||||
|
constructor(type, init) {
|
||||||
|
if (arguments.length < 2 || init == null || !('promise' in Object(init))) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Failed to construct 'PromiseRejectionEvent': required member promise is undefined."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
super(type, init);
|
||||||
|
this.promise = init.promise;
|
||||||
|
this.reason = init.reason;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_markNative(globalThis.PromiseRejectionEvent);
|
||||||
|
|
||||||
|
globalThis.StorageEvent = class StorageEvent extends Event {
|
||||||
|
constructor(type, init = {}) {
|
||||||
|
super(type, init);
|
||||||
|
this.key = init.key !== undefined ? init.key : null;
|
||||||
|
this.oldValue = init.oldValue !== undefined ? init.oldValue : null;
|
||||||
|
this.newValue = init.newValue !== undefined ? init.newValue : null;
|
||||||
|
this.url = init.url || "";
|
||||||
|
this.storageArea = init.storageArea || null;
|
||||||
|
}
|
||||||
|
initStorageEvent(type, bubbles, cancelable, key, oldValue, newValue, url, storageArea) {
|
||||||
|
this.initEvent(type, bubbles, cancelable);
|
||||||
|
this.key = key !== undefined ? key : null;
|
||||||
|
this.oldValue = oldValue !== undefined ? oldValue : null;
|
||||||
|
this.newValue = newValue !== undefined ? newValue : null;
|
||||||
|
this.url = url || "";
|
||||||
|
this.storageArea = storageArea || null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_markNative(globalThis.StorageEvent);
|
||||||
|
|
||||||
// AbortController / AbortSignal. AbortSignal is a real constructor with a
|
// AbortController / AbortSignal. AbortSignal is a real constructor with a
|
||||||
// prototype, so feature-detection and `AbortSignal.prototype` access work. It
|
// prototype, so feature-detection and `AbortSignal.prototype` access work. It
|
||||||
// carries aborted/reason, supports throwIfAborted(), and fires "abort" to
|
// carries aborted/reason, supports throwIfAborted(), and fires "abort" to
|
||||||
|
|||||||
@@ -3085,6 +3085,101 @@ mod tests {
|
|||||||
assert_eq!(kind, serde_json::json!(true));
|
assert_eq!(kind, serde_json::json!(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_promise_rejection_event_requires_promise() {
|
||||||
|
let mut rt = setup_runtime("<html><body></body></html>");
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"(() => {
|
||||||
|
const promise = Promise.resolve(1);
|
||||||
|
const event = new PromiseRejectionEvent('unhandledrejection', {
|
||||||
|
promise,
|
||||||
|
reason: 'failed'
|
||||||
|
});
|
||||||
|
let missingPromiseThrows = false;
|
||||||
|
try {
|
||||||
|
new PromiseRejectionEvent('unhandledrejection');
|
||||||
|
} catch (error) {
|
||||||
|
missingPromiseThrows = error instanceof TypeError;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
event instanceof Event,
|
||||||
|
event.promise === promise,
|
||||||
|
event.reason === 'failed',
|
||||||
|
missingPromiseThrows
|
||||||
|
];
|
||||||
|
})()"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(result, serde_json::json!([true, true, true, true]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_event_rejects_promise_rejection_event() {
|
||||||
|
let mut rt = setup_runtime("<html><body></body></html>");
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"(() => {
|
||||||
|
try {
|
||||||
|
document.createEvent('PromiseRejectionEvent');
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
return [error.name, error instanceof DOMException];
|
||||||
|
}
|
||||||
|
})()"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!(["NotSupportedError", true])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_event_constructor_and_legacy_factory() {
|
||||||
|
let mut rt = setup_runtime("<html><body></body></html>");
|
||||||
|
let result = rt
|
||||||
|
.evaluate(
|
||||||
|
r#"(() => {
|
||||||
|
const event = new StorageEvent('storage', {
|
||||||
|
key: 'theme',
|
||||||
|
oldValue: 'light',
|
||||||
|
newValue: 'dark',
|
||||||
|
url: 'https://example.test/'
|
||||||
|
});
|
||||||
|
const legacy = document.createEvent('StorageEvent');
|
||||||
|
legacy.initStorageEvent(
|
||||||
|
'storage', false, false, 'count', '1', '2',
|
||||||
|
'https://example.test/', null
|
||||||
|
);
|
||||||
|
return [
|
||||||
|
event instanceof Event,
|
||||||
|
event.key,
|
||||||
|
event.oldValue,
|
||||||
|
event.newValue,
|
||||||
|
event.url,
|
||||||
|
legacy instanceof StorageEvent,
|
||||||
|
legacy.key,
|
||||||
|
legacy.newValue
|
||||||
|
];
|
||||||
|
})()"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
serde_json::json!([
|
||||||
|
true,
|
||||||
|
"theme",
|
||||||
|
"light",
|
||||||
|
"dark",
|
||||||
|
"https://example.test/",
|
||||||
|
true,
|
||||||
|
"count",
|
||||||
|
"2"
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_html_to_markdown_headings() {
|
fn test_html_to_markdown_headings() {
|
||||||
let mut rt = setup_runtime("<html><body><h1>Title</h1><h2>Sub</h2><p>Body</p></body></html>");
|
let mut rt = setup_runtime("<html><body><h1>Title</h1><h2>Sub</h2><p>Body</p></body></html>");
|
||||||
|
|||||||
Reference in new issue
Block a user