chore: 清理macOS同步产生的重复文件

详细说明:
- 删除了352个带数字后缀的重复文件
- 更新.gitignore防止未来产生此类文件
- 这些文件是由iCloud或其他同步服务冲突产生的
- 不影响项目功能,仅清理冗余文件
This commit is contained in:
Yep_Q
2025-09-08 12:06:01 +08:00
parent 1564396449
commit d6f48d6d14
365 changed files with 2039 additions and 68301 deletions

View File

@@ -1,66 +0,0 @@
import { BasePage } from './BasePage';
export class AIAssistantPage extends BasePage {
getAskAssistantFloatingButton() {
return this.page.getByTestId('ask-assistant-floating-button');
}
getAskAssistantCanvasActionButton() {
return this.page.getByTestId('ask-assistant-canvas-action-button');
}
getAskAssistantChat() {
return this.page.getByTestId('ask-assistant-chat');
}
getPlaceholderMessage() {
return this.page.getByTestId('placeholder-message');
}
getChatInput() {
return this.page.getByTestId('chat-input');
}
getSendMessageButton() {
return this.page.getByTestId('send-message-button');
}
getCloseChatButton() {
return this.page.getByTestId('close-chat-button');
}
getAskAssistantSidebarResizer() {
return this.page
.getByTestId('ask-assistant-sidebar')
.locator('[class*="_resizer"][data-dir="left"]')
.first();
}
getNodeErrorViewAssistantButton() {
return this.page.getByTestId('node-error-view-ask-assistant-button').locator('button').first();
}
getChatMessagesAll() {
return this.page.locator('[data-test-id^="chat-message"]');
}
getChatMessagesAssistant() {
return this.page.getByTestId('chat-message-assistant');
}
getChatMessagesUser() {
return this.page.getByTestId('chat-message-user');
}
getChatMessagesSystem() {
return this.page.getByTestId('chat-message-system');
}
getQuickReplyButtons() {
return this.page.getByTestId('quick-replies').locator('button');
}
getNewAssistantSessionModal() {
return this.page.getByTestId('new-assistant-session-modal');
}
}

View File

@@ -1,21 +0,0 @@
import type { Page } from '@playwright/test';
export abstract class BasePage {
constructor(protected readonly page: Page) {}
protected async clickByTestId(testId: string) {
await this.page.getByTestId(testId).click();
}
protected async fillByTestId(testId: string, value: string) {
await this.page.getByTestId(testId).fill(value);
}
protected async clickByText(text: string) {
await this.page.getByText(text).click();
}
protected async clickButtonByName(name: string) {
await this.page.getByRole('button', { name }).click();
}
}

View File

@@ -1,15 +0,0 @@
import { BasePage } from './BasePage';
export class BecomeCreatorCTAPage extends BasePage {
getBecomeTemplateCreatorCta() {
return this.page.getByTestId('become-template-creator-cta');
}
getCloseBecomeTemplateCreatorCtaButton() {
return this.page.getByTestId('close-become-template-creator-cta');
}
async closeBecomeTemplateCreatorCta() {
await this.getCloseBecomeTemplateCreatorCtaButton().click();
}
}

View File

