test: Migrate UI tests from Cypress -> Playwright (no-changelog) (#18201)

This commit is contained in:
shortstacked
2025-08-12 12:06:42 +01:00
committed by GitHub
parent ecc4f41a11
commit 514825bd51
52 changed files with 2111 additions and 402 deletions

View File

@@ -4,7 +4,7 @@ import { test, expect } from '@playwright/test';
import { execSync } from 'child_process';
import * as fs from 'fs';
// @ts-expect-error - 'generate-schema' is not typed, so we ignore the TS error.
import GenerateSchema from 'generate-schema';
import generateSchema from 'generate-schema';
import * as path from 'path';
import { findPackagesRoot } from '../../utils/path-helper';
@@ -136,7 +136,7 @@ test.describe('Workflow Tests', () => {
// Optionally, validate the output against a JSON schema snapshot if enabled.
if (SCHEMA_MODE && result.data && workflow.enableSchemaValidation) {
const schema = GenerateSchema.json(result.data);
const schema = generateSchema.json(result.data);
expect(JSON.stringify(schema, null, 2)).toMatchSnapshot(
`workflow-${workflow.id}-schema.snap`,
);

View File

@@ -1,3 +1,5 @@
import { nanoid } from 'nanoid';
import { test, expect } from '../../fixtures/base';
const NOTIFICATIONS = {
@@ -18,7 +20,9 @@ test.describe('Workflows', () => {
await expect(n8n.canvas.getWorkflowTags()).toHaveText(['some-tag-1', 'some-tag-2']);
});
test('should create a new workflow using add workflow button', async ({ n8n }) => {
test('should create a new workflow using add workflow button and save successfully', async ({
n8n,
}) => {
await n8n.workflows.clickAddWorkflowButton();
const workflowName = `Test Workflow ${Date.now()}`;
@@ -31,9 +35,9 @@ test.describe('Workflows', () => {
});
test('should search for workflows', async ({ n8n }) => {
const date = Date.now();
const specificName = `Specific Test ${date}`;
const genericName = `Generic Test ${date}`;
const uniqueId = nanoid(8);
const specificName = `Specific Test ${uniqueId}`;
const genericName = `Generic Test ${uniqueId}`;
await n8n.workflowComposer.createWorkflow(specificName);
await n8n.goHome();
@@ -47,7 +51,7 @@ test.describe('Workflows', () => {
// Search with partial term
await n8n.workflows.clearSearch();
await n8n.workflows.searchWorkflows(date.toString());
await n8n.workflows.searchWorkflows(uniqueId);
await expect(n8n.workflows.getWorkflowItems()).toHaveCount(2);
// Search for non-existent

View File

@@ -0,0 +1,18 @@
import { test, expect } from '../../fixtures/base';
test.describe('Schedule Trigger node', () => {
test.beforeEach(async ({ n8n }) => {
await n8n.goHome();
});
test('should execute schedule trigger node and return timestamp in output', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.execute();
await expect(n8n.ndv.getOutputPanel()).toContainText('timestamp');
await n8n.ndv.clickBackToCanvasButton();
});
});

View File

@@ -0,0 +1,20 @@
import { test, expect } from '../../fixtures/base';
test.describe('ADO-2270 Save button resets on webhook node open', () => {
test('should not reset the save button if webhook node is opened and closed', async ({ n8n }) => {
await n8n.goHome();
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Webhook');
await n8n.page.keyboard.press('Escape');
await n8n.canvas.clickSaveWorkflowButton();
await n8n.canvas.openNode('Webhook');
await n8n.ndv.clickBackToCanvasButton();
await expect(n8n.canvas.workflowSaveButton()).toContainText('Saved');
});
});

View File

@@ -0,0 +1,15 @@
import { test, expect } from '../../fixtures/base';
test.describe('Admin user', () => {
test('should see same Settings sub menu items as instance owner', async ({ n8n, api }) => {
await api.setupTest('signin-only', 'owner');
await n8n.settings.goToSettings();
const ownerMenuItems = await n8n.settings.getMenuItems().count();
await api.setupTest('signin-only', 'admin');
await n8n.settings.goToSettings();
await expect(n8n.settings.getMenuItems()).toHaveCount(ownerMenuItems);
});
});

View File

@@ -0,0 +1,276 @@
import { test, expect } from '../../fixtures/base';
import type { TestRequirements } from '../../Types';
const aiDisabledRequirements: TestRequirements = {
config: {
features: { aiAssistant: false },
},
};
const aiEnabledRequirements: TestRequirements = {
config: {
features: { aiAssistant: true },
},
};
const aiEnabledWithWorkflowRequirements: TestRequirements = {
config: {
features: { aiAssistant: true },
},
workflow: {
'ai_assistant_test_workflow.json': 'AI_Assistant_Test_Workflow',
},
intercepts: {
aiChat: {
url: '**/rest/ai/chat',
response: {
sessionId: '1',
messages: [
{
role: 'assistant',
type: 'message',
text: 'Hey, this is an assistant message',
},
],
},
},
},
};
const aiEnabledWithQuickRepliesRequirements: TestRequirements = {
config: {
features: { aiAssistant: true },
},
workflow: {
'ai_assistant_test_workflow.json': 'AI_Assistant_Test_Workflow',
},
intercepts: {
aiChat: {
url: '**/rest/ai/chat',
response: {
sessionId: '1',
messages: [
{
role: 'assistant',
type: 'message',
text: 'Hey, this is an assistant message',
quickReplies: [
{
text: "Sure, let's do it",
type: 'yes',
},
{
text: "Nah, doesn't sound good",
type: 'no',
},
],
},
],
},
},
},
};
const aiEnabledWithEndSessionRequirements: TestRequirements = {
config: {
features: { aiAssistant: true },
},
workflow: {
'ai_assistant_test_workflow.json': 'AI_Assistant_Test_Workflow',
},
intercepts: {
aiChat: {
url: '**/rest/ai/chat',
response: {
sessionId: '1',
messages: [
{
role: 'assistant',
type: 'message',
title: 'Glad to Help',
text: "I'm glad I could help. If you have any more questions or need further assistance with your n8n workflows, feel free to ask!",
},
{
role: 'assistant',
type: 'event',
eventName: 'end-session',
},
],
},
},
},
};
test.describe('AI Assistant::disabled', () => {
test('does not show assistant button if feature is disabled', async ({
n8n,
setupRequirements,
}) => {
await setupRequirements(aiDisabledRequirements);
await expect(n8n.aiAssistant.getAskAssistantFloatingButton()).toHaveCount(0);
});
});
test.describe('AI Assistant::enabled', () => {
test('renders placeholder UI', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledRequirements);
await n8n.page.goto('/workflow/new');
await expect(n8n.aiAssistant.getAskAssistantCanvasActionButton()).toBeVisible();
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
await expect(n8n.aiAssistant.getPlaceholderMessage()).toBeVisible();
await expect(n8n.aiAssistant.getChatInput()).toBeVisible();
await expect(n8n.aiAssistant.getSendMessageButton()).toBeDisabled();
await expect(n8n.aiAssistant.getCloseChatButton()).toBeVisible();
await n8n.aiAssistant.getCloseChatButton().click();
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeHidden();
});
test('should show resizer when chat is open', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledRequirements);
await n8n.page.goto('/workflow/new');
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
await expect(n8n.aiAssistant.getAskAssistantSidebarResizer()).toBeVisible();
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
await n8n.aiAssistant.getAskAssistantSidebarResizer().hover();
await n8n.aiAssistant.getCloseChatButton().click();
});
test('should start chat session from node error view', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledWithWorkflowRequirements);
await n8n.canvas.openNode('Stop and Error');
await n8n.ndv.execute();
await expect(n8n.aiAssistant.getNodeErrorViewAssistantButton()).toBeVisible();
await expect(n8n.aiAssistant.getNodeErrorViewAssistantButton()).toBeEnabled();
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesAll().first()).toContainText(
'Hey, this is an assistant message',
);
await expect(n8n.aiAssistant.getNodeErrorViewAssistantButton()).toBeDisabled();
});
test('should render chat input correctly', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledWithWorkflowRequirements);
await n8n.aiAssistant.getAskAssistantCanvasActionButton().click();
await expect(n8n.aiAssistant.getAskAssistantChat()).toBeVisible();
await expect(n8n.aiAssistant.getChatInput()).toBeVisible();
await expect(n8n.aiAssistant.getSendMessageButton()).toBeDisabled();
await n8n.aiAssistant.getChatInput().fill('Test message');
await expect(n8n.aiAssistant.getChatInput()).toHaveValue('Test message');
await expect(n8n.aiAssistant.getSendMessageButton()).toBeEnabled();
await n8n.aiAssistant.getSendMessageButton().click();
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatInput()).toHaveValue('');
});
test('should render and handle quick replies', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledWithQuickRepliesRequirements);
await n8n.canvas.openNode('Stop and Error');
await n8n.ndv.execute();
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
await expect(n8n.aiAssistant.getQuickReplyButtons()).toHaveCount(2);
await expect(n8n.aiAssistant.getQuickReplyButtons()).toHaveCount(2);
await n8n.aiAssistant.getQuickReplyButtons().first().click();
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesUser()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesUser().first()).toContainText("Sure, let's do it");
});
test('should warn before starting a new session', async ({ n8n, setupRequirements }) => {
await setupRequirements(aiEnabledWithWorkflowRequirements);
await n8n.canvas.openNode('Edit Fields');
await n8n.ndv.execute();
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
await n8n.aiAssistant.getCloseChatButton().click();
await n8n.ndv.clickBackToCanvasButton();
await n8n.canvas.openNode('Stop and Error');
await n8n.ndv.execute();
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
await expect(n8n.aiAssistant.getNewAssistantSessionModal()).toBeVisible();
await n8n.aiAssistant
.getNewAssistantSessionModal()
.getByRole('button', { name: 'Start new session' })
.click();
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesAll()).toHaveCount(1);
});
test('should end chat session when `end_session` event is received', async ({
n8n,
setupRequirements,
}) => {
await setupRequirements(aiEnabledWithEndSessionRequirements);
await n8n.canvas.openNode('Stop and Error');
await n8n.ndv.execute();
await n8n.aiAssistant.getNodeErrorViewAssistantButton().click();
await expect(n8n.aiAssistant.getChatMessagesSystem()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesSystem()).toHaveCount(1);
await expect(n8n.aiAssistant.getChatMessagesSystem().first()).toContainText(
'session has ended',
);
});
});

