feat(insomnia-test): enhance credential menus and improve spec handling

This commit is contained in:
Bob Qiu committed 2026-11-06 15:51:25 +08:00
1 parent 7bf0e18d9e
commit f1c21291a0
6 files changed
+61 -42

No files matched your search

@@ -27,6 +27,20 @@ export class PreferencesCredentialsComponent extends PreferencesTabComponent {
await this.tabs.tabByName("Credentials").click();
}
private createGitCredentialMenu(): DropdownComponent {
return new DropdownComponent(
this.page,
this.dialog.getByRole("button", { name: "Create Git Credential" }),
);
}
private createCloudCredentialMenu(): DropdownComponent {
return new DropdownComponent(
this.page,
this.dialog.getByRole("button", { name: "Create Cloud Credential" }),
);
}
/**
* Adds a custom (username/PAT) Git credential from the Credentials tab,
* which must already be open (see `openCredentialsTab()`).
@@ -38,9 +52,7 @@ export class PreferencesCredentialsComponent extends PreferencesTabComponent {
name: /Access Token Credential/,
}),
);
await DropdownComponent.byLabel(this.page, "Create Git Credential").select(
"Access Token",
);
await this.createGitCredentialMenu().select("Access Token");
await this.page.getByLabel("Author Email").fill(credential.authorEmail);
await this.page.getByLabel("Author Name").fill(credential.authorName);
await this.page.getByLabel("Username").fill(credential.username);
@@ -63,10 +75,7 @@ export class PreferencesCredentialsComponent extends PreferencesTabComponent {
async createAwsCloudCredential(
credential: Omit<AwsCloudCredential, "provider">,
): Promise<void> {
await new DropdownComponent(
this.page,
this.dialog.getByRole("button", { name: "Create Cloud Credential" }),
).select("AWS", false);
await this.createCloudCredentialMenu().select("AWS", false);
const modal = new ModalComponent(
this.page,
this.page.getByRole("dialog", {
@@ -98,10 +107,7 @@ export class PreferencesCredentialsComponent extends PreferencesTabComponent {
async createGcpCloudCredential(
credential: Omit<GcpCloudCredential, "provider">,
): Promise<void> {
await new DropdownComponent(
this.page,
this.dialog.getByRole("button", { name: "Create Cloud Credential" }),
).select("GCP", false);
await this.createCloudCredentialMenu().select("GCP", false);
const modal = new ModalComponent(
this.page,
this.page.getByRole("dialog", {
@@ -131,10 +137,7 @@ export class PreferencesCredentialsComponent extends PreferencesTabComponent {
async createHashiCorpCloudCredential(
credential: Omit<HashiCorpCloudCredential, "provider">,
): Promise<void> {
await new DropdownComponent(
this.page,
this.dialog.getByRole("button", { name: "Create Cloud Credential" }),
).select("HashiCorp", false);
await this.createCloudCredentialMenu().select("HashiCorp", false);
const modal = new ModalComponent(
this.page,
this.page.getByRole("dialog", {
+17 -10
View File
@@ -245,12 +245,15 @@ export class SpecPage extends BasePage {
* all.
*/
async getOpenApiVersion(): Promise<string> {
const label = this.page
.locator(this.PANE_ONE)
.first()
.getByText(/OpenAPI \d/)
.first();
if ((await label.count()) === 0) return "";
// The span is always rendered once the spec has content, but stays
// empty until the parsed spec's `openapi` field is known, so wait for
// text rather than for the element.
const label = this.page.getByTestId("spec-openapi-version");
try {
await expect(label).toHaveText(/OpenAPI \d/, { timeout: DEFAULT_TIMEOUT });
} catch {
return "";
}
return (await label.innerText()).trim();
}
@@ -379,10 +382,14 @@ export class SpecPage extends BasePage {
* Reads every suite name currently listed in the Test Suites sidebar.
*/
async getTestSuiteNames(): Promise<string[]> {
return new TableComponent(
this.page,
this.page.locator(this.TEST_SUITES_LIST),
).rows.allInnerTexts();
// Re-opening the Tests tab re-renders the list; wait for it to settle
// so a read taken mid-render doesn't return a stale or partial list.
return this.waitForStableValue(() =>
new TableComponent(
this.page,
this.page.locator(this.TEST_SUITES_LIST),
).rows.allInnerTexts(),
);
}
/**
+11 -9
View File
@@ -1144,18 +1144,20 @@ export class WorkspacePage extends BasePage {
const target = indicator.first();
const folderBadge = target.getByTestId("drop-indicator-folder-name");
// The sidebar tags its indicator marker (the "on" wrapper, folder-name
// badge or dot) with `data-valid` and `data-drop-position`. An invalid
// target may render without the marker or the aria label, so only read
// what is actually present rather than letting getAttribute() wait out
// the full action timeout.
const marker = target.locator("[data-drop-position]").first();
const dropLabel = target.locator('[aria-roledescription="drop indicator"]');
const hasMarker = (await marker.count()) > 0;
const [ariaLabel, folderName] = await Promise.all([
target
.locator('[aria-roledescription="drop indicator"]')
.getAttribute("aria-label"),
(await dropLabel.count()) > 0 ? dropLabel.first().getAttribute("aria-label") : null,
(await folderBadge.count()) > 0 ? folderBadge.innerText() : "",
]);
// react-aria labels an "on" drop "Drop on <name>"; line drops say
// "Insert ...".
const isValid =
(await target.locator("[data-valid]").first().getAttribute("data-valid")) ===
"true";
const isInto = (ariaLabel ?? "").startsWith("Drop on");
const isValid = hasMarker && (await marker.getAttribute("data-valid")) === "true";
const isInto = hasMarker && (await marker.getAttribute("data-drop-position")) === "on";
const between = ariaLabel?.match(/^Insert between (.+) and (.+)$/);
return {
@@ -23,7 +23,5 @@ test("Verify Renaming And Deleting A Project Via The Dashboard", async ({
expect(foundByNewName?.id).toBe(project.id);
expect(foundByOldName).toBeUndefined();
await expect
.poll(() => workspaceFlow.getProject(newName), { timeout: 10_000 })
.toBeUndefined();
expect(await workspaceFlow.getProject(newName)).toBeUndefined();
});
@@ -535,10 +535,17 @@ function SidebarDropIndicator({
className={`absolute right-1 left-1 z-10 flex items-center rounded-sm outline-2 outline-solid ${outlineClass}`}
style={{ transform: `translateY(${rowStart}px)`, height: `${rowHeight}px` }}
>
<Icon
icon="turn-down"
className={`ml-2 h-3 w-3 ${isValid ? 'text-(--color-surprise)' : 'text-(--color-danger)'}`}
/>
<span
data-testid="drop-indicator-on"
data-valid={isValid}
data-drop-position={target.dropPosition}
className="flex items-center"
>
<Icon
icon="turn-down"
className={`ml-2 h-3 w-3 ${isValid ? 'text-(--color-surprise)' : 'text-(--color-danger)'}`}
/>
</span>
</DropIndicator>
);
}
@@ -557,6 +564,7 @@ function SidebarDropIndicator({
<span
data-testid="drop-indicator-folder-name"
data-valid={isValid}
data-drop-position={target.dropPosition}
className={`absolute -top-2.5 left-0 z-10 flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] leading-none whitespace-nowrap ${
isValid
? 'bg-(--color-surprise) text-(--color-font-surprise)'
@@ -570,6 +578,7 @@ function SidebarDropIndicator({
<span
data-testid="drop-indicator-dot"
data-valid={isValid}
data-drop-position={target.dropPosition}
className={`absolute -top-0.75 -left-0.75 block h-1.75 w-1.75 rounded-full ${
isValid ? 'bg-(--color-surprise)' : 'bg-(--color-danger)'
}`}
@@ -635,7 +635,7 @@ export const SpecView = ({
const specPaneToolbar = apiSpec?.contents ? (
<div className="flex h-10 shrink-0 items-center gap-2 overflow-hidden border-b border-solid border-(--hl-md) px-(--padding-sm)">
<span className="truncate text-sm text-(--hl) italic">
<span data-testid="spec-openapi-version" className="truncate text-sm text-(--hl) italic">
{parsedSpec?.openapi ? `OpenAPI ${parsedSpec.openapi}` : ''}
</span>
<span className="flex-1" />