@@ -1,449 +0,0 @@
import type { Locator } from '@playwright/test';
import { nanoid } from 'nanoid';
import { BasePage } from './BasePage';
import { resolveFromRoot } from '../utils/path-helper';
export class CanvasPage extends BasePage {
saveWorkflowButton(): Locator {
return this.page.getByRole('button', { name: 'Save' });
}
nodeCreatorItemByName(text: string): Locator {
return this.page.getByTestId('node-creator-item-name').getByText(text, { exact: true });
}
nodeCreatorSubItem(subItemText: string): Locator {
return this.page.getByTestId('node-creator-item-name').getByText(subItemText, { exact: true });
}
nodeByName(nodeName: string): Locator {
return this.page.locator(`[data-test-id="canvas-node"][data-node-name="${nodeName}"]`);
}
nodeToolbar(nodeName: string): Locator {
return this.nodeByName(nodeName).getByTestId('canvas-node-toolbar');
}
nodeDeleteButton(nodeName: string): Locator {
return this.nodeToolbar(nodeName).getByTestId('delete-node-button');
}
nodeDisableButton(nodeName: string): Locator {
return this.nodeToolbar(nodeName).getByTestId('disable-node-button');
}
async clickCanvasPlusButton(): Promise<void> {
await this.clickByTestId('canvas-plus-button');
}
getCanvasNodes() {
return this.page.getByTestId('canvas-node');
}
async clickNodeCreatorPlusButton(): Promise<void> {
await this.clickByTestId('node-creator-plus-button');
}
async clickSaveWorkflowButton(): Promise<void> {
await this.saveWorkflowButton().click();
}
async fillNodeCreatorSearchBar(text: string): Promise<void> {
await this.nodeCreatorSearchBar().fill(text);
}
async clickNodeCreatorItemName(text: string): Promise<void> {
await this.nodeCreatorItemByName(text).click();
}
async addNode(text: string): Promise<void> {
await this.clickNodeCreatorPlusButton();
await this.fillNodeCreatorSearchBar(text);
await this.clickNodeCreatorItemName(text);
}
async addNodeAndCloseNDV(text: string, subItemText?: string): Promise<void> {
if (subItemText) {
await this.addNodeWithSubItem(text, subItemText);
} else {
await this.addNode(text);
}
await this.page.keyboard.press('Escape');
}
async addNodeWithSubItem(searchText: string, subItemText: string): Promise<void> {
await this.addNode(searchText);
await this.nodeCreatorSubItem(subItemText).click();
}
async addActionNode(searchText: string, subItemText: string): Promise<void> {
await this.addNode(searchText);
await this.page.getByText('Actions').click();
await this.nodeCreatorSubItem(subItemText).click();
}
async addTriggerNode(searchText: string, subItemText: string): Promise<void> {
await this.addNode(searchText);
await this.page.getByText('Triggers').click();
await this.nodeCreatorSubItem(subItemText).click();
}
async deleteNodeByName(nodeName: string): Promise<void> {
await this.nodeDeleteButton(nodeName).click();
}
async saveWorkflow(): Promise<void> {
await this.clickSaveWorkflowButton();
}
getExecuteWorkflowButton(): Locator {
return this.page.getByTestId('execute-workflow-button');
}
async clickExecuteWorkflowButton(): Promise<void> {
await this.page.getByTestId('execute-workflow-button').click();
}
async clickDebugInEditorButton(): Promise<void> {
await this.page.getByRole('button', { name: 'Debug in editor' }).click();
}
async pinNode(nodeName: string): Promise<void> {
await this.nodeByName(nodeName).click({ button: 'right' });
await this.page.getByTestId('context-menu').getByText('Pin').click();
}
async unpinNode(nodeName: string): Promise<void> {
await this.nodeByName(nodeName).click({ button: 'right' });
await this.page.getByText('Unpin').click();
}
async openNode(nodeName: string): Promise<void> {
await this.nodeByName(nodeName).dblclick();
}
/**
* Get the names of all pinned nodes on the canvas.
* @returns An array of node names.
*/
async getPinnedNodeNames(): Promise<string[]> {
const pinnedNodesLocator = this.page
.getByTestId('canvas-node')
.filter({ has: this.page.getByTestId('canvas-node-status-pinned') });
const names: string[] = [];
const count = await pinnedNodesLocator.count();
for (let i = 0; i < count; i++) {
const node = pinnedNodesLocator.nth(i);
const name = await node.getAttribute('data-node-name');
if (name) {
names.push(name);
}
}
return names;
}
async clickExecutionsTab(): Promise<void> {
await this.page.getByRole('radio', { name: 'Executions' }).click();
}
async setWorkflowName(name: string): Promise<void> {
await this.clickByTestId('inline-edit-preview');
await this.fillByTestId('inline-edit-input', name);
}
/**
* Import a workflow from a fixture file
* @param fixtureKey - The key of the fixture file to import
* @param workflowName - The name of the workflow to import
* Naming the file causes the workflow to save so we don't need to click save
*/
async importWorkflow(fixtureKey: string, workflowName: string) {
await this.clickByTestId('workflow-menu');
const [fileChooser] = await Promise.all([
this.page.waitForEvent('filechooser'),
this.clickByText('Import from File...'),
]);
await fileChooser.setFiles(resolveFromRoot('workflows', fixtureKey));
await this.clickByTestId('inline-edit-preview');
await this.fillByTestId('inline-edit-input', workflowName);
await this.page.getByTestId('inline-edit-input').press('Enter');
}
getWorkflowTags() {
return this.page.getByTestId('workflow-tags').locator('.el-tag');
}
async activateWorkflow() {
const responsePromise = this.page.waitForResponse(
(response) =>
response.url().includes('/rest/workflows/') && response.request().method() === 'PATCH',
);
await this.page.getByTestId('workflow-activate-switch').click();
await responsePromise;
await this.page.waitForTimeout(200);
}
async clickZoomToFitButton(): Promise<void> {
await this.clickByTestId('zoom-to-fit');
}
/**
* Get node issues for a specific node
*/
getNodeIssuesByName(nodeName: string) {
return this.nodeByName(nodeName).getByTestId('node-issues');
}
/**
* Add tags to the workflow
* @param count - The number of tags to add
* @returns An array of tag names
*/
async addTags(count: number = 1): Promise<string[]> {
const tags: string[] = [];
for (let i = 0; i < count; i++) {
const tag = `tag-${nanoid(8)}-${i}`;
tags.push(tag);
if (i === 0) {
await this.clickByText('Add tag');
} else {
await this.page
.getByTestId('tags-dropdown')
.getByText(tags[i - 1])
.click();
}
await this.page.getByRole('combobox').first().fill(tag);
await this.page.getByRole('combobox').first().press('Enter');
}
await this.page.click('body');
return tags;
}
getWorkflowSaveButton(): Locator {
return this.page.getByTestId('workflow-save-button');
}
// Production Checklist methods
getProductionChecklistButton(): Locator {
return this.page.getByTestId('suggested-action-count');
}
getProductionChecklistPopover(): Locator {
return this.page.locator('[data-reka-popper-content-wrapper=""]').filter({ hasText: /./ });
}
getProductionChecklistActionItem(text?: string): Locator {
const items = this.page.getByTestId('suggested-action-item');
if (text) {
return items.getByText(text);
}
return items;
}
getProductionChecklistIgnoreAllButton(): Locator {
return this.page.getByTestId('suggested-action-ignore-all');
}
getErrorActionItem(): Locator {
return this.getProductionChecklistActionItem('Set up error notifications');
}
getTimeSavedActionItem(): Locator {
return this.getProductionChecklistActionItem('Track time saved');
}
getEvaluationsActionItem(): Locator {
return this.getProductionChecklistActionItem('Test reliability of AI steps');
}
async clickProductionChecklistButton(): Promise<void> {
await this.getProductionChecklistButton().click();
}
async clickProductionChecklistIgnoreAll(): Promise<void> {
await this.getProductionChecklistIgnoreAllButton().click();
}
async clickProductionChecklistAction(actionText: string): Promise<void> {
await this.getProductionChecklistActionItem(actionText).click();
}
async duplicateNode(nodeName: string): Promise<void> {
await this.nodeByName(nodeName).click({ button: 'right' });
await this.page.getByTestId('context-menu').getByText('Duplicate').click();
}
nodeConnections(): Locator {
return this.page.locator('[data-test-id="edge"]');
}
canvasNodePlusEndpointByName(nodeName: string): Locator {
return this.page
.locator(
`[data-test-id="canvas-node-output-handle"][data-node-name="${nodeName}"] [data-test-id="canvas-handle-plus"]`,
)
.first();
}
nodeCreatorSearchBar(): Locator {
return this.page.getByTestId('node-creator-search-bar');
}
nodeCreatorNodeItems(): Locator {
return this.page.getByTestId('node-creator-node-item');
}
nodeCreatorActionItems(): Locator {
return this.page.getByTestId('node-creator-action-item');
}
nodeCreatorCategoryItems(): Locator {
return this.page.getByTestId('node-creator-category-item');
}
selectedNodes(): Locator {
return this.page
.locator('[data-test-id="canvas-node"]')
.locator('xpath=..')
.locator('.selected');
}
disabledNodes(): Locator {
return this.page.locator('[data-canvas-node-render-type][class*="disabled"]');
}
nodeExecuteButton(nodeName: string): Locator {
return this.nodeToolbar(nodeName).getByTestId('execute-node-button');
}
canvasPane(): Locator {
return this.page.getByTestId('canvas-wrapper');
}
// Actions
async addInitialNodeToCanvas(nodeName: string): Promise<void> {
await this.clickCanvasPlusButton();
await this.fillNodeCreatorSearchBar(nodeName);
await this.clickNodeCreatorItemName(nodeName);
}
async clickNodePlusEndpoint(nodeName: string): Promise<void> {
await this.canvasNodePlusEndpointByName(nodeName).click();
}
async executeNode(nodeName: string): Promise<void> {
await this.nodeByName(nodeName).hover();
await this.nodeExecuteButton(nodeName).click();
}
async selectAll(): Promise<void> {
await this.page.keyboard.press('ControlOrMeta+a');
}
async copyNodes(): Promise<void> {
await this.page.keyboard.press('ControlOrMeta+c');
}
async deselectAll(): Promise<void> {
await this.canvasPane().click({ position: { x: 10, y: 10 } });
}
getNodeLeftPosition(nodeLocator: Locator): Promise<number> {
return nodeLocator.evaluate((el) => el.getBoundingClientRect().left);
}
// Connection helpers
connectionBetweenNodes(sourceNodeName: string, targetNodeName: string): Locator {
return this.page.locator(
`[data-test-id="edge"][data-source-node-name="${sourceNodeName}"][data-target-node-name="${targetNodeName}"]`,
);
}
connectionToolbarBetweenNodes(sourceNodeName: string, targetNodeName: string): Locator {
return this.page.locator(
`[data-test-id="edge-label"][data-source-node-name="${sourceNodeName}"][data-target-node-name="${targetNodeName}"] [data-test-id="canvas-edge-toolbar"]`,
);
}
// Canvas action helpers
async addNodeBetweenNodes(
sourceNodeName: string,
targetNodeName: string,
newNodeName: string,
): Promise<void> {
const specificConnection = this.connectionBetweenNodes(sourceNodeName, targetNodeName);
// eslint-disable-next-line playwright/no-force-option
await specificConnection.hover({ force: true });
const addNodeButton = this.connectionToolbarBetweenNodes(
sourceNodeName,
targetNodeName,
).getByTestId('add-connection-button');
await addNodeButton.click();
await this.fillNodeCreatorSearchBar(newNodeName);
await this.clickNodeCreatorItemName(newNodeName);
await this.page.keyboard.press('Escape');
}
async deleteConnectionBetweenNodes(
sourceNodeName: string,
targetNodeName: string,
): Promise<void> {
const specificConnection = this.connectionBetweenNodes(sourceNodeName, targetNodeName);
// eslint-disable-next-line playwright/no-force-option
await specificConnection.hover({ force: true });
const deleteButton = this.connectionToolbarBetweenNodes(
sourceNodeName,
targetNodeName,
).getByTestId('delete-connection-button');
await deleteButton.click();
}
async navigateNodesWithArrows(direction: 'left' | 'right' | 'up' | 'down'): Promise<void> {
const keyMap = {
left: 'ArrowLeft',
right: 'ArrowRight',
up: 'ArrowUp',
down: 'ArrowDown',
};
await this.canvasPane().focus();
await this.page.keyboard.press(keyMap[direction]);
}
async extendSelectionWithArrows(direction: 'left' | 'right' | 'up' | 'down'): Promise<void> {
const keyMap = {
left: 'Shift+ArrowLeft',
right: 'Shift+ArrowRight',
up: 'Shift+ArrowUp',
down: 'Shift+ArrowDown',
};
await this.canvasPane().focus();
await this.page.keyboard.press(keyMap[direction]);
}
/**
* Visit the workflow page with a specific timestamp for NPS survey testing.
* Uses Playwright's clock API to set a fixed time.
*/
async visitWithTimestamp(timestamp: number): Promise<void> {
// Set fixed time using Playwright's clock API
await this.page.clock.setFixedTime(timestamp);
await this.page.goto('/workflow/new');
}
}

