This is an automated email from the ASF dual-hosted git repository.

voidmatcha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new 0fe444ec82 [ZEPPELIN-6536] Stabilize notebook keyboard shortcut e2e 
editor seeding
0fe444ec82 is described below

commit 0fe444ec82673ed3741db387512b0c2afdb3c8f6
Author: YONGJAE LEE (이용재) <[email protected]>
AuthorDate: Sat Jul 18 00:19:45 2026 +0900

    [ZEPPELIN-6536] Stabilize notebook keyboard shortcut e2e editor seeding
    
    ### What is this PR for?
    This PR stabilizes the notebook keyboard-shortcut e2e suite by avoiding 
keyboard-driven fixture seeding in the Monaco editor.
    
    In the Firefox Playwright job, setup could trigger Monaco suggestions while 
typing fixture content. That let Enter accept a suggestion before the shortcut 
assertion ran, corrupting expected text, for example `line` becoming `license`.
    
    The helper now seeds fixture text through the editor textarea without 
shortcut-style key events. The shortcut assertions still use real keyboard 
events.
    
    This also re-enables the clone-content shortcut coverage now that 
https://github.com/apache/zeppelin/pull/5254 has been merged. The skip is no 
longer needed, and keeping it would leave that shortcut path without regression 
coverage.
    
    ### What type of PR is it?
    Bug Fix
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/browse/ZEPPELIN-6536
    
    ### How should this be tested?
    
    ### Screenshots (if appropriate)
    N/A
    
    ### Questions:
    * Does the license files need to update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    
    Closes #5304 from voidmatcha/fix/stabilize-keyboard-shortcut-e2e.
    
    Signed-off-by: YONGJAE LEE <[email protected]>
---
 .../e2e/models/notebook-keyboard-page.ts           | 152 ++++++--------
 .../keyboard/notebook-keyboard-shortcuts.spec.ts   | 232 +++++++++------------
 zeppelin-web-angular/e2e/utils.ts                  |   7 +-
 3 files changed, 165 insertions(+), 226 deletions(-)

