From 93413cedbe27852583516be5713a25cb1a9e2a48 Mon Sep 17 00:00:00 2001 From: Waldemar Mazurek Date: Wed, 9 Oct 2024 10:08:04 +0200 Subject: [PATCH 1/9] Fixes client HTML5 Information Exposure (#3978) --- container/src/services/container.service.ts | 11 +- .../test/services/container.service.spec.ts | 162 +++++++++--------- 2 files changed, 89 insertions(+), 84 deletions(-) diff --git a/container/src/services/container.service.ts b/container/src/services/container.service.ts index 5ac67c92a2..ac67dbcc20 100644 --- a/container/src/services/container.service.ts +++ b/container/src/services/container.service.ts @@ -22,6 +22,7 @@ export class ContainerService { */ sendCustomMessageToIframe(iframeHandle: any, msg: any, msgName?: string) { const messageName = msgName || 'custom'; + if (iframeHandle.iframe.contentWindow) { const iframeUrl = new URL(iframeHandle.iframe.src); messageName === 'custom' @@ -42,22 +43,25 @@ export class ContainerService { */ dispatch(msg: string, targetCnt: HTMLElement, data: any, callback?: Function, callbackName?: string): void { const customEvent = new CustomEvent(msg, { detail: data }); + if (callback && GenericHelperFunctions.isFunction(callback) && callbackName) { (customEvent as any)[callbackName] = data => { callback(data); }; } + targetCnt.dispatchEvent(customEvent); } /** * Retrieves the target container based on the event source. - * + * * @param event The event object representing the source of the container. - @returns {Object| undefined} The target container object or undefined if not found. + * @returns {Object| undefined} The target container object or undefined if not found. */ getTargetContainer(event) { let cnt; + globalThis.__luigi_container_manager.container.forEach(element => { if (element.iframeHandle?.iframe && element.iframeHandle.iframe.contentWindow === event.source) { cnt = element; @@ -113,7 +117,7 @@ export class ContainerService { }, authData: targetCnt.authData || {} }, - '*' + target.origin ); break; case LuigiInternalMessageID.NAVIGATION_REQUEST: @@ -185,6 +189,7 @@ export class ContainerService { }; window.addEventListener('message', globalThis.__luigi_container_manager.messageListener); } + return globalThis.__luigi_container_manager; } diff --git a/container/test/services/container.service.spec.ts b/container/test/services/container.service.spec.ts index 92359dbf84..0b6f1740ef 100644 --- a/container/test/services/container.service.spec.ts +++ b/container/test/services/container.service.spec.ts @@ -5,18 +5,18 @@ import { ContainerService } from '../../src/services/container.service'; describe('getContainerManager messageListener', () => { let service: ContainerService; let gtcSpy; - let cw : any = {}; - let cm ; + let cw: any = {}; + let cm; let dispatchedEvent; service = new ContainerService(); - cm = service.getContainerManager(); + cm = service.getContainerManager(); beforeEach(() => { // only get context scenario relies on postMessage, so we need special case handling for it const testName = expect.getState().currentTestName; - if (testName === 'test get context message'){ - cw = { postMessage: () => {}} - } + if (testName === 'test get context message') { + cw = { postMessage: () => {} }; + } gtcSpy = jest.spyOn(service, 'getTargetContainer').mockImplementation(() => { return { @@ -32,26 +32,29 @@ describe('getContainerManager messageListener', () => { }); }); - afterEach(()=>{ + afterEach(() => { jest.resetAllMocks(); }); - it('test alert request', () => { + it('test alert request', () => { const event = { source: cw, data: { msg: LuigiInternalMessageID.ALERT_REQUEST, data: { - id: 'navRequest', + id: 'navRequest' } } }; cm.messageListener(event); expect(dispatchedEvent.type).toEqual(Events.ALERT_REQUEST); - expect(dispatchedEvent.detail).toEqual({data: {data: {id: "navRequest"}, msg: "luigi.ux.alert.show"}, source: {}}); - }); + expect(dispatchedEvent.detail).toEqual({ + data: { data: { id: 'navRequest' }, msg: 'luigi.ux.alert.show' }, + source: {} + }); + }); - it('test custom message', () => { + it('test custom message', () => { const event = { source: cw, data: { @@ -84,20 +87,21 @@ describe('getContainerManager messageListener', () => { const postMessageMock = jest.fn(); // Replace the real postMessage with the mock - cw.postMessage = postMessageMock; + cw.postMessage = postMessageMock; + cw.origin = '*'; // Define the message to send and target Origin const message = { - "authData":{}, - "context": {}, - "internal": { - "thirdPartyCookieCheck": { - "disabled": false + authData: {}, + context: {}, + internal: { + thirdPartyCookieCheck: { + disabled: false } }, - "msg": "luigi.init" + msg: 'luigi.init' }; - const targetOrigin = "*"; + const targetOrigin = '*'; // Call the method that should trigger postMessage cm.messageListener(event); @@ -106,10 +110,10 @@ describe('getContainerManager messageListener', () => { expect(postMessageMock).toHaveBeenCalledWith(message, targetOrigin); // Clean up by restoring the original postMessage function - cw.postMessage = () => {} + cw.postMessage = () => {}; + cw.origin = undefined; }); - it('test initialized request', () => { const event = { source: cw, @@ -134,7 +138,7 @@ describe('getContainerManager messageListener', () => { }; cm.messageListener(event); expect(dispatchedEvent.type).toEqual(Events.ADD_SEARCH_PARAMS_REQUEST); - expect(dispatchedEvent.detail).toEqual({data: 'some-data', keepBrowserHistory:true}); + expect(dispatchedEvent.detail).toEqual({ data: 'some-data', keepBrowserHistory: true }); }); it('test add node params request', () => { @@ -148,10 +152,9 @@ describe('getContainerManager messageListener', () => { }; cm.messageListener(event); expect(dispatchedEvent.type).toEqual(Events.ADD_NODE_PARAMS_REQUEST); - expect(dispatchedEvent.detail).toEqual({data: 'some-data', keepBrowserHistory:false}); + expect(dispatchedEvent.detail).toEqual({ data: 'some-data', keepBrowserHistory: false }); }); - it('test confirmationModal show request', () => { const event = { source: cw, @@ -344,34 +347,31 @@ describe('getContainerManager messageListener', () => { expect(dispatchedEvent.type).toEqual(Events.SET_DIRTY_STATUS_REQUEST); }); - it('test default', () => { const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn()); const event = { source: cw, data: { - msg: 'no-func', + msg: 'no-func' } }; cm.messageListener(event); expect(consoleWarnSpy).not.toHaveBeenCalled(); }); - }); - describe('isVisible', () => { let service: ContainerService; service = new ContainerService(); - - afterEach(()=>{ + + afterEach(() => { jest.resetAllMocks(); }); it('should return true for a visible element', () => { // Arrange const visibleElement = document.createElement('div'); - jest.spyOn(visibleElement, 'offsetWidth', 'get').mockImplementation(() => 200) + jest.spyOn(visibleElement, 'offsetWidth', 'get').mockImplementation(() => 200); document.body.appendChild(visibleElement); // Act @@ -409,12 +409,11 @@ describe('isVisible', () => { }); }); - describe('sendCustomMessageToIframe', () => { let service: ContainerService; service = new ContainerService(); - - afterEach(()=>{ + + afterEach(() => { jest.resetAllMocks(); }); @@ -423,13 +422,13 @@ describe('sendCustomMessageToIframe', () => { const iframeHandle = { iframe: { contentWindow: { - postMessage: jest.fn(), + postMessage: jest.fn() }, - src: 'https://example.com', - }, + src: 'https://example.com' + } }; const message = { key: 'value' }; - + // Act service.sendCustomMessageToIframe(iframeHandle, message); @@ -445,13 +444,13 @@ describe('sendCustomMessageToIframe', () => { const iframeHandle = { iframe: { contentWindow: { - postMessage: jest.fn(), + postMessage: jest.fn() }, - src: 'https://example.com', - }, + src: 'https://example.com' + } }; const message = { key: 'value' }; - + // Act service.sendCustomMessageToIframe(iframeHandle, message, 'namedMessage'); @@ -465,10 +464,10 @@ describe('sendCustomMessageToIframe', () => { it('should log an error if contentWindow is not available', () => { // Arrange const iframeHandle = { - iframe: {}, + iframe: {} }; const message = { key: 'value' }; - + // Spy on console.error to capture the log message const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); @@ -486,8 +485,8 @@ describe('sendCustomMessageToIframe', () => { describe('dispatch', () => { let service: ContainerService; service = new ContainerService(); - - afterEach(()=>{ + + afterEach(() => { jest.resetAllMocks(); }); @@ -496,7 +495,7 @@ describe('dispatch', () => { const targetContainer = document.createElement('div'); const eventName = 'customEvent'; const eventData = { key: 'value' }; - targetContainer.dispatchEvent = jest.fn() + targetContainer.dispatchEvent = jest.fn(); // Act service.dispatch(eventName, targetContainer, eventData); @@ -511,29 +510,31 @@ describe('dispatch', () => { const targetContainer = document.createElement('div'); const eventName = 'customEvent'; const eventData = { key: 'value' }; - targetContainer.dispatchEvent = jest.fn() + targetContainer.dispatchEvent = jest.fn(); // Define a callback function - const callbackFunction = (data) => { + const callbackFunction = data => { // This function should not be called in this test }; // Act - service. dispatch(eventName, targetContainer, eventData, callbackFunction, 'onCallback'); - + service.dispatch(eventName, targetContainer, eventData, callbackFunction, 'onCallback'); + // Assert - globalThis.CustomEvent = jest.fn().mockImplementation((type, eventInit) => ({ isTrusted: false, onCallback: callbackFunction })); + globalThis.CustomEvent = jest + .fn() + .mockImplementation((type, eventInit) => ({ isTrusted: false, onCallback: callbackFunction })); - const dispatchedEventMock = {"isTrusted": false, "onCallback": expect.any(Function)}; - expect(targetContainer.dispatchEvent).toHaveBeenCalledWith( expect.objectContaining(dispatchedEventMock)); + const dispatchedEventMock = { isTrusted: false, onCallback: expect.any(Function) }; + expect(targetContainer.dispatchEvent).toHaveBeenCalledWith(expect.objectContaining(dispatchedEventMock)); }); }); describe('getTargetContainer', () => { let service: ContainerService; service = new ContainerService(); - - afterEach(()=>{ + + afterEach(() => { jest.resetAllMocks(); }); @@ -542,24 +543,24 @@ describe('getTargetContainer', () => { const mockContainer1 = { iframeHandle: { iframe: { - contentWindow: 'source1', - }, - }, + contentWindow: 'source1' + } + } }; const mockContainer2 = { iframeHandle: { iframe: { - contentWindow: 'source2', - }, - }, + contentWindow: 'source2' + } + } }; globalThis.__luigi_container_manager = { - container: [mockContainer1, mockContainer2], + container: [mockContainer1, mockContainer2] }; const mockEvent = { - source: 'source2', // Matched with mockContainer2 + source: 'source2' // Matched with mockContainer2 }; // Act @@ -574,24 +575,24 @@ describe('getTargetContainer', () => { const mockContainer1 = { iframeHandle: { iframe: { - contentWindow: 'source1', - }, - }, + contentWindow: 'source1' + } + } }; const mockContainer2 = { iframeHandle: { iframe: { - contentWindow: 'source2', - }, - }, + contentWindow: 'source2' + } + } }; globalThis.__luigi_container_manager = { - container: [mockContainer1, mockContainer2], + container: [mockContainer1, mockContainer2] }; const mockEvent = { - source: 'source3', // No matching container + source: 'source3' // No matching container }; // Act @@ -605,7 +606,7 @@ describe('getTargetContainer', () => { describe('getContainerManager branch', () => { let service: ContainerService; service = new ContainerService(); - + beforeEach(() => { globalThis.__luigi_container_manager = undefined; jest.resetAllMocks(); @@ -627,7 +628,7 @@ describe('getContainerManager branch', () => { it('should return the existing container manager if it has been initialized', () => { const existingManager = { container: ['existingData'], - messageListener: jest.fn(), + messageListener: jest.fn() }; globalThis.__luigi_container_manager = existingManager; @@ -644,14 +645,14 @@ describe('getContainerManager branch', () => { // Verify that addEventListener was called with 'message' event type expect(containerManager).toBeDefined(); expect(containerManager.container).toEqual([]); - expect(spy).toHaveBeenCalledWith('message', expect.any(Function)) + expect(spy).toHaveBeenCalledWith('message', expect.any(Function)); }); }); describe('registerContainer', () => { let service: ContainerService; service = new ContainerService(); - + beforeEach(() => { jest.resetAllMocks(); }); @@ -659,10 +660,10 @@ describe('registerContainer', () => { it('should add an HTMLElement to the container', () => { // Arrange const containerManager = { - container: [], + container: [] }; const container = document.createElement('div'); - service.getContainerManager = jest.fn().mockReturnValue(containerManager); + service.getContainerManager = jest.fn().mockReturnValue(containerManager); // Act service.registerContainer(container); @@ -670,6 +671,5 @@ describe('registerContainer', () => { // Assert expect(containerManager.container).toContain(container); expect(service.getContainerManager).toHaveBeenCalled(); - }); -}); \ No newline at end of file +}); From 6d05b1a0a687d62eadc580d0eb8956ca4d60d3f9 Mon Sep 17 00:00:00 2001 From: Waldemar Mazurek Date: Thu, 10 Oct 2024 10:24:13 +0200 Subject: [PATCH 2/9] Adds e2e tests for uxManager chain requests (#3984) --- .../compound/wc-compound-container.cy.js | 23 +++++++++++++++++++ .../e2e/test-app/wc/wc-container.cy.js | 23 +++++++++++++++++++ .../test-app/compound/compoundClientAPI.html | 21 +++++++++-------- container/test-app/compound/helloWorldWC.js | 6 ++--- container/test-app/wc/clientAPI.html | 21 +++++++++-------- container/test-app/wc/helloWorldWC.js | 6 ++--- 6 files changed, 76 insertions(+), 24 deletions(-) diff --git a/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js b/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js index ed5894b384..494d7788cd 100644 --- a/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js +++ b/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js @@ -224,5 +224,28 @@ describe('Compound Container Tests', () => { ); }); }); + + it('LuigiClient API uxManagerChainRequests', () => { + const alertMessages = [ + 'LuigiClient.uxManager().openUserSettings()', + 'LuigiClient.uxManager().closeUserSettings()', + 'LuigiClient.uxManager().removeBackdrop()', + 'LuigiClient.uxManager().collapseLeftSideNav()', + 'LuigiClient.uxManager().hideAppLoadingIndicator()', + 'LuigiClient.uxManager().getDocumentTitle()=my-title' + ]; + + cy.on('window:alert', stub); + + cy.get(containerSelector) + .shadow() + .get('#uxManagerManyRequests') + .click() + .then(() => { + alertMessages.forEach((msg, index) => { + expect(stub.getCall(index)).to.be.calledWith(msg); + }); + }); + }); }); }); diff --git a/container/cypress/e2e/test-app/wc/wc-container.cy.js b/container/cypress/e2e/test-app/wc/wc-container.cy.js index 608cbaf424..a6e4916d65 100644 --- a/container/cypress/e2e/test-app/wc/wc-container.cy.js +++ b/container/cypress/e2e/test-app/wc/wc-container.cy.js @@ -185,5 +185,28 @@ describe('Web Container Test', () => { expect(stub.getCall(0)).to.be.calledWith('My Custom Message from Microfrontend'); }); }); + + it('LuigiClient API uxManagerChainRequests', () => { + const alertMessages = [ + 'LuigiClient.uxManager().openUserSettings()', + 'LuigiClient.uxManager().closeUserSettings()', + 'LuigiClient.uxManager().removeBackdrop()', + 'LuigiClient.uxManager().collapseLeftSideNav()', + 'LuigiClient.uxManager().hideAppLoadingIndicator()', + 'LuigiClient.uxManager().getDocumentTitle()=my-title' + ]; + + cy.on('window:alert', stub); + + cy.get(containerSelector) + .shadow() + .get('#uxManagerManyRequests') + .click() + .then(() => { + alertMessages.forEach((msg, index) => { + expect(stub.getCall(index)).to.be.calledWith(msg); + }); + }); + }); }); }); diff --git a/container/test-app/compound/compoundClientAPI.html b/container/test-app/compound/compoundClientAPI.html index 134e3b1f02..9071cc17e2 100644 --- a/container/test-app/compound/compoundClientAPI.html +++ b/container/test-app/compound/compoundClientAPI.html @@ -194,35 +194,38 @@

compoundContainer.addEventListener(MFEventID.SET_ANCHOR_LINK_REQUEST, event => { console.log('anchor', event.detail); }); - - // uxManager(). closeUserSettings openUserSettings collapseLeftSideNav - // setDocumentTitle removeBackdrop hideAppLoadingIndicator + compoundContainer.addEventListener(MFEventID.OPEN_USER_SETTINGS_REQUEST, event => { + console.log('Open User Settings Request received', event.detail); + alert('LuigiClient.uxManager().openUserSettings()'); + }); compoundContainer.addEventListener(MFEventID.CLOSE_USER_SETTINGS_REQUEST, event => { console.log('Close User Settings Request received', event.detail); + alert('LuigiClient.uxManager().closeUserSettings()'); }); - compoundContainer.addEventListener(MFEventID.OPEN_USER_SETTINGS_REQUEST, event => { - console.log('Open User Settings Request received', event.detail); + compoundContainer.addEventListener(MFEventID.REMOVE_BACKDROP_REQUEST, event => { + console.log('Remove Backdrop Request received', event.detail); + alert('LuigiClient.uxManager().removeBackdrop()'); }); compoundContainer.addEventListener(MFEventID.COLLAPSE_LEFT_NAV_REQUEST, event => { console.log('Collapse Left Side Nav Request received', event.detail); + alert('LuigiClient.uxManager().collapseLeftSideNav()'); }); compoundContainer.addEventListener(MFEventID.SET_DOCUMENT_TITLE_REQUEST, event => { console.log('Set Document Title Request received', event.detail); - }); - compoundContainer.addEventListener(MFEventID.REMOVE_BACKDROP_REQUEST, event => { - console.log('Remove Backdrop Request received', event.detail); + compoundContainer.documentTitle = event.detail; }); compoundContainer.addEventListener( MFEventID.HIDE_LOADING_INDICATOR_REQUEST, event => { console.log('Hide Loading Indicator Request received', event.detail); + alert('LuigiClient.uxManager().hideAppLoadingIndicator()'); } ); // linkManager listeners: // path exists compoundContainer.addEventListener(MFEventID.PATH_EXISTS_REQUEST, event => { - console.log('Remove Backdrop Request received', event.detail, event); + console.log('Path Exists Request received', event.detail, event); // send back result with defined 'callback' // event: MFEventID.PathExistsEvent can be used as an event type to get the callback function event.callback(true); diff --git a/container/test-app/compound/helloWorldWC.js b/container/test-app/compound/helloWorldWC.js index 265ed9b199..422d1270f5 100644 --- a/container/test-app/compound/helloWorldWC.js +++ b/container/test-app/compound/helloWorldWC.js @@ -267,12 +267,12 @@ export default class extends HTMLElement { this.$uxManagerManyRequests = this._shadowRoot.querySelector('#uxManagerManyRequests'); this.$uxManagerManyRequests.addEventListener('click', () => { - this.LuigiClient.uxManager().closeUserSettings(); this.LuigiClient.uxManager().openUserSettings(); - this.LuigiClient.uxManager().collapseLeftSideNav(); - this.LuigiClient.uxManager().setDocumentTitle('my-title'); + this.LuigiClient.uxManager().closeUserSettings(); this.LuigiClient.uxManager().removeBackdrop(); + this.LuigiClient.uxManager().collapseLeftSideNav(); this.LuigiClient.uxManager().hideAppLoadingIndicator(); + this.LuigiClient.uxManager().setDocumentTitle('my-title'); this.LuigiClient.uxManager().showAlert({ text: 'LuigiClient.uxManager().getDocumentTitle()=' + this.LuigiClient.uxManager().getDocumentTitle(), type: 'info' diff --git a/container/test-app/wc/clientAPI.html b/container/test-app/wc/clientAPI.html index 2685fe82c2..7944f307cf 100644 --- a/container/test-app/wc/clientAPI.html +++ b/container/test-app/wc/clientAPI.html @@ -123,35 +123,38 @@

luigiContainer.addEventListener(MFEventID.SET_ANCHOR_LINK_REQUEST, event => { console.log('anchor', event.detail); }); - - // uxManager(). closeUserSettings openUserSettings collapseLeftSideNav - // setDocumentTitle removeBackdrop hideAppLoadingIndicator + luigiContainer.addEventListener(MFEventID.OPEN_USER_SETTINGS_REQUEST, event => { + console.log('Open User Settings Request received', event.detail); + alert('LuigiClient.uxManager().openUserSettings()'); + }); luigiContainer.addEventListener(MFEventID.CLOSE_USER_SETTINGS_REQUEST, event => { console.log('Close User Settings Request received', event.detail); + alert('LuigiClient.uxManager().closeUserSettings()'); }); - luigiContainer.addEventListener(MFEventID.OPEN_USER_SETTINGS_REQUEST, event => { - console.log('Open User Settings Request received', event.detail); + luigiContainer.addEventListener(MFEventID.REMOVE_BACKDROP_REQUEST, event => { + console.log('Remove Backdrop Request received', event.detail); + alert('LuigiClient.uxManager().removeBackdrop()'); }); luigiContainer.addEventListener(MFEventID.COLLAPSE_LEFT_NAV_REQUEST, event => { console.log('Collapse Left Side Nav Request received', event.detail); + alert('LuigiClient.uxManager().collapseLeftSideNav()'); }); luigiContainer.addEventListener(MFEventID.SET_DOCUMENT_TITLE_REQUEST, event => { console.log('Set Document Title Request received', event.detail); - }); - luigiContainer.addEventListener(MFEventID.REMOVE_BACKDROP_REQUEST, event => { - console.log('Remove Backdrop Request received', event.detail); + luigiContainer.documentTitle = event.detail; }); luigiContainer.addEventListener( MFEventID.HIDE_LOADING_INDICATOR_REQUEST, event => { console.log('Hide Loading Indicator Request received', event.detail); + alert('LuigiClient.uxManager().hideAppLoadingIndicator()'); } ); // linkManager listeners: // path exists luigiContainer.addEventListener(MFEventID.PATH_EXISTS_REQUEST, event => { - console.log('Remove Backdrop Request received', event.detail, event); + console.log('Path Exists Request received', event.detail, event); // send back result with defined 'callback' // event: MFEventID.PathExistsEvent can be used as an event type to get the callback function event.callback(true); diff --git a/container/test-app/wc/helloWorldWC.js b/container/test-app/wc/helloWorldWC.js index 93464af71a..be7ba2d12f 100644 --- a/container/test-app/wc/helloWorldWC.js +++ b/container/test-app/wc/helloWorldWC.js @@ -234,12 +234,12 @@ export default class extends HTMLElement { this.$uxManagerManyRequests = this._shadowRoot.querySelector('#uxManagerManyRequests'); this.$uxManagerManyRequests.addEventListener('click', () => { - this.LuigiClient.uxManager().closeUserSettings(); this.LuigiClient.uxManager().openUserSettings(); - this.LuigiClient.uxManager().collapseLeftSideNav(); - this.LuigiClient.uxManager().setDocumentTitle('my-title'); + this.LuigiClient.uxManager().closeUserSettings(); this.LuigiClient.uxManager().removeBackdrop(); + this.LuigiClient.uxManager().collapseLeftSideNav(); this.LuigiClient.uxManager().hideAppLoadingIndicator(); + this.LuigiClient.uxManager().setDocumentTitle('my-title'); this.LuigiClient.uxManager().showAlert({ text: 'LuigiClient.uxManager().getDocumentTitle()=' + this.LuigiClient.uxManager().getDocumentTitle(), type: 'info' From 5584969c4a159f91759df75b21c5d48720976dd1 Mon Sep 17 00:00:00 2001 From: Waldemar Mazurek Date: Thu, 10 Oct 2024 10:35:29 +0200 Subject: [PATCH 3/9] Adds e2e tests for linkManager chain requests (#3979) --- .../compound/wc-compound-container.cy.js | 13 ++++++++++++ .../e2e/test-app/wc/wc-container.cy.js | 13 ++++++++++++ container/test-app/compound/helloWorldWC.js | 21 ++++++++++++++----- container/test-app/wc/helloWorldWC.js | 21 ++++++++++++++----- 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js b/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js index 494d7788cd..37d0cb97f1 100644 --- a/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js +++ b/container/cypress/e2e/test-app/compound/wc-compound-container.cy.js @@ -204,6 +204,19 @@ describe('Compound Container Tests', () => { cy.get('#defer-init-flag').should('exist'); }); + it('linkManagerChainRequests for navigation', () => { + cy.on('window:alert', stub); + + cy.get(containerSelector) + .shadow() + .get('#linkManagerChainRequests') + .click() + .then(() => { + expect(stub.getCall(0)).to.be.calledWith('LuigiClient.linkManager().navigate()'); + cy.hash().should('eq', '#hello-world-wc'); + }); + }); + it('LuigiClient API publishEvent', () => { cy.on('window:alert', stub); diff --git a/container/cypress/e2e/test-app/wc/wc-container.cy.js b/container/cypress/e2e/test-app/wc/wc-container.cy.js index a6e4916d65..f8c4e78b4c 100644 --- a/container/cypress/e2e/test-app/wc/wc-container.cy.js +++ b/container/cypress/e2e/test-app/wc/wc-container.cy.js @@ -145,6 +145,19 @@ describe('Web Container Test', () => { .should('have.text', 'Received Custom Message: cool custom Message'); }); + it('linkManagerChainRequests for navigation', () => { + cy.on('window:alert', stub); + + cy.get(containerSelector) + .shadow() + .get('#linkManagerChainRequests') + .click() + .then(() => { + expect(stub.getCall(0)).to.be.calledWith('LuigiClient.linkManager().navigate()'); + cy.hash().should('eq', '#hello-world-wc'); + }); + }); + it('pathExists', () => { cy.on('window:alert', stub); diff --git a/container/test-app/compound/helloWorldWC.js b/container/test-app/compound/helloWorldWC.js index 422d1270f5..0bd8936510 100644 --- a/container/test-app/compound/helloWorldWC.js +++ b/container/test-app/compound/helloWorldWC.js @@ -281,18 +281,29 @@ export default class extends HTMLElement { this.$linkManagerChainRequests = this._shadowRoot.querySelector('#linkManagerChainRequests'); this.$linkManagerChainRequests.addEventListener('click', () => { + const path = 'hello-world-wc'; + const ctx = { ctx: 123 }; + this.LuigiClient.linkManager() - .fromContext({ ctx: 123 }) - .navigate('hello-world-wc'); + .fromContext(ctx) + .navigate(); this.LuigiClient.linkManager() .fromClosestContext() - .navigate('hello-world-wc'); + .navigate(path); this.LuigiClient.linkManager() .fromVirtualTreeRoot() - .navigate('hello-world-wc'); + .navigate(path); + this.LuigiClient.linkManager() + .fromParent(ctx) + .navigate(path); this.LuigiClient.linkManager() .withParams('my-params') - .navigate('hello-world-wc'); + .navigate(path); + this.LuigiClient.linkManager().navigate(path); + this.LuigiClient.uxManager().showAlert({ + text: 'LuigiClient.linkManager().navigate()', + type: 'info' + }); }); this.$linkManagerOpenAsRequests = this._shadowRoot.querySelector('#linkManagerOpenAsRequests'); diff --git a/container/test-app/wc/helloWorldWC.js b/container/test-app/wc/helloWorldWC.js index be7ba2d12f..2ecaa8d9fb 100644 --- a/container/test-app/wc/helloWorldWC.js +++ b/container/test-app/wc/helloWorldWC.js @@ -248,18 +248,29 @@ export default class extends HTMLElement { this.$linkManagerChainRequests = this._shadowRoot.querySelector('#linkManagerChainRequests'); this.$linkManagerChainRequests.addEventListener('click', () => { + const path = 'hello-world-wc'; + const ctx = { ctx: 123 }; + this.LuigiClient.linkManager() - .fromContext({ ctx: 123 }) - .navigate('hello-world-wc'); + .fromContext(ctx) + .navigate(); this.LuigiClient.linkManager() .fromClosestContext() - .navigate('hello-world-wc'); + .navigate(path); this.LuigiClient.linkManager() .fromVirtualTreeRoot() - .navigate('hello-world-wc'); + .navigate(path); + this.LuigiClient.linkManager() + .fromParent(ctx) + .navigate(path); this.LuigiClient.linkManager() .withParams('my-params') - .navigate('hello-world-wc'); + .navigate(path); + this.LuigiClient.linkManager().navigate(path); + this.LuigiClient.uxManager().showAlert({ + text: 'LuigiClient.linkManager().navigate()', + type: 'info' + }); }); this.$linkManagerOpenAsRequests = this._shadowRoot.querySelector('#linkManagerOpenAsRequests'); From aad422e4be179b480e8eefba8a9022d57a791eb6 Mon Sep 17 00:00:00 2001 From: Waldemar Mazurek Date: Thu, 10 Oct 2024 10:58:41 +0200 Subject: [PATCH 4/9] Fixes client DOM XSS issue (#3974) --- website/docs/src/app.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/website/docs/src/app.html b/website/docs/src/app.html index 5a3c90b1a2..7275db21d4 100644 --- a/website/docs/src/app.html +++ b/website/docs/src/app.html @@ -15,14 +15,18 @@
%sveltekit.body%