Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: capricorn86/happy-dom
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v13.6.2
Choose a base ref
...
head repository: capricorn86/happy-dom
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4c808b62f8dcfb5c85d4ac4e94b8e2ba58195e86
Choose a head ref
  • 11 commits
  • 61 files changed
  • 2 contributors

Commits on Feb 26, 2024

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    c127bc6 View commit details
  2. fix: [#1101] Fixes problem related to form not being submitted when c…

    …licking a button with a "form" attribute set to the form's ID
    capricorn86 committed Feb 26, 2024
    Copy the full SHA
    d4e1ad9 View commit details
  3. feat: [#1101] Adds support for navigating and submitting form data wh…

    …en HTMLFormElement.submit() or HTMLFormElement.requestSubmit() is triggered
    capricorn86 committed Feb 26, 2024
    Copy the full SHA
    10f9ee5 View commit details

Commits on Feb 27, 2024

  1. Copy the full SHA
    deb3da4 View commit details
  2. Copy the full SHA
    7c48e4e View commit details

Commits on Feb 29, 2024

  1. Copy the full SHA
    6e64250 View commit details

Commits on Mar 5, 2024

  1. Copy the full SHA
    a215170 View commit details

Commits on Mar 6, 2024

  1. Copy the full SHA
    5403e18 View commit details

Commits on Mar 7, 2024

  1. Copy the full SHA
    0ea8cec View commit details

Commits on Mar 8, 2024

  1. feat: [#1101] Returns matched Element type in *.querySelector(), *.qu…

    …erySelectorAll(), *.getElementsByTagName(), *.getElementsByTagNameNS(), Document.createElement() and Document.createElementNS()
    capricorn86 committed Mar 8, 2024
    Copy the full SHA
    3aa0ea9 View commit details
  2. Merge pull request #1289 from capricorn86/1101-buttonform=id-not-trig…

    …gering-form-submit
    
    1101 buttonform=id not triggering form submit
    capricorn86 authored Mar 8, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4c808b6 View commit details
Showing with 2,679 additions and 392 deletions.
  1. +3 −3 package-lock.json
  2. +12 −2 packages/happy-dom/src/browser/BrowserFrame.ts
  3. +12 −7 packages/happy-dom/src/browser/detached-browser/DetachedBrowserFrame.ts
  4. +7 −0 packages/happy-dom/src/browser/utilities/BrowserFrameFactory.ts
  5. +28 −18 packages/happy-dom/src/browser/utilities/BrowserFrameNavigator.ts
  6. +2 −2 packages/happy-dom/src/browser/utilities/BrowserFrameURL.ts
  7. +148 −0 packages/happy-dom/src/config/IHTMLElementTagNameMap.ts
  8. +67 −0 packages/happy-dom/src/config/ISVGElementTagNameMap.ts
  9. +25 −0 packages/happy-dom/src/event/events/HashChangeEvent.ts
  10. +6 −0 packages/happy-dom/src/event/events/IHashChangeEventInit.ts
  11. +3 −3 packages/happy-dom/src/fetch/Fetch.ts
  12. +2 −2 packages/happy-dom/src/fetch/Request.ts
  13. +3 −3 packages/happy-dom/src/fetch/SyncFetch.ts
  14. +4 −5 packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts
  15. +2 −1 packages/happy-dom/src/fetch/utilities/FetchRequestReferrerUtility.ts
  16. +2 −2 packages/happy-dom/src/form-data/FormData.ts
  17. +9 −1 packages/happy-dom/src/index.ts
  18. +257 −0 packages/happy-dom/src/location/Location.ts
  19. +1 −0 packages/happy-dom/src/named-node-map/NamedNodeMap.ts
  20. +59 −1 packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
  21. +14 −0 packages/happy-dom/src/nodes/document-fragment/IDocumentFragment.ts
  22. +224 −17 packages/happy-dom/src/nodes/document/Document.ts
  23. +33 −11 packages/happy-dom/src/nodes/document/IDocument.ts
  24. +120 −1 packages/happy-dom/src/nodes/element/Element.ts
  25. +1 −0 packages/happy-dom/src/nodes/element/HTMLCollection.ts
  26. +2 −0 packages/happy-dom/src/nodes/html-anchor-element/HTMLAnchorElement.ts
  27. +107 −5 packages/happy-dom/src/nodes/html-button-element/HTMLButtonElement.ts
  28. +5 −1 packages/happy-dom/src/nodes/html-button-element/IHTMLButtonElement.ts
  29. +1 −0 packages/happy-dom/src/nodes/html-form-element/HTMLFormControlsCollection.ts
  30. +111 −5 packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts
  31. +16 −0 packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElement.ts
  32. +5 −1 packages/happy-dom/src/nodes/html-iframe-element/HTMLIFrameElementPageLoader.ts
  33. +1 −0 packages/happy-dom/src/nodes/html-iframe-element/IHTMLIFrameElement.ts
  34. +111 −57 packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
  35. +2 −0 packages/happy-dom/src/nodes/html-input-element/IHTMLInputElement.ts
  36. +1 −0 packages/happy-dom/src/nodes/html-label-element/HTMLLabelElement.ts
  37. +1 −1 packages/happy-dom/src/nodes/html-link-element/HTMLLinkElementStyleSheetLoader.ts
  38. +1 −1 packages/happy-dom/src/nodes/html-script-element/HTMLScriptElementScriptLoader.ts
  39. +1 −2 packages/happy-dom/src/nodes/html-select-element/HTMLSelectElement.ts
  40. +1 −2 packages/happy-dom/src/nodes/node/Node.ts
  41. +36 −0 packages/happy-dom/src/nodes/parent-node/IParentNode.ts
  42. +1 −0 packages/happy-dom/src/nodes/parent-node/ParentNodeUtility.ts
  43. +76 −2 packages/happy-dom/src/query-selector/QuerySelector.ts
  44. +0 −79 packages/happy-dom/src/url/Location.ts
  45. +11 −4 packages/happy-dom/src/window/BrowserWindow.ts
  46. +1 −1 packages/happy-dom/src/window/CrossOriginBrowserWindow.ts
  47. +3 −2 packages/happy-dom/src/window/IBrowserWindow.ts
  48. +1 −1 packages/happy-dom/src/window/ICrossOriginBrowserWindow.ts
  49. +4 −2 packages/happy-dom/src/window/WindowPageOpenUtility.ts
  50. +1 −1 packages/happy-dom/test/browser/BrowserFrame.test.ts
  51. +1 −1 packages/happy-dom/test/browser/detached-browser/DetachedBrowserFrame.test.ts
  52. +1 −1 packages/happy-dom/test/css/declaration/CSSStyleDeclaration.test.ts
  53. +1 −1 packages/happy-dom/test/css/declaration/element-style/CSSStyleDeclarationElementStyle.test.ts
  54. +33 −11 packages/happy-dom/test/form-data/FormData.test.ts
  55. +272 −7 packages/happy-dom/test/location/Location.test.ts
  56. +0 −74 packages/happy-dom/test/location/URL.test.ts
  57. +10 −10 packages/happy-dom/test/nodes/document/Document.test.ts
  58. +140 −6 packages/happy-dom/test/nodes/html-button-element/HTMLButtonElement.test.ts
  59. +504 −3 packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts
  60. +168 −27 packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
  61. +5 −5 packages/happy-dom/test/nodes/html-slot-element/HTMLSlotElement.test.ts
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions packages/happy-dom/src/browser/BrowserFrame.ts
Original file line number Diff line number Diff line change
@@ -152,7 +152,12 @@ export default class BrowserFrame implements IBrowserFrame {
* @returns Response.
*/
public goto(url: string, options?: IGoToOptions): Promise<IResponse | null> {
return BrowserFrameNavigator.goto(BrowserWindow, this, url, options);
return BrowserFrameNavigator.navigate({
windowClass: BrowserWindow,
frame: this,
url: url,
goToOptions: options
});
}

/**
@@ -162,6 +167,11 @@ export default class BrowserFrame implements IBrowserFrame {
* @returns Response.
*/
public reload(options: IReloadOptions): Promise<IResponse | null> {
return BrowserFrameNavigator.goto(BrowserWindow, this, this.url, options);
return BrowserFrameNavigator.navigate({
windowClass: BrowserWindow,
frame: this,
url: this.url,
goToOptions: options
});
}
}
Original file line number Diff line number Diff line change
@@ -167,7 +167,12 @@ export default class DetachedBrowserFrame implements IBrowserFrame {
* @returns Response.
*/
public goto(url: string, options?: IGoToOptions): Promise<IResponse | null> {
return BrowserFrameNavigator.goto(this.page.context.browser.windowClass, this, url, options);
return BrowserFrameNavigator.navigate({
windowClass: this.page.context.browser.windowClass,
frame: this,
url: url,
goToOptions: options
});
}

/**
@@ -177,11 +182,11 @@ export default class DetachedBrowserFrame implements IBrowserFrame {
* @returns Response.
*/
public reload(options: IReloadOptions): Promise<IResponse | null> {
return BrowserFrameNavigator.goto(
this.page.context.browser.windowClass,
this,
this.url,
options
);
return BrowserFrameNavigator.navigate({
windowClass: this.page.context.browser.windowClass,
frame: this,
url: this.url,
goToOptions: options
});
}
}
Original file line number Diff line number Diff line change
@@ -63,11 +63,18 @@ export default class BrowserFrameFactory {
return frame[PropertySymbol.asyncTaskManager].destroy().then(() => {
frame[PropertySymbol.exceptionObserver]?.disconnect();
if (frame.window) {
const listeners = frame[PropertySymbol.listeners];

frame.window[PropertySymbol.destroy]();
(<IBrowserPage | null>frame.page) = null;
(<IBrowserWindow | null>frame.window) = null;
frame[PropertySymbol.listeners] = null;
frame[PropertySymbol.openerFrame] = null;
frame[PropertySymbol.openerWindow] = null;

for (const listener of listeners.navigation) {
listener();
}
}
resolve();
});
46 changes: 28 additions & 18 deletions packages/happy-dom/src/browser/utilities/BrowserFrameNavigator.ts
Original file line number Diff line number Diff line change
@@ -11,30 +11,38 @@ import BrowserFrameURL from './BrowserFrameURL.js';
import BrowserFrameValidator from './BrowserFrameValidator.js';
import AsyncTaskManager from '../../async-task-manager/AsyncTaskManager.js';
import BrowserErrorCaptureEnum from '../enums/BrowserErrorCaptureEnum.js';
import FormData from '../../form-data/FormData.js';

/**
* Browser frame navigation utility.
*/
export default class BrowserFrameNavigator {
/**
* Go to a page.
* Navigates to a page.
*
* @throws Error if the request can't be resolved (because of SSL error or similar). It will not throw if the response is not ok.
* @param windowClass Window class.
* @param frame Frame.
* @param url URL.
* @param [options] Options.
* @param options Options.
* @param options.windowClass Window class.
* @param options.frame Frame.
* @param options.url URL.
* @param [options.formData] Form data.
* @param [options.method] Method.
* @param [options.goToOptions] Go to options.
* @returns Response.
*/
public static async goto(
public static async navigate(options: {
windowClass: new (
browserFrame: IBrowserFrame,
options?: { url?: string; width?: number; height?: number }
) => IBrowserWindow,
frame: IBrowserFrame,
url: string,
options?: IGoToOptions
): Promise<IResponse | null> {
) => IBrowserWindow;
frame: IBrowserFrame;
url: string;
goToOptions?: IGoToOptions;
method?: string;
formData?: FormData;
}): Promise<IResponse | null> {
const { windowClass, frame, url, formData, method, goToOptions } = options;
const referrer = goToOptions?.referrer || frame.window.location.origin;
const targetURL = BrowserFrameURL.getRelativeURL(frame, url);

if (!frame.window) {
@@ -98,8 +106,8 @@ export default class BrowserFrameNavigator {
(<IBrowserWindow>frame.window) = new windowClass(frame, { url: targetURL.href, width, height });
(<number>frame.window.devicePixelRatio) = devicePixelRatio;

if (options?.referrer) {
frame.window.document[PropertySymbol.referrer] = options.referrer;
if (referrer) {
frame.window.document[PropertySymbol.referrer] = referrer;
}

if (targetURL.protocol === 'about:') {
@@ -118,7 +126,7 @@ export default class BrowserFrameNavigator {

const timeout = frame.window.setTimeout(
() => abortController.abort('Request timed out.'),
options?.timeout ?? 30000
goToOptions?.timeout ?? 30000
);
const finalize = (): void => {
frame.window.clearTimeout(timeout);
@@ -132,16 +140,18 @@ export default class BrowserFrameNavigator {

try {
response = await frame.window.fetch(targetURL.href, {
referrer: options?.referrer,
referrerPolicy: options?.referrerPolicy,
referrer,
referrerPolicy: goToOptions?.referrerPolicy || 'origin',
signal: abortController.signal,
headers: options?.hard ? { 'Cache-Control': 'no-cache' } : undefined
method: method || (formData ? 'POST' : 'GET'),
headers: goToOptions?.hard ? { 'Cache-Control': 'no-cache' } : undefined,
body: formData
});

// Handles the "X-Frame-Options" header for child frames.
if (frame.parentFrame) {
const originURL = frame.parentFrame.window.location;
const xFrameOptions = response.headers.get('X-Frame-Options')?.toLowerCase();
const xFrameOptions = response.headers?.get('X-Frame-Options')?.toLowerCase();
const isSameOrigin = originURL.origin === targetURL.origin || targetURL.origin === 'null';

if (xFrameOptions === 'deny' || (xFrameOptions === 'sameorigin' && !isSameOrigin)) {
4 changes: 2 additions & 2 deletions packages/happy-dom/src/browser/utilities/BrowserFrameURL.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import IBrowserFrame from '../types/IBrowserFrame.js';
import { URL } from 'url';
import DOMException from '../../exception/DOMException.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import { URL } from 'url';

/**
* Browser frame URL utility.
@@ -22,7 +22,7 @@ export default class BrowserFrameURL {
}

try {
return new URL(url, frame.window.location);
return new URL(url, frame.window.location.href);
} catch (e) {
if (frame.window.location.hostname) {
throw new DOMException(
148 changes: 148 additions & 0 deletions packages/happy-dom/src/config/IHTMLElementTagNameMap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import IHTMLAnchorElement from '../nodes/html-anchor-element/IHTMLAnchorElement.js';
import IHTMLElement from '../nodes/html-element/IHTMLElement.js';
import IHTMLAudioElement from '../nodes/html-audio-element/IHTMLAudioElement.js';
import IHTMLBaseElement from '../nodes/html-base-element/IHTMLBaseElement.js';
import IHTMLTemplateElement from '../nodes/html-template-element/IHTMLTemplateElement.js';
import IHTMLFormElement from '../nodes/html-form-element/IHTMLFormElement.js';
import IHTMLInputElement from '../nodes/html-input-element/IHTMLInputElement.js';
import IHTMLTextAreaElement from '../nodes/html-text-area-element/IHTMLTextAreaElement.js';
import IHTMLScriptElement from '../nodes/html-script-element/IHTMLScriptElement.js';
import IHTMLImageElement from '../nodes/html-image-element/IHTMLImageElement.js';
import IHTMLLinkElement from '../nodes/html-link-element/IHTMLLinkElement.js';
import IHTMLStyleElement from '../nodes/html-style-element/IHTMLStyleElement.js';
import IHTMLLabelElement from '../nodes/html-label-element/IHTMLLabelElement.js';
import IHTMLSlotElement from '../nodes/html-slot-element/IHTMLSlotElement.js';
import IHTMLMetaElement from '../nodes/html-meta-element/IHTMLMetaElement.js';
import IHTMLButtonElement from '../nodes/html-button-element/IHTMLButtonElement.js';
import IHTMLDialogElement from '../nodes/html-dialog-element/IHTMLDialogElement.js';
import IHTMLIFrameElement from '../nodes/html-iframe-element/IHTMLIFrameElement.js';
import IHTMLOptGroupElement from '../nodes/html-opt-group-element/IHTMLOptGroupElement.js';
import IHTMLOptionElement from '../nodes/html-option-element/IHTMLOptionElement.js';
import IHTMLSelectElement from '../nodes/html-select-element/IHTMLSelectElement.js';
import IHTMLVideoElement from '../nodes/html-video-element/IHTMLVideoElement.js';

// Makes it work with custom elements when they declare their own interface.
declare global {
/* eslint-disable-next-line @typescript-eslint/naming-convention */
interface HTMLElementTagNameMap {}
}

export default interface IHTMLElementTagNameMap extends HTMLElementTagNameMap {
a: IHTMLAnchorElement;
abbr: IHTMLElement;
address: IHTMLElement;
area: IHTMLElement;
article: IHTMLElement;
aside: IHTMLElement;
audio: IHTMLAudioElement;
b: IHTMLElement;
base: IHTMLBaseElement;
bdi: IHTMLElement;
bdo: IHTMLElement;
blockquaote: IHTMLElement;
body: IHTMLElement;
template: IHTMLTemplateElement;
form: IHTMLFormElement;
input: IHTMLInputElement;
textarea: IHTMLTextAreaElement;
script: IHTMLScriptElement;
img: IHTMLImageElement;
link: IHTMLLinkElement;
style: IHTMLStyleElement;
label: IHTMLLabelElement;
slot: IHTMLSlotElement;
meta: IHTMLMetaElement;
blockquote: IHTMLElement;
br: IHTMLElement;
button: IHTMLButtonElement;
canvas: IHTMLElement;
caption: IHTMLElement;
cite: IHTMLElement;
code: IHTMLElement;
col: IHTMLElement;
colgroup: IHTMLElement;
data: IHTMLElement;
datalist: IHTMLElement;
dd: IHTMLElement;
del: IHTMLElement;
details: IHTMLElement;
dfn: IHTMLElement;
dialog: IHTMLDialogElement;
div: IHTMLElement;
dl: IHTMLElement;
dt: IHTMLElement;
em: IHTMLElement;
embed: IHTMLElement;
fieldset: IHTMLElement;
figcaption: IHTMLElement;
figure: IHTMLElement;
footer: IHTMLElement;
h1: IHTMLElement;
h2: IHTMLElement;
h3: IHTMLElement;
h4: IHTMLElement;
h5: IHTMLElement;
h6: IHTMLElement;
head: IHTMLElement;
header: IHTMLElement;
hgroup: IHTMLElement;
hr: IHTMLElement;
html: IHTMLElement;
i: IHTMLElement;
iframe: IHTMLIFrameElement;
ins: IHTMLElement;
kbd: IHTMLElement;
legend: IHTMLElement;
li: IHTMLElement;
main: IHTMLElement;
map: IHTMLElement;
mark: IHTMLElement;
math: IHTMLElement;
menu: IHTMLElement;
menuitem: IHTMLElement;
meter: IHTMLElement;
nav: IHTMLElement;
noscript: IHTMLElement;
object: IHTMLElement;
ol: IHTMLElement;
optgroup: IHTMLOptGroupElement;
option: IHTMLOptionElement;
output: IHTMLElement;
p: IHTMLElement;
param: IHTMLElement;
picture: IHTMLElement;
pre: IHTMLElement;
progress: IHTMLElement;
q: IHTMLElement;
rb: IHTMLElement;
rp: IHTMLElement;
rt: IHTMLElement;
rtc: IHTMLElement;
ruby: IHTMLElement;
s: IHTMLElement;
samp: IHTMLElement;
section: IHTMLElement;
select: IHTMLSelectElement;
small: IHTMLElement;
source: IHTMLElement;
span: IHTMLElement;
strong: IHTMLElement;
sub: IHTMLElement;
summary: IHTMLElement;
sup: IHTMLElement;
table: IHTMLElement;
tbody: IHTMLElement;
td: IHTMLElement;
tfoot: IHTMLElement;
th: IHTMLElement;
thead: IHTMLElement;
time: IHTMLElement;
title: IHTMLElement;
tr: IHTMLElement;
track: IHTMLElement;
u: IHTMLElement;
ul: IHTMLElement;
var: IHTMLElement;
video: IHTMLVideoElement;
wbr: IHTMLElement;
}
Loading