View File

@@ -0,0 +1,72 @@
import { test, expect } from '../../fixtures/base';
import type { TestRequirements } from '../../Types';
const telemetryDisabledRequirements: TestRequirements = {
config: {
settings: {
telemetry: { enabled: false },
},
},
storage: {
'n8n-telemetry': JSON.stringify({ enabled: false }),
},
};
const telemetryEnabledRequirements: TestRequirements = {
config: {
settings: {
telemetry: { enabled: true },
instanceId: 'test-instance-id',
},
},
storage: {
'n8n-telemetry': JSON.stringify({ enabled: true }),
'n8n-instance-id': 'test-instance-id',
},
intercepts: {
iframeRequest: {
url: 'https://n8n.io/self-install*',
response: '<html><body>Test iframe content</body></html>',
contentType: 'text/html',
},
},
};
test.describe('n8n.io iframe', () => {
test.describe('when telemetry is disabled', () => {
test('should not load the iframe when visiting /home/workflows', async ({
n8n,
setupRequirements,
}) => {
await setupRequirements(telemetryDisabledRequirements);
await n8n.page.goto('/');
await n8n.page.waitForLoadState();
await expect(n8n.iframe.getIframe()).not.toBeAttached();
});
});
test.describe('when telemetry is enabled', () => {
test('should load the iframe when visiting /home/workflows @auth:owner', async ({
n8n,
setupRequirements,
api,
}) => {
await setupRequirements(telemetryEnabledRequirements);
// Get current user ID from the API
const currentUser = await api.get('/rest/login');
const testInstanceId = 'test-instance-id';
const testUserId = currentUser.id;
const iframeUrl = `https://n8n.io/self-install?instanceId=${testInstanceId}&userId=${testUserId}`;
await n8n.page.goto('/');
await n8n.page.waitForLoadState();
const iframeElement = n8n.iframe.getIframeBySrc(iframeUrl);
await expect(iframeElement).toBeAttached();
await expect(iframeElement).toHaveAttribute('src', iframeUrl);
});
});
});

