Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/capricorn86/happy-dom int…
Browse files Browse the repository at this point in the history
…o task/466-windowopen-is-not-a-function
  • Loading branch information
capricorn86 committed Jan 10, 2024
2 parents 2211596 + 9a0062b commit 3a372de
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 39 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ on: pull_request
jobs:
build:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
node-version: [16]
node-version: [16, 18, 20]

steps:
- uses: actions/checkout@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
tmp
lerna-debug.log
.DS_Store
.vscode
.idea
.turbo
2 changes: 1 addition & 1 deletion packages/happy-dom/src/form-data/FormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default class FormData implements Iterable<[string, string | File]> {
*/
public forEach(callback: (key: string, value: string | File, thisArg: FormData) => void): void {
for (const entry of this.#entries) {
callback.call(this, entry.name, entry.value, this);
callback.call(this, entry.value, entry.name, this);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/named-node-map/INamedNodeMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default interface INamedNodeMap {
* Returns named item.
*
* @param name Name.
* @returns Itme.
* @returns Item.
*/
getNamedItem(name: string): IAttr | null;

Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/named-node-map/NamedNodeMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class NamedNodeMap implements INamedNodeMap {
* Returns named item.
*
* @param name Name.
* @returns Itme.
* @returns Item.
*/
public getNamedItem(name: string): IAttr | null {
return this[PropertySymbol.namedItems][name] || null;
Expand Down
20 changes: 20 additions & 0 deletions packages/happy-dom/src/navigator/Navigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import IBrowserWindow from '../window/IBrowserWindow.js';
import Permissions from '../permissions/Permissions.js';
import Clipboard from '../clipboard/Clipboard.js';
import WindowBrowserSettingsReader from '../window/WindowBrowserSettingsReader.js';
import Blob from '../file/Blob.js';
import FormData from '../form-data/FormData.js';

/**
* Browser Navigator API.
Expand Down Expand Up @@ -212,6 +214,24 @@ export default class Navigator {
return new PluginArray([]);
}

/**
* Sends an HTTP POST request containing a small amount of data to a web server.
*
* @param url URL.
* @param data Data.
* @returns "true" if the user agent successfully queued the data for transfer. Otherwise, it returns "false".
*/
public sendBeacon(
url: string,
data: string | Blob | ArrayBuffer | ArrayBufferView | FormData
): boolean {
this.#ownerWindow.fetch(url, {
method: 'POST',
body: data
});
return true;
}

/**
* Returns the object as a string.
*
Expand Down
4 changes: 0 additions & 4 deletions packages/happy-dom/src/nodes/element/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,6 @@ export default class Element extends Node implements IElement {
}

(<string>clone.tagName) = this.tagName;
clone.scrollLeft = this.scrollLeft;
clone.scrollTop = this.scrollTop;
clone.scrollWidth = this.scrollWidth;
clone.scrollHeight = this.scrollHeight;
(<string>clone.namespaceURI) = this.namespaceURI;

return <IElement>clone;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,26 @@ export default class HTMLInputElementDateUtility {
/**
* Returns iso week number from given date
*
* @see https://stackoverflow.com/a/6117889
* @param date Date or number.
* @returns Iso-week string.
*/
public static dateIsoWeek(date: Date | number): string {
date = new Date(date);
const day = (date.getUTCDay() + 6) % 7;
date.setUTCDate(date.getUTCDate() - day + 3);
const firstThursday = date.getTime();
date.setUTCMonth(0, 1);
if (date.getDay() !== 4) {
date.setUTCMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
}
return (
date.getUTCFullYear() +
'-W' +
String(1 + Math.ceil((firstThursday - date.getTime()) / 604800000)).padStart(2, '0')
date = typeof date === 'number' ? new Date(date) : date;
// Copy date so don't modify original
date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7));
// Get first day of year
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
// Calculate full weeks to nearest Thursday
const weekNo = Math.ceil(
((<number>(<unknown>date) - <number>(<unknown>yearStart)) / 86400000 + 1) / 7
);
return `${date.getUTCFullYear()}-W${weekNo < 10 ? '0' : ''}${weekNo}`;
}

/**
* Returns a date object for monday of given iso week string (\d\d\d\d-W\d\d)
*
Expand All @@ -37,7 +39,7 @@ export default class HTMLInputElementDateUtility {
}
const date = new Date(`${Y}-01-01T00:00Z`);
const jan4th = new Date(`${Y}-01-04T00:00Z`);
const jan4thDay = (jan4th.getDay() + 6) % 7;
const jan4thDay = (jan4th.getUTCDay() + 6) % 7;
const ordinalDate = 1 + (Number(W) - 1) * 7 - jan4thDay + 3;
date.setUTCDate(ordinalDate);
if (date.getUTCFullYear() > Number(Y)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/src/query-selector/SelectorParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class SelectorParser {
* Parses a selector string and returns an instance of SelectorItem.
*
* @param selector Selector.
* @returns Selector itme.
* @returns Selector item.
*/
public static getSelectorItem(selector: string): SelectorItem {
return this.getSelectorGroups(selector)[0][0];
Expand Down
1 change: 1 addition & 0 deletions packages/happy-dom/src/window/BrowserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow
public readonly TreeWalker = TreeWalker;
public readonly MutationObserver = MutationObserver;
public readonly MutationRecord = MutationRecord;
public readonly CSSStyleDeclaration = CSSStyleDeclaration;
public readonly EventTarget = EventTarget;
public readonly MessagePort = MessagePort;
public readonly DataTransfer = DataTransfer;
Expand Down
1 change: 1 addition & 0 deletions packages/happy-dom/src/window/IBrowserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ export default interface IBrowserWindow extends IEventTarget, INodeJSGlobal {
readonly DOMParser: new () => DOMParser;
readonly MutationObserver: typeof MutationObserver;
readonly MutationRecord: typeof MutationRecord;
readonly CSSStyleDeclaration: typeof CSSStyleDeclaration;

// Events
onload: ((event: Event) => void) | null;
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/test/form-data/FormData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('FormData', () => {
formData.set('key2', 'value2');
const values: Array<{ key: string; value: string | File }> = [];

formData.forEach((key, value) => values.push({ key, value }));
formData.forEach((value, key) => values.push({ key, value }));

expect(values).toEqual([
{ key: 'key1', value: 'value1' },
Expand Down
110 changes: 110 additions & 0 deletions packages/happy-dom/test/navigator/Navigator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Window from '../../src/window/Window.js';
import IWindow from '../../src/window/IWindow.js';
import Navigator from '../../src/navigator/Navigator.js';
import { beforeEach, afterEach, describe, it, expect, vi } from 'vitest';
import Permissions from '../../src/permissions/Permissions.js';
import Clipboard from '../../src/clipboard/Clipboard.js';
import PackageVersion from '../../src/version.js';
import IResponse from '../../src/fetch/types/IResponse.js';
import IRequest from '../../src/fetch/types/IRequest.js';
import Fetch from '../../src/fetch/Fetch.js';
import Stream from 'stream';

const GET_NAVIGATOR_PLATFORM = (): string => {
return (
'X11; ' +
process.platform.charAt(0).toUpperCase() +
process.platform.slice(1) +
' ' +
process.arch
);
};
const PROPERTIES = {
appCodeName: 'Mozilla',
appName: 'Netscape',
appVersion: `5.0 (${GET_NAVIGATOR_PLATFORM()}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${
PackageVersion.version
}`,
cookieEnabled: true,
credentials: null,
doNotTrack: 'unspecified',
geolocation: null,
hardwareConcurrency: 8,
language: 'en-US',
languages: ['en-US', 'en'],
locks: null,
maxTouchPoints: 0,
mimeTypes: {
length: 0
},
onLine: true,
permissions: new Permissions(),
platform: GET_NAVIGATOR_PLATFORM(),
plugins: {
length: 0
},
product: 'Gecko',
productSub: '20100101',
userAgent: `Mozilla/5.0 (${GET_NAVIGATOR_PLATFORM()}) AppleWebKit/537.36 (KHTML, like Gecko) HappyDOM/${
PackageVersion.version
}`,
vendor: '',
vendorSub: '',
webdriver: true
};

describe('Window', () => {
let window: IWindow;

beforeEach(() => {
window = new Window();
});

afterEach(() => {
resetMockedModules();
vi.restoreAllMocks();
});

describe('constructor()', () => {
it('Is instanceof Navigator.', () => {
expect(window.navigator instanceof Navigator).toBe(true);
});
});

Object.keys(PROPERTIES).forEach((property) => {
describe(`get ${property}()`, () => {
it('Returns an instance of Navigator with browser data.', () => {
expect(window.navigator[property]).toEqual(PROPERTIES[property]);
});
});
});

describe('get clipboard()', () => {
it('Returns an instance of Clipboard.', () => {
expect(window.navigator.clipboard).toEqual(new Clipboard(window));
});
});

describe('sendBeacon()', () => {
it('Sends a beacon request.', async () => {
const expectedURL = 'https://localhost:8080/path/';
let request: IRequest | null = null;

vi.spyOn(Fetch.prototype, 'send').mockImplementation(function (): Promise<IResponse> {
request = <IRequest>this.request;
return Promise.resolve(<IResponse>{});
});

window.navigator.sendBeacon(expectedURL, 'test-data');

const chunks: Buffer[] = [];

for await (const chunk of <Stream.Readable>(<IRequest>(<unknown>request)).body) {
chunks.push(Buffer.from(chunk));
}

expect(Buffer.concat(chunks).toString()).toBe('test-data');
expect((<IRequest>(<unknown>request)).url).toBe(expectedURL);
});
});
});
4 changes: 0 additions & 4 deletions packages/happy-dom/test/nodes/element/Element.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1549,8 +1549,6 @@ describe('Element', () => {
child.className = 'className';

(<string>element.tagName) = 'tagName';
(<number>element.scrollLeft) = 10;
(<number>element.scrollTop) = 10;

// @ts-ignore
element.namespaceURI = 'namespaceURI';
Expand All @@ -1560,8 +1558,6 @@ describe('Element', () => {
const clone = element.cloneNode(false);
const clone2 = element.cloneNode(true);
expect(clone.tagName).toBe('tagName');
expect(clone.scrollLeft).toBe(10);
expect(clone.scrollTop).toBe(10);
expect(clone.namespaceURI).toBe('namespaceURI');
expect(clone.children.length).toEqual(0);
expect(clone2.children.length).toBe(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ describe('HTMLInputElement', () => {
{ type: 'time', value: '00:00', want: new Date('1970-01-01T00:00Z') },
{ type: 'time', value: '12:00', want: new Date('1970-01-01T12:00Z') },
{ type: 'time', value: '18:55', want: new Date('1970-01-01T18:55Z') },
{ type: 'week', value: '1981-W01', want: new Date('1980-12-29T00:00Z') },
{ type: 'week', value: '2023-W22', want: new Date('2023-05-29T00:00Z') }
])(`Should return valid date for type $type with valid value`, ({ type, value, want }) => {
element.type = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
import HTMLInputElementDateUtility from '../../../src/nodes/html-input-element/HTMLInputElementDateUtility.js';

describe('HTMLInputElementDateUtility', () => {
describe('dateToIsoWeek()', () => {
describe('dateIsoWeek()', () => {
it('Returns the ISO week number', () => {
expect(HTMLInputElementDateUtility.dateIsoWeek(new Date('2021-01-01'))).toBe('2020-W53');
expect(HTMLInputElementDateUtility.dateIsoWeek(new Date('2021-01-03'))).toBe('2020-W53');
Expand Down Expand Up @@ -35,7 +35,7 @@ describe('HTMLInputElementDateUtility', () => {
});
});

describe('IsoWeekToDate()', () => {
describe('isoWeekDate()', () => {
it('Returns the ISO week number', () => {
expect(HTMLInputElementDateUtility.isoWeekDate('2020-W53')).toEqual(
new Date('2020-12-28T00:00Z')
Expand Down
Loading

0 comments on commit 3a372de

Please sign in to comment.