diff --git a/zeppelin-web-angular/e2e/models/notebook-keyboard-page.ts 
b/zeppelin-web-angular/e2e/models/notebook-keyboard-page.ts
index 7c2d8b875c..44e5c3118c 100644
--- a/zeppelin-web-angular/e2e/models/notebook-keyboard-page.ts
+++ b/zeppelin-web-angular/e2e/models/notebook-keyboard-page.ts
@@ -58,7 +58,7 @@ export class NotebookKeyboardPage extends BasePage {
     this.settingsButton = page.locator('a[nz-dropdown]');
     this.clearOutputOption = page.locator('li.list-item:has-text("Clear 
output")');
     this.deleteButton = page.locator('button:has-text("Delete"), 
.delete-paragraph-button');
-    this.addParagraphComponent = 
page.locator('zeppelin-notebook-add-paragraph').last(); // last() — the 
add-paragraph strip at the bottom of the notebook; the first() is the top strip 
and is less reliable for insertions
+    this.addParagraphComponent = 
page.locator('zeppelin-notebook-add-paragraph').last(); // last() — bottom 
add-paragraph strip; first() is the top strip
     this.searchDialog = page.locator(
       '.dropdown-menu.search-code, .search-widget, .find-widget, 
[role="dialog"]:has-text("Find")'
     );
@@ -100,15 +100,34 @@ export class NotebookKeyboardPage extends BasePage {
     // Wait for any loading/rendering to complete
     await this.page.waitForLoadState('domcontentloaded');
 
-    const browserName = this.page.context().browser()?.browserType().name();
-    if (browserName === 'firefox' || browserName === 'chromium') {
-      // Additional wait for Firefox to ensure editor is fully ready
-      await this.page.waitForTimeout(200); // JUSTIFIED: Monaco editor 
requires extra settle time in Firefox before focus dispatch
-    }
-
     await this.focusEditorElement(paragraph, paragraphIndex);
   }
 
+  // Focus the paragraph host: it carries the shortcut bindings (tabindex=-1) 
and works even when the editor is hidden (a %md paragraph collapses its editor 
after running).
+  async focusParagraphHost(paragraphIndex: number = 0): Promise<void> {
+    const paragraph = this.getParagraphByIndex(paragraphIndex);
+    // Retry: toggling output re-renders the paragraph, so a single focus() 
can miss.
+    await expect(async () => {
+      await paragraph.evaluate((el: HTMLElement) => el.focus());
+      await expect(paragraph).toBeFocused({ timeout: 1000 });
+    }).toPass({ timeout: 10000 });
+  }
+
+  // Dispatch a paragraph-scoped shortcut and retry only until its effect is 
observed.
+  async pressShortcutFromHostUntil(
+    paragraphIndex: number,
+    press: () => Promise<void>,
+    isSettled: () => Promise<boolean>
+  ): Promise<void> {
+    await expect(async () => {
+      if (!(await isSettled())) {
+        await this.focusParagraphHost(paragraphIndex);
+        await press();
+      }
+      expect(await isSettled()).toBe(true);
+    }).toPass({ timeout: 15000 });
+  }
+
   async typeInEditor(text: string): Promise<void> {
     await this.page.keyboard.type(text);
   }
@@ -209,7 +228,7 @@ export class NotebookKeyboardPage extends BasePage {
 
     // Wait for paragraph count to increase
     await this.page.waitForFunction(
-      expectedCount => 
document.querySelectorAll('zeppelin-notebook-paragraph').length > 
expectedCount, // JUSTIFIED: waitForFunction polls DOM count — Playwright 
toHaveCount() requires exact match, not minimum
+      expectedCount => 
document.querySelectorAll('zeppelin-notebook-paragraph').length > 
expectedCount, // JUSTIFIED: waitForFunction polls DOM count; Playwright 
toHaveCount() requires exact match, not minimum
       currentCount,
       { timeout: 10000 }
     );
@@ -347,40 +366,29 @@ export class NotebookKeyboardPage extends BasePage {
   }
 
   async getCodeEditorContent(): Promise<string> {
-    // Fallback to Angular scope
-    const angularContent = await this.page.evaluate(() => {
-      const paragraphElement = 
document.querySelector('zeppelin-notebook-paragraph'); // JUSTIFIED: accesses 
AngularJS $scope via window.angular — not accessible via Playwright locator API
-      if (paragraphElement) {
-        // eslint-disable-next-line  @typescript-eslint/no-explicit-any
-        const angular = (window as any).angular;
-        if (angular) {
-          const scope = angular.element(paragraphElement).scope();
-          if (scope && scope.$ctrl && scope.$ctrl.paragraph) {
-            return scope.$ctrl.paragraph.text || '';
-          }
-        }
+    return this.readEditorText(this.paragraphContainer.first());
+  }
+
+  // Reconstruct editor text from Monaco's absolutely-positioned `.view-line` 
divs sorted by top (DOM order need not match line order), via textContent; 
innerText is "" for off-layout lines in headless Chromium.
+  private async readEditorText(paragraph: Locator): Promise<string> {
+    const monaco = paragraph.locator('.monaco-editor').first();
+    if ((await monaco.count()) > 0) {
+      const text = await monaco.evaluate((el: Element) => {
+        const lines = Array.from(el.querySelectorAll('.view-line')) as 
HTMLElement[];
+        lines.sort((a, b) => parseInt(a.style.top || '0', 10) - 
parseInt(b.style.top || '0', 10));
+        return lines.map(l => (l.textContent || '').replace(/\u00a0/g, ' 
')).join('\n');
+      });
+      if (text.trim().length > 0) {
+        return text;
       }
-      return null;
-    });
-
-    if (angularContent !== null) {
-      return angularContent;
     }
-
-    // Fallback to DOM-based approaches
-    const selectors = ['.monaco-editor .view-lines', '.CodeMirror-line', 
'.ace_line', 'textarea'];
-
-    for (const selector of selectors) {
-      const element = this.page.locator(selector).first();
-      if (await element.isVisible({ timeout: 1000 })) {
-        if (selector === 'textarea') {
-          return await element.inputValue();
-        } else {
-          return (await element.textContent()) || '';
-        }
+    const textarea = paragraph.locator('.monaco-editor textarea').first();
+    if ((await textarea.count()) > 0) {
+      const value = await textarea.inputValue().catch(() => '');
+      if (value) {
+        return value;
       }
     }
-
     return '';
   }
 
@@ -419,19 +427,26 @@ export class NotebookKeyboardPage extends BasePage {
       for (let i = 0; i < contentLength; i++) {
         await this.page.keyboard.press('Backspace');
       }
-      await this.page.waitForTimeout(100); // JUSTIFIED: Monaco content state 
settle between backspaces and new input
-
-      await this.page.keyboard.type(content);
 
-      await this.page.waitForTimeout(300); // JUSTIFIED: Monaco content state 
settle after keystroke sequence
+      // JUSTIFIED: Monaco textarea can be covered by editor overlays during 
fixture setup.
+      await editorInput.fill(content, { force: true });
     } else {
       // Standard clearing for other browsers
       await this.pressSelectAll();
       await this.page.keyboard.press('Delete');
-      await editorInput.fill(content, { force: true }); // JUSTIFIED: Monaco 
textarea may be overlaid by editor decorations after select+delete; force 
required for programmatic fill
+      // JUSTIFIED: Monaco textarea can be overlaid after select+delete during 
fixture setup.
+      await editorInput.fill(content, { force: true });
     }
 
-    await this.page.waitForTimeout(200); // JUSTIFIED: Monaco content state 
settle after fill completes
+    // Wait for the full normalized editor content to avoid stale Monaco 
renders.
+    const expected = content.replace(/\s+/g, '');
+    if (expected.length === 0) {
+      await expect.poll(async () => (await 
this.readEditorText(paragraph)).trim(), { timeout: 10000 }).toBe('');
+    } else {
+      await expect
+        .poll(async () => (await 
this.readEditorText(paragraph)).replace(/\s+/g, ''), { timeout: 10000 })
+        .toContain(expected);
+    }
   }
 
   // Helper methods for verifying shortcut effects
@@ -496,41 +511,7 @@ export class NotebookKeyboardPage extends BasePage {
   }
 
   async getCodeEditorContentByIndex(paragraphIndex: number): Promise<string> {
-    const paragraph = this.getParagraphByIndex(paragraphIndex);
-
-    const editorTextarea = paragraph.locator('.monaco-editor textarea');
-    if (await editorTextarea.isVisible()) {
-      const textContent = await editorTextarea.inputValue();
-      if (textContent) {
-        return textContent;
-      }
-    }
-
-    const viewLines = paragraph.locator('.monaco-editor .view-lines');
-    if (await viewLines.isVisible()) {
-      const text = await viewLines.evaluate((el: Element) => (el as 
HTMLElement).innerText || '');
-      if (text && text.trim().length > 0) {
-        return text;
-      }
-    }
-
-    const scopeContent = await paragraph.evaluate(el => {
-      // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      const angular = (window as any).angular;
-      if (angular) {
-        const scope = angular.element(el).scope();
-        if (scope && scope.$ctrl && scope.$ctrl.paragraph) {
-          return scope.$ctrl.paragraph.text || '';
-        }
-      }
-      return '';
-    });
-
-    if (scopeContent) {
-      return scopeContent;
-    }
-
-    return '';
+    return this.readEditorText(this.getParagraphByIndex(paragraphIndex));
   }
 
   async waitForParagraphCountChange(expectedCount: number, timeout: number = 
30000): Promise<void> {
@@ -650,9 +631,9 @@ export class NotebookKeyboardPage extends BasePage {
 
     const editor = paragraph.locator('.monaco-editor, .CodeMirror, 
.ace_editor, textarea').first();
 
-    await editor.waitFor({ state: 'visible', timeout: 5000 }).catch(() => {}); 
// JUSTIFIED: UI stabilization — editor may not be visible yet; click attempt 
follows
-    await editor.click({ force: true, trial: true }).catch(async () => {
-      // JUSTIFIED: UI stabilization — falls back to textarea focus if click 
fails
+    await editor.waitFor({ state: 'visible', timeout: 5000 }).catch(() => {}); 
// editor may not be visible yet; the click/focus below retries
+    // A `trial` click only runs actionability checks and never focuses; do a 
real click, falling back to focusing the textarea if it is intercepted.
+    await editor.click({ force: true }).catch(async () => {
       const textArea = editor.locator('textarea').first();
       if ((await textArea.count()) > 0) {
         await textArea.focus({ timeout: 1000 });
@@ -669,6 +650,8 @@ export class NotebookKeyboardPage extends BasePage {
     if (hasTextArea) {
       await textArea.focus();
       await expect(textArea).toBeFocused({ timeout: 3000 });
+      // Monaco sets the `focused` class only after processing the focus 
event; activeElement can be set earlier, dropping the shortcut. Gate on the 
class so focus is real.
+      await expect(editor).toHaveClass(/\bfocused\b/, { timeout: 10000 });
     } else {
       await expect(editor).toHaveClass(/focused|focus|active/, { timeout: 
30000 });
     }
@@ -676,9 +659,8 @@ export class NotebookKeyboardPage extends BasePage {
 
   private async executePlatformShortcut(shortcut: string | string[]): 
Promise<void> {
     const shortcuts = Array.isArray(shortcut) ? shortcut : [shortcut];
-    const isMac = process.platform === 'darwin';
-    const selected = isMac && shortcuts.length > 1 ? shortcuts[1] : 
shortcuts[0];
-    await this.page.keyboard.press(this.formatKey(selected));
+    // Playwright presses by physical key code, so the primary variant works 
on every platform; the macOS special-character variant (e.g. control.alt.∂) is 
unpressable via keyboard.press.
+    await this.page.keyboard.press(this.formatKey(shortcuts[0]));
   }
 
   private formatKey(shortcut: string): string {
diff --git 
a/zeppelin-web-angular/e2e/tests/notebook/keyboard/notebook-keyboard-shortcuts.spec.ts
 
b/zeppelin-web-angular/e2e/tests/notebook/keyboard/notebook-keyboard-shortcuts.spec.ts
index b0818c9f02..2f43bb6f33 100644
--- 
a/zeppelin-web-angular/e2e/tests/notebook/keyboard/notebook-keyboard-shortcuts.spec.ts
+++ 
b/zeppelin-web-angular/e2e/tests/notebook/keyboard/notebook-keyboard-shortcuts.spec.ts
@@ -22,14 +22,10 @@ import {
 
 /**
  * Comprehensive keyboard shortcuts test suite based on ShortcutsMap
- * Tests all keyboard shortcuts defined in src/app/key-binding/shortcuts-map.ts
- *
- * Note: This spec uses waitForTimeout in several places because Monaco editor 
cursor
- * state and editor focus are not observable via DOM events that Playwright 
can detect.
- * These are justified timing gaps to allow Monaco's internal state to settle 
between
- * keystroke sequences. See: 
https://github.com/microsoft/monaco-editor/issues/2688
+ * (src/app/key-binding/shortcuts-map.ts). The page object gates on Monaco's 
`focused`
+ * class before dispatching shortcuts; effects are asserted with web-first 
expectations.
  */
-// JUSTIFIED: Monaco editor focus state is not observable via DOM events; 
serial ordering prevents cross-test editor state corruption
+// Serial ordering prevents cross-test editor state corruption within the 
shared notebook.
 test.describe.serial('Comprehensive Keyboard Shortcuts (ShortcutsMap)', () => {
   addPageAnnotationBeforeEach(PAGES.WORKSPACE.NOTEBOOK);
   addPageAnnotationBeforeEach(PAGES.SHARE.SHORTCUT);
@@ -76,14 +72,13 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.tryFocusCodeEditor();
       await keyboardPage.setCodeEditorContent('%md\n# Test Heading\n\nThis is 
**bold** text.');
 
-      // Verify content was set
-      const content = await keyboardPage.getCodeEditorContent();
-      expect(content.replace(/\s+/g, '')).toContain('#TestHeading');
+      // Verify content was set (setCodeEditorContent already gated on the 
rendered text)
+      await expect(keyboardPage.editorLines.first()).toContainText('Test 
Heading');
 
       // When: User presses Shift+Enter
       await keyboardPage.pressRunParagraph();
 
-      // Then: Paragraph should execute (reach a terminal state — interpreter 
availability varies by env)
+      // Then: Paragraph should execute (reach a terminal state; interpreter 
availability varies by env)
       await keyboardPage.waitForParagraphExecution(0);
       // JUSTIFIED: single-paragraph test notebook; first() is deterministic
       const statusEl = 
keyboardPage.paragraphContainer.first().locator('.status');
@@ -161,7 +156,7 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
   test.describe('ParagraphActions.Cancel: Control+Alt+C', () => {
     test('should cancel running paragraph with Control+Alt+C', async () => {
-      test.skip(!!process.env.CI, 'Requires Python interpreter with running 
indicator — not available in CI');
+      test.skip(!!process.env.CI, 'Requires Python interpreter with running 
indicator; not available in CI');
       // Given: A long-running paragraph
       await keyboardPage.tryFocusCodeEditor();
       await keyboardPage.setCodeEditorContent('%python\nimport 
time;time.sleep(3)\nprint("Should be cancelled")');
@@ -197,21 +192,17 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // Position cursor at end of last line using more reliable cross-browser 
method
       await keyboardPage.pressSelectAll(); // Select all content
       await keyboardPage.pressKey('ArrowRight'); // Move to end
-      await keyboardPage.page.waitForTimeout(500); // Wait for cursor to 
position // JUSTIFIED: Monaco editor internal state settle — cursor/focus state 
not observable via DOM
 
       // When: User presses Control+P (should move cursor up one line)
       await keyboardPage.pressMoveCursorUp();
-      await keyboardPage.page.waitForTimeout(500); // Wait for cursor movement 
// JUSTIFIED: Monaco editor internal state settle — cursor/focus state not 
observable via DOM
 
       // Then: Verify cursor movement by checking if we can type at the 
current position
       // Type a marker and check where it appears in the content
       await keyboardPage.pressKey('End'); // Move to end of current line
       await keyboardPage.page.keyboard.type('MARKER');
 
-      const content = await keyboardPage.getCodeEditorContent();
-      // If cursor moved up correctly, marker should be on line2
-      expect(content).toContain('line2MARKER');
-      expect(content).not.toContain('line3MARKER');
+      await expect.poll(() => 
keyboardPage.getCodeEditorContent()).toContain('line2MARKER');
+      expect(await 
keyboardPage.getCodeEditorContent()).not.toContain('line3MARKER');
     });
   });
 
@@ -225,20 +216,16 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.pressSelectAll(); // Select all content
       await keyboardPage.pressKey('ArrowLeft'); // Move to beginning
       await keyboardPage.pressKey('ArrowDown'); // Move to line1
-      await keyboardPage.page.waitForTimeout(500); // Wait for cursor to 
position // JUSTIFIED: Monaco editor internal state settle — cursor/focus state 
not observable via DOM
 
       // When: User presses Control+N (should move cursor down one line)
       await keyboardPage.pressMoveCursorDown();
-      await keyboardPage.page.waitForTimeout(500); // Wait for cursor movement 
// JUSTIFIED: Monaco editor internal state settle — cursor/focus state not 
observable via DOM
 
       // Then: Verify cursor movement by checking if we can type at the 
current position
       // Type a marker and check where it appears in the content
       await keyboardPage.page.keyboard.type('MARKER');
 
-      const content = await keyboardPage.getCodeEditorContent();
-      // If cursor moved down correctly, marker should be on line2
-      expect(content).toContain('MARKERline2');
-      expect(content).not.toContain('MARKERline1');
+      await expect.poll(() => 
keyboardPage.getCodeEditorContent()).toContain('MARKERline2');
+      expect(await 
keyboardPage.getCodeEditorContent()).not.toContain('MARKERline1');
     });
   });
 
@@ -265,7 +252,6 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // Focus first paragraph
       await firstParagraph.click();
       await keyboardPage.tryFocusCodeEditor(0);
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor requires time to register focus before keyboard shortcut dispatch
 
       // When: User presses Control+Alt+D
       await keyboardPage.pressDeleteParagraph();
@@ -285,7 +271,6 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       expect(initialCount).toBe(1);
 
       await keyboardPage.tryFocusCodeEditor(0);
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
requires time to register focus before keyboard shortcut dispatch
 
       // When: User presses Control+Alt+D on the only paragraph
       await keyboardPage.pressDeleteParagraph();
@@ -324,17 +309,14 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const finalCount = await keyboardPage.getParagraphCount();
       expect(finalCount).toBe(initialCount + 1);
 
-      // And: The new paragraph should be at index 0 (above the original)
-      const newParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(0);
-      const originalParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(1);
+      // And: the new paragraph at index 0 holds no user content; empty or 
just an interpreter directive (poll so the async insert/render settles).
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(0).then(c => 
c.trim())).toMatch(/^(%\w+)?$/);
 
-      // New paragraph may have default interpreter (%python) or be empty
-      expect(newParagraphContent === '' || newParagraphContent === 
'%python').toBe(true);
-
-      // Normalize whitespace for comparison since Monaco editor may format 
differently
+      // And the original content moved to index 1 (normalize whitespace; 
Monaco reflows).
       const normalizedOriginalContent = originalContent.replace(/\s+/g, ' 
').trim();
-      const normalizedReceivedContent = 
originalParagraphContent.replace(/\s+/g, ' ').trim();
-      expect(normalizedReceivedContent).toContain(normalizedOriginalContent); 
// Original content should be at index 1
+      await expect
+        .poll(() => keyboardPage.getCodeEditorContentByIndex(1).then(c => 
c.replace(/\s+/g, ' ').trim()))
+        .toContain(normalizedOriginalContent);
     });
   });
 
@@ -355,49 +337,33 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const finalCount = await keyboardPage.getParagraphCount();
       expect(finalCount).toBe(initialCount + 1);
 
-      // And: The new paragraph should be at index 1 (below the original)
+      // And: the original content stays at index 0 (poll so the async 
insert/render settles).
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(0)).toMatch(/Original\s+Paragraph/);
       const originalParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(0);
-      const newParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(1);
-
-      // Compare content - use regex to handle potential encoding issues
-      expect(originalParagraphContent).toMatch(/Original\s+Paragraph/);
       
expect(originalParagraphContent).toMatch(/Content\s+for\s+insert\s+below\s+test/);
-      expect(newParagraphContent).toBeDefined(); // New paragraph just needs 
to exist
+
+      // And: a new paragraph exists at index 1 holding no user content.
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(1).then(c => 
c.trim())).toMatch(/^(%\w+)?$/);
     });
   });
 
-  // Note (ZEPPELIN-6294):
-  // This test appears to be related to ZEPPELIN-6294.
-  // A proper fix or verification should be added based on the issue details.
-  // In the New UI, the cloned paragraph’s text is empty on PARAGRAPH_ADDED,
-  // while the Classic UI receives the correct text. This discrepancy should 
be addressed
-  // when applying the proper fix for the issue.
   test.describe('ParagraphActions.InsertCopyOfParagraphBelow: 
Control+Shift+C', () => {
     test('should insert copy of paragraph below with Control+Shift+C', async 
() => {
-      test.skip();
       // Given: A paragraph with content
       await keyboardPage.tryFocusCodeEditor();
       await keyboardPage.setCodeEditorContent('%md\n# Copy Test\nContent to be 
copied below');
 
       const initialCount = await keyboardPage.getParagraphCount();
-
-      // Capture the original paragraph content to verify the copy
       const originalContent = await 
keyboardPage.getCodeEditorContentByIndex(0);
 
       // When: User presses Control+Shift+C
       await keyboardPage.pressInsertCopy();
 
-      // Then: A copy of the paragraph should be inserted below
+      // Then: a copy is inserted below carrying the same text, and the 
original is unchanged
       await keyboardPage.waitForParagraphCountChange(initialCount + 1);
-      const finalCount = await keyboardPage.getParagraphCount();
-      expect(finalCount).toBe(initialCount + 1);
-
-      // And: The copied content should be identical to the original
-      const originalParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(0);
-      const copiedParagraphContent = await 
keyboardPage.getCodeEditorContentByIndex(1);
-
-      expect(originalParagraphContent).toBe(originalContent); // Original 
should remain unchanged
-      expect(copiedParagraphContent).toBe(originalContent); // Copied content 
should match original exactly
+      expect(await keyboardPage.getParagraphCount()).toBe(initialCount + 1);
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(0)).toBe(originalContent);
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(1)).toBe(originalContent);
     });
   });
 
@@ -407,10 +373,9 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const firstContent = '%python\nprint("First Paragraph - Content for move 
up test")';
       const secondContent = '%python\nprint("Second Paragraph - This should 
move up")';
 
-      // Set first paragraph content
+      // Set first paragraph content (setCodeEditorContent gates on the 
rendered text)
       await keyboardPage.tryFocusCodeEditor(0);
       await keyboardPage.setCodeEditorContent(firstContent, 0);
-      await keyboardPage.page.waitForTimeout(300); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
 
       // Create second paragraph using InsertBelow shortcut (Control+Alt+B)
       await keyboardPage.pressInsertBelow();
@@ -419,7 +384,6 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // Set second paragraph content
       await keyboardPage.tryFocusCodeEditor(1);
       await keyboardPage.setCodeEditorContent(secondContent, 1);
-      await keyboardPage.page.waitForTimeout(300); // JUSTIFIED: Monaco 
content state settle before read
 
       // Verify we have 2 paragraphs
       const paragraphCount = await keyboardPage.getParagraphCount();
@@ -431,24 +395,17 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
       // Focus on second paragraph for move operation
       await keyboardPage.tryFocusCodeEditor(1);
-      await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
 
       // When: User presses Control+Alt+K from second paragraph
       await keyboardPage.pressMoveParagraphUp();
 
-      // Wait for move operation to complete
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
-
       // Then: Paragraph count should remain the same
       const finalParagraphCount = await keyboardPage.getParagraphCount();
       expect(finalParagraphCount).toBe(2);
 
-      // And: Paragraph positions should be swapped
-      const newFirstParagraph = await 
keyboardPage.getCodeEditorContentByIndex(0);
-      const newSecondParagraph = await 
keyboardPage.getCodeEditorContentByIndex(1);
-
-      expect(newFirstParagraph).toBe(initialSecond); // Second paragraph moved 
to first position
-      expect(newSecondParagraph).toBe(initialFirst); // First paragraph moved 
to second position
+      // And: Paragraph positions should be swapped (poll until the move lands 
in the DOM)
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(0)).toBe(initialSecond);
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(1)).toBe(initialFirst);
     });
   });
 
