test: Migrate 3 specs from Cypress - Playwright (#19269)

This commit is contained in:
shortstacked
2025-09-08 12:46:08 +01:00
committed by GitHub
parent c6be123ddb
commit f7d225e871
24 changed files with 1154 additions and 470 deletions

View File

@@ -76,6 +76,10 @@ export class CanvasPage extends BasePage {
await this.nodeCreatorItemByName(text).click();
}
async clickAddToWorkflowButton(): Promise<void> {
await this.page.getByText('Add to workflow').click();
}
/**
* Add a node to the canvas with flexible options
* @param nodeName - The name of the node to search for and add

View File

@@ -0,0 +1,121 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class CommunityNodesPage extends BasePage {
// Element getters
getCommunityCards(): Locator {
return this.page.getByTestId('community-package-card');
}
getActionBox(): Locator {
return this.page.getByTestId('action-box');
}
getInstallButton(): Locator {
// Try action box first (empty state), fallback to header install button
const actionBoxButton = this.getActionBox().locator('button');
const headerInstallButton = this.page.getByRole('button', { name: 'Install' });
return actionBoxButton.or(headerInstallButton);
}
getInstallModal(): Locator {
return this.page.getByTestId('communityPackageInstall-modal');
}
getConfirmModal(): Locator {
return this.page.getByTestId('communityPackageManageConfirm-modal');
}
getPackageNameInput(): Locator {
return this.getInstallModal().locator('input').first();
}
getUserAgreementCheckbox(): Locator {
return this.page.getByTestId('user-agreement-checkbox');
}
getInstallPackageButton(): Locator {
return this.page.getByTestId('install-community-package-button');
}
getActionToggle(): Locator {
return this.page.getByTestId('action-toggle');
}
getUninstallAction(): Locator {
return this.page.getByTestId('action-uninstall');
}
getUpdateButton(): Locator {
return this.getCommunityCards().first().locator('button');
}
getConfirmUpdateButton(): Locator {
return this.getConfirmModal().getByRole('button', { name: 'Confirm update' });
}
getConfirmUninstallButton(): Locator {
return this.getConfirmModal().getByRole('button', { name: 'Confirm uninstall' });
}
// Simple actions
async clickInstallButton(): Promise<void> {
await this.getInstallButton().click();
}
async fillPackageName(packageName: string): Promise<void> {
await this.getPackageNameInput().fill(packageName);
}
async clickUserAgreementCheckbox(): Promise<void> {
await this.getUserAgreementCheckbox().click();
}
async clickInstallPackageButton(): Promise<void> {
await this.getInstallPackageButton().click();
}
async clickActionToggle(): Promise<void> {
await this.getActionToggle().click();
}
async clickUninstallAction(): Promise<void> {
await this.getUninstallAction().click();
}
async clickUpdateButton(): Promise<void> {
await this.getUpdateButton().click();
}
async clickConfirmUpdate(): Promise<void> {
await this.getConfirmUpdateButton().click();
}
async clickConfirmUninstall(): Promise<void> {
await this.getConfirmUninstallButton().click();
}
// Helper methods for common workflows
async installPackage(packageName: string): Promise<void> {
await this.clickInstallButton();
await this.fillPackageName(packageName);
await this.clickUserAgreementCheckbox();
await this.clickInstallPackageButton();
// Wait for install modal to close
await this.getInstallModal().waitFor({ state: 'hidden' });
}
async updatePackage(): Promise<void> {
await this.clickUpdateButton();
await this.clickConfirmUpdate();
}
async uninstallPackage(): Promise<void> {
await this.clickActionToggle();
await this.clickUninstallAction();
await this.clickConfirmUninstall();
}
}

View File

@@ -725,4 +725,7 @@ export class NodeDetailsViewPage extends BasePage {
getInputSelect() {
return this.page.getByTestId('ndv-input-select').locator('input');
}
getCredentialLabel(credentialType: string) {
return this.page.getByText(credentialType);
}
}

View File

@@ -0,0 +1,96 @@
import { expect, type Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class VariablesPage extends BasePage {
getUnavailableResourcesList() {
return this.page.getByTestId('unavailable-resources-list');
}
getResourcesList() {
return this.page.getByTestId('resources-list');
}
getEmptyResourcesList() {
return this.page.getByTestId('empty-resources-list');
}
getEmptyResourcesListNewVariableButton() {
return this.getEmptyResourcesList().locator('button');
}
getSearchBar() {
return this.page.getByTestId('resources-list-search');
}
getCreateVariableButton() {
return this.page.getByTestId('resources-list-add');
}
getVariablesRows() {
return this.page.getByTestId('variables-row');
}
getVariablesEditableRows() {
return this.page.getByTestId('variables-row').filter({ has: this.page.locator('input') });
}
getVariableRow(key: string) {
return this.getVariablesRows().filter({ hasText: key });
}
getEditableRowCancelButton(row: Locator) {
return row.getByTestId('variable-row-cancel-button');
}
getEditableRowSaveButton(row: Locator) {
return row.getByTestId('variable-row-save-button');
}
async createVariable(key: string, value: string) {
await this.getCreateVariableButton().click();
const editingRow = this.getVariablesEditableRows().first();
await this.setRowValue(editingRow, 'key', key);
await this.setRowValue(editingRow, 'value', value);
await this.saveRowEditing(editingRow);
}
async createVariableFromEmptyState(key: string, value: string) {
await this.getEmptyResourcesListNewVariableButton().click();
const editingRow = this.getVariablesEditableRows().first();
await this.setRowValue(editingRow, 'key', key);
await this.setRowValue(editingRow, 'value', value);
await this.saveRowEditing(editingRow);
}
async deleteVariable(key: string) {
const row = this.getVariableRow(key);
await row.getByTestId('variable-row-delete-button').click();
// Use a more specific selector to avoid strict mode violation with other dialogs
const modal = this.page.getByRole('dialog').filter({ hasText: 'Delete variable' });
await expect(modal).toBeVisible();
await modal.locator('.btn--confirm').click();
}
async editRow(key: string) {
const row = this.getVariableRow(key);
await row.getByTestId('variable-row-edit-button').click();
}
async setRowValue(row: Locator, field: 'key' | 'value', value: string) {
const input = row.getByTestId(`variable-row-${field}-input`).locator('input, textarea');
await input.selectText();
await input.fill(value);
}
async saveRowEditing(row: Locator) {
await this.getEditableRowSaveButton(row).click();
}
async cancelRowEditing(row: Locator) {
await this.getEditableRowCancelButton(row).click();
}
}

View File

@@ -3,6 +3,7 @@ import type { Page } from '@playwright/test';
import { AIAssistantPage } from './AIAssistantPage';
import { BecomeCreatorCTAPage } from './BecomeCreatorCTAPage';
import { CanvasPage } from './CanvasPage';
import { CommunityNodesPage } from './CommunityNodesPage';
import { CredentialsPage } from './CredentialsPage';
import { DemoPage } from './DemoPage';
import { ExecutionsPage } from './ExecutionsPage';
@@ -14,6 +15,7 @@ import { NpsSurveyPage } from './NpsSurveyPage';
import { ProjectSettingsPage } from './ProjectSettingsPage';
import { SettingsPage } from './SettingsPage';
import { SidebarPage } from './SidebarPage';
import { VariablesPage } from './VariablesPage';
import { VersionsPage } from './VersionsPage';
import { WorkerViewPage } from './WorkerViewPage';
import { WorkflowActivationModal } from './WorkflowActivationModal';
@@ -24,6 +26,7 @@ import { CanvasComposer } from '../composables/CanvasComposer';
import { ProjectComposer } from '../composables/ProjectComposer';
import { TestEntryComposer } from '../composables/TestEntryComposer';
import { WorkflowComposer } from '../composables/WorkflowComposer';
import { NavigationHelper } from '../helpers/NavigationHelper';
import type { ApiHelpers } from '../services/api-helper';
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -35,6 +38,7 @@ export class n8nPage {
readonly aiAssistant: AIAssistantPage;
readonly becomeCreatorCTA: BecomeCreatorCTAPage;
readonly canvas: CanvasPage;
readonly communityNodes: CommunityNodesPage;
readonly demo: DemoPage;
readonly iframe: IframePage;
readonly interactions: InteractionsPage;
@@ -42,6 +46,7 @@ export class n8nPage {
readonly npsSurvey: NpsSurveyPage;
readonly projectSettings: ProjectSettingsPage;
readonly settings: SettingsPage;
readonly variables: VariablesPage;
readonly versions: VersionsPage;
readonly workerView: WorkerViewPage;
readonly workflows: WorkflowsPage;
@@ -61,6 +66,9 @@ export class n8nPage {
readonly canvasComposer: CanvasComposer;
readonly start: TestEntryComposer;
// Helpers
readonly navigate: NavigationHelper;
constructor(page: Page, api: ApiHelpers) {
this.page = page;
this.api = api;
@@ -69,6 +77,7 @@ export class n8nPage {
this.aiAssistant = new AIAssistantPage(page);
this.becomeCreatorCTA = new BecomeCreatorCTAPage(page);
this.canvas = new CanvasPage(page);
this.communityNodes = new CommunityNodesPage(page);
this.demo = new DemoPage(page);
this.iframe = new IframePage(page);
this.interactions = new InteractionsPage(page);
@@ -76,6 +85,7 @@ export class n8nPage {
this.npsSurvey = new NpsSurveyPage(page);
this.projectSettings = new ProjectSettingsPage(page);
this.settings = new SettingsPage(page);
this.variables = new VariablesPage(page);
this.versions = new VersionsPage(page);
this.workerView = new WorkerViewPage(page);
this.workflows = new WorkflowsPage(page);
@@ -94,6 +104,9 @@ export class n8nPage {
this.projectComposer = new ProjectComposer(this);
this.canvasComposer = new CanvasComposer(this);
this.start = new TestEntryComposer(this);
// Helpers
this.navigate = new NavigationHelper(page);
}
async goHome() {