mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-20 11:22:15 +00:00
test: Migrate cypress tests batch 1 to playwright (#19569)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import type { n8nPage } from '../pages/n8nPage';
|
||||
|
||||
export class CanvasComposer {
|
||||
@@ -37,4 +39,62 @@ export class CanvasComposer {
|
||||
await this.n8n.canvas.selectAll();
|
||||
await this.copySelectedNodesWithToast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between editor and workflow history and back
|
||||
*/
|
||||
async switchBetweenEditorAndHistory(): Promise<void> {
|
||||
await this.n8n.page.getByTestId('workflow-history-button').click();
|
||||
await this.n8n.page.getByTestId('workflow-history-close-button').click();
|
||||
await this.n8n.page.waitForLoadState();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().first()).toBeVisible();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().last()).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between editor and workflow list and back
|
||||
*/
|
||||
async switchBetweenEditorAndWorkflowList(): Promise<void> {
|
||||
await this.n8n.page.getByTestId('menu-item').first().click();
|
||||
await this.n8n.page.getByTestId('resources-list-item-workflow').first().click();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().first()).toBeVisible();
|
||||
await expect(this.n8n.canvas.getCanvasNodes().last()).toBeVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom in and validate that zoom functionality works
|
||||
*/
|
||||
async zoomInAndCheckNodes(): Promise<void> {
|
||||
await this.n8n.canvas.getCanvasNodes().first().waitFor();
|
||||
|
||||
const initialNodeSize = await this.n8n.page.evaluate(() => {
|
||||
const firstNode = document.querySelector('[data-test-id="canvas-node"]');
|
||||
if (!firstNode) {
|
||||
throw new Error('Canvas node not found during initial measurement');
|
||||
}
|
||||
return firstNode.getBoundingClientRect().width;
|
||||
});
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await this.n8n.canvas.clickZoomInButton();
|
||||
}
|
||||
|
||||
const finalNodeSize = await this.n8n.page.evaluate(() => {
|
||||
const firstNode = document.querySelector('[data-test-id="canvas-node"]');
|
||||
if (!firstNode) {
|
||||
throw new Error('Canvas node not found during final measurement');
|
||||
}
|
||||
return firstNode.getBoundingClientRect().width;
|
||||
});
|
||||
|
||||
// Validate zoom increased node sizes by at least 50%
|
||||
const zoomWorking = finalNodeSize > initialNodeSize * 1.5;
|
||||
|
||||
if (!zoomWorking) {
|
||||
throw new Error(
|
||||
"Zoom functionality not working: nodes didn't scale properly. " +
|
||||
`Initial: ${initialNodeSize.toFixed(1)}px, Final: ${finalNodeSize.toFixed(1)}px`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import type { n8nPage } from '../pages/n8nPage';
|
||||
|
||||
/**
|
||||
* A class for partial execution testing workflows that involve
|
||||
* complex multi-step scenarios across pages.
|
||||
*/
|
||||
export class PartialExecutionComposer {
|
||||
constructor(private readonly n8n: n8nPage) {}
|
||||
|
||||
/**
|
||||
* Sets up partial execution version 2 in localStorage
|
||||
* This enables the v2 partial execution feature
|
||||
*/
|
||||
async enablePartialExecutionV2(): Promise<void> {
|
||||
await this.n8n.page.evaluate(() => {
|
||||
window.localStorage.setItem('PartialExecution.version', '2');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a full workflow and verifies all nodes show success status
|
||||
* @param nodeNames - Array of node names to verify
|
||||
*/
|
||||
async executeFullWorkflowAndVerifySuccess(nodeNames: string[]): Promise<void> {
|
||||
await this.n8n.canvas.clickExecuteWorkflowButton();
|
||||
|
||||
// Verify all nodes show success status
|
||||
for (const nodeName of nodeNames) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures output data from a node for later comparison
|
||||
* @param nodeName - The node to capture data from
|
||||
* @returns The captured text content
|
||||
*/
|
||||
async captureNodeOutputData(nodeName: string): Promise<string> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.outputPanel.getTable().waitFor();
|
||||
// Note: Using row 0 for tbody (equivalent to row 1 in Cypress which includes header)
|
||||
const cell = this.n8n.ndv.outputPanel.getTbodyCell(0, 0);
|
||||
await expect(cell).toHaveText(/.+/);
|
||||
const beforeText = await cell.textContent();
|
||||
await this.n8n.ndv.close();
|
||||
|
||||
return beforeText!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a node parameter to trigger stale state
|
||||
* @param nodeName - The node to modify
|
||||
*/
|
||||
async modifyNodeToTriggerStaleState(nodeName: string): Promise<void> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.clickAssignmentCollectionDropArea();
|
||||
|
||||
// Verify stale node indicator appears after parameter change
|
||||
await expect(this.n8n.ndv.getStaleNodeIndicator()).toBeVisible();
|
||||
await this.n8n.ndv.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies node states after parameter change for partial execution v2
|
||||
* @param unchangedNodes - Nodes that should still show success
|
||||
* @param modifiedNodes - Nodes that should show warning (need re-execution)
|
||||
*/
|
||||
async verifyNodeStatesAfterChange(
|
||||
unchangedNodes: string[],
|
||||
modifiedNodes: string[],
|
||||
): Promise<void> {
|
||||
// Verify unchanged nodes still show success
|
||||
for (const nodeName of unchangedNodes) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
|
||||
// Verify modified nodes show warning status
|
||||
for (const nodeName of modifiedNodes) {
|
||||
await expect(this.n8n.canvas.getNodeWarningStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs partial execution on a node and verifies all nodes return to success
|
||||
* @param targetNodeName - The node to execute from
|
||||
* @param allNodeNames - All nodes that should show success after partial execution
|
||||
*/
|
||||
async performPartialExecutionAndVerifySuccess(
|
||||
targetNodeName: string,
|
||||
allNodeNames: string[],
|
||||
): Promise<void> {
|
||||
// Perform partial execution by clicking execute button on target node
|
||||
await this.n8n.canvas.executeNode(targetNodeName);
|
||||
|
||||
// Verify all nodes show success status after partial execution
|
||||
for (const nodeName of allNodeNames) {
|
||||
await expect(this.n8n.canvas.getNodeSuccessStatusIndicator(nodeName)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a node for data verification (test should handle the assertion)
|
||||
* @param nodeName - The node to open for verification
|
||||
* @returns Promise that resolves when node is open and ready for verification
|
||||
*/
|
||||
async openNodeForDataVerification(nodeName: string): Promise<void> {
|
||||
await this.n8n.canvas.openNode(nodeName);
|
||||
await this.n8n.ndv.outputPanel.getTable().waitFor();
|
||||
}
|
||||
}
|
||||
@@ -62,4 +62,37 @@ export class WorkflowComposer {
|
||||
await this.n8n.canvas.importWorkflow(fileName, workflowName);
|
||||
return { workflowName };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new workflow by importing from a URL
|
||||
* @param url - The URL to import the workflow from
|
||||
* @returns Promise that resolves when the import is complete
|
||||
*/
|
||||
async importWorkflowFromURL(url: string): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.fillImportURLInput(url);
|
||||
await this.n8n.canvas.clickConfirmImportURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the import from URL dialog and then dismisses it by clicking outside
|
||||
*/
|
||||
async openAndDismissImportFromURLDialog(): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.clickOutsideModal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the import from URL dialog and then cancels it
|
||||
*/
|
||||
async openAndCancelImportFromURLDialog(): Promise<void> {
|
||||
await this.n8n.workflows.clickAddWorkflowButton();
|
||||
await this.n8n.canvas.clickWorkflowMenu();
|
||||
await this.n8n.canvas.clickImportFromURL();
|
||||
await this.n8n.canvas.clickCancelImportURL();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user