@@ -458,10 +415,9 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const firstContent = '%python\nprint("First Paragraph - This should move 
down")';
       const secondContent = '%python\nprint("Second Paragraph - Content for 
second paragraph")';
 
-      // Set first paragraph content
+      // Set first paragraph content (setCodeEditorContent gates on the 
rendered text)
       await keyboardPage.tryFocusCodeEditor(0);
       await keyboardPage.setCodeEditorContent(firstContent, 0);
-      await keyboardPage.page.waitForTimeout(300); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
 
       // Create second paragraph using InsertBelow shortcut (Control+Alt+B)
       await keyboardPage.pressInsertBelow();
@@ -470,7 +426,6 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // Set second paragraph content
       await keyboardPage.tryFocusCodeEditor(1);
       await keyboardPage.setCodeEditorContent(secondContent, 1);
-      await keyboardPage.page.waitForTimeout(300); // JUSTIFIED: Monaco 
content state settle before read
 
       // Verify we have 2 paragraphs
       const paragraphCount = await keyboardPage.getParagraphCount();
@@ -482,24 +437,17 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
       // Focus first paragraph for move operation
       await keyboardPage.tryFocusCodeEditor(0);
-      await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
 
       // When: User presses Control+Alt+J from first paragraph
       await keyboardPage.pressMoveParagraphDown();
 