View File

@@ -1,65 +0,0 @@
import { BasePage } from './BasePage';
export class CredentialsPage extends BasePage {
get emptyListCreateCredentialButton() {
return this.page.getByRole('button', { name: 'Add first credential' });
}
get createCredentialButton() {
return this.page.getByTestId('create-credential-button');
}
get credentialCards() {
return this.page.getByTestId('credential-cards');
}
/**
* Create a new credential of the specified type
* @param credentialType - The type of credential to create (e.g. 'Notion API')
*/
async openNewCredentialDialogFromCredentialList(credentialType: string): Promise<void> {
await this.page.getByRole('combobox', { name: 'Search for app...' }).fill(credentialType);
await this.page
.getByTestId('new-credential-type-select-option')
.filter({ hasText: credentialType })
.click();
await this.page.getByTestId('new-credential-type-button').click();
}
async openCredentialSelector() {
await this.page.getByRole('combobox', { name: 'Select Credential' }).click();
}
async createNewCredential() {
await this.clickByText('Create new credential');
}
async fillCredentialField(fieldName: string, value: string) {
const field = this.page
.getByTestId(`parameter-input-${fieldName}`)
.getByTestId('parameter-input-field');
await field.click();
await field.fill(value);
}
async saveCredential() {
await this.clickButtonByName('Save');
}
async closeCredentialDialog() {
await this.clickButtonByName('Close this dialog');
}
async createAndSaveNewCredential(fieldName: string, value: string) {
await this.openCredentialSelector();
await this.createNewCredential();
await this.filLCredentialSaveClose(fieldName, value);
}
async filLCredentialSaveClose(fieldName: string, value: string) {
await this.fillCredentialField(fieldName, value);
await this.saveCredential();
await this.page.getByText('Connection tested successfully').waitFor({ state: 'visible' });
await this.closeCredentialDialog();
}
}

View File

@@ -1,36 +0,0 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class ExecutionsPage extends BasePage {
async clickDebugInEditorButton(): Promise<void> {
await this.clickButtonByName('Debug in editor');
}
async clickCopyToEditorButton(): Promise<void> {
await this.clickButtonByName('Copy to editor');
}
getExecutionItems(): Locator {
return this.page.locator('div.execution-card');
}
getLastExecutionItem(): Locator {
const executionItems = this.getExecutionItems();
return executionItems.nth(0);
}
async clickLastExecutionItem(): Promise<void> {
const executionItem = this.getLastExecutionItem();
await executionItem.click();
}
/**
* Handle the pinned nodes confirmation dialog.
* @param action - The action to take.
*/
async handlePinnedNodesConfirmation(action: 'Unpin' | 'Cancel'): Promise<void> {
const confirmDialog = this.page.locator('.matching-pinned-nodes-confirmation');
await this.page.getByRole('button', { name: action }).click();
}
}

View File

@@ -1,15 +0,0 @@
import { BasePage } from './BasePage';
export class IframePage extends BasePage {
getIframe() {
return this.page.locator('iframe');
}
getIframeBySrc(src: string) {
return this.page.locator(`iframe[src="${src}"]`);
}
async waitForIframeRequest(url: string) {
await this.page.waitForResponse(url);
}
}

View File

