From 0ed7998bf5d8b981c4a979a771c81df03f3ca478 Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Tue, 1 Jul 2025 10:05:14 +1000 Subject: [PATCH 1/6] feat: listen to localStorage instead of hash --- src/app/app.service.ts | 6 +++--- src/main.ts | 34 +++++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/app/app.service.ts b/src/app/app.service.ts index b86ddd91..4ba7548a 100644 --- a/src/app/app.service.ts +++ b/src/app/app.service.ts @@ -47,7 +47,7 @@ export class AppService { constructor() { this.handleLocationHash(); - window.addEventListener('hashchange', () => this.handleLocationHash(), false); + window.addEventListener('storage', () => this.handleLocationHash(), false); } get uriObservable(): Observable { @@ -166,7 +166,7 @@ export class AppService { const tempCustomRequestHeaders: RequestHeader[] = new Array(5); - const fragment = location.hash.substring(1); + const fragment = window.sessionStorage.getItem("hash") || ""; const regex = /([^&=]+)=([^&]*)/g; let m = regex.exec(fragment); while (m) { @@ -286,7 +286,7 @@ export class AppService { newLocationHash += andPrefix + 'uri=' + this.uriParam; } - window.location.hash = newLocationHash; + window.sessionStorage.setItem("hash", newLocationHash); } } diff --git a/src/main.ts b/src/main.ts index ea00b318..20d0550f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,10 +9,30 @@ if (environment.production) { enableProdMode(); } -bootstrapApplication(AppComponent, { - providers: [ - importProvidersFrom(BrowserModule, FormsModule), - provideHttpClient(withInterceptorsFromDi()) - ] -}) - .catch(err => console.log(err)); +let bootstrapped = false; +function bootstrap() { + bootstrapped = true; + bootstrapApplication(AppComponent, { + providers: [ + importProvidersFrom(BrowserModule, FormsModule), + provideHttpClient(withInterceptorsFromDi()) + ] + }) + .catch(err => console.log(err)); +} + +if (window.opener) { + window.addEventListener('message', (event) => { + window.sessionStorage.setItem( + 'hash', + event.data, + ); + window.dispatchEvent(new Event('storage')); + if (!bootstrapped) { + bootstrap(); + } + }); + window.opener.postMessage("ready"); +} else { + bootstrap(); +} From 9f774d81a01789d735d12b14608eb4a86cb53109 Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Mon, 21 Jul 2025 09:40:41 +1000 Subject: [PATCH 2/6] chore: update tests --- src/app/app.service.spec.ts | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/app/app.service.spec.ts b/src/app/app.service.spec.ts index 46768318..352474b4 100644 --- a/src/app/app.service.spec.ts +++ b/src/app/app.service.spec.ts @@ -4,7 +4,7 @@ describe('AppService', () => { let service: AppService; beforeEach(() => { - window.location.hash = ''; + window.sessionStorage.setItem('hash', ''); service = new AppService(); }); @@ -15,49 +15,49 @@ describe('AppService', () => { it('should set custom theme', () => { service.setTheme('Cosmo'); expect(service.getTheme()).toBe('Cosmo'); - expect(window.location.hash).toBe('#theme=Cosmo'); + expect(window.sessionStorage.getItem('hash')).toBe('theme=Cosmo'); }); it('should set default theme', () => { service.setTheme('Default'); expect(service.getTheme()).toBe('Default'); - expect(window.location.hash).toBe(''); + expect(window.sessionStorage.getItem('hash')).toBe(''); }); it('should set 2 column layout', () => { service.setLayout('2'); expect(service.getLayout()).toBe('2'); - expect(window.location.hash).toBe(''); + expect(window.sessionStorage.getItem('hash')).toBe(''); }); it('should set 3 column layout', () => { service.setLayout('3'); expect(service.getLayout()).toBe('3'); - expect(window.location.hash).toBe('#layout=3'); + expect(window.sessionStorage.getItem('hash')).toBe('layout=3'); }); it('should set HTTP OPTIONS', () => { service.setHttpOptions(true); expect(service.getHttpOptions()).toBe(true); - expect(window.location.hash).toBe('#httpOptions=true'); + expect(window.sessionStorage.getItem('hash')).toBe('httpOptions=true'); }); it('should unset HTTP OPTIONS', () => { service.setHttpOptions(false); expect(service.getHttpOptions()).toBe(false); - expect(window.location.hash).toBe(''); + expect(window.sessionStorage.getItem('hash')).toBe(''); }); it('should set all HTTP methods for links', () => { service.setAllHttpMethodsForLinks(true); expect(service.getAllHttpMethodsForLinks()).toBe(true); - expect(window.location.hash).toBe('#allHttpMethodsForLinks=true'); + expect(window.sessionStorage.getItem('hash')).toBe('allHttpMethodsForLinks=true'); }); it('should unset all HTTP methods for links', () => { service.setAllHttpMethodsForLinks(false); expect(service.getAllHttpMethodsForLinks()).toBe(false); - expect(window.location.hash).toBe(''); + expect(window.sessionStorage.getItem('hash')).toBe(''); }); it('should not set invalid layout', () => { @@ -66,7 +66,7 @@ describe('AppService', () => { service.setLayout('4'); expect(service.getLayout()).toBe('2'); - expect(window.location.hash).toBe(''); + expect(window.sessionStorage.getItem('hash')).toBe(''); expect(window.console.error).toHaveBeenCalled(); }); @@ -82,11 +82,11 @@ describe('AppService', () => { expect(service.getCustomRequestHeaders()[0].value).toBe('application/json'); expect(service.getCustomRequestHeaders()[1].key).toBe('authorization'); expect(service.getCustomRequestHeaders()[1].value).toBe('bearer euztsfghfhgwztuzt'); - expect(window.location.hash).toBe('#hkey0=accept&hval0=application/json&hkey1=authorization&hval1=bearer%20euztsfghfhgwztuzt'); + expect(window.sessionStorage.getItem('hash')).toBe('hkey0=accept&hval0=application/json&hkey1=authorization&hval1=bearer euztsfghfhgwztuzt'); }); - it('should parse window location hash', () => { - window.location.hash = '#theme=Cosmo&layout=3&httpOptions=true&allHttpMethodsForLinks=true&hkey0=accept&hval0=text/plain&uri=https://chatty42.herokuapp.com/api/users'; + it('should parse sessionStorage "hash" item', () => { + window.sessionStorage.setItem('hash', 'theme=Cosmo&layout=3&httpOptions=true&allHttpMethodsForLinks=true&hkey0=accept&hval0=text/plain&uri=https://chatty42.herokuapp.com/api/users'); service = new AppService(); expect(service.getCustomRequestHeaders()[0].key).toBe('accept'); @@ -98,8 +98,8 @@ describe('AppService', () => { expect(service.getUri()).toBe('https://chatty42.herokuapp.com/api/users'); }); - it('should parse window location hash with hval before hkey', () => { - window.location.hash = '#theme=Cosmo&layout=3&hval0=text/plain&hkey0=accept&uri=https://chatty42.herokuapp.com/api/users'; + it('should parse sessionStorage "hash" item with hval before hkey', () => { + window.sessionStorage.setItem('hash', 'theme=Cosmo&layout=3&hval0=text/plain&hkey0=accept&uri=https://chatty42.herokuapp.com/api/users'); service = new AppService(); expect(service.getCustomRequestHeaders()[0].key).toBe('accept'); @@ -109,8 +109,8 @@ describe('AppService', () => { expect(service.getUri()).toBe('https://chatty42.herokuapp.com/api/users'); }); - it('should parse window location hash with deprecated hkey "url"', () => { - window.location.hash = '#theme=Cosmo&layout=3&hval0=text/plain&hkey0=accept&url=https://chatty42.herokuapp.com/api/users'; + it('should parse sessionStorage "hash" item with deprecated hkey "url"', () => { + window.sessionStorage.setItem('hash', 'theme=Cosmo&layout=3&hval0=text/plain&hkey0=accept&url=https://chatty42.herokuapp.com/api/users'); service = new AppService(); expect(service.getCustomRequestHeaders()[0].key).toBe('accept'); @@ -120,8 +120,8 @@ describe('AppService', () => { expect(service.getUri()).toBe('https://chatty42.herokuapp.com/api/users'); }); - it('should parse window location hash with unknown hkeys', () => { - window.location.hash = '#theme=Cosmo&xxx=7&layout=3&hval0=text/plain&hkey0=accept&yyy=xxx&url=https://chatty42.herokuapp.com/api/users'; + it('should parse sessionStorage "hash" item with unknown hkeys', () => { + window.sessionStorage.setItem('hash', 'theme=Cosmo&xxx=7&layout=3&hval0=text/plain&hkey0=accept&yyy=xxx&url=https://chatty42.herokuapp.com/api/users'); service = new AppService(); expect(service.getCustomRequestHeaders()[0].key).toBe('accept'); From 578f63cb3f9dccfa0d8f3ae0ce4a40f93cd40ee4 Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Mon, 21 Jul 2025 09:51:11 +1000 Subject: [PATCH 3/6] chore: update e2e-tests --- cypress/e2e/spec.cy.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/spec.cy.ts b/cypress/e2e/spec.cy.ts index 3d5be693..1bf338c0 100644 --- a/cypress/e2e/spec.cy.ts +++ b/cypress/e2e/spec.cy.ts @@ -40,7 +40,8 @@ describe('HAL Explorer App', () => { }); it('should display HAL sections when rendering users resource', () => { - cy.visit('/#uri=http://localhost:3000/movies.hal-forms.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + cy.visit('/'); cy.contains('JSON Properties'); cy.contains('Links'); @@ -51,7 +52,8 @@ describe('HAL Explorer App', () => { }); it('should display only Links section when rendering root api', () => { - cy.visit('/#uri=http://localhost:3000/index.hal.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + cy.visit('/'); cy.get('JSON Properties').should('not.exist'); cy.get('Embedded Resources').should('not.exist'); @@ -60,13 +62,15 @@ describe('HAL Explorer App', () => { }); it('should display POST request dialog', () => { - cy.visit('/#uri=http://localhost:3000/movies.hal-forms.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + cy.visit('/'); cy.get('button.icon-plus').eq(3).click(); cy.contains('HTTP Request Input').should('be.visible'); }); it('should display user profile in POST request dialog', () => { - cy.visit('/#uri=http://localhost:3000/index.hal.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + cy.visit('/'); cy.get('button.icon-plus').eq(0).click(); cy.contains('Email').should('be.visible'); cy.contains('Full name').should('be.visible'); @@ -74,7 +78,8 @@ describe('HAL Explorer App', () => { }); it('should display expanded URI in HAL-FORMS GET request dialog', () => { - cy.visit('/#uri=http://localhost:3000/filter.hal-forms.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + cy.visit('/'); cy.get('button.icon-left-open').last().click(); cy.get('input[id="request-input-title"]').type('myTitle'); @@ -85,7 +90,8 @@ describe('HAL Explorer App', () => { }); it('should display correct properties HAL-FORMS POST request dialog', () => { - cy.visit('/#uri=http://localhost:3000/2posts1get.hal-forms.json'); + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/2posts1get.hal-forms.json'); + cy.visit('/'); cy.get('button.icon-plus').last().click(); cy.get('input[id="request-input-post2"]').type('xxx'); From bc4894f3a8332cb3c4b7f9dbd348a7abc6f5087b Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Tue, 23 Dec 2025 10:18:37 +1100 Subject: [PATCH 4/6] chore: update e2e --- e2e/hal-explorer.spec.ts | 72 ++++++++++++++++++++++++++++++++-------- e2e/ui-blocking.spec.ts | 18 ++++++++-- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/e2e/hal-explorer.spec.ts b/e2e/hal-explorer.spec.ts index 3650b964..04ac866a 100644 --- a/e2e/hal-explorer.spec.ts +++ b/e2e/hal-explorer.spec.ts @@ -40,7 +40,11 @@ test.describe('HAL Explorer App', () => { }); test('should display HAL sections when rendering users resource', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/movies.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); await expect(page.locator('h5:has-text("JSON Properties")').first()).toBeVisible(); @@ -52,7 +56,11 @@ test.describe('HAL Explorer App', () => { }); test('should display only Links section when rendering root api', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); await expect(page.locator('text="JSON Properties"').first()).not.toBeVisible(); @@ -63,7 +71,11 @@ test.describe('HAL Explorer App', () => { }); test('should display POST request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/movies.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS Template Elements section to be loaded @@ -82,7 +94,11 @@ test.describe('HAL Explorer App', () => { }); test('should display user profile in POST request dialog', { tag: '@flaky' }, async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the links section to be fully loaded @@ -112,7 +128,11 @@ test.describe('HAL Explorer App', () => { }); test('should display expanded URI in HAL-FORMS GET request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -140,7 +160,11 @@ test.describe('HAL Explorer App', () => { }); test('should close modal on ESC key', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -169,7 +193,11 @@ test.describe('HAL Explorer App', () => { }); test('should submit request on Enter key in parameterized GET request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -201,11 +229,16 @@ test.describe('HAL Explorer App', () => { await expect(modal).not.toHaveClass(/show/, { timeout: 5000 }); // Verify the URL was updated with the title parameter (meaning the request was made) - await expect(page).toHaveURL(/title=myTitle/); + expect(await page.evaluate(() => sessionStorage.getItem('hash'))) + .toContain('title=myTitle'); }); test('should display correct properties HAL-FORMS POST request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/2posts1get.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/2posts1get.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Click the first POST button (Post 1 template) @@ -224,7 +257,11 @@ test.describe('HAL Explorer App', () => { test('should update URI input field when clicking a link', async ({ page }) => { // Navigate to the root API which has links - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify the initial URI is displayed in the input field @@ -236,7 +273,8 @@ test.describe('HAL Explorer App', () => { await page.locator('button:has(i.bi-chevron-left)').first().click(); // Wait for the browser URL to update - await expect(page).toHaveURL(/#uri=http:\/\/localhost:3000\/users\.hal\.json/); + expect(await page.evaluate(() => sessionStorage.getItem('hash'))) + .toContain('uri=http://localhost:3000/users.hal.json'); // Wait for navigation to complete await page.waitForLoadState('networkidle'); @@ -247,7 +285,11 @@ test.describe('HAL Explorer App', () => { test('should toggle scrollable documentation setting via UI', async ({ page }) => { // Navigate to a simple resource first - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify the page loads correctly @@ -299,7 +341,11 @@ test.describe('HAL Explorer App', () => { }); test('should display links and affordances for 401 error with HAL-FORMS content', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/error-401-with-templates.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/error-401-with-templates.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify that error is displayed diff --git a/e2e/ui-blocking.spec.ts b/e2e/ui-blocking.spec.ts index 3e6e1397..ecc38c09 100644 --- a/e2e/ui-blocking.spec.ts +++ b/e2e/ui-blocking.spec.ts @@ -25,7 +25,11 @@ test.describe('UI Blocking During Requests', () => { test('should disable link buttons during request', async ({ page }) => { // Load initial resource - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify links are visible and enabled @@ -45,7 +49,11 @@ test.describe('UI Blocking During Requests', () => { test('should disable template buttons during request', async ({ page }) => { // Load resource with HAL-FORMS templates - await page.goto('/#uri=http://localhost:3000/movies.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -81,7 +89,11 @@ test.describe('UI Blocking During Requests', () => { test('should disable documentation buttons during request', async ({ page }) => { // Load resource with documentation links - await page.goto('/#uri=http://localhost:3000/index-with-doc-anchor.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index-with-doc-anchor.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Check if documentation button exists and is enabled From 2abe024fea8ee314e04480a8a39f8b7583ff32fe Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Mon, 2 Feb 2026 11:55:09 +1100 Subject: [PATCH 5/6] chore: update e2e tests --- e2e/hal-explorer.spec.ts | 60 +++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/e2e/hal-explorer.spec.ts b/e2e/hal-explorer.spec.ts index 9e7d9845..b1c7ea91 100644 --- a/e2e/hal-explorer.spec.ts +++ b/e2e/hal-explorer.spec.ts @@ -39,7 +39,11 @@ test.describe('HAL Explorer App', () => { }); test('should display HAL sections when rendering users resource', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/movies.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); await expect(page.locator('h5:has-text("JSON Properties")').first()).toBeVisible(); @@ -51,7 +55,11 @@ test.describe('HAL Explorer App', () => { }); test('should display only Links section when rendering root api', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); await expect(page.locator('text="JSON Properties"').first()).not.toBeVisible(); @@ -62,7 +70,11 @@ test.describe('HAL Explorer App', () => { }); test('should display POST request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/movies.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/movies.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS Template Elements section to be loaded @@ -81,7 +93,11 @@ test.describe('HAL Explorer App', () => { }); test('should display user profile in POST request dialog', { tag: '@flaky' }, async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the links section to be fully loaded @@ -114,7 +130,11 @@ test.describe('HAL Explorer App', () => { }); test('should display expanded URI in HAL-FORMS GET request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -141,7 +161,11 @@ test.describe('HAL Explorer App', () => { }); test('should close modal on ESC key', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -170,7 +194,11 @@ test.describe('HAL Explorer App', () => { }); test('should submit request on Enter key in parameterized GET request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/filter.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/filter.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Wait for the HAL-FORMS section to be loaded @@ -206,7 +234,11 @@ test.describe('HAL Explorer App', () => { }); test('should display correct properties HAL-FORMS POST request dialog', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/2posts1get.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/2posts1get.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Click the first POST button (Post 1 template) @@ -225,7 +257,11 @@ test.describe('HAL Explorer App', () => { test('should update URI input field when clicking a link', async ({ page }) => { // Navigate to the root API which has links - await page.goto('/#uri=http://localhost:3000/index.hal.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/index.hal.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify the initial URI is displayed in the input field @@ -247,7 +283,11 @@ test.describe('HAL Explorer App', () => { }); test('should display links and affordances for 401 error with HAL-FORMS content', async ({ page }) => { - await page.goto('/#uri=http://localhost:3000/error-401-with-templates.hal-forms.json'); + await page.goto('/'); + await page.evaluate(() => { + window.sessionStorage.setItem('hash', 'uri=http://localhost:3000/error-401-with-templates.hal-forms.json'); + window.dispatchEvent(new Event('storage')); + }); await page.waitForLoadState('networkidle'); // Verify that error is displayed From b4bf3fe748d7ec8366f6f390706ccff20dfeab9d Mon Sep 17 00:00:00 2001 From: Voon Wong Date: Mon, 2 Feb 2026 12:03:44 +1100 Subject: [PATCH 6/6] chore: fix e2e --- e2e/hal-explorer.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/e2e/hal-explorer.spec.ts b/e2e/hal-explorer.spec.ts index b1c7ea91..1a85fadb 100644 --- a/e2e/hal-explorer.spec.ts +++ b/e2e/hal-explorer.spec.ts @@ -230,7 +230,7 @@ test.describe('HAL Explorer App', () => { await expect(modal).not.toHaveClass(/show/, { timeout: 5000 }); // Verify the URL was updated with the title parameter (meaning the request was made) - await expect(page).toHaveURL(/title=myTitle/); + expect(await page.evaluate(() => sessionStorage.getItem('hash'))).toContain('title=myTitle'); }); test('should display correct properties HAL-FORMS POST request dialog', async ({ page }) => { @@ -273,7 +273,9 @@ test.describe('HAL Explorer App', () => { await page.locator('button:has(i.bi-chevron-left)').first().click(); // Wait for the browser URL to update - await expect(page).toHaveURL(/#uri=http:\/\/localhost:3000\/users\.hal\.json/); + expect(await page.evaluate(() => sessionStorage.getItem('hash'))).toContain( + 'uri=http://localhost:3000/users.hal.json' + ); // Wait for navigation to complete await page.waitForLoadState('networkidle');