-      // Wait for move operation to complete
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
-
       // Then: Paragraph count should remain the same
       const finalParagraphCount = await keyboardPage.getParagraphCount();
       expect(finalParagraphCount).toBe(2);
 
-      // And: Paragraph positions should be swapped
-      const newFirstParagraph = await 
keyboardPage.getCodeEditorContentByIndex(0);
-      const newSecondParagraph = await 
keyboardPage.getCodeEditorContentByIndex(1);
-
-      expect(newFirstParagraph).toBe(initialSecond); // Second paragraph moved 
to first position
-      expect(newSecondParagraph).toBe(initialFirst); // First paragraph moved 
to second position
+      // And: Paragraph positions should be swapped (poll until the move lands 
in the DOM)
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(0)).toBe(initialSecond);
+      await expect.poll(() => 
keyboardPage.getCodeEditorContentByIndex(1)).toBe(initialFirst);
     });
   });
 
@@ -516,10 +464,8 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Alt+E
       await keyboardPage.pressSwitchEditor();
 
-      // Then: Editor visibility should toggle
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
-      const finalEditorVisibility = await keyboardPage.isEditorVisible(0);
-      expect(finalEditorVisibility).not.toBe(initialEditorVisibility);
+      // Then: editor visibility toggles
+      await expect.poll(() => keyboardPage.isEditorVisible(0), { timeout: 
10000 }).toBe(!initialEditorVisibility);
     });
   });
 