@@ -1,460 +0,0 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
import { BasePage } from './BasePage';
import { NodeParameterHelper } from '../helpers/NodeParameterHelper';
import { EditFieldsNode } from './nodes/EditFieldsNode';
export class NodeDetailsViewPage extends BasePage {
readonly setupHelper: NodeParameterHelper;
readonly editFields: EditFieldsNode;
constructor(page: Page) {
super(page);
this.setupHelper = new NodeParameterHelper(this);
this.editFields = new EditFieldsNode(page);
}
async clickBackToCanvasButton() {
await this.clickByTestId('back-to-canvas');
}
getParameterByLabel(labelName: string) {
return this.page.locator('.parameter-item').filter({ hasText: labelName });
}
/**
* Fill a parameter input field
* @param labelName - The label of the parameter e.g URL
* @param value - The value to fill in the input field e.g https://foo.bar
*/
async fillParameterInput(labelName: string, value: string) {
await this.getParameterByLabel(labelName).getByTestId('parameter-input-field').fill(value);
}
async selectWorkflowResource(createItemText: string, searchText: string = '') {
await this.clickByTestId('rlc-input');
if (searchText) {
await this.fillByTestId('rlc-search', searchText);
}
await this.clickByText(createItemText);
}
async togglePinData() {
await this.clickByTestId('ndv-pin-data');
}
async close() {
await this.clickBackToCanvasButton();
}
async execute() {
await this.clickByTestId('node-execute-button');
}
getOutputPanel() {
return this.page.getByTestId('output-panel');
}
getContainer() {
return this.page.getByTestId('ndv');
}
getInputPanel() {
return this.page.getByTestId('ndv-input-panel');
}
getParameterExpressionPreviewValue() {
return this.page.getByTestId('parameter-expression-preview-value');
}
getEditPinnedDataButton() {
return this.page.getByTestId('ndv-edit-pinned-data');
}
getPinDataButton() {
return this.getOutputPanel().getByTestId('ndv-pin-data');
}
getRunDataPaneHeader() {
return this.page.getByTestId('run-data-pane-header');
}
getOutputTable() {
return this.getOutputPanel().getByTestId('ndv-data-container').locator('table');
}
getOutputDataContainer() {
return this.getOutputPanel().getByTestId('ndv-data-container');
}
getOutputTableRows() {
return this.getOutputTable().locator('tr');
}
getOutputTableHeaders() {
return this.getOutputTable().locator('thead th');
}
getOutputTableRow(row: number) {
return this.getOutputTableRows().nth(row);
}
getOutputTableCell(row: number, col: number) {
return this.getOutputTableRow(row).locator('td').nth(col);
}
getOutputTbodyCell(row: number, col: number) {
return this.getOutputTableRow(row).locator('td').nth(col);
}
// Pin data operations
async setPinnedData(data: object | string) {
const pinnedData = typeof data === 'string' ? data : JSON.stringify(data);
await this.getEditPinnedDataButton().click();
// Wait for editor to appear and use broader selector
const editor = this.getOutputPanel().locator('[contenteditable="true"]');
await editor.waitFor();
await editor.click();
await editor.fill(pinnedData);
await this.savePinnedData();
}
async pastePinnedData(data: object) {
await this.getEditPinnedDataButton().click();
const editor = this.getOutputPanel().locator('[contenteditable="true"]');
await editor.waitFor();
await editor.click();
await editor.fill('');
// Set clipboard data and paste
await this.page.evaluate(async (jsonData) => {
await navigator.clipboard.writeText(JSON.stringify(jsonData));
}, data);
await this.page.keyboard.press('ControlOrMeta+V');
await this.savePinnedData();
}
async savePinnedData() {
await this.getRunDataPaneHeader().locator('button:visible').filter({ hasText: 'Save' }).click();
}
// Assignment collection methods for advanced tests
getAssignmentCollectionAdd(paramName: string) {
return this.page
.getByTestId(`assignment-collection-${paramName}`)
.getByTestId('assignment-collection-drop-area');
}
getAssignmentValue(paramName: string) {
return this.page
.getByTestId(`assignment-collection-${paramName}`)
.getByTestId('assignment-value');
}
getInlineExpressionEditorInput() {
return this.page.getByTestId('inline-expression-editor-input');
}
getNodeParameters() {
return this.page.getByTestId('node-parameters');
}
getParameterInputHint() {
return this.page.getByTestId('parameter-input-hint');
}
async makeWebhookRequest(path: string) {
return await this.page.request.get(path);
}
getVisiblePoppers() {
return this.page.locator('.el-popper:visible');
}
async clearExpressionEditor() {
const editor = this.getInlineExpressionEditorInput();
await editor.click();
await this.page.keyboard.press('ControlOrMeta+A');
await this.page.keyboard.press('Delete');
}
async typeInExpressionEditor(text: string) {
const editor = this.getInlineExpressionEditorInput();
await editor.click();
// We have to use type() instead of fill() because the editor is a CodeMirror editor
await editor.type(text);
}
/**
* Get parameter input by name (for Code node and similar)
* @param parameterName - The name of the parameter e.g 'jsCode', 'mode'
*/
getParameterInput(parameterName: string) {
return this.page.getByTestId(`parameter-input-${parameterName}`);
}
/**
* Get parameter input field
* @param parameterName - The name of the parameter
*/
getParameterInputField(parameterName: string) {
return this.getParameterInput(parameterName).getByTestId('parameter-input-field');
}
/**
* Select option in parameter dropdown (improved with Playwright best practices)
* @param parameterName - The parameter name
* @param optionText - The text of the option to select
*/
async selectOptionInParameterDropdown(parameterName: string, optionText: string) {
const dropdown = this.getParameterInput(parameterName);
await dropdown.click();
// Wait for dropdown to be visible and select option - following Playwright best practices
await this.page.getByRole('option', { name: optionText }).click();
}
/**
* Click parameter dropdown by name (test-id based selector)
* @param parameterName - The parameter name e.g 'httpMethod', 'authentication'
*/
async clickParameterDropdown(parameterName: string): Promise<void> {
await this.clickByTestId(`parameter-input-${parameterName}`);
}
/**
* Select option from visible dropdown using Playwright role-based selectors
* This follows the pattern used in working n8n tests
* @param optionText - The text of the option to select
*/
async selectFromVisibleDropdown(optionText: string): Promise<void> {
// Use Playwright's role-based selector - this is more reliable than CSS selectors
await this.page.getByRole('option', { name: optionText }).click();
}
/**
* Fill parameter input field by parameter name
* @param parameterName - The parameter name e.g 'path', 'url'
* @param value - The value to fill
*/
async fillParameterInputByName(parameterName: string, value: string): Promise<void> {
const input = this.getParameterInputField(parameterName);
await input.click();
await input.fill(value);
}
/**
* Click parameter options expansion (e.g. for Response Code)
*/
async clickParameterOptions(): Promise<void> {
await this.page.locator('.param-options').click();
}
/**
* Get visible Element UI popper (dropdown/popover)
* Ported from Cypress pattern with Playwright selectors
*/
getVisiblePopper() {
return this.page
.locator('.el-popper')
.filter({ hasNot: this.page.locator('[aria-hidden="true"]') });
}
/**
* Wait for parameter dropdown to be visible and ready for interaction
* @param parameterName - The parameter name
*/
async waitForParameterDropdown(parameterName: string): Promise<void> {
const dropdown = this.getParameterInput(parameterName);
await dropdown.waitFor({ state: 'visible' });
await expect(dropdown).toBeEnabled();
}
/**
* Click on a floating node in the NDV (for switching between connected nodes)
* @param nodeName - The name of the node to click
*/
async clickFloatingNode(nodeName: string) {
await this.page.locator(`[data-test-id="floating-node"][data-node-name="${nodeName}"]`).click();
}
/**
* Execute the previous node (useful for providing input data)
*/
async executePrevious() {
await this.clickByTestId('execute-previous-node');
}
async clickAskAiTab() {
await this.page.locator('#tab-ask-ai').click();
}
getAskAiTabPanel() {
return this.page.getByTestId('code-node-tab-ai');
}
getAskAiCtaButton() {
return this.page.getByTestId('ask-ai-cta');
}
getAskAiPromptInput() {
return this.page.getByTestId('ask-ai-prompt-input');
}
getAskAiPromptCounter() {
return this.page.getByTestId('ask-ai-prompt-counter');
}
getAskAiCtaTooltipNoInputData() {
return this.page.getByTestId('ask-ai-cta-tooltip-no-input-data');
}
getAskAiCtaTooltipNoPrompt() {
return this.page.getByTestId('ask-ai-cta-tooltip-no-prompt');
}
getAskAiCtaTooltipPromptTooShort() {
return this.page.getByTestId('ask-ai-cta-tooltip-prompt-too-short');
}
getCodeTabPanel() {
return this.page.getByTestId('code-node-tab-code');
}
getCodeTab() {
return this.page.locator('#tab-code');
}
getCodeEditor() {
return this.getParameterInput('jsCode').locator('.cm-content');
}
getLintErrors() {
return this.getParameterInput('jsCode').locator('.cm-lintRange-error');
}
getLintTooltip() {
return this.page.locator('.cm-tooltip-lint');
}
getPlaceholderText(text: string) {
return this.page.getByText(text);
}
getHeyAiText() {
return this.page.locator('text=Hey AI, generate JavaScript');
}
getCodeGenerationCompletedText() {
return this.page.locator('text=Code generation completed');
}
getErrorMessageText(message: string) {
return this.page.locator(`text=${message}`);
}
async setParameterDropdown(parameterName: string, optionText: string): Promise<void> {
await this.getParameterInput(parameterName).click();
await this.page.getByRole('option', { name: optionText }).click();
}
async setParameterInput(parameterName: string, value: string): Promise<void> {
await this.fillParameterInputByName(parameterName, value);
}
async setParameterSwitch(parameterName: string, enabled: boolean): Promise<void> {
const switchElement = this.getParameterInput(parameterName).locator('.el-switch');
const isCurrentlyEnabled = (await switchElement.getAttribute('aria-checked')) === 'true';
if (isCurrentlyEnabled !== enabled) {
await switchElement.click();
}
}
async setMultipleParameters(
parameters: Record<string, string | number | boolean>,
): Promise<void> {
for (const [parameterName, value] of Object.entries(parameters)) {
if (typeof value === 'string') {
const parameterType = await this.setupHelper.detectParameterType(parameterName);
if (parameterType === 'dropdown') {
await this.setParameterDropdown(parameterName, value);
} else {
await this.setParameterInput(parameterName, value);
}
} else if (typeof value === 'boolean') {
await this.setParameterSwitch(parameterName, value);
} else if (typeof value === 'number') {
await this.setParameterInput(parameterName, value.toString());
}
}
}
async getParameterValue(parameterName: string): Promise<string> {
const parameterType = await this.setupHelper.detectParameterType(parameterName);
switch (parameterType) {
case 'text':
return await this.getTextParameterValue(parameterName);
case 'dropdown':
return await this.getDropdownParameterValue(parameterName);
case 'switch':
return await this.getSwitchParameterValue(parameterName);
default:
// Fallback for unknown types
return (await this.getParameterInput(parameterName).textContent()) ?? '';
}
}
/**
* Get value from a text parameter - simplified approach
*/
private async getTextParameterValue(parameterName: string): Promise<string> {
const parameterContainer = this.getParameterInput(parameterName);
const input = parameterContainer.locator('input').first();
return await input.inputValue();
}
/**
* Get value from a dropdown parameter
*/
private async getDropdownParameterValue(parameterName: string): Promise<string> {
const selectedOption = this.getParameterInput(parameterName).locator('.el-select__tags-text');
return (await selectedOption.textContent()) ?? '';
}
/**
* Get value from a switch parameter
*/
private async getSwitchParameterValue(parameterName: string): Promise<string> {
const switchElement = this.getParameterInput(parameterName).locator('.el-switch');
const isEnabled = (await switchElement.getAttribute('aria-checked')) === 'true';
return isEnabled ? 'true' : 'false';
}
async validateParameter(parameterName: string, expectedValue: string): Promise<void> {
const actualValue = await this.getParameterValue(parameterName);
if (actualValue !== expectedValue) {
throw new Error(
`Parameter ${parameterName} has value "${actualValue}", expected "${expectedValue}"`,
);
}
}
getAssignmentCollectionContainer(paramName: string) {
return this.page.getByTestId(`assignment-collection-${paramName}`);
}
getAssignmentName(paramName: string, index = 0) {
return this.getAssignmentCollectionContainer(paramName)
.getByTestId('assignment')
.nth(index)
.getByTestId('assignment-name');
}
}

