test: Migrate cloud tests from cypress to playwright (#19925)

This commit is contained in:
Artem Sorokin 2025-09-24 11:28:19 +02:00 committed by GitHub
parent fd63f37e9a
commit abfde2a334
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 189 additions and 124 deletions

View File

@ -1,124 +0,0 @@
import { overrideFeatureFlag } from '../../composables/featureFlags';
import { expandSidebar } from '../../composables/sidebar';
import planData from '../../fixtures/Plan_data_opt_in_trial.json';
import {
MainSidebar,
WorkflowPage,
visitPublicApiPage,
getPublicApiUpgradeCTA,
WorkflowsPage,
} from '../../pages';
const NUMBER_OF_AI_CREDITS = 100;
const mainSidebar = new MainSidebar();
const workflowPage = new WorkflowPage();
const workflowsPage = new WorkflowsPage();
describe('Cloud', () => {
before(() => {
const now = new Date();
const fiveDaysFromNow = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000);
planData.expirationDate = fiveDaysFromNow.toJSON();
});
beforeEach(() => {
cy.overrideSettings({
deployment: { type: 'cloud' },
n8nMetadata: { userId: '1' },
aiCredits: {
enabled: true,
credits: NUMBER_OF_AI_CREDITS,
},
});
cy.intercept('GET', '/rest/admin/cloud-plan', planData).as('getPlanData');
cy.intercept('GET', '/rest/cloud/proxy/user/me', {}).as('getCloudUserInfo');
cy.intercept('GET', new RegExp('/rest/projects*')).as('projects');
cy.intercept('GET', new RegExp('/rest/roles')).as('roles');
});
function visitWorkflowPage() {
cy.visit(workflowPage.url);
cy.wait('@getPlanData');
cy.wait('@projects');
cy.wait('@roles');
}
describe('BannerStack', () => {
it('should not render trial banner on first visit', () => {
// Clear localStorage to simulate first visit
cy.clearLocalStorage();
visitWorkflowPage();
cy.getByTestId('banner-stack').should('not.be.visible');
});
it('should render trial banner on subsequent visits', () => {
// Set visit count to simulate returning user
cy.window().then((win) => {
win.localStorage.setItem('n8n-trial-visit-count', '1');
});
visitWorkflowPage();
expandSidebar();
cy.getByTestId('banner-stack').should('be.visible');
mainSidebar.actions.signout();
cy.getByTestId('banner-stack').should('not.be.visible');
});
});
describe('Admin Home', () => {
it('Should show admin button', () => {
visitWorkflowPage();
expandSidebar();
mainSidebar.getters.adminPanel().should('be.visible');
});
});
describe('Public API', () => {
it('Should show upgrade CTA for Public API if user is trialing', () => {
visitPublicApiPage();
cy.wait(['@loadSettings', '@projects', '@roles', '@getPlanData']);
getPublicApiUpgradeCTA().should('be.visible');
});
});
describe('Easy AI workflow experiment', () => {
it('should not show option to take you to the easy AI workflow if experiment is control', () => {
overrideFeatureFlag('026_easy_ai_workflow', 'control');
cy.visit(workflowsPage.url);
cy.getByTestId('easy-ai-workflow-card').should('not.exist');
});
it('should show option to take you to the easy AI workflow if experiment is variant', () => {
overrideFeatureFlag('026_easy_ai_workflow', 'variant');
cy.visit(workflowsPage.url);
cy.getByTestId('easy-ai-workflow-card').should('to.exist');
});
it('should show default instructions if free AI credits experiment is control', () => {
overrideFeatureFlag('026_easy_ai_workflow', 'variant');
cy.visit(workflowsPage.url);
cy.getByTestId('easy-ai-workflow-card').click();
workflowPage.getters
.stickies()
.eq(0)
.should(($el) => {
expect($el).contains.text('Start by saying');
});
});
});
});

View File

@ -0,0 +1,28 @@
{
"id": 200,
"planId": 1,
"pruneExecutionsInterval": 168,
"monthlyExecutionsLimit": 1000,
"activeWorkflowsLimit": 20,
"credentialsLimit": 100,
"supportTier": "community",
"displayName": "Trial",
"enabledFeatures": ["userManagement", "advancedExecutionFilters", "sharing"],
"licenseFeatures": {
"feat:sharing": true,
"feat:advancedExecutionFilters": true,
"quota:users": -1,
"quota:maxVariables": -1,
"feat:variables": true
},
"metadata": {
"version": "v1",
"group": "trial",
"slug": "trial-2",
"trial": {
"length": 14,
"gracePeriod": 3
}
},
"expirationDate": "2023-08-30T15:47:27.611Z"
}

View File

@ -124,4 +124,8 @@ export class SettingsPage extends BasePage {
await this.getMfaCodeOrRecoveryCodeInput().fill(code);
await this.getMfaSaveButton().click();
}
getUpgradeCta(): Locator {
return this.page.getByTestId('public-api-upgrade-cta');
}
}

View File