@@ -534,10 +480,8 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Alt+R
       await keyboardPage.pressSwitchEnable();
 
-      // Then: Paragraph enabled state should toggle
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
-      const finalEnabledState = await keyboardPage.isParagraphEnabled(0);
-      expect(finalEnabledState).not.toBe(initialEnabledState);
+      // Then: paragraph enabled state toggles
+      await expect.poll(() => keyboardPage.isParagraphEnabled(0), { timeout: 
10000 }).toBe(!initialEnabledState);
     });
   });
 
@@ -552,14 +496,19 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const resultLocator = 
keyboardPage.getParagraphByIndex(0).locator('[data-testid="paragraph-result"]');
       await expect(resultLocator).toBeVisible();
 
-      const initialOutputVisibility = await keyboardPage.isOutputVisible(0);
+      // When: User presses Control+Alt+O from the paragraph host
+      await keyboardPage.pressShortcutFromHostUntil(
+        0,
+        () => keyboardPage.pressSwitchOutputShow(),
+        () => resultLocator.isHidden()
+      );
 
-      // When: User presses Control+Alt+O
-      await keyboardPage.tryFocusCodeEditor(0);
-      await keyboardPage.pressSwitchOutputShow();
-
-      const finalOutputVisibility = await keyboardPage.isOutputVisible(0);
-      expect(finalOutputVisibility).not.toBe(initialOutputVisibility);
+      // And toggling again restores it
+      await keyboardPage.pressShortcutFromHostUntil(
+        0,
+        () => keyboardPage.pressSwitchOutputShow(),
+        () => resultLocator.isVisible()
+      );
     });
   });
 
