mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-06 22:23:53 -05:00
adding cypress tests
This commit is contained in:
7
cypress.config.js
Normal file
7
cypress.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
e2e: {
|
||||||
|
baseUrl: 'http://localhost:8080',
|
||||||
|
viewportWidth: 1480,
|
||||||
|
viewportHeight: 920,
|
||||||
|
},
|
||||||
|
};
|
||||||
47
cypress/e2e/org-overview.cy.js
Normal file
47
cypress/e2e/org-overview.cy.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
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.')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
24
cypress/e2e/org-settings.cy.js
Normal file
24
cypress/e2e/org-settings.cy.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
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 ")
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
84
cypress/e2e/project-overview.cy.js
Normal file
84
cypress/e2e/project-overview.cy.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
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()
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
51
cypress/support/commands.js
Normal file
51
cypress/support/commands.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// ***********************************************
|
||||||
|
// This example commands.js shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
// Need to make this work for CSRF tokens; Cypress has an example in the docs
|
||||||
|
// cy.session(
|
||||||
|
// username,
|
||||||
|
// () => {
|
||||||
|
// 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')
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// validate: () => {
|
||||||
|
// cy.getCookie('jid').should('exist')
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
})
|
||||||
|
|
||||||
20
cypress/support/e2e.js
Normal file
20
cypress/support/e2e.js
Normal file
@@ -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')
|
||||||
1920
frontend/package-lock.json
generated
1920
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -124,6 +124,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.7",
|
||||||
|
"cypress": "^13.3.2",
|
||||||
"eslint": "^8.32.0",
|
"eslint": "^8.32.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type Props = {
|
|||||||
title: string;
|
title: string;
|
||||||
subTitle?: string;
|
subTitle?: string;
|
||||||
onDeleteApproved: () => Promise<void>;
|
onDeleteApproved: () => Promise<void>;
|
||||||
|
buttonText?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteActionModal = ({
|
export const DeleteActionModal = ({
|
||||||
@@ -24,7 +25,8 @@ export const DeleteActionModal = ({
|
|||||||
deleteKey,
|
deleteKey,
|
||||||
onDeleteApproved,
|
onDeleteApproved,
|
||||||
title,
|
title,
|
||||||
subTitle = "This action is irreversible!"
|
subTitle = "This action is irreversible!",
|
||||||
|
buttonText = "Delete"
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const [inputData, setInputData] = useState("");
|
const [inputData, setInputData] = useState("");
|
||||||
const [isLoading, setIsLoading] = useToggle();
|
const [isLoading, setIsLoading] = useToggle();
|
||||||
@@ -56,7 +58,7 @@ export const DeleteActionModal = ({
|
|||||||
title={title}
|
title={title}
|
||||||
subTitle={subTitle}
|
subTitle={subTitle}
|
||||||
footerContent={
|
footerContent={
|
||||||
<div className="flex items-center">
|
<div className="flex items-center mx-2">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
@@ -64,7 +66,7 @@ export const DeleteActionModal = ({
|
|||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
Delete
|
{buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
<ModalClose asChild>
|
<ModalClose asChild>
|
||||||
<Button variant="plain" colorSchema="secondary" onClick={onClose}>
|
<Button variant="plain" colorSchema="secondary" onClick={onClose}>
|
||||||
@@ -87,9 +89,9 @@ export const DeleteActionModal = ({
|
|||||||
Type <span className="font-bold">{deleteKey}</span> to delete the resource
|
Type <span className="font-bold">{deleteKey}</span> to delete the resource
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
className="mb-4"
|
className="mb-0"
|
||||||
>
|
>
|
||||||
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} />
|
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} placeholder="Type to delete..." />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</form>
|
</form>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export const SecretMainPage = () => {
|
|||||||
onClickRollbackMode={() => handlePopUpToggle("snapshots", true)}
|
onClickRollbackMode={() => handlePopUpToggle("snapshots", true)}
|
||||||
/>
|
/>
|
||||||
<div className="mt-3 overflow-y-auto overflow-x-hidden thin-scrollbar bg-mineshaft-800 text-left text-bunker-300 rounded-md text-sm">
|
<div className="mt-3 overflow-y-auto overflow-x-hidden thin-scrollbar bg-mineshaft-800 text-left text-bunker-300 rounded-md text-sm">
|
||||||
<div className="flex flex-col ">
|
<div className="flex flex-col" id="dashboard">
|
||||||
{isNotEmtpy && (
|
{isNotEmtpy && (
|
||||||
<div className="flex font-medium border-b border-mineshaft-600">
|
<div className="flex font-medium border-b border-mineshaft-600">
|
||||||
<div style={{ width: "2.8rem" }} className="px-4 py-3 flex-shrink-0" />
|
<div style={{ width: "2.8rem" }} className="px-4 py-3 flex-shrink-0" />
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ export const ActionBar = ({
|
|||||||
className="h-10"
|
className="h-10"
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
>
|
>
|
||||||
{snapshotCount} Commits
|
{`${snapshotCount} ${snapshotCount === 1 ? "Commit" : "Commits"}`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const CreateSecretForm = ({
|
|||||||
>
|
>
|
||||||
<SecretInput
|
<SecretInput
|
||||||
{...field}
|
{...field}
|
||||||
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 py-1.5"
|
containerClassName="text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 py-1.5"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -339,6 +339,7 @@ export const SecretListView = ({
|
|||||||
title="Do you want to delete this secret?"
|
title="Do you want to delete this secret?"
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteSecret", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("deleteSecret", isOpen)}
|
||||||
onDeleteApproved={handleSecretDelete}
|
onDeleteApproved={handleSecretDelete}
|
||||||
|
buttonText="Delete Secret"
|
||||||
/>
|
/>
|
||||||
<SecretDetailSidebar
|
<SecretDetailSidebar
|
||||||
environment={environment}
|
environment={environment}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const OrgGeneralTab = () => {
|
|||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { data: members } = useGetOrgUsers(currentOrg?._id ?? "");
|
const { data: members } = useGetOrgUsers(currentOrg?._id ?? "");
|
||||||
|
|
||||||
const membershipOrg = members?.find((member) => member.user._id === user._id);
|
const membershipOrg = members?.find((member) => member.user._id === user?._id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -77,9 +77,10 @@ export const DeleteProjectSection = () => {
|
|||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.deleteWorkspace.isOpen}
|
isOpen={popUp.deleteWorkspace.isOpen}
|
||||||
title="Are you sure want to delete this project?"
|
title="Are you sure want to delete this project?"
|
||||||
subTitle={`Permanently remove ${currentWorkspace?.name} and all of its data. This action is not reversible, so please be careful.`}
|
subTitle={`Permanently delete ${currentWorkspace?.name} and all of its data. This action is not reversible, so please be careful.`}
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteWorkspace", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("deleteWorkspace", isOpen)}
|
||||||
deleteKey="confirm"
|
deleteKey="confirm"
|
||||||
|
buttonText="Delete Project"
|
||||||
onDeleteApproved={handleDeleteWorkspaceSubmit}
|
onDeleteApproved={handleDeleteWorkspaceSubmit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user