Repository: SAP/luigi (js)

Issue:
PopState event (history update) triggered by Luigi Client when updating context from Luigi Container
**Description**
`Luigi Container` is injected to the host application, it communicates with `Luigi Client` in a Microfrontend. Microfrontend is embedded in the host application via an iframe.

Whenever the host application updates a context (using https://github.com/SAP/luigi/blob/main/container/src/api/container-api.ts#L11), the Microfrontend receives it and dispatches a PopState event as well as replaces current state in browser history (https://github.com/SAP/luigi/blob/v2.7.5/client/src/lifecycleManager.js#L109-L110). It triggers some unexpected navigation issues in the Microfrontend integration.

Library versions:
- @luigi-project/container@1.0.0
- @luigi-project/client@2.7.5

**Expected result**

PopState event / history update are not triggered. Context update is not related to navigation, at least for such setup like Luigi Container+Luigi Client

**Actual result**

PopState event / history update happen.

**Steps to reproduce**

Details provided above

**Troubleshooting**
The only workaround I've found so far is to make a call with `isNavigateBack` flag. It doesn't have any side effects but according to source code it prevents history to be updated:
```ts
container.updateContext(context, {isNavigateBack: true});
```

I consider this as a workaround because this flag seems to be irrelevant here

Pull request:
Avoid browser history change from luigi client on context update
Fixes #3597

Patch:
diff --git a/container/src/api/container-api.ts b/container/src/api/container-api.ts
index fa4a5d1e..6b07f543 100644
--- a/container/src/api/container-api.ts
+++ b/container/src/api/container-api.ts
@@ -15,7 +15,9 @@ export class ContainerAPIFunctions {
         iframeHandle,
         {
           context: contextObj,
-          internal: internalParameter
+          internal: internalParameter,
+          // set withoutSync to true for the container case to avoid browser history changes from luigi client
+          withoutSync: true
         },
         LuigiInternalMessageID.SEND_CONTEXT_OBJECT
       );
@@ -51,7 +53,7 @@ export class ContainerAPIFunctions {
    * @param dismissKey the dismiss key being sent if any
    * @param iframeHandle the handle of the iframe to send the message to
    */
-  closeAlert (id: any, dismissKey: any, iframeHandle: any) {
+  closeAlert(id: any, dismissKey: any, iframeHandle: any) {
     containerService.sendCustomMessageToIframe(iframeHandle, { id, dismissKey }, LuigiInternalMessageID.ALERT_CLOSED);
   }
 }


Test patch:
diff --git a/container/test/api/container-api.spec.ts b/container/test/api/container-api.spec.ts
index 762bcb26..0fd2d85f 100644
--- a/container/test/api/container-api.spec.ts
+++ b/container/test/api/container-api.spec.ts
@@ -11,7 +11,7 @@ describe('Container Service', () => {
         it('iframeHandle exists, WITH internalParam', () => {
             // mock and spy
             const contextObj = {};
-            const internal = { empty: false};
+            const internal = { empty: false };
             const iframeHandle = {
                 data: 'test'
             };
@@ -19,10 +19,10 @@ describe('Container Service', () => {
             const spy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
 
             // act
-            containerAPI.updateContext(contextObj, internal,iframeHandle);
+            containerAPI.updateContext(contextObj, internal, iframeHandle);
 
             // assert
-            expect(spy).toHaveBeenCalledWith(iframeHandle, {context: contextObj, internal: internal }, LuigiInternalMessageID.SEND_CONTEXT_OBJECT)
+            expect(spy).toHaveBeenCalledWith(iframeHandle, { context: contextObj, internal: internal, withoutSync: true }, LuigiInternalMessageID.SEND_CONTEXT_OBJECT)
         });
 
         it('iframeHandle exists, UNDEFINED internalParam ', () => {
@@ -36,10 +36,10 @@ describe('Container Service', () => {
             const spy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
 
             // act
-            containerAPI.updateContext(contextObj, internal,iframeHandle);
+            containerAPI.updateContext(contextObj, internal, iframeHandle);
 
             // assert
-            expect(spy).toHaveBeenCalledWith(iframeHandle, {context: contextObj, internal: {} }, LuigiInternalMessageID.SEND_CONTEXT_OBJECT)
+            expect(spy).toHaveBeenCalledWith(iframeHandle, { context: contextObj, internal: {}, withoutSync: true }, LuigiInternalMessageID.SEND_CONTEXT_OBJECT)
         });
 
 
@@ -53,7 +53,7 @@ describe('Container Service', () => {
             const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn());
 
             // act
-            containerAPI.updateContext(contextObj, internal,iframeHandle);
+            containerAPI.updateContext(contextObj, internal, iframeHandle);
 
             // assert
             expect(sendCustomMSGSpy).not.toHaveBeenCalled();
@@ -74,12 +74,12 @@ describe('Container Service', () => {
             containerService.sendCustomMessageToIframe = jest.fn();
             const spy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
 
-            containerAPI.closeAlert(id, dismissKey,iframeHandle );
+            containerAPI.closeAlert(id, dismissKey, iframeHandle);
 
-            expect(spy).toHaveBeenCalledWith(iframeHandle, {id, dismissKey}, LuigiInternalMessageID.ALERT_CLOSED)
+            expect(spy).toHaveBeenCalledWith(iframeHandle, { id, dismissKey }, LuigiInternalMessageID.ALERT_CLOSED)
         });
     });
-    
+
 
     describe('sendCustomMessage', () => {
         let containerAPI = new ContainerAPIFunctions();
@@ -107,7 +107,7 @@ describe('Container Service', () => {
             containerService.dispatch = jest.fn();
             const spy = jest.spyOn(containerService, 'dispatch');
 
-            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent,iframeHandle, data );
+            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent, iframeHandle, data);
 
             expect(spy).toHaveBeenCalledWith(id, mainComponent._luigi_mfe_webcomponent, data)
         });
@@ -115,7 +115,7 @@ describe('Container Service', () => {
         it('isWebComponent FALSE, WITH msg ID', () => {
             // mock and spy
             const id = 'some-id';
-            const mainComponent = { };
+            const mainComponent = {};
             const data = {
                 id: id
             };
@@ -129,18 +129,18 @@ describe('Container Service', () => {
             const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(jest.fn());
 
             // act
-            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent, iframeHandle, data );
+            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent, iframeHandle, data);
 
             // assert
-            expect(sendCustomMSGSpy).toHaveBeenCalledWith(iframeHandle, data )
+            expect(sendCustomMSGSpy).toHaveBeenCalledWith(iframeHandle, data)
             expect(consoleWarnSpy).toHaveBeenCalledWith('Property "id" is reserved and can not be used in custom message data')
         });
 
         it('isWebComponent FALSE, UNDEFINED msg ID', () => {
             // mock and spy
             const id = 'some-id';
-            const mainComponent = { };
-            const data = { };
+            const mainComponent = {};
+            const data = {};
             const isWebComponent = false;
             const iframeHandle = {
                 data: 'test'
@@ -148,13 +148,13 @@ describe('Container Service', () => {
 
             containerService.sendCustomMessageToIframe = jest.fn();
             const sendCustomMSGSpy = jest.spyOn(containerService, 'sendCustomMessageToIframe');
-            const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(()=>{jest.fn()});
-            
+            const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { jest.fn() });
+
             // act
-            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent, iframeHandle, data );
+            containerAPI.sendCustomMessage(id, mainComponent, isWebComponent, iframeHandle, data);
 
             // assert
-            expect(sendCustomMSGSpy).toHaveBeenCalledWith(iframeHandle, {id} )
+            expect(sendCustomMSGSpy).toHaveBeenCalledWith(iframeHandle, { id })
             expect(consoleWarnSpy).not.toHaveBeenCalled();
         });
     });