diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 692fecd97..85ed9c2c9 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -96,7 +96,7 @@ module.exports = { } ] }, - ignorePatterns: ["next.config.js"], + ignorePatterns: ["next.config.js", "cypress/**/*.js", "cypress.config.js"], settings: { "import/resolver": { typescript: { diff --git a/frontend/cypress.config.js b/frontend/cypress.config.js new file mode 100644 index 000000000..f50a1c607 --- /dev/null +++ b/frontend/cypress.config.js @@ -0,0 +1,7 @@ +module.exports = { + e2e: { + baseUrl: 'http://localhost:8080', + viewportWidth: 1480, + viewportHeight: 920, + }, +}; diff --git a/frontend/cypress/e2e/org-overview.cy.js b/frontend/cypress/e2e/org-overview.cy.js new file mode 100644 index 000000000..a18fe208e --- /dev/null +++ b/frontend/cypress/e2e/org-overview.cy.js @@ -0,0 +1,47 @@ +/// + +describe('organization Overview', () => { + beforeEach(() => { + cy.login(`test@localhost.local`, `testInfisical1`) + }) + + const projectName = "projectY" + + it('can`t create projects with empty names', () => { + cy.get('.button').click() + cy.get('input[placeholder="Type your project name"]').type('abc').clear() + cy.intercept('*').as('anyRequest'); + cy.get('@anyRequest').should('not.exist'); + }) + + it('can delete a newly-created project', () => { + // Create a project + cy.get('.button').click() + cy.get('input[placeholder="Type your project name"]').type(`${projectName}`) + cy.contains('button', 'Create Project').click() + cy.url().should('include', '/project') + + // Delete a project + cy.get(`[href^="/project/"][href$="/settings"] > a > .group`).click() + cy.contains('button', `Delete ${projectName}`).click() + cy.contains('button', 'Delete Project').should('have.attr', 'disabled') + cy.get('input[placeholder="Type to delete..."]').type('confirm') + cy.contains('button', 'Delete Project').should('not.have.attr', 'disabled') + cy.url().then((currentUrl) => { + let projectId = currentUrl.split("/")[4] + cy.intercept('DELETE', `/api/v1/workspace/${projectId}`).as('deleteProject'); + cy.contains('button', 'Delete Project').click(); + cy.get('@deleteProject').should('have.property', 'response').and('have.property', 'statusCode', 200); + }) + }) + + it('can display no projects', () => { + cy.intercept('/api/v1/workspace', { + body: { + "workspaces": [] + }, + }) + cy.get('.border-mineshaft-700 > :nth-child(2)').should('have.text', 'You are not part of any projects in this organization yet. When you are, they will appear here.') + }) + +}) diff --git a/frontend/cypress/e2e/org-settings.cy.js b/frontend/cypress/e2e/org-settings.cy.js new file mode 100644 index 000000000..b9aff2516 --- /dev/null +++ b/frontend/cypress/e2e/org-settings.cy.js @@ -0,0 +1,24 @@ +/// + +describe('Organization Settings', () => { + let orgId; + + beforeEach(() => { + cy.login(`test@localhost.local`, `testInfisical1`) + cy.url().then((currentUrl) => { + orgId = currentUrl.split("/")[4] + cy.visit(`org/${orgId}/settings`) + }) + }) + + it('can rename org', () => { + cy.get('input[placeholder="Acme Corp"]').clear().type('ABC') + + cy.intercept('PATCH', `/api/v1/organization/${orgId}/name`).as('renameOrg'); + cy.get('form.p-4 > .button').click() + cy.get('@renameOrg').should('have.property', 'response').and('have.property', 'statusCode', 200); + + cy.get('.pl-3').should("have.text", "ABC ") + }) + +}) diff --git a/frontend/cypress/e2e/project-secret-operations.cy.js b/frontend/cypress/e2e/project-secret-operations.cy.js new file mode 100644 index 000000000..b4c3a7787 --- /dev/null +++ b/frontend/cypress/e2e/project-secret-operations.cy.js @@ -0,0 +1,84 @@ +/// + +describe('Project Overview', () => { + const projectName = "projectY" + let projectId; + let isFirstTest = true; + + before(() => { + cy.login(`test@localhost.local`, `testInfisical1`) + + // Create a project + cy.get('.button').click() + cy.get('input[placeholder="Type your project name"]').type(`${projectName}`) + cy.contains('button', 'Create Project').click() + cy.url().should('include', '/project').then((currentUrl) => { + projectId = currentUrl.split("/")[4] + }) + }) + + beforeEach(() => { + if (isFirstTest) { + isFirstTest = false; + return; // Skip the rest of the beforeEach for the first test + } + cy.login(`test@localhost.local`, `testInfisical1`) + cy.visit(`/project/${projectId}/secrets/overview`) + }) + + it('can create secrets', () => { + cy.contains('button', 'Go to Development').click() + cy.contains('button', 'Add a new secret').click() + cy.get('input[placeholder="Type your secret name"]').type('SECRET_A') + cy.contains('button', 'Create Secret').click() + cy.get('.w-80 > .inline-flex > .input').should('have.value', 'SECRET_A') + cy.get(':nth-child(6) > .button > .w-min').should('have.text', '1 Commit') + }) + + it('can update secrets', () => { + cy.get(':nth-child(2) > .flex > .button').click() + cy.get('.overflow-auto > .relative > .absolute').type('VALUE_A') + cy.get('.button.text-primary > .svg-inline--fa').click() + cy.get(':nth-child(6) > .button > .w-min').should('have.text', '2 Commits') + }) + + it('can`t create duplicate-name secrets', () => { + cy.get(':nth-child(2) > .flex > .button').click() + cy.contains('button', 'Add Secret').click() + cy.get('input[placeholder="Type your secret name"]').type('SECRET_A') + cy.intercept('POST', `/api/v3/secrets/SECRET_A`).as('createSecret'); + cy.contains('button', 'Create Secret').click() + cy.get('@createSecret').should('have.property', 'response').and('have.property', 'statusCode', 400); + }) + + it('can add another secret', () => { + cy.get(':nth-child(2) > .flex > .button').click() + cy.contains('button', 'Add Secret').click() + cy.get('input[placeholder="Type your secret name"]').type('SECRET_B') + cy.contains('button', 'Create Secret').click() + cy.get(':nth-child(6) > .button > .w-min').should('have.text', '3 Commits') + }) + + it('can delete a secret', () => { + cy.get(':nth-child(2) > .flex > .button').click() + // cy.get(':nth-child(3) > .shadow-none').trigger('mouseover') + cy.get(':nth-child(3) > .shadow-none > .group > .h-10 > .border-red').click() + cy.contains('button', 'Delete Secret').should('have.attr', 'disabled') + cy.get('input[placeholder="Type to delete..."]').type('SECRET_B') + cy.intercept('DELETE', `/api/v3/secrets/SECRET_B`).as('deleteSecret'); + cy.contains('button', 'Delete Secret').should('not.have.attr', 'disabled') + cy.contains('button', 'Delete Secret').click(); + cy.get('@deleteSecret').should('have.property', 'response').and('have.property', 'statusCode', 200); + }) + + it('can add a comment', () => { + return; + cy.get(':nth-child(2) > .flex > .button').click() + // for some reason this hover does not want to work + cy.get('.overflow-auto').trigger('mouseover').then(() => { + cy.get('.shadow-none > .group > .pl-4 > .h-8 > button[aria-label="add-comment"]').should('be.visible').click() + }); + + }) + +}) diff --git a/frontend/cypress/fixtures/example.json b/frontend/cypress/fixtures/example.json new file mode 100644 index 000000000..02e425437 --- /dev/null +++ b/frontend/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.js new file mode 100644 index 000000000..6778e394c --- /dev/null +++ b/frontend/cypress/support/commands.js @@ -0,0 +1,19 @@ + +Cypress.Commands.add('login', (username, password) => { + cy.visit('/login') + cy.get('input[placeholder="Enter your email..."]').type(username) + cy.get('input[placeholder="Enter your password..."]').type(password) + cy.contains('Continue with Email').click() + cy.url().should('include', '/overview') +}) + +// Cypress.Commands.add('login', (username, password) => { +// cy.session([username, password], () => { +// cy.visit('/login') +// cy.get('input[placeholder="Enter your email..."]').type(username) +// cy.get('input[placeholder="Enter your password..."]').type(password) +// cy.contains('Continue with Email').click() +// cy.url().should('include', '/overview') +// cy.wait(2000); +// }) +// }) diff --git a/frontend/cypress/support/e2e.js b/frontend/cypress/support/e2e.js new file mode 100644 index 000000000..0e7290a13 --- /dev/null +++ b/frontend/cypress/support/e2e.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') \ No newline at end of file