Skip to content

Commit deb3da4

Browse files
committed
feat: [#1101] Adds support for HashChangeEvent
1 parent 10f9ee5 commit deb3da4

File tree

2 files changed

+88
-69
lines changed

2 files changed

+88
-69
lines changed

packages/happy-dom/src/location/Location.ts

Lines changed: 35 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import IBrowserFrame from '../browser/types/IBrowserFrame.js';
2-
import BrowserFrameValidator from '../browser/utilities/BrowserFrameValidator.js';
32
import HashChangeEvent from '../event/events/HashChangeEvent.js';
43
import * as PropertySymbol from '../PropertySymbol.js';
54
import { URL } from 'url';
@@ -45,7 +44,7 @@ export default class Location {
4544
this.#url.hash = hash;
4645
const newURL = this.#url.href;
4746
if (newURL !== oldURL) {
48-
this.#browserFrame.window.dispatchEvent(
47+
this.#browserFrame.window?.dispatchEvent(
4948
new HashChangeEvent('hashchange', { oldURL, newURL })
5049
);
5150
}
@@ -66,16 +65,11 @@ export default class Location {
6665
* @param host Value.
6766
*/
6867
public set host(host: string) {
69-
const frame = this.#browserFrame;
70-
if (
71-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
72-
!BrowserFrameValidator.validateFrameNavigation(frame)
73-
) {
74-
return;
75-
}
76-
77-
this.#url.host = host;
78-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
68+
const url = new URL(this.#url.href);
69+
url.host = host;
70+
this.#browserFrame
71+
.goto(url.href)
72+
.catch((error) => this.#browserFrame.page.console.error(error));
7973
}
8074

8175
/**
@@ -93,37 +87,25 @@ export default class Location {
9387
* @param hostname Value.
9488
*/
9589
public set hostname(hostname: string) {
96-
const frame = this.#browserFrame;
97-
if (
98-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
99-
!BrowserFrameValidator.validateFrameNavigation(frame)
100-
) {
101-
return;
102-
}
103-
this.#url.hostname = hostname;
104-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
90+
const url = new URL(this.#url.href);
91+
url.hostname = hostname;
92+
this.#browserFrame
93+
.goto(url.href)
94+
.catch((error) => this.#browserFrame.page.console.error(error));
10595
}
10696

10797
/**
10898
* Override set href.
10999
*/
110-
public set href(url: string) {
111-
const frame = this.#browserFrame;
112-
if (
113-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
114-
!BrowserFrameValidator.validateFrameNavigation(frame)
115-
) {
116-
return;
117-
}
118-
this.#url.href = url;
119-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
100+
public get href(): string {
101+
return this.#url.href;
120102
}
121103

122104
/**
123105
* Override set href.
124106
*/
125-
public get href(): string {
126-
return this.#url.href;
107+
public set href(url: string) {
108+
this.#browserFrame.goto(url).catch((error) => this.#browserFrame.page.console.error(error));
127109
}
128110

129111
/**
@@ -150,15 +132,11 @@ export default class Location {
150132
* @param pathname Value.
151133
*/
152134
public set pathname(pathname: string) {
153-
const frame = this.#browserFrame;
154-
if (
155-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
156-
!BrowserFrameValidator.validateFrameNavigation(frame)
157-
) {
158-
return;
159-
}
160-
this.#url.pathname = pathname;
161-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
135+
const url = new URL(this.#url.href);
136+
url.pathname = pathname;
137+
this.#browserFrame
138+
.goto(url.href)
139+
.catch((error) => this.#browserFrame.page.console.error(error));
162140
}
163141

164142
/**
@@ -176,15 +154,11 @@ export default class Location {
176154
* @param port Value.
177155
*/
178156
public set port(port: string) {
179-
const frame = this.#browserFrame;
180-
if (
181-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
182-
!BrowserFrameValidator.validateFrameNavigation(frame)
183-
) {
184-
return;
185-
}
186-
this.#url.port = port;
187-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
157+
const url = new URL(this.#url.href);
158+
url.port = port;
159+
this.#browserFrame
160+
.goto(url.href)
161+
.catch((error) => this.#browserFrame.page.console.error(error));
188162
}
189163

190164
/**
@@ -202,15 +176,11 @@ export default class Location {
202176
* @param protocol Value.
203177
*/
204178
public set protocol(protocol: string) {
205-
const frame = this.#browserFrame;
206-
if (
207-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
208-
!BrowserFrameValidator.validateFrameNavigation(frame)
209-
) {
210-
return;
211-
}
212-
this.#url.protocol = protocol;
213-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
179+
const url = new URL(this.#url.href);
180+
url.protocol = protocol;
181+
this.#browserFrame
182+
.goto(url.href)
183+
.catch((error) => this.#browserFrame.page.console.error(error));
214184
}
215185

216186
/**
@@ -228,15 +198,11 @@ export default class Location {
228198
* @param search Value.
229199
*/
230200
public set search(search: string) {
231-
const frame = this.#browserFrame;
232-
if (
233-
frame.page.context.browser.settings.navigation.disableFallbackToSetURL &&
234-
!BrowserFrameValidator.validateFrameNavigation(frame)
235-
) {
236-
return;
237-
}
238-
this.#url.search = search;
239-
frame.goto(this.#url.href).catch((error) => frame.page.console.error(error));
201+
const url = new URL(this.#url.href);
202+
url.search = search;
203+
this.#browserFrame
204+
.goto(url.href)
205+
.catch((error) => this.#browserFrame.page.console.error(error));
240206
}
241207

242208
/**

packages/happy-dom/test/location/Location.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import IGoToOptions from '../../src/browser/types/IGoToOptions.js';
55
import IResponse from '../../src/fetch/types/IResponse.js';
66
import Location from '../../src/location/Location.js';
77
import { beforeEach, describe, it, expect, vi } from 'vitest';
8+
import HashChangeEvent from '../../src/event/events/HashChangeEvent.js';
89

910
const HREF = 'https://google.com/some-path/?key=value&key2=value2#hash';
1011

@@ -17,6 +18,58 @@ describe('Location', () => {
1718
location = new Location(browserFrame, 'about:blank');
1819
});
1920

21+
describe('get hash()', () => {
22+
it('Returns the hash of the URL.', () => {
23+
location = new Location(
24+
browserFrame,
25+
'https://localhost:8080/some-path/?key=value&key2=value2#hash'
26+
);
27+
28+
expect(location.hash).toBe('#hash');
29+
});
30+
});
31+
32+
describe('set hash()', () => {
33+
it('Sets the hash of the URL.', () => {
34+
const events: HashChangeEvent[] = [];
35+
36+
browserFrame.window.addEventListener('hashchange', (event) => {
37+
events.push(<HashChangeEvent>event);
38+
});
39+
40+
location = new Location(
41+
browserFrame,
42+
'https://localhost:8080/some-path/?key=value&key2=value2'
43+
);
44+
45+
location.hash = '#new-hash';
46+
47+
expect(location.hash).toBe('#new-hash');
48+
expect(location.href).toBe(
49+
'https://localhost:8080/some-path/?key=value&key2=value2#new-hash'
50+
);
51+
52+
location.hash = '#new-hash2';
53+
54+
expect(location.hash).toBe('#new-hash2');
55+
expect(location.href).toBe(
56+
'https://localhost:8080/some-path/?key=value&key2=value2#new-hash2'
57+
);
58+
59+
expect(events.length).toBe(2);
60+
expect(events[0].oldURL).toBe('https://localhost:8080/some-path/?key=value&key2=value2');
61+
expect(events[0].newURL).toBe(
62+
'https://localhost:8080/some-path/?key=value&key2=value2#new-hash'
63+
);
64+
expect(events[1].oldURL).toBe(
65+
'https://localhost:8080/some-path/?key=value&key2=value2#new-hash'
66+
);
67+
expect(events[1].newURL).toBe(
68+
'https://localhost:8080/some-path/?key=value&key2=value2#new-hash2'
69+
);
70+
});
71+
});
72+
2073
describe('set href()', () => {
2174
it('Calls browserFrame.goto() to navigate to the URL.', () => {
2275
let calledURL: string | null = null;

0 commit comments

Comments
 (0)