diff --git a/packages/happy-dom/src/fetch/FetchHandler.ts b/packages/happy-dom/src/fetch/FetchHandler.ts index 3b4e1e4c4..733be38ea 100644 --- a/packages/happy-dom/src/fetch/FetchHandler.ts +++ b/packages/happy-dom/src/fetch/FetchHandler.ts @@ -4,6 +4,9 @@ import IDocument from '../nodes/document/IDocument'; import IResponse from './IResponse'; import Response from './Response'; import NodeFetch from 'node-fetch'; +import Request from './Request'; +import RequestInfo from './RequestInfo'; +import { URL } from 'url'; /** * Helper class for performing fetch. @@ -17,22 +20,47 @@ export default class FetchHandler { * @param [init] Init. * @returns Response. */ - public static fetch(document: IDocument, url: string, init?: IRequestInit): Promise { - // We want to only load NodeFetch when it is needed to improve performance and not have direct dependencies to server side packages. + public static fetch( + document: IDocument, + url: RequestInfo, + init?: IRequestInit + ): Promise { const taskManager = document.defaultView.happyDOM.asyncTaskManager; const requestInit = { ...init, headers: { ...init?.headers } }; + const cookie = document.defaultView.document.cookie; + const referer = document.defaultView.location.origin; + + requestInit.headers['user-agent'] = document.defaultView.navigator.userAgent; // We need set referer to solve anti-hotlinking. // And the browser will set the referer to the origin of the page. - requestInit.headers['referer'] = document.defaultView.location.origin; + // Referer is "null" when the URL is set to "about:blank". + // This is also how the browser behaves. + if (referer !== 'null') { + requestInit.headers['referer'] = referer; + } - requestInit.headers['user-agent'] = document.defaultView.navigator.userAgent; - requestInit.headers['cookie'] = document.defaultView.document.cookie; + if (cookie) { + requestInit.headers['set-cookie'] = cookie; + } + + let request; + + if (typeof url === 'string') { + request = new Request(RelativeURL.getAbsoluteURL(document.defaultView.location, url)); + } else if (url instanceof URL) { + // URLs are always absolute, no need for getAbsoluteURL. + request = new Request(url); + } else { + request = new Request(RelativeURL.getAbsoluteURL(document.defaultView.location, url.url), { + ...url + }); + } return new Promise((resolve, reject) => { const taskID = taskManager.startTask(); - NodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url).href, requestInit) + NodeFetch(request, requestInit) .then((response) => { if (taskManager.getTaskCount() === 0) { reject(new Error('Failed to complete fetch request. Task was canceled.')); diff --git a/packages/happy-dom/src/fetch/RequestInfo.ts b/packages/happy-dom/src/fetch/RequestInfo.ts new file mode 100644 index 000000000..f8d99f25b --- /dev/null +++ b/packages/happy-dom/src/fetch/RequestInfo.ts @@ -0,0 +1,6 @@ +import { URL } from 'url'; +import IRequest from './IRequest'; + +type RequestInfo = IRequest | string | URL; + +export default RequestInfo; diff --git a/packages/happy-dom/src/window/IWindow.ts b/packages/happy-dom/src/window/IWindow.ts index 306b88cda..fd3d72424 100644 --- a/packages/happy-dom/src/window/IWindow.ts +++ b/packages/happy-dom/src/window/IWindow.ts @@ -103,6 +103,7 @@ import { Performance } from 'perf_hooks'; import IElement from '../nodes/element/IElement'; import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction'; import IHappyDOMSettings from './IHappyDOMSettings'; +import RequestInfo from '../fetch/RequestInfo'; /** * Window without dependencies to server side specific packages. @@ -342,7 +343,7 @@ export default interface IWindow extends IEventTarget, NodeJS.Global { * @param [init] Init. * @returns Promise. */ - fetch(url: string, init?: IRequestInit): Promise; + fetch(url: RequestInfo, init?: IRequestInit): Promise; /** * Creates a Base64-encoded ASCII string from a binary string (i.e., a string in which each character in the string is treated as a byte of binary data). diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts index 0b1442d97..ed4854f9f 100644 --- a/packages/happy-dom/src/window/Window.ts +++ b/packages/happy-dom/src/window/Window.ts @@ -114,6 +114,7 @@ import NamedNodeMap from '../named-node-map/NamedNodeMap'; import IElement from '../nodes/element/IElement'; import ProcessingInstruction from '../nodes/processing-instruction/ProcessingInstruction'; import IHappyDOMSettings from './IHappyDOMSettings'; +import RequestInfo from '../fetch/RequestInfo'; const ORIGINAL_SET_TIMEOUT = setTimeout; const ORIGINAL_CLEAR_TIMEOUT = clearTimeout; @@ -659,7 +660,7 @@ export default class Window extends EventTarget implements IWindow { * @param [init] Init. * @returns Promise. */ - public async fetch(url: string, init?: IRequestInit): Promise { + public async fetch(url: RequestInfo, init?: IRequestInit): Promise { return await FetchHandler.fetch(this.document, url, init); } diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts index 02be3f61a..af8d7744e 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequestCertificate.ts @@ -1,52 +1,52 @@ // SSL certificate generated for Happy DOM to be able to perform HTTPS requests export default { cert: `-----BEGIN CERTIFICATE----- - MIIDYzCCAkugAwIBAgIUJRKB/H66hpet1VfUlm0CiXqePA4wDQYJKoZIhvcNAQEL - BQAwQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVNYWxt - bzESMBAGA1UECgwJSGFwcHkgRE9NMB4XDTIyMTAxMTIyMDM0OVoXDTMyMTAwODIy - MDM0OVowQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVN - YWxtbzESMBAGA1UECgwJSGFwcHkgRE9NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A - MIIBCgKCAQEAqerQSQEg/SxVxRiwlItithr5e5EMZo1nsqt/xOxagbmpW3IEmt0j - bpbH7iEF4DDEo7KAOwUCOwVWeFxRoag8lG2ax48wrgjlCna45XDn0Xeg1ARajL04 - gs46HZ0VrzIloVGfln0zgt/Vum5BNqs9Oc5fQoBmoP3cAn3dn4ZVcP0AKthtcyPl - q2DuNRN0PV0D2RtMSiAy9l1Ko6N5x+sAeClDyOL+sTDLngZBVeZyOKt9Id15S8Zt - XtA6VMgHnnF3jChn7pag77rsd/y5iANAVNZYqRl+Eg7xaDcsvbgH46UBOrBcB39Q - tTh5Mtjoxep5e3ZDFG+kQ1HUE+iz5O5n0wIDAQABo1MwUTAdBgNVHQ4EFgQU69s9 - YSobG/m2SN4L/7zTaF7iDbwwHwYDVR0jBBgwFoAU69s9YSobG/m2SN4L/7zTaF7i - DbwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAi/WUXx2oal8L - YnPlIuKfh49n/K18wXSYG//oFYwxfVxqpYH8hUiXVm/GUcXCxS++hUkaKLqXmH9q - MKJiCrZr3vS+2nsBKopkICu/TLdROl0sAI9lByfnEbfSAzjxe1IWJdK8NdY0y5m5 - 9pEr/URVIAp/CxrneyASb4q0Jg5To3FR7vYc+2X6wZn0MundKMg6Dp9/A37jiF3l - Tt/EJp299YZcsUzh+LnRuggRjnoOVu1aLcLFlaUiwZfy9m8mLG6B/mdW/qNzNMh9 - Oqvg1zfGdpz/4D/2UUUBn6pq1vbsoAaF3OesoA3mfDcegDf/H9woJlpT0Wql+e68 - Y3FblSokcA== - -----END CERTIFICATE-----`, +MIIDYzCCAkugAwIBAgIUJRKB/H66hpet1VfUlm0CiXqePA4wDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVNYWxt +bzESMBAGA1UECgwJSGFwcHkgRE9NMB4XDTIyMTAxMTIyMDM0OVoXDTMyMTAwODIy +MDM0OVowQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVN +YWxtbzESMBAGA1UECgwJSGFwcHkgRE9NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAqerQSQEg/SxVxRiwlItithr5e5EMZo1nsqt/xOxagbmpW3IEmt0j +bpbH7iEF4DDEo7KAOwUCOwVWeFxRoag8lG2ax48wrgjlCna45XDn0Xeg1ARajL04 +gs46HZ0VrzIloVGfln0zgt/Vum5BNqs9Oc5fQoBmoP3cAn3dn4ZVcP0AKthtcyPl +q2DuNRN0PV0D2RtMSiAy9l1Ko6N5x+sAeClDyOL+sTDLngZBVeZyOKt9Id15S8Zt +XtA6VMgHnnF3jChn7pag77rsd/y5iANAVNZYqRl+Eg7xaDcsvbgH46UBOrBcB39Q +tTh5Mtjoxep5e3ZDFG+kQ1HUE+iz5O5n0wIDAQABo1MwUTAdBgNVHQ4EFgQU69s9 +YSobG/m2SN4L/7zTaF7iDbwwHwYDVR0jBBgwFoAU69s9YSobG/m2SN4L/7zTaF7i +DbwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAi/WUXx2oal8L +YnPlIuKfh49n/K18wXSYG//oFYwxfVxqpYH8hUiXVm/GUcXCxS++hUkaKLqXmH9q +MKJiCrZr3vS+2nsBKopkICu/TLdROl0sAI9lByfnEbfSAzjxe1IWJdK8NdY0y5m5 +9pEr/URVIAp/CxrneyASb4q0Jg5To3FR7vYc+2X6wZn0MundKMg6Dp9/A37jiF3l +Tt/EJp299YZcsUzh+LnRuggRjnoOVu1aLcLFlaUiwZfy9m8mLG6B/mdW/qNzNMh9 +Oqvg1zfGdpz/4D/2UUUBn6pq1vbsoAaF3OesoA3mfDcegDf/H9woJlpT0Wql+e68 +Y3FblSokcA== +-----END CERTIFICATE-----`, key: `-----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCp6tBJASD9LFXF - GLCUi2K2Gvl7kQxmjWeyq3/E7FqBualbcgSa3SNulsfuIQXgMMSjsoA7BQI7BVZ4 - XFGhqDyUbZrHjzCuCOUKdrjlcOfRd6DUBFqMvTiCzjodnRWvMiWhUZ+WfTOC39W6 - bkE2qz05zl9CgGag/dwCfd2fhlVw/QAq2G1zI+WrYO41E3Q9XQPZG0xKIDL2XUqj - o3nH6wB4KUPI4v6xMMueBkFV5nI4q30h3XlLxm1e0DpUyAeecXeMKGfulqDvuux3 - /LmIA0BU1lipGX4SDvFoNyy9uAfjpQE6sFwHf1C1OHky2OjF6nl7dkMUb6RDUdQT - 6LPk7mfTAgMBAAECggEAKkwTkTjAt4UjzK56tl+EMQTB+ep/hb/JgoaChci4Nva6 - m9LkJpDJ0yuhlTuPNOGu8XjrxsVWas7HWarRf0Zb3i7yip6wZYI9Ub+AA015x4DZ - /i0fRU2NFbK0cM67qSL4jxG8gj+kZP3HPGNZxHwX/53JxMolwgmvjMc8NgvAlSFd - NnV9h4xtbhUh1NGS5zmP3iU2rwnE8JrIEzwy6axLom7nekAgkdcbAr0UoBs8gcgH - aYNhU4Gz3tGcZZ0IXAfT/bJIH1Ko8AGv4pssWc3BXcmmNdm/+kzvHIxEIV7Qegmo - XG1ZyZCyD/0b4/3e8ySDBEDqwR+HeyTW2isWG2agAQKBgQDp44aTwr3dkIXY30xv - FPfUOipg/B49dWnffYJ9MWc1FT9ijNPAngWSk0EIiEQIazICcUBI4Yji6/KeyqLJ - GdLpDi1CkKqtyh73mjELinYp3EUQgEa77aQogGa2+nMOVfu+O5CtloUrv/A18jX3 - +VEyaEASK0fWmnSI0OdlxQHIAQKBgQC5+xOls2F3MlKASvWRLlnW1wHqlDTtVoYg - 5Nh8syZH4Ci2UH8tON3A5/7SWNM0t1cgV6Cw4zW8Z2spgIT/W0iYYrQ4hHL1xdCu - +CxL1km4Gy8Uwpsd+KdFahFqF/XTmLzW0HXLxWSK0fTwmdV0SFrKF3MXfTCU2AeZ - jJoMFb6P0wKBgQC3Odw6s0vkYAzLGhuZxfZkVvDOK5RRF0NKpttr0iEFL9EJFkPo - 2KKK8jr3QTDy229BBJGUxsJi6u6VwS8HlehpVQbV59kd7oKV/EBBx0XMg1fDlopT - PNbmN7i/zbIG4AsoOyebJZjL7kBzMn1e9vzKHWtcEHXlw/hZGja8vjooAQKBgAeg - xK2HLfg1mCyq5meN/yFQsENu0LzrT5UJzddPgcJw7zqLEqxIKNBAs7Ls8by3yFsL - PQwERa/0jfCl1M6kb9XQNpQa2pw6ANUsWKTDpUJn2wZ+9N3F1RaDwzMWyH5lRVmK - M0qoTfdjpSg5Jwgd75taWt4bxGJWeflSSv8z5R0BAoGAWL8c527AbeBvx2tOYKkD - 2TFranvANNcoMrbeviZSkkGvMNDP3p8b6juJwXOIeWNr8q4vFgCzLmq6d1/9gYm2 - 3XJwwyD0LKlqzkBrrKU47qrnmMosUrIRlrAzd3HbShOptxc6Iz2apSaUDKGKXkaw - gl5OpEjeliU7Mus0BVS858g= - -----END PRIVATE KEY-----` +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCp6tBJASD9LFXF +GLCUi2K2Gvl7kQxmjWeyq3/E7FqBualbcgSa3SNulsfuIQXgMMSjsoA7BQI7BVZ4 +XFGhqDyUbZrHjzCuCOUKdrjlcOfRd6DUBFqMvTiCzjodnRWvMiWhUZ+WfTOC39W6 +bkE2qz05zl9CgGag/dwCfd2fhlVw/QAq2G1zI+WrYO41E3Q9XQPZG0xKIDL2XUqj +o3nH6wB4KUPI4v6xMMueBkFV5nI4q30h3XlLxm1e0DpUyAeecXeMKGfulqDvuux3 +/LmIA0BU1lipGX4SDvFoNyy9uAfjpQE6sFwHf1C1OHky2OjF6nl7dkMUb6RDUdQT +6LPk7mfTAgMBAAECggEAKkwTkTjAt4UjzK56tl+EMQTB+ep/hb/JgoaChci4Nva6 +m9LkJpDJ0yuhlTuPNOGu8XjrxsVWas7HWarRf0Zb3i7yip6wZYI9Ub+AA015x4DZ +/i0fRU2NFbK0cM67qSL4jxG8gj+kZP3HPGNZxHwX/53JxMolwgmvjMc8NgvAlSFd +NnV9h4xtbhUh1NGS5zmP3iU2rwnE8JrIEzwy6axLom7nekAgkdcbAr0UoBs8gcgH +aYNhU4Gz3tGcZZ0IXAfT/bJIH1Ko8AGv4pssWc3BXcmmNdm/+kzvHIxEIV7Qegmo +XG1ZyZCyD/0b4/3e8ySDBEDqwR+HeyTW2isWG2agAQKBgQDp44aTwr3dkIXY30xv +FPfUOipg/B49dWnffYJ9MWc1FT9ijNPAngWSk0EIiEQIazICcUBI4Yji6/KeyqLJ +GdLpDi1CkKqtyh73mjELinYp3EUQgEa77aQogGa2+nMOVfu+O5CtloUrv/A18jX3 ++VEyaEASK0fWmnSI0OdlxQHIAQKBgQC5+xOls2F3MlKASvWRLlnW1wHqlDTtVoYg +5Nh8syZH4Ci2UH8tON3A5/7SWNM0t1cgV6Cw4zW8Z2spgIT/W0iYYrQ4hHL1xdCu ++CxL1km4Gy8Uwpsd+KdFahFqF/XTmLzW0HXLxWSK0fTwmdV0SFrKF3MXfTCU2AeZ +jJoMFb6P0wKBgQC3Odw6s0vkYAzLGhuZxfZkVvDOK5RRF0NKpttr0iEFL9EJFkPo +2KKK8jr3QTDy229BBJGUxsJi6u6VwS8HlehpVQbV59kd7oKV/EBBx0XMg1fDlopT +PNbmN7i/zbIG4AsoOyebJZjL7kBzMn1e9vzKHWtcEHXlw/hZGja8vjooAQKBgAeg +xK2HLfg1mCyq5meN/yFQsENu0LzrT5UJzddPgcJw7zqLEqxIKNBAs7Ls8by3yFsL +PQwERa/0jfCl1M6kb9XQNpQa2pw6ANUsWKTDpUJn2wZ+9N3F1RaDwzMWyH5lRVmK +M0qoTfdjpSg5Jwgd75taWt4bxGJWeflSSv8z5R0BAoGAWL8c527AbeBvx2tOYKkD +2TFranvANNcoMrbeviZSkkGvMNDP3p8b6juJwXOIeWNr8q4vFgCzLmq6d1/9gYm2 +3XJwwyD0LKlqzkBrrKU47qrnmMosUrIRlrAzd3HbShOptxc6Iz2apSaUDKGKXkaw +gl5OpEjeliU7Mus0BVS858g= +-----END PRIVATE KEY-----` }; diff --git a/packages/happy-dom/test/setup.js b/packages/happy-dom/test/setup.js index 24eb9daee..567c1d783 100644 --- a/packages/happy-dom/test/setup.js +++ b/packages/happy-dom/test/setup.js @@ -115,13 +115,19 @@ class NodeFetchResponse { } } -class NodeFetchRequest extends NodeFetchResponse {} +class NodeFetchRequest extends NodeFetchResponse { + constructor(url) { + super(); + this.url = url; + } +} + class NodeFetchHeaders {} jest.mock('node-fetch', () => { return Object.assign( - (url, options) => { - global.mockedModules.modules['node-fetch'].parameters.url = url; + (request, options) => { + global.mockedModules.modules['node-fetch'].parameters.url = request.url.href; global.mockedModules.modules['node-fetch'].parameters.init = options; if (global.mockedModules.modules['node-fetch'].error) { return Promise.reject(global.mockedModules.modules['node-fetch'].returnValue.error); diff --git a/packages/happy-dom/test/window/Window.test.ts b/packages/happy-dom/test/window/Window.test.ts index 3528fb5b9..857ed964c 100644 --- a/packages/happy-dom/test/window/Window.test.ts +++ b/packages/happy-dom/test/window/Window.test.ts @@ -12,6 +12,7 @@ import Selection from '../../src/selection/Selection'; import DOMException from '../../src/exception/DOMException'; import DOMExceptionNameEnum from '../../src/exception/DOMExceptionNameEnum'; import CustomElement from '../../test/CustomElement'; +import { URL } from 'url'; describe('Window', () => { let window: IWindow; @@ -457,48 +458,85 @@ describe('Window', () => { describe('fetch()', () => { for (const method of ['arrayBuffer', 'blob', 'buffer', 'json', 'text', 'textConverted']) { it(`Handles successful "${method}" request.`, async () => { + window.location.href = 'https://localhost:8080'; + document.cookie = 'name1=value1'; + document.cookie = 'name2=value2'; + const expectedUrl = 'https://localhost:8080/path/'; - const expectedOptions = {}; + const expectedOptions = { + method: 'PUT', + headers: { + 'test-header': 'test-value' + } + }; const response = await window.fetch(expectedUrl, expectedOptions); const result = await response[method](); + expect(mockedModules.modules['node-fetch'].parameters.init).toEqual({ + ...expectedOptions, + headers: { + ...expectedOptions.headers, + 'user-agent': window.navigator.userAgent, + 'set-cookie': 'name1=value1; name2=value2', + referer: window.location.origin + } + }); expect(mockedModules.modules['node-fetch'].parameters.url).toBe(expectedUrl); - - expect(mockedModules.modules['node-fetch'].parameters.init.headers['user-agent']).toBe( - window.navigator.userAgent - ); - expect(mockedModules.modules['node-fetch'].parameters.init.headers['cookie']).toBe( - window.document.cookie - ); - expect(mockedModules.modules['node-fetch'].parameters.init.headers['referer']).toBe( - window.location.origin - ); - expect(result).toEqual(mockedModules.modules['node-fetch'].returnValue.response[method]); }); } it('Handles relative URL.', async () => { const expectedPath = '/path/'; - const expectedOptions = {}; window.location.href = 'https://localhost:8080'; - const response = await window.fetch(expectedPath, expectedOptions); + const response = await window.fetch(expectedPath); const textResponse = await response.text(); expect(mockedModules.modules['node-fetch'].parameters.url).toBe( 'https://localhost:8080' + expectedPath ); - expect(mockedModules.modules['node-fetch'].parameters.init.headers['user-agent']).toBe( - window.navigator.userAgent - ); - expect(mockedModules.modules['node-fetch'].parameters.init.headers['cookie']).toBe( - window.document.cookie - ); - expect(mockedModules.modules['node-fetch'].parameters.init.headers['referer']).toBe( - window.location.origin + expect(textResponse).toEqual(mockedModules.modules['node-fetch'].returnValue.response.text); + }); + + it('Handles URL object.', async () => { + const expectedURL = 'https://localhost:8080/path/'; + + window.location.href = 'https://localhost:8080'; + + const response = await window.fetch(new URL(expectedURL)); + const textResponse = await response.text(); + + expect(mockedModules.modules['node-fetch'].parameters.url).toBe(expectedURL); + + expect(textResponse).toEqual(mockedModules.modules['node-fetch'].returnValue.response.text); + }); + + it('Handles Request object with absolute URL.', async () => { + const expectedURL = 'https://localhost:8080/path/'; + + window.location.href = 'https://localhost:8080'; + + const response = await window.fetch(new window.Request(expectedURL)); + const textResponse = await response.text(); + + expect(mockedModules.modules['node-fetch'].parameters.url).toBe(expectedURL); + + expect(textResponse).toEqual(mockedModules.modules['node-fetch'].returnValue.response.text); + }); + + it('Handles Request object with relative URL.', async () => { + const expectedPath = '/path/'; + + window.location.href = 'https://localhost:8080'; + + const response = await window.fetch(new window.Request(expectedPath)); + const textResponse = await response.text(); + + expect(mockedModules.modules['node-fetch'].parameters.url).toBe( + 'https://localhost:8080' + expectedPath ); expect(textResponse).toEqual(mockedModules.modules['node-fetch'].returnValue.response.text); @@ -507,8 +545,9 @@ describe('Window', () => { it('Handles error JSON request.', async () => { mockedModules.modules['node-fetch'].returnValue.error = new Error('error'); window.location.href = 'https://localhost:8080'; + try { - await window.fetch('/url/', {}); + await window.fetch('/url/'); } catch (error) { expect(error).toBe(mockedModules.modules['node-fetch'].returnValue.error); } diff --git a/packages/jest-environment/test/javascript/JavaScript.test.ts b/packages/jest-environment/test/javascript/JavaScript.test.ts index 3729d5838..5c952d170 100644 --- a/packages/jest-environment/test/javascript/JavaScript.test.ts +++ b/packages/jest-environment/test/javascript/JavaScript.test.ts @@ -26,6 +26,49 @@ describe('JavaScript', () => { expect(body.includes('node_modules')).toBe(true); }); + it('Can perform a real asynchronous XMLHttpRequest request to Github.com', (done) => { + const request = new XMLHttpRequest(); + + request.open( + 'GET', + 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore', + true + ); + + request.addEventListener('load', () => { + expect(request.getResponseHeader('content-type')).toBe('text/plain; charset=utf-8'); + expect(request.responseText.includes('node_modules')).toBe(true); + expect(request.status).toBe(200); + expect(request.statusText).toBe('OK'); + expect(request.responseURL).toBe( + 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore' + ); + done(); + }); + + request.send(); + }); + + it('Can perform a real synchronous XMLHttpRequest request to Github.com', () => { + const request = new XMLHttpRequest(); + + request.open( + 'GET', + 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore', + false + ); + + request.send(); + + expect(request.getResponseHeader('content-type')).toBe('text/plain; charset=utf-8'); + expect(request.responseText.includes('node_modules')).toBe(true); + expect(request.status).toBe(200); + expect(request.statusText).toBe('OK'); + expect(request.responseURL).toBe( + 'https://raw.githubusercontent.com/capricorn86/happy-dom/master/.gitignore' + ); + }); + it('Binds global methods to the Window context', () => { const eventListener = (): void => {}; addEventListener('click', eventListener);