From 97f410b58ea9f8a42497921dfc46a225e7675bdb Mon Sep 17 00:00:00 2001
From: Benjamin Franzke <bfr@qbus.de>
Date: Wed, 15 Dec 2021 15:26:15 +0100
Subject: [PATCH] [TASK] Prepare ImmediateActionElementTest for ES6 modules

In ES6 mode Viewport.Topbar and ModuleMenu.App properties
cannot be overwritten (in order to be substituted with a call spy),
as they reference other JavaScript modules.
The properties of ES6 models are readonly ("frozen") and can
therefore not be overwritten in a test setup.

Therefore the test is adapted to overwrite the references
in Viewport and ModuleMenu instead.

Also a timeout is added as await import('TYPO3/CMS/Backend/ModuleMenu')
does not guarantee a loading order between modules in ES6, which
causes test asserts to be executed too early.

Releases: main
Resolves: #96396
Related #96323
Change-Id: I5c213b70e43c5e1256e91ffe0d9f12f96dbb6615
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72736
Tested-by: core-ci <typo3@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Benjamin Franzke <bfr@qbus.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Benjamin Franzke <bfr@qbus.de>
---
 .../Element/ImmediateActionElementTest.ts     | 58 ++++++++++---------
 .../Element/ImmediateActionElementTest.js     |  2 +-
 2 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/Tests/Element/ImmediateActionElementTest.ts b/Build/Sources/TypeScript/backend/Tests/Element/ImmediateActionElementTest.ts
