diff --git a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js
index b6e60b2f44a67ced97f23048c0dbe2ceb4fa9f50..878d3c63c9cdfd1d1de5830a0aaf094b724702b0 100644
--- a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js
+++ b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js
@@ -305,22 +305,60 @@ const styles = {
 export function toggleFormat(editor, format) {
     const selection = editor.document.getSelection();
     if (!selection.rangeCount) return;
-    if (selection.getRangeAt(0).collapsed) {
-        insertAndSelectZws(selection);
+    const wasCollapsed = selection.getRangeAt(0).collapsed;
+    let zws;
+    if (wasCollapsed) {
+        if (selection.anchorNode.nodeType === Node.TEXT_NODE && selection.anchorNode.textContent === '\u200b') {
+            zws = selection.anchorNode;
+            selection.getRangeAt(0).selectNode(zws);
+        } else {
+            zws = insertAndSelectZws(selection);
+        }
     }
     getDeepRange(editor.editable, { splitText: true, select: true, correctTripleClick: true });
+    const {anchorNode, anchorOffset, focusNode, focusOffset} = editor.document.getSelection();
     const style = styles[format];
-    const isAlreadyFormatted = !!getSelectedNodes(editor.editable)
-        .filter(n => n.nodeType === Node.TEXT_NODE && n.nodeValue.trim().length)
-        .find(n => style.is(n.parentElement));
-    applyInlineStyle(editor, el => {
-        if (isAlreadyFormatted) {
-            const block = closestBlock(el);
-            el.style[style.name] = style.is(block) ? 'normal' : getComputedStyle(block)[style.name];
+    const selectedTextNodes = getSelectedNodes(editor.editable)
+        .filter(n => n.nodeType === Node.TEXT_NODE && n.nodeValue.trim().length);
+    const isAlreadyFormatted = !!selectedTextNodes.find(n => style.is(n.parentElement));
+    if (isAlreadyFormatted && style.name === 'textDecorationLine') {
+        const decoratedPairs = new Set(selectedTextNodes.map(n => [closestElement(n, `[style*="text-decoration-line: ${style.value}"]`), n]));
+        for (const [closestDecorated, textNode] of decoratedPairs) {
+            const splitResult = splitAroundUntil(textNode, closestDecorated);
+            const decorationToRemove = splitResult[0] || splitResult[1] || closestDecorated;
+            decorationToRemove.style.removeProperty('text-decoration-line');
+            if (!decorationToRemove.style.cssText) {
+                for (const child of decorationToRemove.childNodes) {
+                    decorationToRemove.before(child);
+                }
+                decorationToRemove.remove();
+            }
+        }
+        if (wasCollapsed) {
+            setSelection(zws, 1);
         } else {
-            el.style[style.name] = style.value;
+            setSelection(anchorNode, anchorOffset, focusNode, focusOffset);
         }
-    });
+    } else {
+        applyInlineStyle(editor, el => {
+            if (isAlreadyFormatted) {
+                const block = closestBlock(el);
+                el.style[style.name] = style.is(block) ? 'normal' : getComputedStyle(block)[style.name];
+            } else if (style.name === 'textDecorationLine' && el.style[style.name]) {
+                // The <span> (el) has a text decoration and we want to set
+                // another. We don't want to replace the old with the new, we
+                // want to add a new one (eg it was underlined, we want it also
+                // strikeThrough).
+                const newChild = document.createElement('span');
+                const children = [...el.childNodes];
+                el.prepend(newChild);
+                newChild.append(...children);
+                newChild.style[style.name] = style.value;
+            } else {
+                el.style[style.name] = style.value;
+            }
+        });
+    }
 }
 function addColumn(editor, beforeOrAfter) {
     getDeepRange(editor.editable, { select: true }); // Ensure deep range for finding td.
diff --git a/addons/web_editor/static/lib/odoo-editor/src/utils/sanitize.js b/addons/web_editor/static/lib/odoo-editor/src/utils/sanitize.js
index fbc971db0a61334bafc8f076861a6e3bd4b04263..4c66f2349a4a9317cbe7041881ba06ef15a52c9d 100644
--- a/addons/web_editor/static/lib/odoo-editor/src/utils/sanitize.js
+++ b/addons/web_editor/static/lib/odoo-editor/src/utils/sanitize.js
@@ -113,7 +113,9 @@ class Sanitize {
             node = nodeP;
         }
 
-        // Remove zero-width spaces added by `fillEmpty` when there is content.
+        // Remove zero-width spaces added by `fillEmpty` when there is content
+        // and the selection is not next to it.
+        const anchor = this.root.ownerDocument.getSelection().anchorNode;
         if (
             node.nodeType === Node.TEXT_NODE &&
             node.textContent.includes('\u200B') &&
@@ -129,7 +131,8 @@ class Sanitize {
                         sibling.length > 0
                 )
             ) &&
-            !isBlock(node.parentElement)
+            !isBlock(node.parentElement) &&
+            anchor !== node
         ) {
             const restoreCursor = preserveCursor(this.root.ownerDocument);
             node.textContent = node.textContent.replace('\u200B', '');
diff --git a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js
index 4e3a1c88cfa904405dd67b8c6049c1d0d321d8b5..3b6f31c55239c3c2bb111897907398247324df62 100644
--- a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js
+++ b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js
@@ -883,7 +883,14 @@ export function isItalic(node) {
  * @returns {boolean}
  */
 export function isUnderline(node) {
-    return getComputedStyle(closestElement(node)).textDecorationLine === 'underline';
+    let parent = closestElement(node);
+    while (parent) {
+        if (getComputedStyle(parent).textDecorationLine === 'underline') {
+            return true;
+        }
+        parent = parent.parentElement;
+    }
+    return false;
 }
 /**
  * Return true if the given node appears struck through.
@@ -892,7 +899,14 @@ export function isUnderline(node) {
  * @returns {boolean}
  */
 export function isStrikeThrough(node) {
-    return getComputedStyle(closestElement(node)).textDecorationLine === 'line-through';
+    let parent = closestElement(node);
+    while (parent) {
+        if (getComputedStyle(parent).textDecorationLine === 'line-through') {
+            return true;
+        }
+        parent = parent.parentElement;
+    }
+    return false;
 }
 
 export function isUnbreakable(node) {
@@ -1475,12 +1489,14 @@ export function fillEmpty(el) {
  * its anchor point. Then, select that zero-width space.
  *
  * @param {Selection} selection
+ * @returns {Node} the inserted zero-width space
  */
 export function insertAndSelectZws(selection) {
     const offset = selection.anchorOffset;
     const zws = insertText(selection, '\u200B');
     splitTextNode(zws, offset);
     selection.getRangeAt(0).selectNode(zws);
+    return zws;
 }
 /**
  * Removes the given node if invisible and all its invisible ancestors.
diff --git a/addons/web_editor/static/lib/odoo-editor/test/spec/format.test.js b/addons/web_editor/static/lib/odoo-editor/test/spec/format.test.js
index 77e86d31c79953917ac5bd39e8fd690077279d5a..edd3b7f377be557bcd742bb0a1158fd6caa6833e 100644
--- a/addons/web_editor/static/lib/odoo-editor/test/spec/format.test.js
+++ b/addons/web_editor/static/lib/odoo-editor/test/spec/format.test.js
@@ -15,6 +15,8 @@ const strikeThrough = async editor => {
 };
 
 describe('Format', () => {
+    const b = content => `<span style="font-weight: bolder;">${content}</span>`;
+    const notB = (content, weight) => `<span style="font-weight: ${weight || 'normal'};">${content}</span>`;
     describe('bold', () => {
         it('should make a few characters bold', async () => {
             await testEditor(BasicEditor, {
@@ -30,6 +32,20 @@ describe('Format', () => {
                 contentAfter: '<p><span style="font-weight: bolder;">ab<span style="font-weight: 400;">[cde]</span>fg</span></p>',
             });
         });
+        it('should make two paragraphs bold', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: '<p>[abc</p><p>def]</p>',
+                stepFunction: bold,
+                contentAfter: `<p>${b(`[abc`)}</p><p>${b(`def]`)}</p>`,
+            });
+        });
+        it('should make two paragraphs not bold', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>${b(`[abc`)}</p><p>${b(`def]`)}</p>`,
+                stepFunction: bold,
+                contentBefore: `<p>${notB(`[abc`)}</p><p>${notB(`def]`, 400)}</p>`,
+            });
+        });
         it('should make a whole heading bold after a triple click', async () => {
             await testEditor(BasicEditor, {
                 contentBefore: '<h1><span style="font-weight: normal;">[ab</span></h1><p>]cd</p>',
@@ -60,138 +76,312 @@ describe('Format', () => {
             });
         });
     });
+    const i = content => `<span style="font-style: italic;">${content}</span>`;
+    const notI = content => `<span style="font-style: normal;">${content}</span>`;
     describe('italic', () => {
         it('should make a few characters italic', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[cde]fg</p>',
+                contentBefore: `<p>ab[cde]fg</p>`,
                 stepFunction: italic,
-                contentAfter: '<p>ab<span style="font-style: italic;">[cde]</span>fg</p>',
+                contentAfter: `<p>ab${i(`[cde]`)}fg</p>`,
             });
         });
         it('should make a few characters not italic', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="font-style: italic;">ab[cde]fg</span></p>',
+                contentBefore: `<p>${i(`ab[cde]fg`)}</p>`,
                 stepFunction: italic,
-                contentAfter: '<p><span style="font-style: italic;">ab<span style="font-style: normal;">[cde]</span>fg</span></p>',
+                contentAfter: `<p>${i(`ab${notI(`[cde]`)}fg`)}</p>`,
+            });
+        });
+        it('should make two paragraphs italic', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: '<p>[abc</p><p>def]</p>',
+                stepFunction: italic,
+                contentAfter: `<p>${i(`[abc`)}</p><p>${i(`def]`)}</p>`,
+            });
+        });
+        it('should make two paragraphs not italic', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>${i(`[abc`)}</p><p>${i(`def]`)}</p>`,
+                stepFunction: italic,
+                contentBefore: `<p>${notI(`[abc`)}</p><p>${notI(`def]`)}</p>`,
             });
         });
         it('should make a whole heading italic after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1>[ab</h1><p>]cd</p>',
+                contentBefore: `<h1>[ab</h1><p>]cd</p>`,
                 stepFunction: italic,
-                contentAfter: '<h1><span style="font-style: italic;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>${i(`[ab]`)}</h1><p>cd</p>`,
             });
         });
         it('should make a whole heading not italic after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1><span style="font-style: italic;">[ab</span></h1><p>]cd</p>',
+                contentBefore: `<h1>${i(`[ab`)}</h1><p>]cd</p>`,
                 stepFunction: italic,
                 // TODO: ideally should restore regular h1 without span instead.
-                contentAfter: '<h1><span style="font-style: normal;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>${notI(`[ab]`)}</h1><p>cd</p>`,
             });
         });
         it('should get ready to type in italic', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[]cd</p>',
+                contentBefore: `<p>ab[]cd</p>`,
                 stepFunction: italic,
-                contentAfter: '<p>ab<span style="font-style: italic;">[]\u200B</span>cd</p>',
+                contentAfter: `<p>ab${i(`[]\u200B`)}cd</p>`,
             });
         });
         it('should get ready to type in not italic', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="font-style: italic;">ab[]cd</span></p>',
+                contentBefore: `<p>${i(`ab[]cd`)}</p>`,
                 stepFunction: italic,
-                contentAfter: '<p><span style="font-style: italic;">ab<span style="font-style: normal;">[]\u200B</span>cd</span></p>',
+                contentAfter: `<p>${i(`ab${notI(`[]\u200B`)}cd`)}</p>`,
             });
         });
     });
+    const u = content => `<span style="text-decoration-line: underline;">${content}</span>`;
     describe('underline', () => {
         it('should make a few characters underline', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[cde]fg</p>',
+                contentBefore: `<p>ab[cde]fg</p>`,
                 stepFunction: underline,
-                contentAfter: '<p>ab<span style="text-decoration-line: underline;">[cde]</span>fg</p>',
+                contentAfter: `<p>ab${u(`[cde]`)}fg</p>`,
             });
         });
         it('should make a few characters not underline', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="text-decoration-line: underline;">ab[cde]fg</span></p>',
+                contentBefore: `<p>${u(`ab[cde]fg`)}</p>`,
                 stepFunction: underline,
-                contentAfter: '<p><span style="text-decoration-line: underline;">ab<span style="text-decoration-line: none;">[cde]</span>fg</span></p>',
+                contentAfter: `<p>${u(`ab[`)}cde]${u(`fg`)}</p>`,
+            });
+        });
+        it('should make two paragraphs underline', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: '<p>[abc</p><p>def]</p>',
+                stepFunction: underline,
+                contentAfter: `<p>${u(`[abc`)}</p><p>${u(`def]`)}</p>`,
+            });
+        });
+        it('should make two paragraphs not underline', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>${u(`[abc`)}</p><p>${u(`def]`)}</p>`,
+                stepFunction: underline,
+                contentAfter: '<p>[abc</p><p>def]</p>',
             });
         });
         it('should make a whole heading underline after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1>[ab</h1><p>]cd</p>',
+                contentBefore: `<h1>[ab</h1><p>]cd</p>`,
                 stepFunction: underline,
-                contentAfter: '<h1><span style="text-decoration-line: underline;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>${u(`[ab]`)}</h1><p>cd</p>`,
             });
         });
         it('should make a whole heading not underline after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1><span style="text-decoration-line: underline;">[ab</span></h1><p>]cd</p>',
+                contentBefore: `<h1>${u(`[ab`)}</h1><p>]cd</p>`,
                 stepFunction: underline,
-                // TODO: ideally should restore regular h1 without span instead.
-                contentAfter: '<h1><span style="text-decoration-line: none;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>[ab]</h1><p>cd</p>`,
             });
         });
         it('should get ready to type in underline', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[]cd</p>',
+                contentBefore: `<p>ab[]cd</p>`,
                 stepFunction: underline,
-                contentAfter: '<p>ab<span style="text-decoration-line: underline;">[]\u200B</span>cd</p>',
+                contentAfter: `<p>ab${u(`[]\u200B`)}cd</p>`,
             });
         });
         it('should get ready to type in not underline', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="text-decoration-line: underline;">ab[]cd</span></p>',
+                contentBefore: `<p>${u(`ab[]cd`)}</p>`,
                 stepFunction: underline,
-                contentAfter: '<p><span style="text-decoration-line: underline;">ab<span style="text-decoration-line: none;">[]\u200B</span>cd</span></p>',
+                contentAfter: `<p>${u(`ab`)}\u200B[]${u(`cd`)}</p>`,
             });
         });
     });
+    const s = content => `<span style="text-decoration-line: line-through;">${content}</span>`;
     describe('strikeThrough', () => {
         it('should make a few characters strikeThrough', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[cde]fg</p>',
+                contentBefore: `<p>ab[cde]fg</p>`,
                 stepFunction: strikeThrough,
-                contentAfter: '<p>ab<span style="text-decoration-line: line-through;">[cde]</span>fg</p>',
+                contentAfter: `<p>ab${s(`[cde]`)}fg</p>`,
             });
         });
         it('should make a few characters not strikeThrough', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="text-decoration-line: line-through;">ab[cde]fg</span></p>',
+                contentBefore: `<p>${s(`ab[cde]fg`)}</p>`,
+                stepFunction: strikeThrough,
+                contentAfter: `<p>${s(`ab[`)}cde]${s(`fg`)}</p>`,
+            });
+        });
+        it('should make two paragraphs strikeThrough', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: '<p>[abc</p><p>def]</p>',
+                stepFunction: strikeThrough,
+                contentAfter: `<p>${s(`[abc`)}</p><p>${s(`def]`)}</p>`,
+            });
+        });
+        it('should make two paragraphs not strikeThrough', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>${s(`[abc`)}</p><p>${s(`def]`)}</p>`,
                 stepFunction: strikeThrough,
-                contentAfter: '<p><span style="text-decoration-line: line-through;">ab<span style="text-decoration-line: none;">[cde]</span>fg</span></p>',
+                contentAfter: '<p>[abc</p><p>def]</p>',
             });
         });
         it('should make a whole heading strikeThrough after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1>[ab</h1><p>]cd</p>',
+                contentBefore: `<h1>[ab</h1><p>]cd</p>`,
                 stepFunction: strikeThrough,
-                contentAfter: '<h1><span style="text-decoration-line: line-through;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>${s(`[ab]`)}</h1><p>cd</p>`,
             });
         });
         it('should make a whole heading not strikeThrough after a triple click', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<h1><span style="text-decoration-line: line-through;">[ab</span></h1><p>]cd</p>',
+                contentBefore: `<h1>${s(`[ab`)}</h1><p>]cd</p>`,
                 stepFunction: strikeThrough,
-                // TODO: ideally should restore regular h1 without span instead.
-                contentAfter: '<h1><span style="text-decoration-line: none;">[ab]</span></h1><p>cd</p>',
+                contentAfter: `<h1>[ab]</h1><p>cd</p>`,
             });
         });
         it('should get ready to type in strikeThrough', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p>ab[]cd</p>',
+                contentBefore: `<p>ab[]cd</p>`,
                 stepFunction: strikeThrough,
-                contentAfter: '<p>ab<span style="text-decoration-line: line-through;">[]\u200B</span>cd</p>',
+                contentAfter: `<p>ab${s(`[]\u200B`)}cd</p>`,
             });
         });
         it('should get ready to type in not strikeThrough', async () => {
             await testEditor(BasicEditor, {
-                contentBefore: '<p><span style="text-decoration-line: line-through;">ab[]cd</span></p>',
+                contentBefore: `<p>${s(`ab[]cd`)}</p>`,
                 stepFunction: strikeThrough,
-                contentAfter: '<p><span style="text-decoration-line: line-through;">ab<span style="text-decoration-line: none;">[]\u200B</span>cd</span></p>',
+                contentAfter: `<p>${s(`ab`)}\u200B[]${s(`cd`)}</p>`,
+            });
+        });
+    });
+    describe('underline + strikeThrough', () => {
+        it('should get ready to write in strikeThrough without underline (underline was first)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(s(`cd[]ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(s(`cd`))}${s(`\u200b[]`)}${u(s(`ef`))}</p>`,
+            });
+        });
+        it('should restore underline after removing it (collapsed, strikeThrough)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(s(`cd`))}${s(`\u200b[]`)}${u(s(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(s(`cd`))}${s(u(`[]\u200b`))}${u(s(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline after restoring it after removing it (collapsed, strikeThrough)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(s(`cd`))}${s(u(`[]\u200b`))}${u(s(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(s(`cd`))}${s(`\u200b[]`)}${u(s(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline after restoring it and writing after removing it (collapsed, strikeThrough)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(s(`cd`))}${s(u(`ghi[]`))}${u(s(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(s(`cd`))}${s(u(`ghi`) + `\u200b[]`)}${u(s(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline, write, restore underline, write, remove underline again, write (collapsed, strikeThrough)', async () => {
+            const uselessSpan = u(''); // TODO: clean
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(s(`cd[]ef`))}</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'A');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'B');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'C');
+                },
+                contentAfterEdit: `<p>ab${u(s(`cd`))}${s(`A${u(`B`)}C[]${uselessSpan}`)}${u(s(`ef`))}</p>`,
+            });
+        });
+    });
+    describe('underline + italic', () => {
+        const iAndU = content => `<span style="font-style: italic; text-decoration-line: underline;">${content}</span>`;
+        it('should get ready to write in italic and underline', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab[]cd</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('italic');
+                    await editor.execCommand('underline');
+                },
+                contentAfter: `<p>ab${iAndU(`[]\u200B`)}cd</p>`,
+            });
+        });
+        it('should get ready to write in italic, after changing one\'s mind about underline', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab[]cd</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('italic');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('underline');
+                },
+                contentAfter: `<p>ab${i(`\u200B[]`)}cd</p>`,
+            });
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab[]cd</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('underline');
+                    await editor.execCommand('italic');
+                    await editor.execCommand('underline');
+                },
+                contentAfter: `<p>ab${i(`\u200B[]`)}cd</p>`,
+            });
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab[]cd</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('underline');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('italic');
+                },
+                contentAfter: `<p>ab${i(`[]\u200B`)}cd</p>`,
+            });
+        });
+        it('should get ready to write in italic without underline (underline was first)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(i(`cd[]ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(i(`cd`))}${i(`\u200b[]`)}${u(i(`ef`))}</p>`,
+            });
+        });
+        it('should restore underline after removing it (collapsed, italic)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(i(`cd`))}${i(`\u200b[]`)}${u(i(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(i(`cd`))}${iAndU(`[]\u200b`)}${u(i(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline after restoring it after removing it (collapsed, italic)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(i(`cd`))}${i(u(`[]\u200b`))}${u(i(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(i(`cd`))}${i(`\u200b[]`)}${u(i(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline after restoring it and writing after removing it (collapsed, italic)', async () => {
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(i(`cd`))}${i(u(`ghi[]`))}${u(i(`ef`))}</p>`,
+                stepFunction: underline,
+                contentAfter: `<p>ab${u(i(`cd`))}${i(u(`ghi`) + `\u200b[]`)}${u(i(`ef`))}</p>`,
+            });
+        });
+        it('should remove underline, write, restore underline, write, remove underline again, write (collapsed, italic)', async () => {
+            const uselessSpan = u(''); // TODO: clean
+            await testEditor(BasicEditor, {
+                contentBefore: `<p>ab${u(i(`cd[]ef`))}</p>`,
+                stepFunction: async editor => {
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'A');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'B');
+                    await editor.execCommand('underline');
+                    await editor.execCommand('insertText', 'C');
+                },
+                contentAfterEdit: `<p>ab${u(i(`cd`))}${i(`A${u(`B`)}C[]${uselessSpan}`)}${u(i(`ef`))}</p>`,
             });
         });
     });