@@ -574,10 +523,10 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Alt+M
       await keyboardPage.pressSwitchLineNumber();
 
-      // Then: Line numbers visibility should toggle
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
-      const finalLineNumbersVisibility = await 
keyboardPage.areLineNumbersVisible(0);
-      
expect(finalLineNumbersVisibility).not.toBe(initialLineNumbersVisibility);
+      // Then: line numbers visibility toggles
+      await expect
+        .poll(() => keyboardPage.areLineNumbersVisible(0), { timeout: 10000 })
+        .toBe(!initialLineNumbersVisibility);
     });
   });
 
@@ -592,9 +541,8 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Alt+T
       await keyboardPage.pressSwitchTitleShow();
 
-      // Then: Title visibility should toggle
-      const finalTitleVisibility = await keyboardPage.isTitleVisible(0);
-      expect(finalTitleVisibility).not.toBe(initialTitleVisibility);
+      // Then: title visibility toggles (poll; the DOM updates asynchronously)
+      await expect.poll(() => keyboardPage.isTitleVisible(0), { timeout: 10000 
}).toBe(!initialTitleVisibility);
     });
   });
 
@@ -611,12 +559,15 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       const statusElBefore = 
keyboardPage.paragraphContainer.first().locator('.status');
       await 
expect(statusElBefore).toHaveText(/FINISHED|ERROR|PENDING|RUNNING/);
 