View File

@@ -1,268 +0,0 @@
import type { Locator, Page } from '@playwright/test';
export class NotificationsPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
/**
* Gets the main container locator for a notification by searching in its title text.
* @param text The text or a regular expression to find within the notification's title.
* @returns A Locator for the notification container element.
*/
getNotificationByTitle(text: string | RegExp): Locator {
return this.page.getByRole('alert').filter({
has: this.page.locator('.el-notification__title').filter({ hasText: text }),
});
}
/**
* Gets the main container locator for a notification by searching in its content/body text.
* This is useful for finding notifications where the detailed message is in the content
* rather than the title (e.g., error messages with detailed descriptions).
* @param text The text or a regular expression to find within the notification's content.
* @returns A Locator for the notification container element.
*/
getNotificationByContent(text: string | RegExp): Locator {
return this.page.getByRole('alert').filter({
has: this.page.locator('.el-notification__content').filter({ hasText: text }),
});
}
/**
* Gets the main container locator for a notification by searching in both title and content.
* This is the most flexible method as it will find notifications regardless of whether
* the text appears in the title or content section.
* @param text The text or a regular expression to find within the notification's title or content.
* @returns A Locator for the notification container element.
*/
getNotificationByTitleOrContent(text: string | RegExp): Locator {
return this.page.getByRole('alert').filter({ hasText: text });
}
/**
* Gets the main container locator for a notification by searching in both title and content,
* filtered to a specific node name. This is useful when multiple notifications might be present
* and you want to ensure you're checking the right one for a specific node.
* @param text The text or a regular expression to find within the notification's title or content.
* @param nodeName The name of the node to filter notifications for.
* @returns A Locator for the notification container element.
*/
getNotificationByTitleOrContentForNode(text: string | RegExp, nodeName: string): Locator {
return this.page.getByRole('alert').filter({ hasText: text }).filter({ hasText: nodeName });
}
/**
* Clicks the close button on the FIRST notification matching the text.
* Fast execution with short timeouts for snappy notifications.
* @param text The text of the notification to close.
* @param options Optional configuration
*/
async closeNotificationByText(
text: string | RegExp,
options: { timeout?: number } = {},
): Promise<boolean> {
const { timeout = 2000 } = options;
try {
const notification = this.getNotificationByTitle(text).first();
await notification.waitFor({ state: 'visible', timeout });
const closeBtn = notification.locator('.el-notification__closeBtn');
await closeBtn.click({ timeout: 500 });
// Quick check that it's gone - don't wait long
await notification.waitFor({ state: 'hidden', timeout: 1000 });
return true;
} catch (error) {
return false;
}
}
/**
* Closes ALL currently visible notifications that match the given text.
* Uses aggressive polling for fast cleanup.
* @param text The text of the notifications to close.
* @param options Optional configuration
*/
async closeAllNotificationsWithText(
text: string | RegExp,
options: { timeout?: number; maxRetries?: number } = {},
): Promise<number> {
const { timeout = 1500, maxRetries = 15 } = options;
let closedCount = 0;
let retries = 0;
while (retries < maxRetries) {
try {
const notifications = this.getNotificationByTitle(text);
const count = await notifications.count();
if (count === 0) {
break;
}
// Close the first visible notification quickly
const firstNotification = notifications.first();
if (await firstNotification.isVisible({ timeout: 200 })) {
const closeBtn = firstNotification.locator('.el-notification__closeBtn');
await closeBtn.click({ timeout: 300 });
// Brief wait for disappearance, then continue
await firstNotification.waitFor({ state: 'hidden', timeout: 500 }).catch(() => {});
closedCount++;
} else {
// If not visible, likely already gone
break;
}
} catch (error) {
// Continue quickly on any error
break;
}
retries++;
}
return closedCount;
}
/**
* Check if a notification is visible based on text.
* Fast check with short timeout.
* @param text The text to search for in notification title.
* @param options Optional configuration
*/
async isNotificationVisible(
text: string | RegExp,
options: { timeout?: number } = {},
): Promise<boolean> {
const { timeout = 500 } = options;
try {
const notification = this.getNotificationByTitle(text).first();
await notification.waitFor({ state: 'visible', timeout });
return true;
} catch {
return false;
}
}
/**
* Wait for a notification to appear with specific text.
* Reasonable timeout for waiting, but still faster than before.
* @param text The text to search for in notification title.
* @param options Optional configuration
*/
async waitForNotification(
text: string | RegExp,
options: { timeout?: number } = {},
): Promise<boolean> {
const { timeout = 5000 } = options;
try {
const notification = this.getNotificationByTitle(text).first();
await notification.waitFor({ state: 'visible', timeout });
return true;
} catch {
return false;
}
}
// Wait for notification and then close it
async waitForNotificationAndClose(
text: string | RegExp,
options: { timeout?: number } = {},
): Promise<boolean> {
const { timeout = 3000 } = options;
await this.waitForNotification(text, { timeout });
await this.closeNotificationByText(text, { timeout });
return true;
}
/**
* Get all visible notification texts.
* @returns Array of notification title texts
*/
async getAllNotificationTexts(): Promise<string[]> {
try {
const titles = this.page.getByRole('alert').locator('.el-notification__title');
return await titles.allTextContents();
} catch {
return [];
}
}
/**
* Wait for all notifications to disappear.
* Fast check with short timeout.
* @param options Optional configuration
*/
async waitForAllNotificationsToDisappear(options: { timeout?: number } = {}): Promise<boolean> {
const { timeout = 2000 } = options;
try {
// Wait for no alerts to be visible
await this.page.getByRole('alert').first().waitFor({
state: 'detached',
timeout,
});
return true;
} catch {
// Check if any are still visible
const count = await this.getNotificationCount();
return count === 0;
}
}
/**
* Get the count of visible notifications.
* @param text Optional text to filter notifications
*/
async getNotificationCount(text?: string | RegExp): Promise<number> {
try {
const notifications = text ? this.getNotificationByTitle(text) : this.page.getByRole('alert');
return await notifications.count();
} catch {
return 0;
}
}
/**
* Quick utility to close any notification and continue.
* Uses the most aggressive timeouts for maximum speed.
* @param text The text of the notification to close.
*/
async quickClose(text: string | RegExp): Promise<void> {
try {
const notification = this.getNotificationByTitle(text).first();
if (await notification.isVisible({ timeout: 100 })) {
await notification.locator('.el-notification__closeBtn').click({ timeout: 200 });
}
} catch {
// Silent fail for speed
}
}
/**
* Nuclear option: Close everything as fast as possible.
* No waiting, no error handling, just close and move on.
*/
async quickCloseAll(): Promise<void> {
try {
const closeButtons = this.page.locator('.el-notification__closeBtn');
const count = await closeButtons.count();
for (let i = 0; i < count; i++) {
try {
await closeButtons.nth(i).click({ timeout: 100 });
} catch {
// Continue silently
}
}
} catch {
// Silent fail
}
}
}

