n8n/packages/testing/playwright/pages/components/InstanceAiWorkflowSetup.ts
n8n-cat-bot[bot] 31f718f8f8
Some checks failed
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.16.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Build: Benchmark Image / build (push) Has been cancelled
Util: Sync API Docs / sync-public-api (push) Has been cancelled
Release: Schedule Patch Release PRs / Create patch release PR (${{ matrix.track }}) (beta) (push) Has been cancelled
Release: Schedule Patch Release PRs / Create patch release PR (${{ matrix.track }}) (stable) (push) Has been cancelled
Release: Schedule Patch Release PRs / Create patch release PR (${{ matrix.track }}) (v1) (push) Has been cancelled
chore: Route option selections through FloatingUiHelper (#32620)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 22:22:20 +00:00

161 lines
4.6 KiB
TypeScript

import type { Locator } from '@playwright/test';
import { FloatingUiHelper } from './FloatingUiHelper';
export class InstanceAiWorkflowSetup {
private floatingUi: FloatingUiHelper;
constructor(private root: Locator) {
this.floatingUi = new FloatingUiHelper(root.page());
}
getContainer(): Locator {
return this.root;
}
getWizard(): Locator {
return this.root;
}
getCard(): Locator {
return this.root;
}
getStepText(text: string | RegExp = /\d+ of \d+/): Locator {
return this.root.getByText(text);
}
getSetupCredentialButton(): Locator {
return this.root.getByRole('button', { name: /^Set up credential(?:s)?$/ });
}
getCredentialSelect(): Locator {
return this.getCard().getByRole('combobox').first();
}
getCredentialOption(credentialName: string): Locator {
return this.floatingUi.getVisiblePopoverOption(credentialName);
}
getCredentialOptionById(credentialId: string): Locator {
return this.root.page().getByTestId(`node-credentials-select-item-${credentialId}`);
}
getApplyButton(): Locator {
return this.root.getByRole('button', { name: /^(Apply|Continue)$/ });
}
getLaterButton(): Locator {
return this.root.getByRole('button', { name: 'Skip setup for now' });
}
getCardCheck(): Locator {
return this.root.getByText('Complete', { exact: true });
}
getCardSkipped(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-card-skipped');
}
getUsedByNodesHint(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-card-nodes-hint');
}
getParameterInput(parameterName: string): Locator {
return this.root.getByTestId(`parameter-input-${parameterName}`).getByRole('textbox');
}
getParameterIssues(parameterName: string): Locator {
return this.root
.getByTestId(`parameter-input-${parameterName}`)
.getByTestId('parameter-issues');
}
getPrevButton(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-prev');
}
getNextButton(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-next');
}
getGroupCard(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-group-card');
}
getGroupCheck(): Locator {
return this.root.getByTestId('instance-ai-workflow-setup-group-card-check');
}
getSection(nodeName: string): Locator {
return this.root
.getByTestId('instance-ai-workflow-setup-section')
.filter({ hasText: nodeName });
}
getSectionHeader(nodeName: string): Locator {
return this.getSection(nodeName).getByTestId('instance-ai-workflow-setup-section-header');
}
async selectSectionCredential(nodeName: string, credentialName: string): Promise<void> {
await this.ensureSectionExpanded(nodeName);
await this.getSection(nodeName).getByRole('combobox').first().click();
await this.getCredentialOption(credentialName).click();
}
async fillSectionParameter(
nodeName: string,
parameterName: string,
value: string,
): Promise<void> {
await this.ensureSectionExpanded(nodeName);
await this.getSection(nodeName)
.getByTestId(`parameter-input-${parameterName}`)
.getByRole('textbox')
.fill(value);
}
async selectCredential(credentialName: string): Promise<void> {
await this.selectCredentialOption(this.getCredentialOption(credentialName));
}
async selectCredentialById(credentialId: string): Promise<void> {
await this.selectCredentialOption(this.getCredentialOptionById(credentialId));
}
async getSelectedCredentialLabel(): Promise<string> {
return await this.getCredentialSelect().evaluate((element) => {
if (element instanceof HTMLInputElement) {
return element.value || element.placeholder;
}
return element.textContent?.trim() ?? '';
});
}
private async selectCredentialOption(option: Locator): Promise<void> {
await this.getCredentialSelect().click();
await option.waitFor({ state: 'attached' });
await option.dispatchEvent('click');
await this.getCredentialSelect().press('Escape');
}
async fillParameter(parameterName: string, value: string): Promise<void> {
await this.getParameterInput(parameterName).fill(value);
}
private async ensureSectionExpanded(nodeName: string): Promise<void> {
const section = this.getSection(nodeName);
const body = section.getByTestId('instance-ai-workflow-setup-section-body');
// Allow any in-flight auto-expand to settle before deciding whether to
// click the header — otherwise we can race the wizard's auto-expand-next
// watcher and toggle a section that was already opening.
try {
await body.waitFor({ state: 'visible', timeout: 500 });
} catch {
await this.getSectionHeader(nodeName).click();
await body.waitFor({ state: 'visible' });
}
}
}