refactor: update E2E test configurations and improve field handling in auth and sync pages

This commit is contained in:
Bob Qiu committed 2026-10-09 12:28:57 +08:00
1 parent 8f91dac6ef
commit b799cff164
5 files changed
+56 -23

No files matched your search

+2 -2
View File
@@ -102,8 +102,8 @@ jobs:
fail-fast: false
matrix:
# To scale up, add indices here and bump shardTotal to match.
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
shardTotal: [16]
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
shardTotal: [24]
steps:
- name: Checkout repo
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
@@ -244,23 +244,26 @@ export class AuthTabComponent extends TabPanelPage {
reapply: () => this.setCodeMirrorValue(editor, value),
};
await edit.reapply!();
await this.waitForAuthFieldsPersisted([edit]);
// Verify every field written so far, not just this one: its save can
// overwrite an earlier field from a stale copy of the auth config.
await this.waitForAuthFieldsPersisted([...edits, edit]);
edits.push(edit);
}
if (fields.signatureMethod) {
const signatureMethod = fields.signatureMethod;
const selectMethod = () =>
this.page
const selectMethod = async () => {
await this.page
.locator(this.TABPANEL)
.locator("#Signature-Method")
.selectOption(signatureMethod);
};
await selectMethod();
const edit: AuthFieldEdit = {
field: "signatureMethod",
value: signatureMethod,
reapply: selectMethod,
};
await this.waitForAuthFieldsPersisted([edit]);
await this.waitForAuthFieldsPersisted([...edits, edit]);
edits.push(edit);
}
// A later field's debounced save can overwrite an earlier one from a
@@ -317,11 +317,13 @@ export class CloudSyncPage extends BasePage {
* @param branch - The local branch to delete
*/
async deleteLocalBranch(branch: string): Promise<void> {
await this.branchesDialog
const row = this.branchesDialog
.getByLabel("Branches list", { exact: true })
.getByLabel(branch, { exact: true })
.getByRole("button", { name: "Delete" })
.dblclick();
.getByLabel(branch, { exact: true });
await row.getByRole("button", { name: "Delete" }).click();
// A dblclick can fire its 2nd click before React re-renders the
// button into its "Confirm" state, so wait for it explicitly.
await row.getByRole("button", { name: "Confirm" }).click();
}
/**
@@ -347,11 +349,13 @@ export class CloudSyncPage extends BasePage {
* @param branch - The local branch to merge in
*/
async mergeBranch(branch: string): Promise<void> {
await this.branchesDialog
const row = this.branchesDialog
.getByLabel("Branches list", { exact: true })
.getByLabel(branch, { exact: true })
.getByRole("button", { name: "Merge" })
.dblclick();
.getByLabel(branch, { exact: true });
await row.getByRole("button", { name: "Merge" }).click();
// A dblclick can fire its 2nd click before React re-renders the
// button into its "Confirm" state, so wait for it explicitly.
await row.getByRole("button", { name: "Confirm" }).click();
}
/**
@@ -38,15 +38,25 @@ export class McpClientPage extends RequestPage {
*/
async getTools(): Promise<string[]> {
// The sidebar is populated asynchronously once the server answers
// tools/list, so wait for the first row before reading the list.
await expect(this.page.locator(this.TOOL_ROW).first()).toBeVisible({
timeout: DEFAULT_TIMEOUT,
});
return this.page
.locator(this.TOOL_ROW)
.evaluateAll((rows) =>
rows.map((row) => row.getAttribute("aria-label") ?? ""),
);
// tools/list, and it can re-render (briefly empty) while it settles,
// so poll until a non-empty list of names has been read.
let tools: string[] = [];
await expect
.poll(
async () => {
tools = (
await this.page
.locator(this.TOOL_ROW)
.evaluateAll((rows) =>
rows.map((row) => row.getAttribute("aria-label") ?? ""),
)
).filter(Boolean);
return tools.length;
},
{ timeout: DEFAULT_TIMEOUT },
)
.toBeGreaterThan(0);
return tools;
}
/**
@@ -1,3 +1,6 @@
import { expect } from "@playwright/test";
import { DEFAULT_TIMEOUT } from "../misc/constants";
import type { RequestHeader,SocketIOMessage } from "../models/socket-io-request";
import { RequestPage } from "./request.page";
@@ -5,6 +8,19 @@ export class SocketIORequestPage extends RequestPage {
protected readonly urlBarId = "websocket-url-bar";
protected readonly persistedDbFile = "insomnia.SocketIORequest.db";
/**
* Clicks Connect and waits for the button to flip to "Disconnect", so a
* following send doesn't race the handshake and get dropped.
*/
async connect(): Promise<void> {
await super.connect();
await expect(
this.page
.locator(this.PANE)
.getByRole("button", { name: "Disconnect", exact: true }),
).toBeVisible({ timeout: DEFAULT_TIMEOUT });
}
/**
* Reads the full Socket.IO request state from the UI (url, headers, and
* the composed message).