View File

@@ -1,57 +0,0 @@
import type { Locator, Page } from '@playwright/test';
import { BasePage } from './BasePage';
export class NpsSurveyPage extends BasePage {
constructor(page: Page) {
super(page);
}
getNpsSurveyModal(): Locator {
return this.page.getByTestId('nps-survey-modal');
}
getNpsSurveyRatings(): Locator {
return this.page.getByTestId('nps-survey-ratings');
}
getNpsSurveyFeedback(): Locator {
return this.page.getByTestId('nps-survey-feedback');
}
getNpsSurveySubmitButton(): Locator {
return this.page.getByTestId('nps-survey-feedback-button');
}
getNpsSurveyCloseButton(): Locator {
return this.getNpsSurveyModal().locator('button.el-drawer__close-btn');
}
getRatingButton(rating: number): Locator {
return this.getNpsSurveyRatings().locator('button').nth(rating);
}
getFeedbackTextarea(): Locator {
return this.getNpsSurveyFeedback().locator('textarea');
}
async clickRating(rating: number): Promise<void> {
await this.getRatingButton(rating).click();
}
async fillFeedback(feedback: string): Promise<void> {
await this.getFeedbackTextarea().fill(feedback);
}
async clickSubmitButton(): Promise<void> {
await this.getNpsSurveySubmitButton().click();
}
async closeSurvey(): Promise<void> {
await this.getNpsSurveyCloseButton().click();
}
async getRatingButtonCount(): Promise<number> {
return await this.getNpsSurveyRatings().locator('button').count();
}
}

