From ac744d6702ac2b04abdb7524674cb43062c2fce4 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:18:10 +0200 Subject: [PATCH] test: Ado 1296 add e2e tests (#7792) Adds e2e tests for the template credential setup. --- .../e2e/34-template-credentials-setup.cy.ts | 105 +++++++++++ cypress/fixtures/Test_Template_1.json | 177 ++++++++++++++++++ cypress/pages/template-credential-setup.ts | 40 ++++ cypress/pages/template-workflow.ts | 19 ++ cypress/pages/templates.ts | 15 +- .../CredentialPicker/CredentialPicker.vue | 1 + .../SetupTemplateFormStep.vue | 6 +- .../SetupWorkflowFromTemplateView.vue | 3 +- .../src/views/TemplatesWorkflowView.vue | 1 + 9 files changed, 358 insertions(+), 9 deletions(-) create mode 100644 cypress/e2e/34-template-credentials-setup.cy.ts create mode 100644 cypress/fixtures/Test_Template_1.json create mode 100644 cypress/pages/template-credential-setup.ts create mode 100644 cypress/pages/template-workflow.ts diff --git a/cypress/e2e/34-template-credentials-setup.cy.ts b/cypress/e2e/34-template-credentials-setup.cy.ts new file mode 100644 index 0000000000..cb16ee8219 --- /dev/null +++ b/cypress/e2e/34-template-credentials-setup.cy.ts @@ -0,0 +1,105 @@ +import { CredentialsModal, MessageBox } from '../pages/modals'; +import { TemplateCredentialSetupPage } from '../pages/template-credential-setup'; +import { TemplateWorkflowPage } from '../pages/template-workflow'; +import { WorkflowPage } from '../pages/workflow'; + +const templateWorkflowPage = new TemplateWorkflowPage(); +const templateCredentialsSetupPage = new TemplateCredentialSetupPage(); +const credentialsModal = new CredentialsModal(); +const messageBox = new MessageBox(); +const workflowPage = new WorkflowPage(); + +const testTemplate = templateCredentialsSetupPage.testData.simpleTemplate; + +describe('Template credentials setup', () => { + beforeEach(() => { + cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${testTemplate.id}`, { + fixture: testTemplate.fixture, + }); + }); + + it('can be opened from template workflow page', () => { + templateWorkflowPage.actions.visit(testTemplate.id); + templateCredentialsSetupPage.actions.enableFeatureFlag(); + templateWorkflowPage.actions.clickUseThisWorkflowButton(); + + templateCredentialsSetupPage.getters + .title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`) + .should('be.visible'); + }); + + it('can be opened with a direct url', () => { + templateCredentialsSetupPage.actions.visit(testTemplate.id); + + templateCredentialsSetupPage.getters + .title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`) + .should('be.visible'); + }); + + it('has all the elements on page', () => { + templateCredentialsSetupPage.actions.visit(testTemplate.id); + + templateCredentialsSetupPage.getters + .title(`Setup 'Promote new Shopify products on Twitter and Telegram' template`) + .should('be.visible'); + + templateCredentialsSetupPage.getters + .infoCallout() + .should( + 'contain.text', + 'You need 1x Shopify, 1x X (Formerly Twitter) and 1x Telegram account to setup this template', + ); + + const expectedAppNames = ['1. Shopify', '2. X (Formerly Twitter)', '3. Telegram']; + const expectedAppDescriptions = [ + 'The credential you select will be used in the product created node of the workflow template.', + 'The credential you select will be used in the Twitter node of the workflow template.', + 'The credential you select will be used in the Telegram node of the workflow template.', + ]; + + templateCredentialsSetupPage.getters.appCredentialSteps().each(($el, index) => { + templateCredentialsSetupPage.getters + .stepHeading($el) + .should('have.text', expectedAppNames[index]); + templateCredentialsSetupPage.getters + .stepDescription($el) + .should('have.text', expectedAppDescriptions[index]); + }); + }); + + it('can skip template creation', () => { + templateCredentialsSetupPage.actions.visit(testTemplate.id); + + templateCredentialsSetupPage.getters.skipLink().click(); + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + it('can create credentials and workflow from the template', () => { + templateCredentialsSetupPage.actions.visit(testTemplate.id); + + templateCredentialsSetupPage.getters.continueButton().should('be.disabled'); + + templateCredentialsSetupPage.getters.createAppCredentialsButton('Shopify').click(); + credentialsModal.getters.editCredentialModal().find('input:first()').type('test'); + credentialsModal.actions.save(false); + credentialsModal.actions.close(); + + templateCredentialsSetupPage.getters.createAppCredentialsButton('X (Formerly Twitter)').click(); + credentialsModal.getters.editCredentialModal().find('input:first()').type('test'); + credentialsModal.actions.save(false); + credentialsModal.actions.close(); + messageBox.actions.cancel(); + + templateCredentialsSetupPage.getters.createAppCredentialsButton('Telegram').click(); + credentialsModal.getters.editCredentialModal().find('input:first()').type('test'); + credentialsModal.actions.save(false); + credentialsModal.actions.close(); + + cy.intercept('POST', '/rest/workflows').as('createWorkflow'); + templateCredentialsSetupPage.getters.continueButton().should('be.enabled'); + templateCredentialsSetupPage.getters.continueButton().click(); + cy.wait('@createWorkflow'); + + workflowPage.getters.canvasNodes().should('have.length', 3); + }); +}); diff --git a/cypress/fixtures/Test_Template_1.json b/cypress/fixtures/Test_Template_1.json new file mode 100644 index 0000000000..f15970677e --- /dev/null +++ b/cypress/fixtures/Test_Template_1.json @@ -0,0 +1,177 @@ +{ + "workflow": { + "id": 1205, + "name": "Promote new Shopify products on Twitter and Telegram", + "views": 478, + "recentViews": 9880, + "totalViews": 478, + "createdAt": "2021-08-24T10:40:50.007Z", + "description": "This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.", + "workflow": { + "nodes": [ + { + "name": "Twitter", + "type": "n8n-nodes-base.twitter", + "position": [ + 720, + -220 + ], + "parameters": { + "text": "=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️", + "additionalFields": {} + }, + "credentials": { + "twitterOAuth1Api": "twitter" + }, + "typeVersion": 1 + }, + { + "name": "Telegram", + "type": "n8n-nodes-base.telegram", + "position": [ + 720, + -20 + ], + "parameters": { + "text": "=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})", + "chatId": "123456", + "additionalFields": {} + }, + "credentials": { + "telegramApi": "telegram_habot" + }, + "typeVersion": 1 + }, + { + "name": "product created", + "type": "n8n-nodes-base.shopifyTrigger", + "position": [ + 540, + -110 + ], + "webhookId": "2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0", + "parameters": { + "topic": "products/create" + }, + "credentials": { + "shopifyApi": "shopify_nodeqa" + }, + "typeVersion": 1 + } + ], + "connections": { + "product created": { + "main": [ + [ + { + "node": "Twitter", + "type": "main", + "index": 0 + }, + { + "node": "Telegram", + "type": "main", + "index": 0 + } + ] + ] + } + } + }, + "workflowInfo": { + "nodeCount": 3, + "nodeTypes": { + "n8n-nodes-base.twitter": { + "count": 1 + }, + "n8n-nodes-base.telegram": { + "count": 1 + }, + "n8n-nodes-base.shopifyTrigger": { + "count": 1 + } + } + }, + "user": { + "username": "lorenanda" + }, + "nodes": [ + { + "id": 49, + "icon": "file:telegram.svg", + "name": "n8n-nodes-base.telegram", + "defaults": { + "name": "Telegram" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg==" + }, + "categories": [ + { + "id": 6, + "name": "Communication" + } + ], + "displayName": "Telegram", + "typeVersion": 1 + }, + { + "id": 107, + "icon": "file:shopify.svg", + "name": "n8n-nodes-base.shopifyTrigger", + "defaults": { + "name": "Shopify Trigger" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4=" + }, + "categories": [ + { + "id": 2, + "name": "Sales" + } + ], + "displayName": "Shopify Trigger", + "typeVersion": 1 + }, + { + "id": 325, + "icon": "file:x.svg", + "name": "n8n-nodes-base.twitter", + "defaults": { + "name": "X" + }, + "iconData": { + "type": "file", + "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K" + }, + "categories": [ + { + "id": 1, + "name": "Marketing & Content" + } + ], + "displayName": "X (Formerly Twitter)", + "typeVersion": 2 + } + ], + "categories": [ + { + "id": 2, + "name": "Sales" + }, + { + "id": 19, + "name": "Marketing & Growth" + } + ], + "image": [ + { + "id": 527, + "url": "https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png" + } + ] + } +} diff --git a/cypress/pages/template-credential-setup.ts b/cypress/pages/template-credential-setup.ts new file mode 100644 index 0000000000..2ccaf5f807 --- /dev/null +++ b/cypress/pages/template-credential-setup.ts @@ -0,0 +1,40 @@ +import { BasePage } from './base'; + +export type TemplateTestData = { + id: number; + fixture: string; +}; + +export class TemplateCredentialSetupPage extends BasePage { + testData = { + simpleTemplate: { + id: 1205, + fixture: 'Test_Template_1.json', + }, + }; + + getters = { + continueButton: () => cy.getByTestId('continue-button'), + skipLink: () => cy.get('a:contains("Skip")'), + title: (title: string) => cy.get(`h1:contains(${title})`), + infoCallout: () => cy.getByTestId('info-callout'), + createAppCredentialsButton: (appName: string) => + cy.get(`button:contains("Create new ${appName} credential")`), + appCredentialSteps: () => cy.getByTestId('setup-credentials-form-step'), + stepHeading: ($el: JQuery) => + cy.wrap($el).findChildByTestId('credential-step-heading'), + stepDescription: ($el: JQuery) => + cy.wrap($el).findChildByTestId('credential-step-description'), + }; + + actions = { + visit: (templateId: number) => { + cy.visit(`/templates/${templateId}/setup`); + }, + enableFeatureFlag: () => { + cy.window().then((window) => { + window.localStorage.setItem('template-credentials-setup', 'true'); + }); + }, + }; +} diff --git a/cypress/pages/template-workflow.ts b/cypress/pages/template-workflow.ts new file mode 100644 index 0000000000..b41c5f8513 --- /dev/null +++ b/cypress/pages/template-workflow.ts @@ -0,0 +1,19 @@ +import { BasePage } from './base'; + +export class TemplateWorkflowPage extends BasePage { + url = '/templates'; + + getters = { + useTemplateButton: () => cy.get('[data-test-id="use-template-button"]'), + }; + + actions = { + visit: (templateId: number) => { + cy.visit(`${this.url}/${templateId}`); + }, + + clickUseThisWorkflowButton: () => { + this.getters.useTemplateButton().click(); + }, + }; +} diff --git a/cypress/pages/templates.ts b/cypress/pages/templates.ts index 1222bc0edc..d3a3c95420 100644 --- a/cypress/pages/templates.ts +++ b/cypress/pages/templates.ts @@ -4,10 +4,16 @@ export class TemplatesPage extends BasePage { url = '/templates'; getters = { + useTemplateButton: () => cy.get('[data-testid="use-template-button"]'), }; actions = { - openOnboardingFlow: (id: number, name: string , workflow: object) => { + openSingleTemplateView: (templateId: number) => { + cy.visit(`${this.url}/${templateId}`); + cy.waitForLoad(); + }, + + openOnboardingFlow: (id: number, name: string, workflow: object) => { const apiResponse = { id, name, @@ -41,8 +47,7 @@ export class TemplatesPage extends BasePage { cy.visit(`/workflows/templates/${id}`); cy.wait('@getTemplate'); - cy.wait( '@getWorkflow'); - } - } + cy.wait('@getWorkflow'); + }, + }; } - diff --git a/packages/editor-ui/src/components/CredentialPicker/CredentialPicker.vue b/packages/editor-ui/src/components/CredentialPicker/CredentialPicker.vue index 31256fc73d..b0c9223c91 100644 --- a/packages/editor-ui/src/components/CredentialPicker/CredentialPicker.vue +++ b/packages/editor-ui/src/components/CredentialPicker/CredentialPicker.vue @@ -110,6 +110,7 @@ listenForCredentialChanges({ v-else :label="`Create new ${props.appName} credential`" @click="createNewCredential" + data-test-id="create-credential" /> diff --git a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue index 5975368750..5db317c1f9 100644 --- a/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue +++ b/packages/editor-ui/src/views/SetupWorkflowFromTemplateView/SetupTemplateFormStep.vue @@ -77,16 +77,16 @@ const onCredentialDeselected = () => {