index 2bb339a1fc59..413da55a9298 100644
--- a/Build/Sources/TypeScript/backend/Tests/Element/ImmediateActionElementTest.ts
+++ b/Build/Sources/TypeScript/backend/Tests/Element/ImmediateActionElementTest.ts
@@ -29,70 +29,74 @@ describe('TYPO3/CMS/Backend/Element/ImmediateActionElement:', () => {
   });
 
   it('dispatches action when created via constructor', async () => {
-    const backup = viewportObject.Topbar.refresh;
+    const backup = viewportObject.Topbar;
     const observer = {
-      callback: (): void => {
+      refresh: (): void => {
         return;
       },
     };
-    spyOn(observer, 'callback').and.callThrough();
-    viewportObject.Topbar.refresh = observer.callback;
+    spyOn(observer, 'refresh').and.callThrough();
+    (viewportObject as any).Topbar = observer;
     const element = new ImmediateActionElement;
     element.setAttribute('action', 'TYPO3.Backend.Topbar.refresh');
-    expect(observer.callback).not.toHaveBeenCalled();
+    expect(observer.refresh).not.toHaveBeenCalled();
     root.appendChild(element);
     await import('TYPO3/CMS/Backend/Viewport');
-    expect(observer.callback).toHaveBeenCalled();
-    viewportObject.Topbar.refresh = backup;
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    expect(observer.refresh).toHaveBeenCalled();
+    (viewportObject as any).Topbar = backup;
   });
 
   it('dispatches action when created via createElement', async () => {
-    const backup = viewportObject.Topbar.refresh;
+    const backup = viewportObject.Topbar;
     const observer = {
-      callback: (): void => {
+      refresh: (): void => {
         return;
       },
     };
-    spyOn(observer, 'callback').and.callThrough();
-    viewportObject.Topbar.refresh = observer.callback;
+    spyOn(observer, 'refresh').and.callThrough();
+    (viewportObject as any).Topbar = observer;
     const element = <ImmediateActionElement>document.createElement('typo3-immediate-action');
     element.setAttribute('action', 'TYPO3.Backend.Topbar.refresh');
-    expect(observer.callback).not.toHaveBeenCalled();
+    expect(observer.refresh).not.toHaveBeenCalled();
     root.appendChild(element);
     await import('TYPO3/CMS/Backend/Viewport');
-    expect(observer.callback).toHaveBeenCalled();
-    viewportObject.Topbar.refresh = backup;
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    expect(observer.refresh).toHaveBeenCalled();
+    (viewportObject as any).Topbar = backup;
   });
 
   it('dispatches action when created from string', async () => {
-    const backup = moduleMenuApp.App.refreshMenu;
+    const backup = moduleMenuApp.App;
     const observer = {
-      callback: (): void => {
+      refreshMenu: (): void => {
         return;
       },
     };
-    spyOn(observer, 'callback').and.callThrough();
-    moduleMenuApp.App.refreshMenu = observer.callback;
+    spyOn(observer, 'refreshMenu').and.callThrough();
+    (moduleMenuApp as any).App = observer;
     const element = document.createRange().createContextualFragment('<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>').querySelector('typo3-immediate-action');
-    expect(observer.callback).not.toHaveBeenCalled();
+    expect(observer.refreshMenu).not.toHaveBeenCalled();
     root.appendChild(element);
     await import('TYPO3/CMS/Backend/ModuleMenu');
-    expect(observer.callback).toHaveBeenCalled();
-    moduleMenuApp.App.refreshMenu = backup;
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    expect(observer.refreshMenu).toHaveBeenCalled();
+    (viewportObject as any).App = backup;
   });
 
   it('dispatches action when created via innerHTML', async () => {
-    const backup = moduleMenuApp.App.refreshMenu;
+    const backup = moduleMenuApp.App;
     const observer = {
-      callback: (): void => {
+      refreshMenu: (): void => {
         return;
       },
     };
-    spyOn(observer, 'callback').and.callThrough();
-    moduleMenuApp.App.refreshMenu = observer.callback;
+    spyOn(observer, 'refreshMenu').and.callThrough();
+    (moduleMenuApp as any).App = observer;
     root.innerHTML = '<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>';
     await import('TYPO3/CMS/Backend/ModuleMenu');
-    expect(observer.callback).toHaveBeenCalled();
-    moduleMenuApp.App.refreshMenu = backup;
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    expect(observer.refreshMenu).toHaveBeenCalled();
+    (moduleMenuApp as any).App = backup;
   });
 });
diff --git a/typo3/sysext/backend/Tests/JavaScript/Element/ImmediateActionElementTest.js b/typo3/sysext/backend/Tests/JavaScript/Element/ImmediateActionElementTest.js
index aa1b765e4c09..c7fb9d94c15a 100644
--- a/typo3/sysext/backend/Tests/JavaScript/Element/ImmediateActionElementTest.js
+++ b/typo3/sysext/backend/Tests/JavaScript/Element/ImmediateActionElementTest.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,a,n){void 0===n&&(n=a),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[a]}})}:function(e,t,a,n){void 0===n&&(n=a),e[n]=t[a]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)"default"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&__createBinding(t,e,a);return __setModuleDefault(t,e),t};define(["require","exports","TYPO3/CMS/Backend/Element/ImmediateActionElement","TYPO3/CMS/Backend/ModuleMenu","TYPO3/CMS/Backend/Viewport"],(function(e,t,a,n,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),describe("TYPO3/CMS/Backend/Element/ImmediateActionElement:",()=>{let t;beforeEach(()=>{t=document.createElement("div"),document.body.appendChild(t)}),afterEach(()=>{t.remove(),t=null}),it("dispatches action when created via constructor",async()=>{const n=c.Topbar.refresh,r={callback:()=>{}};spyOn(r,"callback").and.callThrough(),c.Topbar.refresh=r.callback;const o=new a.ImmediateActionElement;o.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(r.callback).not.toHaveBeenCalled(),t.appendChild(o),await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Viewport"],t,a)}).then(__importStar),expect(r.callback).toHaveBeenCalled(),c.Topbar.refresh=n}),it("dispatches action when created via createElement",async()=>{const a=c.Topbar.refresh,n={callback:()=>{}};spyOn(n,"callback").and.callThrough(),c.Topbar.refresh=n.callback;const r=document.createElement("typo3-immediate-action");r.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(n.callback).not.toHaveBeenCalled(),t.appendChild(r),await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Viewport"],t,a)}).then(__importStar),expect(n.callback).toHaveBeenCalled(),c.Topbar.refresh=a}),it("dispatches action when created from string",async()=>{const a=n.App.refreshMenu,c={callback:()=>{}};spyOn(c,"callback").and.callThrough(),n.App.refreshMenu=c.callback;const r=document.createRange().createContextualFragment('<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>').querySelector("typo3-immediate-action");expect(c.callback).not.toHaveBeenCalled(),t.appendChild(r),await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,a)}).then(__importStar),expect(c.callback).toHaveBeenCalled(),n.App.refreshMenu=a}),it("dispatches action when created via innerHTML",async()=>{const a=n.App.refreshMenu,c={callback:()=>{}};spyOn(c,"callback").and.callThrough(),n.App.refreshMenu=c.callback,t.innerHTML='<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>',await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,a)}).then(__importStar),expect(c.callback).toHaveBeenCalled(),n.App.refreshMenu=a})})}));
\ No newline at end of file
+var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,n,a){void 0===a&&(a=n),Object.defineProperty(e,a,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,a){void 0===a&&(a=n),e[a]=t[n]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&__createBinding(t,e,n);return __setModuleDefault(t,e),t};define(["require","exports","TYPO3/CMS/Backend/Element/ImmediateActionElement","TYPO3/CMS/Backend/ModuleMenu","TYPO3/CMS/Backend/Viewport"],(function(e,t,n,a,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),describe("TYPO3/CMS/Backend/Element/ImmediateActionElement:",()=>{let t;beforeEach(()=>{t=document.createElement("div"),document.body.appendChild(t)}),afterEach(()=>{t.remove(),t=null}),it("dispatches action when created via constructor",async()=>{const a=r.Topbar,i={refresh:()=>{}};spyOn(i,"refresh").and.callThrough(),r.Topbar=i;const o=new n.ImmediateActionElement;o.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(i.refresh).not.toHaveBeenCalled(),t.appendChild(o),await new Promise((t,n)=>{e(["TYPO3/CMS/Backend/Viewport"],t,n)}).then(__importStar),await new Promise(e=>setTimeout(e,100)),expect(i.refresh).toHaveBeenCalled(),r.Topbar=a}),it("dispatches action when created via createElement",async()=>{const n=r.Topbar,a={refresh:()=>{}};spyOn(a,"refresh").and.callThrough(),r.Topbar=a;const i=document.createElement("typo3-immediate-action");i.setAttribute("action","TYPO3.Backend.Topbar.refresh"),expect(a.refresh).not.toHaveBeenCalled(),t.appendChild(i),await new Promise((t,n)=>{e(["TYPO3/CMS/Backend/Viewport"],t,n)}).then(__importStar),await new Promise(e=>setTimeout(e,100)),expect(a.refresh).toHaveBeenCalled(),r.Topbar=n}),it("dispatches action when created from string",async()=>{const n=a.App,i={refreshMenu:()=>{}};spyOn(i,"refreshMenu").and.callThrough(),a.App=i;const o=document.createRange().createContextualFragment('<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>').querySelector("typo3-immediate-action");expect(i.refreshMenu).not.toHaveBeenCalled(),t.appendChild(o),await new Promise((t,n)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,n)}).then(__importStar),await new Promise(e=>setTimeout(e,100)),expect(i.refreshMenu).toHaveBeenCalled(),r.App=n}),it("dispatches action when created via innerHTML",async()=>{const n=a.App,r={refreshMenu:()=>{}};spyOn(r,"refreshMenu").and.callThrough(),a.App=r,t.innerHTML='<typo3-immediate-action action="TYPO3.ModuleMenu.App.refreshMenu"></typo3-immediate-action>',await new Promise((t,n)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,n)}).then(__importStar),await new Promise(e=>setTimeout(e,100)),expect(r.refreshMenu).toHaveBeenCalled(),a.App=n})})}));
\ No newline at end of file
-- 
GitLab