@ -56,6 +56,14 @@ export class SidebarPage {
return this.page.getByTestId('user-menu-item-logout');
}
getAdminPanel(): Locator {
return this.page.getByRole('menuitem', { name: 'Admin Panel' });
}
getTrialBanner(): Locator {
return this.page.getByTestId('banners-TRIAL');
}
async openUserMenu(): Promise<void> {
await this.getUserMenu().click();
}

View File

@ -29,6 +29,14 @@ export class WorkflowsPage extends BasePage {
return this.page.getByTestId('new-workflow-card');
}
getEasyAiWorkflowCard() {
return this.page.getByTestId('easy-ai-workflow-card');
}
async clickEasyAiWorkflowCard() {
await this.clickByTestId('easy-ai-workflow-card');
}
async clearSearch() {
await this.clickByTestId('resources-list-search');
await this.page.getByTestId('resources-list-search').clear();

View File

@ -0,0 +1,141 @@
import { test, expect } from '../../fixtures/base';
import basePlanData from '../../fixtures/plan-data-trial.json';
import type { n8nPage } from '../../pages/n8nPage';
import type { TestRequirements } from '../../Types';
const fiveDaysFromNow = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000);
const planData = { ...basePlanData, expirationDate: fiveDaysFromNow.toJSON() };
const cloudTrialRequirements = {
config: {
settings: {
deployment: { type: 'cloud' },
n8nMetadata: { userId: '1' },
aiCredits: {
enabled: true,
credits: 100,
},
banners: {
dismissed: ['V1'], // Prevent V1 banner interference
},
},
},
intercepts: {
'cloud-plan': {
url: '**/rest/admin/cloud-plan',
response: planData,
},
'cloud-user': {
url: '**/rest/cloud/proxy/user/me',
response: {},
},
},
};
const firstVisitRequirements: TestRequirements = {
...cloudTrialRequirements,
};
const subsequentVisitRequirements: TestRequirements = {
...cloudTrialRequirements,
storage: {
'n8n-trial-visit-count': '1',
},
};
const setupCloudTest = async (
n8n: n8nPage,
setupRequirements: (requirements: TestRequirements) => Promise<void>,
requirements: TestRequirements,
) => {
await setupRequirements(requirements);
await n8n.page.waitForLoadState();
};
test.describe('Cloud @db:reset @auth:owner', () => {
test.describe('Trial Banner', () => {
test('should not render trial banner on first visit', async ({ n8n, setupRequirements }) => {
await setupCloudTest(n8n, setupRequirements, firstVisitRequirements);
await n8n.start.fromBlankCanvas();
await expect(n8n.sideBar.getTrialBanner()).toBeHidden();
});
test('should render trial banner on subsequent visits', async ({ n8n, setupRequirements }) => {
await setupCloudTest(n8n, setupRequirements, subsequentVisitRequirements);
await n8n.start.fromBlankCanvas();
await n8n.sideBar.expand();
await expect(n8n.sideBar.getTrialBanner()).toBeVisible();
});
});
test.describe('Admin Home', () => {
test('should show admin button', async ({ n8n, setupRequirements }) => {
await setupCloudTest(n8n, setupRequirements, cloudTrialRequirements);
await n8n.start.fromBlankCanvas();
await n8n.sideBar.expand();
await expect(n8n.sideBar.getAdminPanel()).toBeVisible();
});
});
test.describe('Public API', () => {
test('should show upgrade CTA for Public API if user is trialing', async ({
n8n,
setupRequirements,
}) => {
await setupCloudTest(n8n, setupRequirements, cloudTrialRequirements);
await n8n.navigate.toApiSettings();
await n8n.page.waitForLoadState();
await expect(n8n.settings.getUpgradeCta()).toBeVisible();
});
});
test.describe('Easy AI workflow experiment', () => {
test('should not show option to take you to the easy AI workflow if experiment is control', async ({
n8n,
api,
setupRequirements,
}) => {
await api.setEnvFeatureFlags({ '026_easy_ai_workflow': 'control' });
await setupCloudTest(n8n, setupRequirements, cloudTrialRequirements);
await n8n.navigate.toWorkflows();
await expect(n8n.workflows.getEasyAiWorkflowCard()).toBeHidden();
});
test('should show option to take you to the easy AI workflow if experiment is variant', async ({
n8n,
api,
setupRequirements,
}) => {
await api.setEnvFeatureFlags({ '026_easy_ai_workflow': 'variant' });
await setupCloudTest(n8n, setupRequirements, cloudTrialRequirements);
await n8n.navigate.toWorkflows();
await expect(n8n.workflows.getEasyAiWorkflowCard()).toBeVisible();
});
test('should show default instructions if free AI credits experiment is control', async ({
n8n,
api,
setupRequirements,
}) => {
await api.setEnvFeatureFlags({ '026_easy_ai_workflow': 'variant' });
await setupCloudTest(n8n, setupRequirements, cloudTrialRequirements);
await n8n.navigate.toWorkflows();
await n8n.workflows.clickEasyAiWorkflowCard();
await n8n.page.waitForLoadState();
const firstSticky = n8n.canvas.sticky.getStickies().first();
await expect(firstSticky).toContainText('Start by saying');
});
});
});