From 008ab1c9971928411ee797d50d0aef71dde5f5ed Mon Sep 17 00:00:00 2001
From: Stanislas Gueniffey <stgu@odoo.com>
Date: Mon, 22 May 2023 17:23:33 +0000
Subject: [PATCH] [IMP] web_editor: re-run tests with mobile version

Some tests use methods that have two different implementations (one for
desktop and one for mobile). So far, the only example is deleteBackward
and its mobile counterpart deleteBackwardMobile.

This commit introduces a mechanism in the web_editor tests to avoid
duplicating such tests. When a test calls a method that has two
implementations, it uses the desktop one by default and a flag is set
that indicates the test should be re-run with its mobile implementation
next.

When errors are raised while the mobile version is in use, we prefix
their message with '[MOBILE VERSION]' to ease debugging.

Note that this mechanism relies on proper flag management by the
aforementioned methods.

Task-3054808

closes odoo/odoo#121977

X-original-commit: 712382c6ae200037c58a6677069c5f2ae783ccec
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
---
 .../editor/odoo-editor/test/spec/link.test.js | 35 +-----------
 .../src/js/editor/odoo-editor/test/utils.js   | 57 ++++++++++++-------
 2 files changed, 38 insertions(+), 54 deletions(-)

diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js
index 1ca1f153058b..e5e7298e624b 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js
@@ -3,7 +3,6 @@ import {
     BasicEditor,
     click,
     deleteBackward,
-    deleteBackwardMobile,
     insertText,
     insertParagraphBreak,
     insertLineBreak,
@@ -555,7 +554,7 @@ describe('Link', () => {
                 }
             });
         });
-        it('should keep isolated link after a keyboard delete', async () => {
+        it('should keep isolated link after a delete', async () => {
             await testEditor(BasicEditor, {
                 contentBefore: '<p>a<a href="#/">b[]</a>c</p>',
                 stepFunction: async editor => {
@@ -569,20 +568,6 @@ describe('Link', () => {
                 contentAfter: '<p>a[]c</p>',
             });
         });
-        it('should keep isolated link after a mobile delete', async () => {
-            await testEditor(BasicEditor, {
-                contentBefore: '<p>a<a href="#/">b[]</a>c</p>',
-                stepFunction: async editor => {
-                    const a = await clickOnLink(editor);
-                    console.log(a.closest('.odoo-editor-editable').outerHTML);
-                    await deleteBackwardMobile(editor);
-                    console.log(a.closest('.odoo-editor-editable').outerHTML);
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                },
-                contentAfterEdit: '<p>a<a href="#/" contenteditable="true" data-oe-zws-empty-inline="">[]\u200B</a>c</p>',
-                contentAfter: '<p>a[]c</p>',
-            });
-        });
         it('should keep isolated link after a delete and typing', async () => {
             await testEditor(BasicEditor, {
                 contentBefore: '<p>a<a href="#/">b[]</a>c</p>',
@@ -601,24 +586,6 @@ describe('Link', () => {
                 contentAfter: '<p>a<a href="#/">123[]</a>c</p>',
             });
         });
-        it('should keep isolated link after a mobile delete and typing', async () => {
-            await testEditor(BasicEditor, {
-                contentBefore: '<p>a<a href="#/">b[]</a>c</p>',
-                stepFunction: async editor => {
-                    const a = await clickOnLink(editor);
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                    await deleteBackwardMobile(editor);
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                    await insertText(editor, '1');
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                    await insertText(editor, '2');
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                    await insertText(editor, '3');
-                    window.chai.expect(a.parentElement.isContentEditable).to.be.equal(false);
-                },
-                contentAfter: '<p>a<a href="#/">123[]</a>c</p>',
-            });
-        });
         it('should delete the content from the link when popover is active', async () => {
             await testEditor(BasicEditor, {
                 contentBefore: '<p><a href="#/">abc[]abc</a></p>',
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/utils.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/utils.js
index 7a842122b53c..572a656ac8b3 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/test/utils.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/utils.js
@@ -9,6 +9,11 @@ export const Direction = {
     FORWARD: 'FORWARD',
 };
 
+// True iff test is being run with its mobile implementation.
+let isMobileTest = false;
+// True iff test has mobile implementation for any called method.
+let hasMobileTest = false;
+
 function _nextNode(node) {
     let next = node.firstChild || node.nextSibling;
     if (!next) {
@@ -276,6 +281,9 @@ export function customErrorMessage(assertLocation, value, expected) {
 }
 
 export async function testEditor(Editor = OdooEditor, spec, options = {}) {
+    hasMobileTest = false;
+    isMobileTest = options.isMobile;
+
     const testNode = document.createElement('div');
     const testContainer = document.querySelector('#editor-test-container');
     testContainer.innerHTML = '';
@@ -324,7 +332,12 @@ export async function testEditor(Editor = OdooEditor, spec, options = {}) {
 
         if (spec.stepFunction) {
             editor.observerActive('beforeUnitTests');
-            await spec.stepFunction(editor);
+            try {
+                await spec.stepFunction(editor);
+            } catch (e) {
+                e.message = (isMobileTest ? '[MOBILE VERSION] ' : '') + e.message;
+                throw e;
+            }
             editor.observerUnactive('afterUnitTests');
         }
 
@@ -374,6 +387,8 @@ export async function testEditor(Editor = OdooEditor, spec, options = {}) {
 
     if (error) {
         throw error;
+    } else if (hasMobileTest && !isMobileTest) {
+        await testEditor(Editor, spec, { ...options, isMobile: true });
     }
 }
 
@@ -445,29 +460,31 @@ export async function deleteForward(editor) {
 }
 
 export async function deleteBackward(editor) {
-    const selection = document.getSelection();
-    if (selection.isCollapsed) {
-        editor.execCommand('oDeleteBackward');
+    // This method has two implementations (desktop and mobile).
+    if (isMobileTest) {
+        // Some mobile keyboard use input event to trigger delete.
+        // This is a way to simulate this behavior.
+        const inputEvent = new InputEvent('input', {
+            inputType: 'deleteContentBackward',
+            data: null,
+            bubbles: true,
+            cancelable: false,
+        });
+        editor._onInput(inputEvent);
     } else {
-        // Better representation of what happened in the editor when the user
-        // presses the backspace key.
-        await triggerEvent(editor.editable, 'keydown', { key: 'Backspace' });
-        editor.document.execCommand('delete');
+        hasMobileTest = true; // Flag test for a re-run as mobile.
+        const selection = document.getSelection();
+        if (selection.isCollapsed) {
+            editor.execCommand('oDeleteBackward');
+        } else {
+            // Better representation of what happened in the editor when the user
+            // presses the backspace key.
+            await triggerEvent(editor.editable, 'keydown', { key: 'Backspace' });
+            editor.document.execCommand('delete');
+        }
     }
 }
 
-export async function deleteBackwardMobile(editor) {
-    // Some mobile keyboard use input event to trigger delete.
-    // This is a way to simulate this behavior.
-    const inputEvent = new InputEvent('input', {
-        inputType: 'deleteContentBackward',
-        data: null,
-        bubbles: true,
-        cancelable: false,
-    });
-    editor._onInput(inputEvent);
-}
-
 export async function insertParagraphBreak(editor) {
     editor.execCommand('oEnter');
 }
-- 
GitLab