View File

@@ -1,14 +1,3 @@
import {
getErrorActionItem,
getEvaluationsActionItem,
getIgnoreAllButton,
getSuggestedActionItem,
getSuggestedActionsButton,
getSuggestedActionsPopover,
getTimeSavedActionItem,
} from '../../composables/ProductionChecklist';
import { closeActivationModal } from '../../composables/WorkflowActivationModal';
import { openWorkflowSettings } from '../../composables/WorkflowSettingsModal';
import { test, expect } from '../../fixtures/base';
const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger';
@@ -22,35 +11,29 @@ test.describe('Workflow Production Checklist', () => {
test('should show suggested actions automatically when workflow is first activated', async ({
n8n,
}) => {
// Add a schedule trigger node (activatable)
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
// Verify suggested actions button is not visible
await expect(getSuggestedActionsButton(n8n.page)).toBeHidden();
await expect(n8n.canvas.getProductionChecklistButton()).toBeHidden();
// Activate the workflow
await n8n.canvas.activateWorkflow();
// Activation Modal should be visible since it's first activation
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
// Verify suggested actions button and popover is visible
await expect(getSuggestedActionsButton(n8n.page)).toBeVisible();
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(getSuggestedActionItem(n8n.page)).toHaveCount(2);
await expect(getErrorActionItem(n8n.page)).toBeVisible();
await expect(getTimeSavedActionItem(n8n.page)).toBeVisible();
await expect(n8n.canvas.getProductionChecklistButton()).toBeVisible();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
await expect(n8n.canvas.getProductionChecklistActionItem()).toHaveCount(2);
await expect(n8n.canvas.getErrorActionItem()).toBeVisible();
await expect(n8n.canvas.getTimeSavedActionItem()).toBeVisible();
});
test('should display evaluations action when AI node exists and feature is enabled', async ({
n8n,
api,
}) => {
// Enable evaluations feature
await api.enableFeature('evaluation');
// Add schedule trigger and AI node
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.addNodeAndCloseNDV('OpenAI', 'Create an assistant');
@@ -58,56 +41,50 @@ test.describe('Workflow Production Checklist', () => {
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
// Suggested actions should be open
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(getSuggestedActionItem(n8n.page)).toHaveCount(3);
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
await expect(n8n.canvas.getProductionChecklistActionItem()).toHaveCount(3);
// Verify evaluations action is present
await expect(getEvaluationsActionItem(n8n.page)).toBeVisible();
await getEvaluationsActionItem(n8n.page).click();
await expect(n8n.canvas.getEvaluationsActionItem()).toBeVisible();
await n8n.canvas.getEvaluationsActionItem().click();
// Verify navigation to evaluations page
await expect(n8n.page).toHaveURL(/\/evaluation/);
});
test('should open workflow settings modal when error workflow action is clicked', async ({
n8n,
}) => {
// Add schedule trigger
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
// Click error workflow action
const errorAction = getErrorActionItem(n8n.page);
const errorAction = n8n.canvas.getErrorActionItem();
await expect(errorAction).toBeVisible();
await errorAction.click();
// Verify workflow settings modal opens
await expect(n8n.page.getByTestId('workflow-settings-dialog')).toBeVisible();
await expect(n8n.page.getByTestId('workflow-settings-error-workflow')).toBeVisible();
});
test('should open workflow settings modal when time saved action is clicked', async ({ n8n }) => {
// Add schedule trigger
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
// Click time saved action
const timeAction = getTimeSavedActionItem(n8n.page);
const timeAction = n8n.canvas.getTimeSavedActionItem();
await expect(timeAction).toBeVisible();
await timeAction.click();
// Verify workflow settings modal opens
await expect(n8n.page.getByTestId('workflow-settings-dialog')).toBeVisible();
});
@@ -115,73 +92,64 @@ test.describe('Workflow Production Checklist', () => {
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
// Suggested actions popover should be open
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
// Verify error workflow action is visible
await expect(getSuggestedActionItem(n8n.page).first()).toContainText('error');
await getSuggestedActionItem(n8n.page).first().getByTitle('Ignore').click();
await n8n.page.waitForTimeout(500); // items disappear after timeout, not arbitrary
await expect(getErrorActionItem(n8n.page)).toBeHidden();
await expect(n8n.canvas.getProductionChecklistActionItem().first()).toContainText('error');
await n8n.canvas.getProductionChecklistActionItem().first().getByTitle('Ignore').click();
await expect(n8n.canvas.getErrorActionItem()).toBeHidden();
// Close and reopen popover
await n8n.page.locator('body').click({ position: { x: 0, y: 0 } });
await getSuggestedActionsButton(n8n.page).click();
await n8n.canvas.clickProductionChecklistButton();
// Verify error workflow action is still no longer visible
await expect(getErrorActionItem(n8n.page)).toBeHidden();
await expect(getTimeSavedActionItem(n8n.page)).toBeVisible();
await expect(n8n.canvas.getErrorActionItem()).toBeHidden();
await expect(n8n.canvas.getTimeSavedActionItem()).toBeVisible();
});
test('should show completed state for configured actions', async ({ n8n }) => {
// Add schedule trigger and activate workflow
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
// Open workflow settings and set error workflow
await openWorkflowSettings(n8n.page);
await n8n.workflowSettingsModal.open();
await expect(n8n.workflowSettingsModal.getModal()).toBeVisible();
// Set an error workflow (we'll use a dummy value)
await n8n.page.getByTestId('workflow-settings-error-workflow').click();
await n8n.page.getByRole('option', { name: 'My workflow' }).first().click();
await n8n.page.getByRole('button', { name: 'Save' }).click();
await n8n.workflowSettingsModal.selectErrorWorkflow('My workflow');
await n8n.workflowSettingsModal.clickSave();
await expect(n8n.page.getByTestId('workflow-settings-dialog')).toBeHidden();
// Open suggested actions
await getSuggestedActionsButton(n8n.page).click();
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await n8n.canvas.clickProductionChecklistButton();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
// Verify error workflow action shows as completed
await expect(
getSuggestedActionItem(n8n.page).first().locator('svg[data-icon="circle-check"]'),
n8n.canvas
.getProductionChecklistActionItem()
.first()
.locator('svg[data-icon="circle-check"]'),
).toBeVisible();
});
test('should allow ignoring all actions with confirmation', async ({ n8n }) => {
// Add schedule trigger
await n8n.canvas.addNodeAndCloseNDV(SCHEDULE_TRIGGER_NODE_NAME);
await n8n.canvas.saveWorkflow();
await n8n.canvas.activateWorkflow();
await closeActivationModal(n8n.page);
await expect(n8n.workflowActivationModal.getModal()).toBeVisible();
await n8n.workflowActivationModal.close();
// Suggested actions should be open
await expect(getSuggestedActionsPopover(n8n.page)).toBeVisible();
await expect(n8n.canvas.getProductionChecklistPopover()).toBeVisible();
// Click ignore all button
await getIgnoreAllButton(n8n.page).click();
await n8n.canvas.clickProductionChecklistIgnoreAll();
// Confirm in the dialog
await expect(n8n.page.locator('.el-message-box')).toBeVisible();
await n8n.page
.locator('.el-message-box__btns button')
.filter({ hasText: /ignore for all workflows/i })
.click();
// Verify suggested actions button is no longer visible
await expect(getSuggestedActionsButton(n8n.page)).toBeHidden();
await expect(n8n.canvas.getProductionChecklistButton()).toBeHidden();
});
});

View File

@@ -0,0 +1,20 @@
import { test, expect } from '../../fixtures/base';
test.describe('AI-716 Correctly set up agent model shows error', () => {
test('should not show error when adding a sub-node with credential set-up', async ({ n8n }) => {
await n8n.goHome();
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('AI Agent');
await n8n.page.keyboard.press('Escape');
await n8n.canvas.addNode('OpenAI Chat Model');
await n8n.credentials.createAndSaveNewCredential('apiKey', 'sk-123');
await n8n.page.keyboard.press('Escape');
await expect(n8n.canvas.getNodeIssuesByName('OpenAI Chat Model')).toHaveCount(0);
});
});

View File

@@ -0,0 +1,29 @@
import { test, expect } from '../../fixtures/base';
import type { TestRequirements } from '../../Types';
const requirements: TestRequirements = {
workflow: {
'Test_9999_SUG_38.json': 'SUG_38_Test_Workflow',
},
};
test.describe('SUG-38 Inline expression previews are not displayed in NDV', () => {
test("should show resolved inline expression preview in NDV if the node's input data is populated", async ({
n8n,
setupRequirements,
}) => {
await setupRequirements(requirements);
await n8n.canvas.clickZoomToFitButton();
await n8n.workflowComposer.executeWorkflowAndWaitForNotification(
'Workflow executed successfully',
);
await n8n.canvas.openNode('Repro1');
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toBeVisible();
await expect(n8n.ndv.getParameterExpressionPreviewValue()).toHaveText('hello there');
});
});

View File

@@ -1,26 +1,18 @@
import { test, expect } from '../../fixtures/base';
test('default signin is as owner', async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(/\/workflow/);
});
test.describe('Authentication', () => {
const testCases = [
{ role: 'default', expectedUrl: /\/workflow/, auth: '' },
{ role: 'owner', expectedUrl: /\/workflow/, auth: '@auth:owner' },
{ role: 'admin', expectedUrl: /\/workflow/, auth: '@auth:admin' },
{ role: 'member', expectedUrl: /\/workflow/, auth: '@auth:member' },
{ role: 'none', expectedUrl: /\/signin/, auth: '@auth:none' },
];
test('owner can access dashboard @auth:owner', async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(/\/workflow/);
});
test('admin can access dashboard @auth:admin', async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(/\/workflow/);
});
test('member can access dashboard @auth:member', async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(/\/workflow/);
});
test('no auth can not access dashboard @auth:none', async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(/\/signin/);
for (const { role, expectedUrl, auth } of testCases) {
test(`${role} authentication ${auth}`, async ({ n8n }) => {
await n8n.goHome();
await expect(n8n.page).toHaveURL(expectedUrl);
});
}
});

