diff --git a/cypress/e2e/group2/27-cloud.cy.ts b/cypress/e2e/group2/27-cloud.cy.ts deleted file mode 100644 index 2391fa84644..00000000000 --- a/cypress/e2e/group2/27-cloud.cy.ts +++ /dev/null @@ -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'); - }); - }); - }); -}); diff --git a/packages/testing/playwright/fixtures/plan-data-trial.json b/packages/testing/playwright/fixtures/plan-data-trial.json new file mode 100644 index 00000000000..7a805708c65 --- /dev/null +++ b/packages/testing/playwright/fixtures/plan-data-trial.json @@ -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" +} diff --git a/packages/testing/playwright/pages/SettingsPage.ts b/packages/testing/playwright/pages/SettingsPage.ts index 1f87a9792d8..da03b9cf8a6 100644 --- a/packages/testing/playwright/pages/SettingsPage.ts +++ b/packages/testing/playwright/pages/SettingsPage.ts @@ -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'); + } } diff --git a/packages/testing/playwright/pages/SidebarPage.ts b/packages/testing/playwright/pages/SidebarPage.ts index a8747ad4eed..dafd0d05cdc 100644 --- a/packages/testing/playwright/pages/SidebarPage.ts +++ b/packages/testing/playwright/pages/SidebarPage.ts @@ -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 { await this.getUserMenu().click(); } diff --git a/packages/testing/playwright/pages/WorkflowsPage.ts b/packages/testing/playwright/pages/WorkflowsPage.ts index ebe6accb899..e7ef0fe56ed 100644 --- a/packages/testing/playwright/pages/WorkflowsPage.ts +++ b/packages/testing/playwright/pages/WorkflowsPage.ts @@ -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(); diff --git a/packages/testing/playwright/tests/ui/27-cloud.spec.ts b/packages/testing/playwright/tests/ui/27-cloud.spec.ts new file mode 100644 index 00000000000..301a0dbc06c --- /dev/null +++ b/packages/testing/playwright/tests/ui/27-cloud.spec.ts @@ -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, + 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'); + }); + }); +});