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