View File

@@ -0,0 +1,44 @@
import { test, expect } from '../../fixtures/base';
test.describe
.serial('Environment Feature Flags', () => {
test('should set feature flags at runtime and load it back in envFeatureFlags from backend settings', async ({
api,
}) => {
const setResponse = await api.setEnvFeatureFlags({
N8N_ENV_FEAT_TEST: 'true',
});
expect(setResponse.data.success).toBe(true);
expect(setResponse.data.message).toBe('Environment feature flags updated');
expect(setResponse.data.flags).toBeInstanceOf(Object);
expect(setResponse.data.flags['N8N_ENV_FEAT_TEST']).toBe('true');
const currentFlags = await api.getEnvFeatureFlags();
expect(currentFlags).toBeInstanceOf(Object);
expect(currentFlags.data['N8N_ENV_FEAT_TEST']).toBe('true');
});
test('should reset feature flags at runtime', async ({ api }) => {
const setResponse1 = await api.setEnvFeatureFlags({
N8N_ENV_FEAT_TEST: 'true',
});
expect(setResponse1.data.success).toBe(true);
expect(setResponse1.data.flags['N8N_ENV_FEAT_TEST']).toBe('true');
const clearResponse = await api.clearEnvFeatureFlags();
expect(clearResponse.data.success).toBe(true);
expect(clearResponse.data.flags).toBeInstanceOf(Object);
expect(clearResponse.data.flags['N8N_ENV_FEAT_TEST']).toBeUndefined();
const currentFlags = await api.getEnvFeatureFlags();
expect(currentFlags).toBeInstanceOf(Object);
expect(currentFlags.data['N8N_ENV_FEAT_TEST']).toBeUndefined();
});
});

View File

@@ -1,9 +1,7 @@
import { expect, test } from '../../fixtures/base';
// Example of importing a workflow from a file
test.describe('PDF Test', () => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip('Can read and write PDF files and extract text', async ({ n8n }) => {
test('Can read and write PDF files and extract text', async ({ n8n }) => {
await n8n.goHome();
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.importWorkflow('test_pdf_workflow.json', 'PDF Workflow');