diff options
Diffstat (limited to 'cypress')
| -rw-r--r-- | cypress/docker-compose.yml | 52 | ||||
| -rw-r--r-- | cypress/e2e/api-team.cy.ts | 209 | ||||
| -rw-r--r-- | cypress/e2e/api-user.cy.ts | 125 | ||||
| -rw-r--r-- | cypress/e2e/api-website.cy.ts | 198 | ||||
| -rw-r--r-- | cypress/e2e/login.cy.ts | 36 | ||||
| -rw-r--r-- | cypress/e2e/user.cy.ts | 65 | ||||
| -rw-r--r-- | cypress/e2e/website.cy.ts | 89 | ||||
| -rw-r--r-- | cypress/fixtures/teams.json | 8 | ||||
| -rw-r--r-- | cypress/fixtures/users.json | 11 | ||||
| -rw-r--r-- | cypress/fixtures/websites.json | 10 | ||||
| -rw-r--r-- | cypress/support/e2e.ts | 123 | ||||
| -rw-r--r-- | cypress/support/index.d.ts | 56 | ||||
| -rw-r--r-- | cypress/tsconfig.json | 8 |
13 files changed, 990 insertions, 0 deletions
diff --git a/cypress/docker-compose.yml b/cypress/docker-compose.yml new file mode 100644 index 0000000..01a47bd --- /dev/null +++ b/cypress/docker-compose.yml @@ -0,0 +1,52 @@ +--- +version: '3' +services: + umami: + build: ../ + #image: ghcr.io/umami-software/umami:postgresql-latest + ports: + - '3000:3000' + environment: + DATABASE_URL: postgresql://umami:umami@db:5432/umami + DATABASE_TYPE: postgresql + APP_SECRET: replace-me-with-a-random-string + depends_on: + db: + condition: service_healthy + restart: always + healthcheck: + test: ['CMD-SHELL', 'curl http://localhost:3000/api/heartbeat'] + interval: 5s + timeout: 5s + retries: 5 + db: + image: postgres:15-alpine + environment: + POSTGRES_DB: umami + POSTGRES_USER: umami + POSTGRES_PASSWORD: umami + volumes: + - umami-db-data:/var/lib/postgresql/data + restart: always + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'] + interval: 5s + timeout: 5s + retries: 5 + cypress: + image: 'cypress/included:13.6.0' + depends_on: + - umami + - db + environment: + - CYPRESS_baseUrl=http://umami:3000 + - CYPRESS_umami_user=admin + - CYPRESS_umami_password=umami + volumes: + - ./tsconfig.json:/tsconfig.json + - ../cypress.config.ts:/cypress.config.ts + - ./:/cypress + - ../node_modules/:/node_modules + - ../src/lib/crypto.ts:/src/lib/crypto.ts +volumes: + umami-db-data: diff --git a/cypress/e2e/api-team.cy.ts b/cypress/e2e/api-team.cy.ts new file mode 100644 index 0000000..2281113 --- /dev/null +++ b/cypress/e2e/api-team.cy.ts @@ -0,0 +1,209 @@ +describe('Team API tests', () => { + Cypress.session.clearAllSavedSessions(); + + let teamId; + let userId; + + before(() => { + cy.login(Cypress.env('umami_user'), Cypress.env('umami_password')); + cy.fixture('users').then(data => { + const userCreate = data.userCreate; + cy.request({ + method: 'POST', + url: '/api/users', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: userCreate, + }).then(response => { + userId = response.body.id; + expect(response.status).to.eq(200); + expect(response.body).to.have.property('username', 'cypress1'); + expect(response.body).to.have.property('role', 'user'); + }); + }); + }); + + it('Creates a team.', () => { + cy.fixture('teams').then(data => { + const teamCreate = data.teamCreate; + cy.request({ + method: 'POST', + url: '/api/teams', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: teamCreate, + }).then(response => { + teamId = response.body[0].id; + expect(response.status).to.eq(200); + expect(response.body[0]).to.have.property('name', 'cypress'); + expect(response.body[1]).to.have.property('role', 'team-owner'); + }); + }); + }); + + it('Gets a teams by ID.', () => { + cy.request({ + method: 'GET', + url: `/api/teams/${teamId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('id', teamId); + }); + }); + + it('Updates a team.', () => { + cy.fixture('teams').then(data => { + const teamUpdate = data.teamUpdate; + cy.request({ + method: 'POST', + url: `/api/teams/${teamId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: teamUpdate, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('id', teamId); + expect(response.body).to.have.property('name', 'cypressUpdate'); + }); + }); + }); + + it('Get all users that belong to a team.', () => { + cy.request({ + method: 'GET', + url: `/api/teams/${teamId}/users`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body.data[0]).to.have.property('id'); + expect(response.body.data[0]).to.have.property('teamId'); + expect(response.body.data[0]).to.have.property('userId'); + expect(response.body.data[0]).to.have.property('user'); + }); + }); + + it('Get a user belonging to a team.', () => { + cy.request({ + method: 'GET', + url: `/api/teams/${teamId}/users/${Cypress.env('umami_user_id')}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('teamId'); + expect(response.body).to.have.property('userId'); + expect(response.body).to.have.property('role'); + }); + }); + + it('Get all websites belonging to a team.', () => { + cy.request({ + method: 'GET', + url: `/api/teams/${teamId}/websites`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('data'); + }); + }); + + it('Add a user to a team.', () => { + cy.request({ + method: 'POST', + url: `/api/teams/${teamId}/users`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + userId, + role: 'team-member', + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('userId', userId); + expect(response.body).to.have.property('role', 'team-member'); + }); + }); + + it(`Update a user's role on a team.`, () => { + cy.request({ + method: 'POST', + url: `/api/teams/${teamId}/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + role: 'team-view-only', + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('userId', userId); + expect(response.body).to.have.property('role', 'team-view-only'); + }); + }); + + it(`Remove a user from a team.`, () => { + cy.request({ + method: 'DELETE', + url: `/api/teams/${teamId}/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + }); + }); + + it('Deletes a team.', () => { + cy.request({ + method: 'DELETE', + url: `/api/teams/${teamId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('ok', true); + }); + }); + + // it('Gets all teams that belong to a user.', () => { + // cy.request({ + // method: 'GET', + // url: `/api/users/${userId}/teams`, + // headers: { + // 'Content-Type': 'application/json', + // Authorization: Cypress.env('authorization'), + // }, + // }).then(response => { + // expect(response.status).to.eq(200); + // expect(response.body).to.have.property('data'); + // }); + // }); + + after(() => { + cy.deleteUser(userId); + }); +}); diff --git a/cypress/e2e/api-user.cy.ts b/cypress/e2e/api-user.cy.ts new file mode 100644 index 0000000..696cd21 --- /dev/null +++ b/cypress/e2e/api-user.cy.ts @@ -0,0 +1,125 @@ +describe('User API tests', () => { + Cypress.session.clearAllSavedSessions(); + + before(() => { + cy.login(Cypress.env('umami_user'), Cypress.env('umami_password')); + }); + + let userId; + + it('Creates a user.', () => { + cy.fixture('users').then(data => { + const userCreate = data.userCreate; + cy.request({ + method: 'POST', + url: '/api/users', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: userCreate, + }).then(response => { + userId = response.body.id; + expect(response.status).to.eq(200); + expect(response.body).to.have.property('username', 'cypress1'); + expect(response.body).to.have.property('role', 'user'); + }); + }); + }); + + it('Returns all users. Admin access is required.', () => { + cy.request({ + method: 'GET', + url: '/api/admin/users', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body.data[0]).to.have.property('id'); + expect(response.body.data[0]).to.have.property('username'); + expect(response.body.data[0]).to.have.property('password'); + expect(response.body.data[0]).to.have.property('role'); + }); + }); + + it('Updates a user.', () => { + cy.fixture('users').then(data => { + const userUpdate = data.userUpdate; + cy.request({ + method: 'POST', + url: `/api/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: userUpdate, + }).then(response => { + userId = response.body.id; + expect(response.status).to.eq(200); + expect(response.body).to.have.property('id', userId); + expect(response.body).to.have.property('username', 'cypress1'); + expect(response.body).to.have.property('role', 'view-only'); + }); + }); + }); + + it('Gets a user by ID.', () => { + cy.request({ + method: 'GET', + url: `/api/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('id', userId); + expect(response.body).to.have.property('username', 'cypress1'); + expect(response.body).to.have.property('role', 'view-only'); + }); + }); + + it('Deletes a user.', () => { + cy.request({ + method: 'DELETE', + url: `/api/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('ok', true); + }); + }); + + it('Gets all websites that belong to a user.', () => { + cy.request({ + method: 'GET', + url: `/api/users/${userId}/websites`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('data'); + }); + }); + + it('Gets all teams that belong to a user.', () => { + cy.request({ + method: 'GET', + url: `/api/users/${userId}/teams`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('data'); + }); + }); +}); diff --git a/cypress/e2e/api-website.cy.ts b/cypress/e2e/api-website.cy.ts new file mode 100644 index 0000000..cd336bd --- /dev/null +++ b/cypress/e2e/api-website.cy.ts @@ -0,0 +1,198 @@ +import { uuid } from '../../src/lib/crypto'; + +describe('Website API tests', () => { + Cypress.session.clearAllSavedSessions(); + + let websiteId; + let teamId; + + before(() => { + cy.login(Cypress.env('umami_user'), Cypress.env('umami_password')); + cy.fixture('teams').then(data => { + const teamCreate = data.teamCreate; + cy.request({ + method: 'POST', + url: '/api/teams', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: teamCreate, + }).then(response => { + teamId = response.body[0].id; + expect(response.status).to.eq(200); + expect(response.body[0]).to.have.property('name', 'cypress'); + expect(response.body[1]).to.have.property('role', 'team-owner'); + }); + }); + }); + + it('Creates a website for user.', () => { + cy.fixture('websites').then(data => { + const websiteCreate = data.websiteCreate; + cy.request({ + method: 'POST', + url: '/api/websites', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: websiteCreate, + }).then(response => { + websiteId = response.body.id; + expect(response.status).to.eq(200); + expect(response.body).to.have.property('name', 'Cypress Website'); + expect(response.body).to.have.property('domain', 'cypress.com'); + }); + }); + }); + + it('Creates a website for team.', () => { + cy.request({ + method: 'POST', + url: '/api/websites', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + name: 'Team Website', + domain: 'teamwebsite.com', + teamId: teamId, + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('name', 'Team Website'); + expect(response.body).to.have.property('domain', 'teamwebsite.com'); + }); + }); + + it('Creates a website with a fixed ID.', () => { + cy.fixture('websites').then(data => { + const websiteCreate = data.websiteCreate; + const fixedId = uuid(); + cy.request({ + method: 'POST', + url: '/api/websites', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { ...websiteCreate, id: fixedId }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('id', fixedId); + expect(response.body).to.have.property('name', 'Cypress Website'); + expect(response.body).to.have.property('domain', 'cypress.com'); + + // cleanup + cy.request({ + method: 'DELETE', + url: `/api/websites/${fixedId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }); + }); + }); + }); + + it('Returns all tracked websites.', () => { + cy.request({ + method: 'GET', + url: '/api/websites', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body.data[0]).to.have.property('id'); + expect(response.body.data[0]).to.have.property('name'); + expect(response.body.data[0]).to.have.property('domain'); + }); + }); + + it('Gets a website by ID.', () => { + cy.request({ + method: 'GET', + url: `/api/websites/${websiteId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('name', 'Cypress Website'); + expect(response.body).to.have.property('domain', 'cypress.com'); + }); + }); + + it('Updates a website.', () => { + cy.fixture('websites').then(data => { + const websiteUpdate = data.websiteUpdate; + cy.request({ + method: 'POST', + url: `/api/websites/${websiteId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: websiteUpdate, + }).then(response => { + websiteId = response.body.id; + expect(response.status).to.eq(200); + expect(response.body).to.have.property('name', 'Cypress Website Updated'); + expect(response.body).to.have.property('domain', 'cypressupdated.com'); + }); + }); + }); + + it('Updates a website with only shareId.', () => { + cy.request({ + method: 'POST', + url: `/api/websites/${websiteId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { shareId: 'ABCDEF' }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('shareId', 'ABCDEF'); + }); + }); + + it('Resets a website by removing all data related to the website.', () => { + cy.request({ + method: 'POST', + url: `/api/websites/${websiteId}/reset`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('ok', true); + }); + }); + + it('Deletes a website.', () => { + cy.request({ + method: 'DELETE', + url: `/api/websites/${websiteId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + expect(response.body).to.have.property('ok', true); + }); + }); + + after(() => { + cy.deleteTeam(teamId); + }); +}); diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts new file mode 100644 index 0000000..507b1b5 --- /dev/null +++ b/cypress/e2e/login.cy.ts @@ -0,0 +1,36 @@ +describe('Login tests', () => { + beforeEach(() => { + cy.visit('/login'); + }); + + it( + 'logs user in with correct credentials and logs user out', + { + defaultCommandTimeout: 10000, + }, + () => { + cy.getDataTest('input-username').find('input').as('inputUsername').click(); + cy.get('@inputUsername').type(Cypress.env('umami_user'), { delay: 0 }); + cy.get('@inputUsername').click(); + cy.getDataTest('input-password') + .find('input') + .type(Cypress.env('umami_password'), { delay: 0 }); + cy.getDataTest('button-submit').click(); + cy.url().should('eq', Cypress.config().baseUrl + '/dashboard'); + cy.logout(); + }, + ); + + it('login with blank inputs or incorrect credentials', () => { + cy.getDataTest('button-submit').click(); + cy.contains(/Required/i).should('be.visible'); + + cy.getDataTest('input-username').find('input').as('inputUsername'); + cy.get('@inputUsername').click(); + cy.get('@inputUsername').type(Cypress.env('umami_user'), { delay: 0 }); + cy.get('@inputUsername').click(); + cy.getDataTest('input-password').find('input').type('wrongpassword', { delay: 0 }); + cy.getDataTest('button-submit').click(); + cy.contains(/Incorrect username and\/or password./i).should('be.visible'); + }); +}); diff --git a/cypress/e2e/user.cy.ts b/cypress/e2e/user.cy.ts new file mode 100644 index 0000000..24df305 --- /dev/null +++ b/cypress/e2e/user.cy.ts @@ -0,0 +1,65 @@ +describe('User tests', () => { + Cypress.session.clearAllSavedSessions(); + + beforeEach(() => { + cy.login(Cypress.env('umami_user'), Cypress.env('umami_password')); + cy.visit('/settings/users'); + }); + + it('Add a User', () => { + // add user + cy.contains(/Create user/i).should('be.visible'); + cy.getDataTest('button-create-user').click(); + cy.getDataTest('input-username').find('input').as('inputName').click(); + cy.get('@inputName').type('Test-user', { delay: 0 }); + cy.getDataTest('input-password').find('input').as('inputPassword').click(); + cy.get('@inputPassword').type('testPasswordCypress', { delay: 0 }); + cy.getDataTest('dropdown-role').click(); + cy.getDataTest('dropdown-item-user').click(); + cy.getDataTest('button-submit').click(); + cy.get('td[label="Username"]').should('contain.text', 'Test-user'); + cy.get('td[label="Role"]').should('contain.text', 'User'); + }); + + it('Edit a User role and password', () => { + // edit user + cy.get('table tbody tr') + .contains('td', /Test-user/i) + .parent() + .within(() => { + cy.getDataTest('link-button-edit').click(); // Clicks the button inside the row + }); + cy.getDataTest('input-password').find('input').as('inputPassword').click(); + cy.get('@inputPassword').type('newPassword', { delay: 0 }); + cy.getDataTest('dropdown-role').click(); + cy.getDataTest('dropdown-item-viewOnly').click(); + cy.getDataTest('button-submit').click(); + + cy.visit('/settings/users'); + cy.get('table tbody tr') + .contains('td', /Test-user/i) + .parent() + .should('contain.text', 'View only'); + + cy.logout(); + cy.url().should('eq', Cypress.config().baseUrl + '/login'); + cy.getDataTest('input-username').find('input').as('inputUsername').click(); + cy.get('@inputUsername').type('Test-user', { delay: 0 }); + cy.get('@inputUsername').click(); + cy.getDataTest('input-password').find('input').type('newPassword', { delay: 0 }); + cy.getDataTest('button-submit').click(); + cy.url().should('eq', Cypress.config().baseUrl + '/dashboard'); + }); + + it('Delete a user', () => { + // delete user + cy.get('table tbody tr') + .contains('td', /Test-user/i) + .parent() + .within(() => { + cy.getDataTest('button-delete').click(); // Clicks the button inside the row + }); + cy.contains(/Are you sure you want to delete Test-user?/i).should('be.visible'); + cy.getDataTest('button-confirm').click(); + }); +}); diff --git a/cypress/e2e/website.cy.ts b/cypress/e2e/website.cy.ts new file mode 100644 index 0000000..2dcd602 --- /dev/null +++ b/cypress/e2e/website.cy.ts @@ -0,0 +1,89 @@ +describe('Website tests', () => { + Cypress.session.clearAllSavedSessions(); + + beforeEach(() => { + cy.login(Cypress.env('umami_user'), Cypress.env('umami_password')); + }); + + it('Add a website', () => { + // add website + cy.visit('/settings/websites'); + cy.getDataTest('button-website-add').click(); + cy.contains(/Add website/i).should('be.visible'); + cy.getDataTest('input-name').find('input').as('inputUsername').click(); + cy.getDataTest('input-name').find('input').type('Add test', { delay: 0 }); + cy.getDataTest('input-domain').find('input').click(); + cy.getDataTest('input-domain').find('input').type('addtest.com', { delay: 0 }); + cy.getDataTest('button-submit').click(); + cy.get('td[label="Name"]').should('contain.text', 'Add test'); + cy.get('td[label="Domain"]').should('contain.text', 'addtest.com'); + + // clean-up data + cy.getDataTest('link-button-edit').first().click(); + cy.contains(/Details/i).should('be.visible'); + cy.getDataTest('text-field-websiteId') + .find('input') + .then($input => { + const websiteId = $input[0].value; + cy.deleteWebsite(websiteId); + }); + cy.visit('/settings/websites'); + cy.contains(/Add test/i).should('not.exist'); + }); + + it('Edit a website', () => { + // prep data + cy.addWebsite('Update test', 'updatetest.com'); + cy.visit('/settings/websites'); + + // edit website + cy.getDataTest('link-button-edit').first().click(); + cy.contains(/Details/i).should('be.visible'); + cy.getDataTest('input-name').find('input').click(); + cy.getDataTest('input-name').find('input').clear(); + cy.getDataTest('input-name').find('input').type('Updated website', { delay: 0 }); + cy.getDataTest('input-domain').find('input').click(); + cy.getDataTest('input-domain').find('input').clear(); + cy.getDataTest('input-domain').find('input').type('updatedwebsite.com', { delay: 0 }); + cy.getDataTest('button-submit').click({ force: true }); + cy.getDataTest('input-name').find('input').should('have.value', 'Updated website'); + cy.getDataTest('input-domain').find('input').should('have.value', 'updatedwebsite.com'); + + // verify tracking script + cy.get('div') + .contains(/Tracking code/i) + .click(); + cy.get('textarea').should('contain.text', Cypress.config().baseUrl + '/script.js'); + + // clean-up data + cy.get('div') + .contains(/Details/i) + .click(); + cy.contains(/Details/i).should('be.visible'); + cy.getDataTest('text-field-websiteId') + .find('input') + .then($input => { + const websiteId = $input[0].value; + cy.deleteWebsite(websiteId); + }); + cy.visit('/settings/websites'); + cy.contains(/Add test/i).should('not.exist'); + }); + + it('Delete a website', () => { + // prep data + cy.addWebsite('Delete test', 'deletetest.com'); + cy.visit('/settings/websites'); + + // delete website + cy.getDataTest('link-button-edit').first().click(); + cy.contains(/Data/i).should('be.visible'); + cy.get('div').contains(/Data/i).click(); + cy.contains(/All website data will be deleted./i).should('be.visible'); + cy.getDataTest('button-delete').click(); + cy.contains(/Type DELETE in the box below to confirm./i).should('be.visible'); + cy.get('input[name="confirm"').type('DELETE'); + cy.get('button[type="submit"]').click(); + cy.contains(/Delete test/i).should('not.exist'); + }); +}); diff --git a/cypress/fixtures/teams.json b/cypress/fixtures/teams.json new file mode 100644 index 0000000..4c001e6 --- /dev/null +++ b/cypress/fixtures/teams.json @@ -0,0 +1,8 @@ +{ + "teamCreate": { + "name": "cypress" + }, + "teamUpdate": { + "name": "cypressUpdate" + } +} diff --git a/cypress/fixtures/users.json b/cypress/fixtures/users.json new file mode 100644 index 0000000..235f0d6 --- /dev/null +++ b/cypress/fixtures/users.json @@ -0,0 +1,11 @@ +{ + "userCreate": { + "username": "cypress1", + "password": "password", + "role": "user" + }, + "userUpdate": { + "username": "cypress1", + "role": "view-only" + } +} diff --git a/cypress/fixtures/websites.json b/cypress/fixtures/websites.json new file mode 100644 index 0000000..0b81788 --- /dev/null +++ b/cypress/fixtures/websites.json @@ -0,0 +1,10 @@ +{ + "websiteCreate": { + "name": "Cypress Website", + "domain": "cypress.com" + }, + "websiteUpdate": { + "name": "Cypress Website Updated", + "domain": "cypressupdated.com" + } +} diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 0000000..a95035b --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,123 @@ +/// <reference types="cypress" /> +import { uuid } from '../../src/lib/crypto'; + +Cypress.Commands.add('getDataTest', (value: string) => { + return cy.get(`[data-test=${value}]`); +}); + +Cypress.Commands.add('logout', () => { + cy.getDataTest('button-profile').click(); + cy.getDataTest('item-logout').click(); + cy.url().should('eq', Cypress.config().baseUrl + '/login'); +}); + +Cypress.Commands.add('login', (username: string, password: string) => { + cy.session([username, password], () => { + cy.request({ + method: 'POST', + url: '/api/auth/login', + body: { + username, + password, + }, + }) + .then(response => { + Cypress.env('authorization', `bearer ${response.body.token}`); + window.localStorage.setItem('umami.auth', JSON.stringify(response.body.token)); + }) + .its('status') + .should('eq', 200); + }); +}); + +Cypress.Commands.add('addWebsite', (name: string, domain: string) => { + cy.request({ + method: 'POST', + url: '/api/websites', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + id: uuid(), + createdBy: '41e2b680-648e-4b09-bcd7-3e2b10c06264', + name: name, + domain: domain, + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); + +Cypress.Commands.add('deleteWebsite', (websiteId: string) => { + cy.request({ + method: 'DELETE', + url: `/api/websites/${websiteId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); + +Cypress.Commands.add('addUser', (username: string, password: string, role: string) => { + cy.request({ + method: 'POST', + url: '/api/users', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + username: username, + password: password, + role: role, + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); + +Cypress.Commands.add('deleteUser', (userId: string) => { + cy.request({ + method: 'DELETE', + url: `/api/users/${userId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); + +Cypress.Commands.add('addTeam', (name: string) => { + cy.request({ + method: 'POST', + url: '/api/teams', + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + body: { + name: name, + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); + +Cypress.Commands.add('deleteTeam', (teamId: string) => { + cy.request({ + method: 'DELETE', + url: `/api/teams/${teamId}`, + headers: { + 'Content-Type': 'application/json', + Authorization: Cypress.env('authorization'), + }, + }).then(response => { + expect(response.status).to.eq(200); + }); +}); diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts new file mode 100644 index 0000000..b630269 --- /dev/null +++ b/cypress/support/index.d.ts @@ -0,0 +1,56 @@ +/// <reference types="cypress" /> +/* global JQuery */ + +declare namespace Cypress { + interface Chainable { + /** + * Custom command to select DOM element by data-test attribute. + * @example cy.getDataTest('greeting') + */ + getDataTest(value: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to logout through UI. + * @example cy.logout() + */ + logout(): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to login user into the app. + * @example cy.login('admin', 'password) + */ + login(username: string, password: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to create a website + * @example cy.addWebsite('test', 'test.com') + */ + addWebsite(name: string, domain: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to delete a website + * @example cy.deleteWebsite('02d89813-7a72-41e1-87f0-8d668f85008b') + */ + deleteWebsite(websiteId: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to create a website + * @example cy.deleteWebsite('02d89813-7a72-41e1-87f0-8d668f85008b') + */ + /** + * Custom command to create a user + * @example cy.addUser('cypress', 'password', 'User') + */ + addUser(username: string, password: string, role: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to delete a user + * @example cy.deleteUser('02d89813-7a72-41e1-87f0-8d668f85008b') + */ + deleteUser(userId: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to create a team + * @example cy.addTeam('cypressTeam') + */ + addTeam(name: string): Chainable<JQuery<HTMLElement>>; + /** + * Custom command to create a website + * @example cy.deleteTeam('02d89813-7a72-41e1-87f0-8d668f85008b') + */ + deleteTeam(teamId: string): Chainable<JQuery<HTMLElement>>; + } +} diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000..48c3e14 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom"], + "types": ["cypress", "node"] + }, + "include": ["**/*.ts", "../cypress.config.ts"] +} |