-      // When: User presses Control+Alt+L
-      await keyboardPage.tryFocusCodeEditor(0);
-      await keyboardPage.pressClearOutput();
+      // When: User presses Control+Alt+L (editor hidden after %md run; 
dispatch from the host)
+      const resultLocator = 
keyboardPage.getParagraphByIndex(0).locator('[data-testid="paragraph-result"]');
+      await keyboardPage.pressShortcutFromHostUntil(
+        0,
+        () => keyboardPage.pressClearOutput(),
+        () => resultLocator.isHidden()
+      );
 
       // Then: Output should be cleared
-      const resultLocator = 
keyboardPage.getParagraphByIndex(0).locator('[data-testid="paragraph-result"]');
       await expect(resultLocator).not.toBeVisible();
     });
   });
@@ -666,9 +617,8 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Shift+-
       await keyboardPage.pressReduceWidth();
 
-      // Then: Paragraph width should be reduced
-      const finalWidth = await keyboardPage.getParagraphWidth(0);
-      expect(finalWidth).toBeLessThan(initialWidth);
+      // Then: paragraph width reduces (poll; the layout updates 
asynchronously)
+      await expect.poll(() => keyboardPage.getParagraphWidth(0), { timeout: 
10000 }).toBeLessThan(initialWidth);
     });
   });
 
@@ -679,17 +629,18 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.setCodeEditorContent('%python\nprint("Test width 
increase")');
 
       // First, reduce width to ensure there's room to increase
+      const fullWidth = await keyboardPage.getParagraphWidth(0);
       await keyboardPage.pressReduceWidth();
-      await keyboardPage.page.waitForTimeout(500); // Give UI a moment to 
update after reduction // JUSTIFIED: Monaco editor internal state settle — 
cursor/focus state not observable via DOM
+      // Poll until the reduction is reflected in the layout instead of a 
fixed settle
+      await expect.poll(() => keyboardPage.getParagraphWidth(0), { timeout: 
10000 }).toBeLessThan(fullWidth);
 
       const initialWidth = await keyboardPage.getParagraphWidth(0);
 
       // When: User presses Control+Shift+=
       await keyboardPage.pressIncreaseWidth();
 
-      // Then: Paragraph width should be increased
-      const finalWidth = await keyboardPage.getParagraphWidth(0);
-      expect(finalWidth).toBeGreaterThan(initialWidth);
+      // Then: paragraph width increases (poll; the layout updates 
asynchronously)
+      await expect.poll(() => keyboardPage.getParagraphWidth(0), { timeout: 
10000 }).toBeGreaterThan(initialWidth);
     });
   });
 
@@ -709,20 +660,23 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // Additional wait and focus for Firefox compatibility
       const browserName = test.info().project.name;
       if (browserName === 'firefox') {
-        await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
+        // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+        await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco 
editor internal state settle; cursor/focus state not observable via DOM
         // Ensure Monaco editor is properly focused
         // JUSTIFIED: single Monaco editor per paragraph; first() picks the 
active textarea
         const editorTextarea = keyboardPage.page.locator('.monaco-editor 
textarea').first();
         await editorTextarea.click();
         await editorTextarea.focus();
-        await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
+        // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+        await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco 
editor internal state settle; cursor/focus state not observable via DOM
       }
 
       // When: User presses Control+K (cut to end of line)
       await keyboardPage.pressCutLine();
 
       // Then: First line content should be cut (cut from cursor position to 
end of line)
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle; cursor/focus state not observable via DOM
       const finalContent = await keyboardPage.getCodeEditorContent();
       expect(finalContent).toBeDefined();
       expect(typeof finalContent).toBe('string');
@@ -744,13 +698,15 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.setCodeEditorContent(originalContent);
 
       // Wait for content to be properly set and verify it
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle; cursor/focus state not observable via DOM
       const initialContent = await keyboardPage.getCodeEditorContent();
       expect(initialContent.replace(/\s+/g, ' 
').trim()).toContain(originalContent);
 
       // When: User presses Control+K to cut the line
       await keyboardPage.pressCutLine();
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle; cursor/focus state not observable via DOM
 
       // Then: Content should be reduced (line was cut)
       const afterCutContent = await keyboardPage.getCodeEditorContent();
@@ -758,13 +714,15 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
       // Clear the editor to verify paste works from clipboard
       await keyboardPage.setCodeEditorContent('');
-      await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+      await keyboardPage.page.waitForTimeout(200); // JUSTIFIED: Monaco editor 
internal state settle; cursor/focus state not observable via DOM
       const emptyContent = await keyboardPage.getCodeEditorContent();
       expect(emptyContent.trim()).toBe('');
 
       // When: User presses Control+Y to paste
       await keyboardPage.pressPasteLine();
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      // JUSTIFIED: wait for Monaco focus/cursor state after editor shortcut
+      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle; cursor/focus state not observable via DOM
 
       // Then: Original content should be restored from clipboard
       const finalContent = await keyboardPage.getCodeEditorContent();
@@ -797,8 +755,7 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Control+Alt+F
       await keyboardPage.pressFindInCode();
 
-      // Then: Find functionality should be triggered
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
+      // Then: Find functionality should be triggered (toBeVisible 
auto-retries)
       await expect(keyboardPage.searchDialog).toBeVisible();
 
       // Close search dialog
@@ -817,9 +774,9 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
       // When: User presses Control+Space to trigger autocomplete
       await keyboardPage.pressControlSpace();
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
+      await keyboardPage.autocompletePopup.waitFor({ state: 'visible', 
timeout: 3000 }).catch(() => {});
 
-      // Then: Editor must remain functional after shortcut (baseline — always 
asserts)
+      // Then: Editor must remain functional after shortcut (baseline; always 
asserts)
       // JUSTIFIED: single-paragraph test notebook; first() is deterministic
       await expect(keyboardPage.codeEditor.first()).toBeVisible();
 