View File

@@ -1,11 +0,0 @@
import { BasePage } from './BasePage';
export class ProjectSettingsPage extends BasePage {
async fillProjectName(name: string) {
await this.page.getByTestId('project-settings-name-input').locator('input').fill(name);
}
async clickSaveButton() {
await this.clickButtonByName('Save');
}
}

View File

@@ -1,15 +0,0 @@
import { BasePage } from './BasePage';
export class SettingsPage extends BasePage {
getMenuItems() {
return this.page.getByTestId('menu-item');
}
getMenuItem(id: string) {
return this.page.getByTestId('menu-item').getByTestId(id);
}
async goToSettings() {
await this.page.goto('/settings');
}
}

View File

@@ -1,46 +0,0 @@
import type { Locator, Page } from '@playwright/test';
export class SidebarPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async clickAddProjectButton() {
await this.page.getByTestId('project-plus-button').click();
}
async universalAdd() {
await this.page.getByTestId('universal-add').click();
}
async addProjectFromUniversalAdd() {
await this.universalAdd();
await this.page.getByTestId('navigation-menu-item').filter({ hasText: 'Project' }).click();
}
async addWorkflowFromUniversalAdd(projectName: string) {
await this.universalAdd();
await this.page.getByTestId('universal-add').getByText('Workflow').click();
await this.page.getByTestId('universal-add').getByRole('link', { name: projectName }).click();
}
async openNewCredentialDialogForProject(projectName: string) {
await this.universalAdd();
await this.page.getByTestId('universal-add').getByText('Credential').click();
await this.page.getByTestId('universal-add').getByRole('link', { name: projectName }).click();
}
getProjectMenuItems(): Locator {
return this.page.getByTestId('project-menu-item');
}
async clickProjectMenuItem(projectName: string) {
await this.getProjectMenuItems().filter({ hasText: projectName }).click();
}
getAddFirstProjectButton(): Locator {
return this.page.getByTestId('add-first-project-button');
}
}

View File

@@ -1,35 +0,0 @@
import { BasePage } from './BasePage';
export class VersionsPage extends BasePage {
getVersionUpdatesPanelOpenButton() {
return this.page.getByTestId('version-update-next-versions-link');
}
getVersionUpdatesPanel() {
return this.page.getByTestId('version-updates-panel');
}
getVersionUpdatesPanelCloseButton() {
return this.getVersionUpdatesPanel().getByRole('button', { name: 'Close' });
}
getVersionCard() {
return this.page.getByTestId('version-card');
}
getWhatsNewMenuItem() {
return this.page.getByTestId('menu-item').getByTestId('whats-new');
}
async openWhatsNewMenu() {
await this.getWhatsNewMenuItem().click();
}
async openVersionUpdatesPanel() {
await this.getVersionUpdatesPanelOpenButton().click();
}
async closeVersionUpdatesPanel() {
await this.getVersionUpdatesPanelCloseButton().click();
}
}

View File

@@ -1,31 +0,0 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class WorkflowActivationModal extends BasePage {
getModal(): Locator {
return this.page.getByTestId('activation-modal');
}
getDontShowAgainCheckbox(): Locator {
return this.getModal().getByText("Don't show again");
}
getGotItButton(): Locator {
return this.getModal().getByRole('button', { name: 'Got it' });
}
async close(): Promise<void> {
await this.getDontShowAgainCheckbox().click();
await this.getGotItButton().click();
}
async clickDontShowAgain(): Promise<void> {
await this.getDontShowAgainCheckbox().click();
}
async clickGotIt(): Promise<void> {
await this.getGotItButton().click();
}
}

View File

@@ -1,39 +0,0 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class WorkflowSettingsModal extends BasePage {
getModal(): Locator {
return this.page.getByTestId('workflow-settings-dialog');
}
getWorkflowMenu(): Locator {
return this.page.getByTestId('workflow-menu');
}
getSettingsMenuItem(): Locator {
return this.page.getByTestId('workflow-menu-item-settings');
}
getErrorWorkflowField(): Locator {
return this.page.getByTestId('workflow-settings-error-workflow');
}
getSaveButton(): Locator {
return this.page.getByRole('button', { name: 'Save' });
}
async open(): Promise<void> {
await this.getWorkflowMenu().click();
await this.getSettingsMenuItem().click();
}
async clickSave(): Promise<void> {
await this.getSaveButton().click();
}
async selectErrorWorkflow(workflowName: string): Promise<void> {
await this.getErrorWorkflowField().click();
await this.page.getByRole('option', { name: workflowName }).first().click();
}
}

View File

@@ -1,27 +0,0 @@
import { BasePage } from './BasePage';
export class WorkflowSharingModal extends BasePage {
getModal() {
return this.page.getByTestId('workflowShare-modal');
}
async waitForModal() {
await this.getModal().waitFor({ state: 'visible', timeout: 5000 });
}
async addUser(email: string) {
await this.clickByTestId('project-sharing-select');
await this.page
.locator('.el-select-dropdown__item')
.filter({ hasText: email.toLowerCase() })
.click();
}
async save() {
await this.clickByTestId('workflow-sharing-modal-save-button');
}
async close() {
await this.getModal().locator('.el-dialog__close').first().click();
}
}

View File

