mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-23 20:50:00 +00:00
test: Migrate 1-workflows to Playwright (#17360)
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
import type { Locator } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { resolveFromRoot } from '../utils/path-helper';
|
||||
|
||||
export class CanvasPage extends BasePage {
|
||||
saveWorkflowButton(): Locator {
|
||||
return this.page.getByRole('button', { name: 'Save' });
|
||||
}
|
||||
|
||||
canvasAddButton(): Locator {
|
||||
return this.page.getByTestId('canvas-add-button');
|
||||
}
|
||||
|
||||
nodeCreatorItemByName(text: string): Locator {
|
||||
return this.page.getByTestId('node-creator-item-name').getByText(text, { exact: true });
|
||||
}
|
||||
@@ -114,4 +119,34 @@ export class CanvasPage extends BasePage {
|
||||
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.page.waitForTimeout(250);
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class ProjectWorkflowsPage extends BasePage {
|
||||
async clickCreateWorkflowButton() {
|
||||
await this.clickByTestId('add-resource-workflow');
|
||||
}
|
||||
|
||||
async clickProjectMenuItem(projectName: string) {
|
||||
await this.page.getByTestId('project-menu-item').filter({ hasText: projectName }).click();
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ export class SidebarPage {
|
||||
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');
|
||||
}
|
||||
|
||||
27
packages/testing/playwright/pages/WorkflowSharingModal.ts
Normal file
27
packages/testing/playwright/pages/WorkflowSharingModal.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Locator } from '@playwright/test';
|
||||
|
||||
import { BasePage } from './BasePage';
|
||||
import { resolveFromRoot } from '../utils/path-helper';
|
||||
|
||||
export class WorkflowsPage extends BasePage {
|
||||
async clickNewWorkflowCard() {
|
||||
@@ -14,33 +15,135 @@ export class WorkflowsPage extends BasePage {
|
||||
await this.clickByTestId('project-plus-button');
|
||||
}
|
||||
|
||||
async clickAddWorklowButton() {
|
||||
async clickAddWorkflowButton() {
|
||||
await this.clickByTestId('add-resource-workflow');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await this.page.waitForTimeout(250);
|
||||
|
||||
await this.clickByTestId('inline-edit-preview');
|
||||
await this.fillByTestId('inline-edit-input', workflowName);
|
||||
await this.page.getByTestId('inline-edit-input').press('Enter');
|
||||
getNewWorkflowCard() {
|
||||
return this.page.getByTestId('new-workflow-card');
|
||||
}
|
||||
|
||||
workflowTags() {
|
||||
return this.page.getByTestId('workflow-tags').locator('.el-tag');
|
||||
async clearSearch() {
|
||||
await this.clickByTestId('resources-list-search');
|
||||
await this.page.getByTestId('resources-list-search').clear();
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import { ExecutionsPage } from './ExecutionsPage';
|
||||
import { NodeDisplayViewPage } from './NodeDisplayViewPage';
|
||||
import { NotificationsPage } from './NotificationsPage';
|
||||
import { ProjectSettingsPage } from './ProjectSettingsPage';
|
||||
import { ProjectWorkflowsPage } from './ProjectWorkflowsPage';
|
||||
import { SidebarPage } from './SidebarPage';
|
||||
import { WorkflowSharingModal } from './WorkflowSharingModal';
|
||||
import { WorkflowsPage } from './WorkflowsPage';
|
||||
import { CanvasComposer } from '../composables/CanvasComposer';
|
||||
import { ProjectComposer } from '../composables/ProjectComposer';
|
||||
@@ -19,28 +19,18 @@ export class n8nPage {
|
||||
|
||||
// Pages
|
||||
readonly canvas: CanvasPage;
|
||||
|
||||
readonly ndv: NodeDisplayViewPage;
|
||||
|
||||
readonly projectWorkflows: ProjectWorkflowsPage;
|
||||
|
||||
readonly projectSettings: ProjectSettingsPage;
|
||||
|
||||
readonly workflows: WorkflowsPage;
|
||||
|
||||
readonly notifications: NotificationsPage;
|
||||
|
||||
readonly credentials: CredentialsPage;
|
||||
|
||||
readonly executions: ExecutionsPage;
|
||||
|
||||
readonly sideBar: SidebarPage;
|
||||
|
||||
// Composables
|
||||
readonly workflowComposer: WorkflowComposer;
|
||||
|
||||
readonly workflowSharingModal: WorkflowSharingModal;
|
||||
readonly projectComposer: ProjectComposer;
|
||||
|
||||
readonly canvasComposer: CanvasComposer;
|
||||
|
||||
constructor(page: Page) {
|
||||
@@ -49,13 +39,13 @@ export class n8nPage {
|
||||
// Pages
|
||||
this.canvas = new CanvasPage(page);
|
||||
this.ndv = new NodeDisplayViewPage(page);
|
||||
this.projectWorkflows = new ProjectWorkflowsPage(page);
|
||||
this.projectSettings = new ProjectSettingsPage(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);
|
||||
|
||||
// Composables
|
||||
this.workflowComposer = new WorkflowComposer(this);
|
||||
|
||||
Reference in New Issue
Block a user