Skip to content

Commit

Permalink
Lexical type-ahead plugin e2e helpers better scope actions (#10363)
Browse files Browse the repository at this point in the history
* Edge case bug fix: Type-ahead panel should not apply suggestion on SHIFT+TAB

tl;dr Our tests were checking for any visible type-ahead popup and then dismiss by typing "Escape" into a specific input which would sometimes do things like dismiss a pending log-point or comment edit.

I believe this PR removes all possible ambiguity for these cases.

* Changed Lexical / type-ahead e2e helpers to better scope actions
  • Loading branch information
bvaughn authored Feb 26, 2024
1 parent e2d3683 commit bd8d041
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 39 deletions.
11 changes: 7 additions & 4 deletions packages/e2e-tests/helpers/comments.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import assert from "assert";
import { Locator, Page, expect } from "@playwright/test";
import chalk from "chalk";

import { selectContextMenuItem } from "./context-menu";
import { confirmDialog } from "./dialog";
import { clearText, focus, isEditable, type as typeText } from "./lexical";
import { clearText, focus, isEditable, typeComment as typeText } from "./lexical";
import { findNetworkRequestRow } from "./network-panel";
import { openSource } from "./source-explorer-panel";
import { getSourceLine } from "./source-panel";
Expand Down Expand Up @@ -45,7 +46,7 @@ async function addCommentHelper(

const lexicalSelector = `[data-test-id="CommentInput-${id}"]`;
await focus(page, lexicalSelector);
await typeText(page, lexicalSelector, text, true);
await typeText(page, { commentId: id, shouldSubmit: true, text });

await expect(await isEditable(page, lexicalSelector)).toBe(false);
}
Expand Down Expand Up @@ -232,7 +233,7 @@ export async function editComment(page: Page, commentLocator: Locator, options:

await focus(page, lexicalSelector);
await clearText(page, lexicalSelector);
await typeText(page, lexicalSelector, text, true);
await typeText(page, { commentId: id, shouldSubmit: true, text });

commentLocator = await getComment(page, id);

Expand Down Expand Up @@ -305,9 +306,11 @@ export async function replyToComment(
.last()
.getAttribute("data-test-comment-id");

assert(replyId != null);

const lexicalSelector = `[data-test-id="CommentInput-${replyId}"]`;
await focus(page, lexicalSelector);
await typeText(page, lexicalSelector, text, true);
await typeText(page, { commentId: replyId, shouldSubmit: true, text });

await expect(await isEditable(page, lexicalSelector)).toBe(false);

Expand Down
9 changes: 6 additions & 3 deletions packages/e2e-tests/helpers/console-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import chalk from "chalk";

import { Badge } from "shared/client/types";

import { submitCurrentText as submitCurrentTextLexical, type as typeLexical } from "./lexical";
import {
submitCurrentText as submitCurrentTextLexical,
typeTerminalExpression as typeLexical,
} from "./lexical";
import { waitForPaused } from "./pause-information-panel";
import { Expected, MessageType } from "./types";
import { debugPrint, toggleExpandable, waitFor } from "./utils";
Expand Down Expand Up @@ -122,7 +125,7 @@ export async function executeTerminalExpression(
// Wait for the Console to stop loading
await consoleRoot.locator("text=Unavailable...").waitFor({ state: "hidden" });

await typeLexical(page, '[data-test-id="ConsoleTerminalInput"]', text, shouldSubmit);
await typeLexical(page, { shouldSubmit, text });
}

export async function executeAndVerifyTerminalExpression(
Expand Down Expand Up @@ -284,7 +287,7 @@ export async function seekToConsoleMessage(
}

export async function submitCurrentText(page: Page) {
await submitCurrentTextLexical(page, '[data-test-id="ConsoleTerminalInput"]');
await submitCurrentTextLexical(page, { type: "terminal" });
}

export async function toggleSideFilters(page: Page, open: boolean): Promise<void> {
Expand Down
140 changes: 130 additions & 10 deletions packages/e2e-tests/helpers/lexical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import { Page } from "@playwright/test";

import { delay, getCommandKey } from "./utils";

type InputAndTypeAheadOptions =
| {
commentId: string;
type: "comment";
}
| {
sourceLineNumber: number;
type: "log-point-condition" | "log-point-content";
}
| {
type: "terminal";
};

export async function clearText(page: Page, selector: string) {
await focus(page, selector);

Expand All @@ -23,11 +36,36 @@ export async function focus(page: Page, selector: string) {
await page.focus(selector);
}

export async function hideTypeAheadSuggestions(page: Page, selector: string) {
const list = page.locator('[data-test-name$="CodeTypeAhead"]');
export async function hideTypeAheadSuggestions(page: Page, options: InputAndTypeAheadOptions) {
// Make sure to match the type-ahead and input selectors;
// else we may type "Escape" into the wrong input and e.g. dismiss it or erase its content

let inputSelector: string;
let typeAheadSelector: string;

switch (options.type) {
case "comment":
inputSelector = `[data-test-id="CommentInput-${options.commentId}"]`;
typeAheadSelector = `[data-test-id="CommentInput-${options.commentId}-CodeTypeAhead"]`;
break;
case "log-point-condition":
inputSelector = `[data-test-id="PointPanel-ConditionInput-${options.sourceLineNumber}"]`;
typeAheadSelector = `[data-test-id="PointPanel-ConditionInput-${options.sourceLineNumber}-CodeTypeAhead"]`;
break;
case "log-point-content":
inputSelector = `[data-test-id="PointPanel-ContentInput-${options.sourceLineNumber}"]`;
typeAheadSelector = `[data-test-id="PointPanel-ContentInput-${options.sourceLineNumber}-CodeTypeAhead"]`;
break;
case "terminal":
inputSelector = '[data-test-id="ConsoleTerminalInput"]';
typeAheadSelector = '[data-test-id="ConsoleTerminalInput-CodeTypeAhead"]';
break;
}

const list = page.locator(typeAheadSelector);

if (await list.isVisible()) {
const input = page.locator(selector);
const input = page.locator(inputSelector);
await input.press("Escape");
}
}
Expand All @@ -38,11 +76,27 @@ export async function isEditable(page: Page, selector: string): Promise<boolean>
return editable === "true";
}

export async function submitCurrentText(page: Page, selector: string) {
const input = page.locator(selector);
export async function submitCurrentText(page: Page, options: InputAndTypeAheadOptions) {
let inputSelector: string;
switch (options.type) {
case "comment":
inputSelector = `[data-test-id="CommentInput-${options.commentId}"]`;
break;
case "log-point-condition":
inputSelector = `[data-test-id="PointPanel-ConditionInput-${options.sourceLineNumber}"]`;
break;
case "log-point-content":
inputSelector = `[data-test-id="PointPanel-ContentInput-${options.sourceLineNumber}"]`;
break;
case "terminal":
inputSelector = '[data-test-id="ConsoleTerminalInput"]';
break;
}

const input = page.locator(inputSelector);
const initialText = await input.textContent();

await hideTypeAheadSuggestions(page, selector);
await hideTypeAheadSuggestions(page, options);

let loopCounter = 0;

Expand All @@ -61,14 +115,80 @@ export async function submitCurrentText(page: Page, selector: string) {
}
}

export async function type(page: Page, selector: string, text: string, shouldSubmit: boolean) {
await clearText(page, selector);
export async function typeComment(
page: Page,
options: {
commentId: string;
text: string;
shouldSubmit: boolean;
}
) {
const { commentId, text, shouldSubmit } = options;

const input = page.locator(selector);
const inputSelector = `[data-test-id="CommentInput-${commentId}"]`;

await clearText(page, inputSelector);

const input = page.locator(inputSelector);
await input.type(text);

if (shouldSubmit) {
await delay(200);
await submitCurrentText(page, {
commentId,
type: "comment",
});
}
}

export async function typeLogPoint(
page: Page,
options: {
shouldSubmit: boolean;
sourceLineNumber: number;
text: string;
type: "condition" | "content";
}
) {
const { sourceLineNumber, text, shouldSubmit, type } = options;

const inputSelector =
type === "condition"
? `[data-test-id="PointPanel-ConditionInput-${sourceLineNumber}"]`
: `[data-test-id="PointPanel-ContentInput-${sourceLineNumber}"]`;

await clearText(page, inputSelector);

const input = page.locator(inputSelector);
await input.type(text);

if (shouldSubmit) {
await delay(200);
await submitCurrentText(page, {
sourceLineNumber,
type: `log-point-${type}`,
});
}
}

export async function typeTerminalExpression(
page: Page,
options: {
text: string;
shouldSubmit: boolean;
}
) {
const { text, shouldSubmit } = options;

const inputSelector = '[data-test-id="ConsoleTerminalInput"]';

await clearText(page, inputSelector);

const input = page.locator(inputSelector);
await input.type(text);

if (shouldSubmit) {
await delay(200);
await submitCurrentText(page, selector);
await submitCurrentText(page, { type: "terminal" });
}
}
44 changes: 22 additions & 22 deletions packages/e2e-tests/helpers/source-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@ import chalk from "chalk";

import { Badge } from "shared/client/types";

import { hideTypeAheadSuggestions, type as typeLexical } from "./lexical";
import { hideTypeAheadSuggestions, typeLogPoint as typeLexical } from "./lexical";
import { findPoints, openPauseInformationPanel, removePoint } from "./pause-information-panel";
import { openSource } from "./source-explorer-panel";
import {
clearTextArea,
debugPrint,
delay,
getByTestName,
getCommandKey,
mapLocators,
waitFor,
} from "./utils";
import { clearTextArea, debugPrint, delay, getByTestName, getCommandKey, waitFor } from "./utils";
import { openDevToolsTab } from ".";

export async function addBreakpoint(
Expand Down Expand Up @@ -138,12 +130,12 @@ export async function editConditional(
"addConditional"
);

await typeLexical(
page,
`${getSourceLineSelector(lineNumber)} [data-test-name="PointPanel-ConditionInput"]`,
condition,
false
);
await typeLexical(page, {
shouldSubmit: false,
sourceLineNumber: lineNumber,
text: condition,
type: "condition",
});
}

export async function jumpToLogPointHit(
Expand Down Expand Up @@ -322,10 +314,6 @@ export async function editLogPoint(
await editConditional(page, { condition, lineNumber });
}

const selector = `${getSourceLineSelector(
lineNumber
)} [data-test-name="PointPanel-ContentInput"]`;

if (content != null) {
const isEditing = await line
.locator('[data-test-name="PointPanel-ContentWrapper"] [data-lexical-editor="true"]')
Expand All @@ -336,13 +324,25 @@ export async function editLogPoint(

await debugPrint(page, `Setting log-point content "${chalk.bold(content)}"`, "addLogpoint");

await typeLexical(page, selector, content, false);
await typeLexical(page, {
sourceLineNumber: lineNumber,
shouldSubmit: false,
text: content,
type: "content",
});
}

if (saveAfterEdit) {
// The typeahead popup sometimes sticks around and overlaps the save button.
// Ensure it goes away.
await hideTypeAheadSuggestions(page, selector);
await hideTypeAheadSuggestions(page, {
sourceLineNumber: lineNumber,
type: "log-point-condition",
});
await hideTypeAheadSuggestions(page, {
sourceLineNumber: lineNumber,
type: "log-point-content",
});

const saveButton = line.locator('[data-test-name="PointPanel-SaveButton"]');
await expect(saveButton).toBeEnabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ function TypeAheadPopUp<Item>({
// Register Lexical command listeners for mouse and keyboard interactions
useLayoutEffect(() => {
function onKeyPress(event: KeyboardEvent) {
if (event.shiftKey) {
// Edge case but SHIFT+TAB should not accept a suggestion
return true;
}

if (!editor.isEditable()) {
return false;
}
Expand Down

0 comments on commit bd8d041

Please sign in to comment.