@@ -1,159 +0,0 @@
import type { Locator } from '@playwright/test';
import { BasePage } from './BasePage';
export class WorkflowsPage extends BasePage {
async clickAddFirstProjectButton() {
await this.clickByTestId('add-first-project-button');
}
async clickAddProjectButton() {
await this.clickByTestId('project-plus-button');
}
/**
* This is the add workflow button on the workflows page, visible when there are already workflows.
*/
async clickAddWorkflowButton() {
await this.clickByTestId('add-resource-workflow');
}
/**
* This is the new workflow button on the workflows page, visible when there are no workflows.
*/
async clickNewWorkflowCard() {
await this.clickByTestId('new-workflow-card');
}
getNewWorkflowCard() {
return this.page.getByTestId('new-workflow-card');
}
async clearSearch() {
await this.clickByTestId('resources-list-search');
await this.page.getByTestId('resources-list-search').clear();
}
getProjectName() {
return this.page.getByTestId('project-name');
}
getSearchBar() {
return this.page.getByTestId('resources-list-search');
}
getWorkflowFilterButton() {
return this.page.getByTestId('workflow-filter-button');
}
getWorkflowTagsDropdown() {
return this.page.getByTestId('workflow-tags-dropdown');
}
getWorkflowTagItem(tagName: string) {
return this.page.getByTestId('workflow-tag-item').filter({ hasText: tagName });
}
getWorkflowArchivedCheckbox() {
return this.page.getByTestId('workflow-archived-checkbox');
}
async unarchiveWorkflow(workflowItem: Locator) {
await workflowItem.getByTestId('workflow-card-actions').click();
await this.page.getByRole('menuitem', { name: 'Unarchive' }).click();
}
async deleteWorkflow(workflowItem: Locator) {
await workflowItem.getByTestId('workflow-card-actions').click();
await this.page.getByRole('menuitem', { name: 'Delete' }).click();
await this.page.getByRole('button', { name: 'delete' }).click();
}
async searchWorkflows(searchTerm: string) {
await this.clickByTestId('resources-list-search');
await this.fillByTestId('resources-list-search', searchTerm);
}
getWorkflowItems() {
return this.page.getByTestId('resources-list-item-workflow');
}
getWorkflowByName(name: string) {
return this.getWorkflowItems().filter({ hasText: name });
}
async shareWorkflow(workflowName: string) {
const workflow = this.getWorkflowByName(workflowName);
await workflow.getByTestId('workflow-card-actions').click();
await this.page.getByRole('menuitem', { name: 'Share...' }).click();
}
getArchiveMenuItem() {
return this.page.getByRole('menuitem', { name: 'Archive' });
}
async archiveWorkflow(workflowItem: Locator) {
await workflowItem.getByTestId('workflow-card-actions').click();
await this.getArchiveMenuItem().click();
}
getFiltersButton() {
return this.page.getByTestId('resources-list-filters-trigger');
}
async openFilters() {
await this.clickByTestId('resources-list-filters-trigger');
}
async closeFilters() {
await this.clickByTestId('resources-list-filters-trigger');
}
getShowArchivedCheckbox() {
return this.page.getByTestId('show-archived-checkbox');
}
async toggleShowArchived() {
await this.openFilters();
await this.getShowArchivedCheckbox().locator('span').nth(1).click();
await this.closeFilters();
}
getStatusDropdown() {
return this.page.getByTestId('status-dropdown');
}
/**
* Select a status filter (for active/deactivated workflows)
* @param status - 'All', 'Active', or 'Deactivated'
*/
async selectStatusFilter(status: 'All' | 'Active' | 'Deactivated') {
await this.openFilters();
await this.getStatusDropdown().getByRole('combobox', { name: 'Select' }).click();
if (status === 'All') {
await this.page.getByRole('option', { name: 'All' }).click();
} else {
await this.page.getByText(status, { exact: true }).click();
}
await this.closeFilters();
}
getTagsDropdown() {
return this.page.getByTestId('tags-dropdown');
}
async filterByTags(tags: string[]) {
await this.openFilters();
await this.clickByTestId('tags-dropdown');
for (const tag of tags) {
await this.page.getByRole('option', { name: tag }).locator('span').click();
}
await this.closeFilters();
}
async filterByTag(tag: string) {
await this.filterByTags([tag]);
}
}

View File

@@ -1,95 +0,0 @@
import type { Page } from '@playwright/test';
import { AIAssistantPage } from './AIAssistantPage';
import { BecomeCreatorCTAPage } from './BecomeCreatorCTAPage';
import { CanvasPage } from './CanvasPage';
import { CredentialsPage } from './CredentialsPage';
import { ExecutionsPage } from './ExecutionsPage';
import { IframePage } from './IframePage';
import { NodeDetailsViewPage } from './NodeDetailsViewPage';
import { NotificationsPage } from './NotificationsPage';
import { NpsSurveyPage } from './NpsSurveyPage';
import { ProjectSettingsPage } from './ProjectSettingsPage';
import { SettingsPage } from './SettingsPage';
import { SidebarPage } from './SidebarPage';
import { VersionsPage } from './VersionsPage';
import { WorkflowActivationModal } from './WorkflowActivationModal';
import { WorkflowSettingsModal } from './WorkflowSettingsModal';
import { WorkflowSharingModal } from './WorkflowSharingModal';
import { WorkflowsPage } from './WorkflowsPage';
import { CanvasComposer } from '../composables/CanvasComposer';
import { ProjectComposer } from '../composables/ProjectComposer';
import { TestEntryComposer } from '../composables/TestEntryComposer';
import { WorkflowComposer } from '../composables/WorkflowComposer';
import type { ApiHelpers } from '../services/api-helper';
// eslint-disable-next-line @typescript-eslint/naming-convention
export class n8nPage {
readonly page: Page;
readonly api: ApiHelpers;
// Pages
readonly aiAssistant: AIAssistantPage;
readonly becomeCreatorCTA: BecomeCreatorCTAPage;
readonly canvas: CanvasPage;
readonly iframe: IframePage;
readonly ndv: NodeDetailsViewPage;
readonly npsSurvey: NpsSurveyPage;
readonly projectSettings: ProjectSettingsPage;
readonly settings: SettingsPage;
readonly versions: VersionsPage;
readonly workflows: WorkflowsPage;
readonly notifications: NotificationsPage;
readonly credentials: CredentialsPage;
readonly executions: ExecutionsPage;
readonly sideBar: SidebarPage;
// Modals
readonly workflowActivationModal: WorkflowActivationModal;
readonly workflowSettingsModal: WorkflowSettingsModal;
readonly workflowSharingModal: WorkflowSharingModal;
// Composables
readonly workflowComposer: WorkflowComposer;
readonly projectComposer: ProjectComposer;
readonly canvasComposer: CanvasComposer;
readonly start: TestEntryComposer;
constructor(page: Page, api: ApiHelpers) {
this.page = page;
this.api = api;
// Pages
this.aiAssistant = new AIAssistantPage(page);
this.becomeCreatorCTA = new BecomeCreatorCTAPage(page);
this.canvas = new CanvasPage(page);
this.iframe = new IframePage(page);
this.ndv = new NodeDetailsViewPage(page);
this.npsSurvey = new NpsSurveyPage(page);
this.projectSettings = new ProjectSettingsPage(page);
this.settings = new SettingsPage(page);
this.versions = new VersionsPage(page);
this.workflows = new WorkflowsPage(page);
this.notifications = new NotificationsPage(page);
this.credentials = new CredentialsPage(page);
this.executions = new ExecutionsPage(page);
this.sideBar = new SidebarPage(page);
this.workflowSharingModal = new WorkflowSharingModal(page);
// Modals
this.workflowActivationModal = new WorkflowActivationModal(page);
this.workflowSettingsModal = new WorkflowSettingsModal(page);
// Composables
this.workflowComposer = new WorkflowComposer(this);
this.projectComposer = new ProjectComposer(this);
this.canvasComposer = new CanvasComposer(this);
this.start = new TestEntryComposer(this);
}
async goHome() {
await this.page.goto('/');
}
}