From 3aa0ea975915b98a973421cea5efa8adefd2b5e0 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Fri, 8 Mar 2024 12:41:31 +0100 Subject: [PATCH] feat: [#1101] Returns matched Element type in *.querySelector(), *.querySelectorAll(), *.getElementsByTagName(), *.getElementsByTagNameNS(), Document.createElement() and Document.createElementNS() --- .../html-form-element/HTMLFormElement.ts | 4 +- packages/happy-dom/test/CustomElement.ts | 4 +- .../html-form-element/HTMLFormElement.test.ts | 203 ++++++++++++++++++ 3 files changed, 206 insertions(+), 5 deletions(-) diff --git a/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts b/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts index 8d082f582..4d0f78cb5 100644 --- a/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts +++ b/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts @@ -436,6 +436,7 @@ export default class HTMLFormElement extends HTMLElement implements IHTMLFormEle let targetFrame: IBrowserFrame; switch (submitter?.formTarget || this.target) { + default: case '_self': targetFrame = this.#browserFrame; break; @@ -450,9 +451,6 @@ export default class HTMLFormElement extends HTMLElement implements IHTMLFormEle targetFrame = newPage.mainFrame; targetFrame[PropertySymbol.openerFrame] = this.#browserFrame; break; - default: - targetFrame = this.#browserFrame; - break; } if (method === 'get') { diff --git a/packages/happy-dom/test/CustomElement.ts b/packages/happy-dom/test/CustomElement.ts index a337a1e9d..399bd1990 100644 --- a/packages/happy-dom/test/CustomElement.ts +++ b/packages/happy-dom/test/CustomElement.ts @@ -62,8 +62,8 @@ export default class CustomElement extends HTMLElement {
key1 is "${this.getAttribute('key1')}" and key2 is "${this.getAttribute( - 'key2' - )}". + 'key2' + )}". ${this.childNodes .map( diff --git a/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts b/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts index e7533259a..10c9a2eb6 100644 --- a/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts +++ b/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts @@ -382,6 +382,12 @@ describe('HTMLFormElement', () => { describe('submit()', () => { it('Fallbacks to set location URL when in the main frame of a detached Window.', () => { + vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise { + throw new Error('Request should not be sent.'); + }); + + expect(window.location.href).toBe('about:blank'); + element.action = 'https://localhost:3000'; element.submit(); @@ -603,6 +609,106 @@ describe('HTMLFormElement', () => { ); }); + it(`Supports "_parent" as target.`, async () => { + let request: IRequest | null = null; + + vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise { + request = this.request; + return Promise.resolve({ + url: request?.url, + text: () => + new Promise((resolve) => + setTimeout( + () => + resolve( + request?.url === 'http://example.com/iframe' + ? ` + +
+ + + + + + + +
+ ` + : 'Test' + ), + 2 + ) + ) + }); + }); + + const browser = new Browser(); + const page = browser.newPage(); + const oldWindow = page.mainFrame.window; + + page.mainFrame.url = 'http://example.com'; + + oldWindow.document.write(``); + + await new Promise((resolve) => + oldWindow.document.querySelector('iframe')?.addEventListener('load', resolve) + ); + + (( + (oldWindow.document.body.children[0]).contentWindow + )).document.body + .querySelector('button') + ?.click(); + + await page.mainFrame.waitForNavigation(); + + expect(page.mainFrame.url).toBe( + 'http://example.com/?text1=value1&text2=value2&checkbox1=value1&radio1=value2' + ); + }); + + it(`Supports "_blank" as target.`, async () => { + let request: IRequest | null = null; + + vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise { + request = this.request; + return Promise.resolve({ + url: request?.url, + text: () => + new Promise((resolve) => setTimeout(() => resolve('Test'), 2)) + }); + }); + + const browser = new Browser(); + const page = browser.newPage(); + const oldWindow = page.mainFrame.window; + + oldWindow.document.write(` +
+ + + + + + + + +
+ `); + + oldWindow.document.body.querySelector('button')?.click(); + + const newPage = page.context.pages[1]; + + expect(newPage.mainFrame.url).toBe( + 'http://example.com/?text1=value1&text2=value2&checkbox1=value1&radio1=value2' + ); + + await newPage.waitForNavigation(); + + expect(newPage.mainFrame.document.body.innerHTML).toBe('Test'); + }); + it(`Uses "action" from button when "formaction" is set as an attribute on the button.`, async () => { let request: IRequest | null = null; @@ -639,6 +745,29 @@ describe('HTMLFormElement', () => { expect(page.mainFrame.url).toBe('http://button.example.com/'); expect(page.mainFrame.window.location.href).toBe('http://button.example.com/'); }); + + it(`Sets hash to "#blocked" when action is invalid.`, async () => { + const browser = new Browser(); + const page = browser.newPage(); + const oldWindow = page.mainFrame.window; + + oldWindow.document.write(` +
+ + + + + + + + +
+ `); + + oldWindow.document.querySelector('button')?.click(); + + expect(page.mainFrame.url).toBe('about:blank#blocked'); + }); }); describe('requestSubmit()', () => { @@ -720,6 +849,80 @@ describe('HTMLFormElement', () => { expect(((submitEvent)).type).toBe('submit'); }); + + it('Fallbacks to set location URL when in the main frame of a detached Window.', () => { + vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise { + throw new Error('Request should not be sent.'); + }); + + expect(window.location.href).toBe('about:blank'); + + element.action = 'https://localhost:3000'; + element.requestSubmit(); + + expect(window.location.href).toBe('https://localhost:3000/'); + }); + + it('Submits form as query string when method is "GET".', async () => { + let request: IRequest | null = null; + + vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise { + request = this.request; + return Promise.resolve({ + url: request?.url, + text: () => + new Promise((resolve) => setTimeout(() => resolve('Test'), 2)) + }); + }); + + const browser = new Browser(); + const page = browser.newPage(); + const oldWindow = page.mainFrame.window; + + page.mainFrame.url = 'http://referrer.example.com'; + + oldWindow.document.write(` +
+ + + + + + + + +
+ `); + + (oldWindow.document.querySelector('input[name="text1"]')).value = + 'invalid'; + + oldWindow.document.body.children[0]['button1'].click(); + + await new Promise((resolve) => setTimeout(resolve, 2)); + + expect(page.mainFrame.url).toBe('http://referrer.example.com/'); + + (oldWindow.document.querySelector('input[name="text1"]')).value = 'test'; + + oldWindow.document.body.children[0]['button1'].click(); + + await page.mainFrame.waitForNavigation(); + + expect(((request)).referrer).toBe('about:client'); + expect(((request)).referrerPolicy).toBe('origin'); + expect(((request)).method).toBe('GET'); + + expect(page.mainFrame.url).toBe( + 'http://example.com/?text1=test&text2=value2&checkbox1=value1&radio1=value2' + ); + expect(page.mainFrame.window).not.toBe(oldWindow); + expect(oldWindow.location.href).toBe('http://referrer.example.com/'); + expect(page.mainFrame.window.location.href).toBe( + 'http://example.com/?text1=test&text2=value2&checkbox1=value1&radio1=value2' + ); + expect(page.mainFrame.window.document.body.innerHTML).toBe('Test'); + }); }); describe('reset()', () => {