@@ -844,7 +801,7 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
 
       // When: User triggers autocomplete and selects an option
       await keyboardPage.pressControlSpace();
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
+      await keyboardPage.autocompletePopup.waitFor({ state: 'visible', 
timeout: 3000 }).catch(() => {});
 
       const isAutocompleteVisible = await keyboardPage.isAutocompleteVisible();
       if (isAutocompleteVisible) {
@@ -882,7 +839,7 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       // When: User presses Tab for indentation
       await keyboardPage.pressTab();
 
-      // Then: Content should be longer (indentation added) — poll until 
CodeMirror processes the Tab asynchronously
+      // Then: Content should be longer (indentation added); poll until 
CodeMirror processes the Tab asynchronously
       let contentAfterTab = '';
       await expect(async () => {
         contentAfterTab = await keyboardPage.getCodeEditorContent();
@@ -988,8 +945,7 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.setCodeEditorContent('%md\n# Recovery Test\nShortcuts 
work after error', newParagraphIndex);
       await keyboardPage.pressRunParagraph();
 
-      // Then: New paragraph should execute (FINISHED or ERROR is acceptable — 
the key assertion is
-      // that execution completed, proving shortcuts are functional after an 
error occurred)
+      // Then: Shortcut execution still reaches a terminal state
       await keyboardPage.waitForParagraphExecution(newParagraphIndex);
       // JUSTIFIED: newParagraphIndex is dynamically computed from 
getParagraphCount(); nth() is the only way to address this specific paragraph
       const statusElNew = 
keyboardPage.paragraphContainer.nth(newParagraphIndex).locator('.status');
@@ -1001,23 +957,22 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
       await keyboardPage.tryFocusCodeEditor();
       await keyboardPage.setCodeEditorContent('%md\n# Test paragraph');
 
-      // Remove focus by clicking on empty area
+      // Remove focus by clicking on empty area, then confirm no editor holds 
focus
       await keyboardPage.page.locator('body').click();
-      await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: Monaco editor 
internal state settle — cursor/focus state not observable via DOM
+      await 
expect(keyboardPage.page.locator('.monaco-editor.focused')).toHaveCount(0, { 
timeout: 5000 });
 
       const initialCount = await keyboardPage.getParagraphCount();
 
       // When: User tries keyboard shortcuts that require paragraph focus
       // These should either not work or gracefully handle the lack of focus
       await keyboardPage.pressInsertBelow(); // This may not work without focus
-      await keyboardPage.page.waitForTimeout(1000); // JUSTIFIED: Monaco 
editor internal state settle — cursor/focus state not observable via DOM
 
       const afterShortcut = await keyboardPage.getParagraphCount();
 
       // Then: Either the shortcut works (creates new paragraph) or is 
gracefully ignored
       expect(afterShortcut === initialCount || afterShortcut === initialCount 
+ 1).toBe(true);
 
-      // System must remain stable — editor still accessible
+      // Editor remains usable after error recovery.
       // JUSTIFIED: single-paragraph test notebook; first() is deterministic
       await expect(keyboardPage.codeEditor.first()).toBeVisible();
     });
@@ -1032,7 +987,6 @@ test.describe.serial('Comprehensive Keyboard Shortcuts 
(ShortcutsMap)', () => {
         await keyboardPage.waitForParagraphExecution(0, 60000);
         // JUSTIFIED: single-paragraph test notebook; first() is deterministic
         await expect(keyboardPage.paragraphResult.first()).toBeVisible({ 
timeout: 60000 });
-        await keyboardPage.page.waitForTimeout(500); // JUSTIFIED: brief gap 
between rapid sequential runs to prevent WebSocket message overlap
       }
 
       // Then: System should remain stable
diff --git a/zeppelin-web-angular/e2e/utils.ts 
b/zeppelin-web-angular/e2e/utils.ts
index 8305759647..93aaf67f70 100644
--- a/zeppelin-web-angular/e2e/utils.ts
+++ b/zeppelin-web-angular/e2e/utils.ts
@@ -363,7 +363,7 @@ export const navigateToNotebookWithFallback = async (
     // Strategy 1: Direct navigation
     await page.goto(`/#/notebook/${noteId}`, { waitUntil: 'networkidle', 
timeout: 30000 });
     navigationSuccessful = true;
-  } catch (error) {
+  } catch {
     // Strategy 2: Wait for loading completion and check URL
     await page.waitForFunction(
       () => {
@@ -485,7 +485,10 @@ export const createTestNotebookWithName = async (
   options: CreateTestNotebookWithNameOptions = {}
 ): Promise<{ noteId: string; paragraphId: string; notebookName: string; 
notebookPath: string }> => {
   const isRetryableError = (message: string): boolean =>
-    /REST request failed: (404|409|500)\b/.test(message) || 
message.includes('Fetch notebook REST request failed');
+    /REST request failed: (404|409|500)\b/.test(message) ||
+    message.includes('Fetch notebook REST request failed') ||
+    // TODO: transient WebKit-on-Linux crash (microsoft/playwright#34450); 
drop once fixed upstream.
+    /WebKit encountered an internal error|Target crashed/.test(message);
 
   const tryCreate = async () => {
     const prefix = options.namePrefix ?? 'TestNotebook';

Reply via email to