Files
insomnia/packages/insomnia-test/components/script-tab.component.ts
T
Bob QiuandClaude Sonnet 5.5 884b562eae refactor(insomnia-test): reuse component layer in page objects
Replace hand-written dialog/grid/menu/tab/toggle locators in pages with
the existing ModalComponent, TableComponent, DropdownComponent,
TabsComponent, PressedToggleComponent, SwitchComponent, BannerComponent,
BreadcrumbComponent, EnvironmentPickerComponent and TooltipComponent.
Behavior-preserving: existing locators are passed as component roots
where semantics differ.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 13:38:07 +08:00

156 lines
5.9 KiB
TypeScript

import { ScriptTab } from "@package/enums/script-tab";
import { expect, type Locator } from "@playwright/test";
import { TabPanelComponent } from "./tab-panel.component";
export interface RequestScripts {
preRequest?: string;
afterResponse?: string;
}
/**
* The Scripts tab component, shared by any page that renders it — a
* request's own pane (`RequestPage`, composing an instance as
* `this.scripts`) and a Folder's own settings tab (`FolderPage`, same,
* since folder-level scripts run for every descendant request before/
* after that request's own script). Constructed with the owning page's
* `pane` selector (its tab-bar's outer container), since that's the only
* thing that differs between them.
*/
export class ScriptTabComponent extends TabPanelComponent {
protected readonly AFTER_RESPONSE_EDITOR = "[role='tab'][data-key='after-response']";
protected readonly PRE_REQUEST_EDITOR = "[role='tab'][data-key='pre-request']";
/**
* Reads back both the pre-request and after-response scripts by
* switching to each script's editor pane in turn. Returns undefined
* if both scripts are empty.
*/
async getScripts(): Promise<RequestScripts | undefined> {
await this.tabs.tab("scripts").click();
const panel = this.page.locator(this.TABPANEL);
const editors = panel.locator(".CodeMirror");
const preEditor = panel.locator(this.PRE_REQUEST_EDITOR);
await preEditor.isVisible();
await preEditor.click();
const preRequest = await this.readCodeMirror(editors.first());
const afterEditor = panel.locator(this.AFTER_RESPONSE_EDITOR);
await afterEditor.isVisible();
await afterEditor.click();
const afterResponse = await this.readCodeMirror(editors.first());
if (!preRequest && !afterResponse) return undefined;
return {
...(preRequest ? { preRequest } : {}),
...(afterResponse ? { afterResponse } : {}),
};
}
/**
* Sets the pre-request and/or after-response scripts, switching to
* each script's editor pane before writing into it. Only scripts that
* are provided are written.
*/
async setScripts(scripts: RequestScripts): Promise<void> {
if (scripts.preRequest) {
await this.writeScript(this.PRE_REQUEST_EDITOR, scripts.preRequest);
}
if (scripts.afterResponse) {
await this.writeScript(this.AFTER_RESPONSE_EDITOR, scripts.afterResponse);
}
}
/**
* Switches to the Scripts tab and the given editor pane, then writes
* `value` into it. The whole sequence is retried because the owning
* page (notably a Folder tab in a Git Sync project) can re-mount after
* the switch, resetting to its default tab and leaving the write on a
* detached editor.
*/
private async writeScript(paneSelector: string, value: string): Promise<void> {
await expect(async () => {
await this.switchTab("scripts");
const panel = this.page.locator(this.TABPANEL);
const pane = panel.locator(paneSelector);
if ((await pane.getAttribute("aria-selected", { timeout: 2000 })) !== "true") {
await pane.click({ timeout: 2000 });
}
const editor = panel.locator(".CodeMirror").first();
// Fails fast if the panel was reset to another tab, so the retry re-switches.
await expect(editor).toBeVisible({ timeout: 2000 });
await this.setCodeMirrorValue(editor, value);
}).toPass({ timeout: 45_000 });
}
/**
* Types into the Scripts tab's Pre-request and/or After-response
* editors via real keystrokes (rather than replacing the value
* outright like `setScripts()`), so it builds genuine CodeMirror undo
* history the way a user's typing would. Only scripts that are
* provided are typed into.
*/
async typeScripts(scripts: RequestScripts): Promise<void> {
if (scripts.preRequest) {
await this.switchScriptTab(ScriptTab.PreRequest);
const panel = this.page.locator(this.TABPANEL);
const editor = panel.locator(".CodeMirror").first();
await editor.click();
await this.page.keyboard.press("End");
await this.page.keyboard.type(scripts.preRequest);
await this.waitForScriptPersisted("preRequestScript", editor);
}
if (scripts.afterResponse) {
await this.switchScriptTab(ScriptTab.AfterResponse);
const panel = this.page.locator(this.TABPANEL);
const editor = panel.locator(".CodeMirror").first();
await editor.click();
await this.page.keyboard.press("End");
await this.page.keyboard.type(scripts.afterResponse);
await this.waitForScriptPersisted("afterResponseScript", editor);
}
}
/**
* Polls the on-disk request/folder documents until one holds `editor`'s
* current content as its `field` script, confirming the editor's
* debounced autosave has landed after typing into it.
*/
private async waitForScriptPersisted(
field: "preRequestScript" | "afterResponseScript",
editor: Locator,
): Promise<void> {
const typed = await this.readCodeMirror(editor);
await this.waitForFieldPersisted(
["insomnia.Request.db", "insomnia.RequestGroup.db"],
(doc) => doc[field],
typed,
);
}
/**
* Switches to the Scripts tab's Pre-request or After-response sub-tab,
* clicks into its editor, and sends the platform's Undo shortcut
* (Cmd+Z on macOS, Ctrl+Z elsewhere).
*/
async undoScript(which: ScriptTab): Promise<void> {
await this.switchScriptTab(which);
const panel = this.page.locator(this.TABPANEL);
const editor = panel.locator(".CodeMirror").first();
await this.undo(() => editor.click());
}
/**
* Switches to the Scripts tab, then to the given script's own sub-tab.
*/
async switchScriptTab(which: ScriptTab): Promise<void> {
await this.switchTab("scripts");
const panel = this.page.locator(this.TABPANEL);
const editorSelector =
which === ScriptTab.PreRequest
? this.PRE_REQUEST_EDITOR
: this.AFTER_RESPONSE_EDITOR;
await panel.locator(editorSelector).click();
}
}