diff --git a/.github/workflows/cypress-test-run.yml b/.github/workflows/cypress-test-run.yml new file mode 100644 index 000000000..81b99fc5d --- /dev/null +++ b/.github/workflows/cypress-test-run.yml @@ -0,0 +1,34 @@ +name: Cypress Test Run + +on: + push: + branches: + - main + +jobs: + build: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install dependencies + run: npm install + - name: setup local server + run: start npm start + - name: Run Cypress tests + run: npx cypress run + - uses: cypress-io/github-action@v2 + - uses: actions/upload-artifact@v2 + if: always() + with: + name: cypress-videos + path: cypress/videos + + +# - name: "Upload screenshots to Slack" +# uses: trymbill/cypress-slack-video-upload-action@v1.3.0 +# if: failure() +# with: +# token: ${{ secrets.SLACK_TOKEN }} +# channels: "_tolk" +# message-text: "Cypress tests failed! They have been placed in this thread, good luck."R \ No newline at end of file diff --git a/.github/workflows/run-cypress.yml b/.github/workflows/run-cypress.yml deleted file mode 100644 index 16af3e6fa..000000000 --- a/.github/workflows/run-cypress.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Run tests manually -on: workflow_dispatch -jobs: - cypress-run: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - containers: [1, 2, 3] - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Cypress run - uses: cypress-io/github-action@v4 - with: - start: npm start - record: true - parallel: true - env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Codecov - uses: codecov/codecov-action@v2 - with: - token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml deleted file mode 100644 index 2b067675a..000000000 --- a/.github/workflows/static.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Lint checks -on: [push] -jobs: - static-checks: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - uses: bahmutov/npm-install@v1 - with: - useLockFile: false - - run: npm run selectorcheck - - run: npm run typecheck - - run: npm run lint diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index a48305f71..000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: e2e-tests -on: [pull_request] -jobs: - cypress-run: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - containers: [1, 2, 3] - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Cypress run - uses: cypress-io/github-action@v4 - with: - start: npm start - record: true - parallel: true - env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Codecov - uses: codecov/codecov-action@v2 - with: - token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0f36b718a..a863bfe23 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ coverage cypress/screenshots cypress/videos cypress/downloads -backend/data/uploaded/* .env .vscode/* diff --git a/README.md b/README.md index 68c2c724b..531298ff6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,7 @@ -## Trello clone app written in Vue 3 + Typescript + Vite + TailwindCSS - -[![trello app](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/qmz9cz&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/qmz9cz/runs) [![codecov](https://codecov.io/gh/filiphric/trelloapp-vue-vite-ts/branch/main/graph/badge.svg?token=0CFFEB154E)](https://codecov.io/gh/filiphric/trelloapp-vue-vite-ts) - -

- -

- -This is a second version of [Trello clone](https://github.com/filiphric/trelloapp) app, which I use for my Cypress.io workshops. I create this to explain and showcase Cypress capabilities, much like [Real world application](https://github.com/cypress-io/cypress-realworld-app) by Cypress. - -To install, simply clone this project and - -1. `npm install` -2. `npm start` - -What you can see here is pretty much still work in progress and far from done. This is my Playground, don’t judge the code quality. +To run + +npm install + +npm start + +npm cypress run diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..9701a0205 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,20 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- main + +pool: + vmImage: ubuntu-latest + +steps: +- task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + +- script: | + npm install + displayName: 'npm install' diff --git a/backend/data/uploaded/3_chrome_q4SL915vQ0.png b/backend/data/uploaded/3_chrome_q4SL915vQ0.png new file mode 100644 index 000000000..e4a9b6dc9 Binary files /dev/null and b/backend/data/uploaded/3_chrome_q4SL915vQ0.png differ diff --git a/cypress.config.ts b/cypress.config.ts index 7aae0b8ab..eb5676a65 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,37 +1,17 @@ -import { defineConfig } from 'cypress' -import constants from './constants' -const { APP } = constants +import { defineConfig } from "cypress"; export default defineConfig({ - env: { - coverage: true, - }, - retries: { - openMode: 0, - runMode: 1, - }, - videoUploadOnPasses: false, - viewportHeight: 550, - viewportWidth: 700, - projectId: 'qmz9cz', + video: true, + projectId: 'm2fvqo', e2e: { - // We've imported your old cypress plugins here. - // You may want to clean this up later by importing these. + specPattern: "**/e2e/*/*.spec.ts", + baseUrl: 'http://localhost:3000', + reporter: 'spec', setupNodeEvents(on, config) { - return require('./cypress/plugins/index.ts')(on, config) - }, - baseUrl: `http://localhost:${APP}`, - specPattern: 'cypress/e2e/**/*.spec.ts', - }, - component: { - devServer: { - framework: 'vue', - bundler: 'vite', - }, - setupNodeEvents(on, config) { }, - env: { - coverage: false, + // implement node event listeners here + require('@cypress/grep/src/plugin')(config); + return config; }, - specPattern: 'src/**/*.spec.ts', + }, -}) +}); diff --git a/cypress/e2e/board.spec.ts b/cypress/e2e/board.spec.ts deleted file mode 100644 index 3d7632912..000000000 --- a/cypress/e2e/board.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ - -it('board actions', () => { - cy.request('POST', '/api/reset'); - cy.intercept({ - method: 'POST', - url: '/api/boards', - times: 3 - }).as('createBoard'); - cy.visit('/'); - - cy.step('create first board') - cy.getDataCy('first-board').type('new board{enter}'); - cy.wait('@createBoard').then(({ response }) => { - cy.location('pathname') - .should('eq', `/board/${response!.body.id}`); - }); - cy.getDataCy('board-detail').should('be.visible'); - cy.getDataCy('loading').should('not.exist'); - cy.getDataCy('home').click(); - - cy.step('star board') - cy.intercept('PATCH', '/api/boards/*').as('starBoard'); - cy.getDataCy('board-item').should('be.visible'); - cy.getDataCy('starred-boards').should('not.exist'); - cy.getDataCy('board-item').trigger('mouseover'); - cy.getDataCy('star').click(); - cy.wait('@starBoard') - .its('request.body.starred') - .should('be.true'); - cy.getDataCy('starred-boards').should('be.visible'); - - cy.step('board create field') - cy.getDataCy('create-board').click(); - cy.getDataCy('board-list').click('bottomRight'); - cy.getDataCy('new-board-input').should('not.be.visible'); - cy.getDataCy('create-board').click(); - cy.getDataCy('new-board-input').type('{enter}'); - cy.getDataCy('create-board').click(); - cy.getDataCy('cancel').click(); - cy.location('pathname').should('eq', '/'); - cy.getDataCy('board-item').should('have.length', 1); - cy.getDataCy('create-board').should('be.visible'); - - cy.step('create second board with enter key') - cy.getDataCy('create-board').click(); - cy.getDataCy('new-board-input').should('be.focused'); - cy.getDataCy('new-board-input').type('new board{enter}'); - cy.wait('@createBoard').then(({ response }) => { - cy.location('pathname') - .should('eq', `/board/${response!.body.id}`); - }); - cy.getDataCy('board-detail').should('be.visible'); - cy.getDataCy('loading').should('not.exist'); - - cy.getDataCy('trello-logo').click(); - - cy.step('create third board with click') - cy.getDataCy('create-board').click(); - cy.getDataCy('new-board-input').type('new board'); - cy.getDataCy('new-board-create').click(); - cy.wait('@createBoard').then(({ response }) => { - cy.location('pathname').should('eq', `/board/${response!.body.id}`); - }); - cy.getDataCy('board-detail').should('be.visible'); - cy.getDataCy('loading').should('not.exist'); - - cy.go('back') - - cy.step('show error message on network error') - cy.intercept({ - method: 'POST', - url: '/api/boards', - times: 1 - }, { - statusCode: 500 - }).as('createBoard'); - cy.clock(); - cy.getDataCy('create-board').click(); - cy.getDataCy('new-board-input').type('new{enter}'); - cy.getDataCy('notification-message').should('be.visible'); - cy.tick(4000); - cy.getDataCy('notification-message').should('not.exist'); - cy.location('pathname').should('eq', '/'); - cy.getDataCy('board-item').should('have.length', 3); - cy.getDataCy('new-board-input').should('be.visible'); - - - cy.step('shows network error when board are not loaded') - cy.intercept({ - url:'/api/boards', - times: 1 - }, { - statusCode: 404 - }) - cy.reload() - cy.getDataCy('board-list-error-message') - .should('contain.text', 'There was an error loading your boards') - - cy.contains('Try again').click() - - cy.location('pathname').should('eq', '/') - cy.getDataCy('board-item').should('be.visible') -}); \ No newline at end of file diff --git a/cypress/e2e/boardDetail.spec.ts b/cypress/e2e/boardDetail.spec.ts deleted file mode 100644 index fe15922a2..000000000 --- a/cypress/e2e/boardDetail.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -const oldName = 'board'; -const newName = 'new board'; - -beforeEach(() => { - cy.request('POST', '/api/reset'); - cy.addBoardApi(oldName); -}); - -it('board actions', function() { - - const boardId = this.board.id; - - cy.visit(`/board/${boardId}`); - - cy.step('rename cancel') - cy.getDataCy('board-title').should('have.value', oldName); - cy.getDataCy('board-title').click(); - cy.getDataCy('board-detail').click(); - cy.getDataCy('board-title').type('{esc}'); - cy.getDataCy('board-title').should('have.value', oldName); - - cy.step('renames of a board') - cy.intercept('PATCH', '/api/boards/*').as('boardChange'); - cy.getDataCy('board-title') - .clear() - .type(`${newName}{enter}`); - cy.wait('@boardChange') - .its('request.body.name') - .should('eq', newName); - cy.getDataCy('board-title').should('have.value', newName); - - cy.step('star board') - cy.getDataCy('star').click() - cy.wait('@boardChange') - .its('request.body.starred') - .should('be.true') - - cy.step('dropdown actions') - cy.getDataCy('board-options').click() - cy.getDataCy('board-dropdown').should('be.visible') - cy.getDataCy('cancel').click() - cy.getDataCy('board-dropdown').should('not.exist') - cy.getDataCy('board-options').click() - cy.getDataCy('board-dropdown').should('be.visible') - cy.root().click() - cy.getDataCy('board-dropdown').should('not.exist') - - cy.step('delete board') - cy.intercept('DELETE', '/api/boards/*').as('boardDelete'); - cy.getDataCy('board-options').click() - cy.getDataCy('delete-board').click() - cy.wait('@boardDelete') - .its('response.statusCode') - .should('eq', 200) - cy.getDataCy('board-dropdown').should('not.exist') - cy.location('pathname').should('eq', '/') - -}); diff --git a/cypress/e2e/card.spec.ts b/cypress/e2e/card.spec.ts deleted file mode 100644 index 974abc14d..000000000 --- a/cypress/e2e/card.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -beforeEach(() => { - cy.request('POST', '/api/reset'); - cy.addBoardApi('new board'); -}); - -it('card actions', function() { - - const boardId = this.board.id - - cy.intercept('POST', '/api/cards').as('createCard'); - cy.addListApi({name: 'list 1'}) - cy.addListApi({name: 'list 2'}) - cy.visit(`/board/${boardId}`); - - cy.step('card create cancel') - // esc key - cy.getDataCy('new-card').eq(0).click(); - cy.getDataCy('new-card-input').type('new card{esc}'); - cy.getDataCy('new-card-input').should('not.exist'); - cy.getDataCy('card').should('not.exist'); - // click away - cy.getDataCy('new-card').eq(0).click(); - cy.getDataCy('board-detail').click(); - cy.getDataCy('new-card-input').should('not.exist'); - cy.getDataCy('card').should('not.exist'); - // enter no input - cy.getDataCy('new-card').eq(0).click(); - cy.getDataCy('new-card-input').type('{enter}'); - cy.getDataCy('card').should('not.exist'); - // cancel button - cy.getDataCy('cancel').click(); - cy.getDataCy('new-card-input').should('not.exist'); - cy.getDataCy('card').should('not.exist'); - - cy.step('create card') - cy.getDataCy('new-card').eq(0).click(); - cy.getDataCy('new-card-input').eq(0).type('card 1{enter}'); - cy.wait('@createCard') - .its('request.body.name') - .should('eq', 'card 1'); - cy.getDataCy('card').should('be.visible'); - - cy.step('card edit icon') - cy.getDataCy('card').realHover(); - cy.getDataCy('card-edit').should('be.visible'); - - cy.step('card complete') - cy.intercept('PATCH', '/api/cards/*').as('patchCard'); - cy.getDataCy('card-checkbox').check(); - cy.wait('@patchCard') - .its('request.body.completed') - .should('be.true'); - cy.getDataCy('card-checkbox').uncheck(); - cy.wait('@patchCard') - .its('request.body.completed') - .should('be.false'); - - cy.step('uses dropdown to create card') - cy.getDataCy('list-options').eq(1).click(); - cy.getDataCy('card-add').click(); - cy.getDataCy('new-card-input') - .should('be.visible') - .should('be.focused'); - cy.getDataCy('new-card-input').type('card 2{enter}'); - cy.wait('@createCard') - .its('request.body.name') - .should('eq', 'card 2'); - cy.getDataCy('card').should('have.length', 2); - cy.getDataCy('new-card-input').type('{esc}'); - - cy.step('card move') - cy.getDataCy('card-list').eq(0).as('list1') - cy.getDataCy('card-list').eq(1).as('list2') - cy.getDataCy('card').eq(0).drag('@list2') - - cy.step('card create error') - cy.intercept('POST', '/api/cards', { forceNetworkError: true }).as('createCard'); - cy.getDataCy('new-card').eq(0).click(); - cy.getDataCy('new-card-input').type('new card{enter}'); - cy.getDataCy('notification-message').should('be.visible').and('contain.text', 'Card was not created') - -}); diff --git a/cypress/e2e/cardDetail.spec.ts b/cypress/e2e/cardDetail.spec.ts deleted file mode 100644 index caa53f422..000000000 --- a/cypress/e2e/cardDetail.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -beforeEach(() => { - cy.request('POST', '/api/reset'); - cy.addBoardApi('new board') - .addListApi({ name: 'new list' }) - .addCardApi({ name: 'new card' }); -}); - -it('card detail actions', function() { - - const boardId = this.board.id - const cardId = this.card.id - const card = this.card - - cy.intercept('PATCH', '/api/cards/*').as('updateCard'); - cy.intercept('DELETE', '/api/cards/*').as('deleteCard'); - cy.visit(`/board/${boardId}?card=${cardId}`); - - cy.step('closing and opening card') - cy.getDataCy('card-detail').should('be.visible'); - cy.getDataCy('card-detail-backdrop').click('topRight'); - cy.getDataCy('card-detail').should('not.exist'); - cy.getDataCy('card').click(); - cy.getDataCy('card-detail').should('be.visible'); - cy.getDataCy('cancel').click() - cy.getDataCy('card-detail').should('not.exist'); - cy.getDataCy('card').click(); - - cy.step('card properties') - cy.getDataCy('copy-properties').realClick(); - cy.window().its('navigator.clipboard') - .invoke('readText').should('eq', JSON.stringify(card, null, 2)); - cy.getDataCy('notification-message') - .should('exist') - .and('contain.text', 'Card info copied to clipboard'); - - cy.step('card rename') - cy.getDataCy('card-detail-title').click(); - cy.getDataCy('card-detail-title').type('new name{enter}'); - cy.wait('@updateCard').its('request.body.name').should('eq', 'new name'); - cy.getDataCy('card-detail-title').should('have.value', 'new name'); - cy.getDataCy('card-detail-title').type('{esc}'); - cy.getDataCy('card-detail-title').should('have.value', 'new name'); - cy.getDataCy('notification-message') - .should('exist') - .and('contain.text', 'Card was renamed'); - - cy.step('card deadline hide') - cy.getDataCy('calendar-dropdown').click(); - cy.getDataCy('card-detail-deadline').should('be.visible'); - cy.getDataCy('calendar-dropdown').click(); - cy.getDataCy('card-detail-deadline').should('not.exist'); - - cy.step('card deadline hide') - cy.getDataCy('calendar-button').click(); - cy.getDataCy('card-detail-deadline').should('be.visible'); - cy.getDataCy('header-year').click(); - cy.contains('[data-cy=year]', '2021').click(); - cy.getDataCy('header-month').click(); - cy.contains('[data-cy=month]', 'Aug').click(); - cy.contains('[data-cy=day]', '15').click(); - cy.wait('@updateCard') - .its('response.body.deadline') - .should('eq', '2021-08-15'); - - cy.step('card description') - cy.getDataCy('card-description').type('new description{enter}'); - cy.wait('@updateCard').its('request.body').should('have.property', 'description', 'new description'); - - cy.step('image upload') - cy.intercept({ - method: 'POST', - url: '/api/upload?card=*', - }).as('imageUpload'); - cy.getDataCy('upload-image').selectFile('cypress/fixtures/cypressLogo.png', { action: 'drag-drop' }); - cy.wait('@imageUpload').its('response.body').should('have.property', 'image').and('not.be.empty'); - cy.getDataCy('image-attachment').should('exist'); - cy.getDataCy('notification-message').should('exist').and('contain.text', 'File was sucessfully uploaded'); - cy.getDataCy('image-delete').click(); - cy.wait('@updateCard').its('response.body.image').should('be.null'); - cy.getDataCy('image-attachment').should('not.exist'); - cy.getDataCy('upload-image').should('be.visible'); - - cy.step('error when upload does not work') - cy.intercept({ - method: 'POST', - url: '/api/upload?card=*' - }, { - statusCode: 400 - }).as('imageUpload'); - cy.getDataCy('upload-image').selectFile('cypress/fixtures/cypressLogo.png', { action: 'drag-drop' }); - cy.getDataCy('notification-message').should('exist').and('contain.text', 'There was an error uploading file'); - - cy.step('delete a card') - cy.getDataCy('card-detail-delete').click(); - cy.wait('@deleteCard').its('response.statusCode').should('eq', 200); - cy.getDataCy('card-detail').should('not.exist'); - cy.getDataCy('notification-message').should('exist').and('contain.text', 'Card was deleted'); - -}); \ No newline at end of file diff --git a/cypress/e2e/google.spec.ts b/cypress/e2e/google.spec.ts deleted file mode 100644 index 0beb34004..000000000 --- a/cypress/e2e/google.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { onlyOn } from '@cypress/skip-test' - -beforeEach( () => { - cy.request('POST', '/api/reset') -}) - -it('Google SSO signup and login', () => { - - onlyOn(Cypress.env('googleEnabled') === 'true') - - cy.step('Signup with Google') - cy.googleSignup() - - cy.visit('/') - - cy.step('User is logged in') - cy.getDataCy('logged-user') - .should('contain.text', 'filip@filiphric.sk') - - cy.step('create board') - cy.getDataCy('first-board') - .type('google board{enter}') - - cy.step('go back to home page') - cy.go('back') - - cy.step('board is visible') - cy.getDataCy('board-item') - .should('be.visible') - - cy.step('logout') - cy.getDataCy('logged-user') - .click() - - cy.step('board disappears from view') - cy.getDataCy('board-item') - .should('not.exist') - - cy.googleLogin() - cy.reload() - - cy.step('board is visible') - cy.getDataCy('board-item') - .should('be.visible') - -}) \ No newline at end of file diff --git a/cypress/e2e/list.spec.ts b/cypress/e2e/list.spec.ts deleted file mode 100644 index 18d023cb0..000000000 --- a/cypress/e2e/list.spec.ts +++ /dev/null @@ -1,86 +0,0 @@ -beforeEach(() => { - cy.request('POST', '/api/reset'); - cy.addBoardApi('new board'); -}); - -it('list actions', function() { - - const list1 = 'list1' - const list2 = 'list2' - const boardId = this.board.id - - cy.intercept({ - method: 'POST', - url:'/api/lists', - times: 1 - }, { - statusCode: 500 - }).as('createList'); - - cy.visit(`/board/${boardId}`); - - cy.step('show error message') - cy.getDataCy('add-list-input').type('new list{enter}'); - cy.getDataCy('notification-message').should('be.visible').and('contain.text', 'List was not created') - - cy.step('does not accept empty list names') - cy.getDataCy('add-list-input').type('{enter}'); - cy.getDataCy('list').should('not.exist'); - cy.getDataCy('add-list-input').should('be.focused'); - - cy.step('cancels list creation') - cy.getDataCy('add-list-input').type(`${list1}{esc}`); - cy.getDataCy('list').should('not.exist'); - cy.getDataCy('add-list-input').should('not.exist'); - - cy.step('cancel button') - cy.getDataCy('create-list').click(); - cy.getDataCy('cancel').click(); - cy.getDataCy('list').should('not.exist'); - cy.getDataCy('add-list-input').should('not.exist'); - - cy.step('click away') - cy.getDataCy('create-list').click(); - cy.getDataCy('add-list-input').click(); - cy.getDataCy('board-detail').click(); - - cy.step('create a list') - cy.getDataCy('create-list').click(); - cy.getDataCy('add-list-input').should('be.focused').type(`${list1}{enter}`); - cy.contains('Add list').click(); - cy.getDataCy('list').should('have.length', 1); - - cy.step('rename list') - cy.getDataCy('list-name').type('renamed list{enter}'); - cy.getDataCy('list-name').should('have.value', 'renamed list'); - - cy.step('open and close dropdown ') - cy.getDataCy('list-options').click(); - cy.getDataCy('list-dropdown').should('be.visible'); - cy.getDataCy('cancel').click(); - cy.getDataCy('list-options').click(); - cy.getDataCy('board-detail').click('bottomRight'); - cy.getDataCy('list-dropdown').should('not.exist'); - - cy.step('delete list') - cy.getDataCy('list-options').click(); - cy.getDataCy('delete-list').click(); - cy.getDataCy('list').should('not.exist'); - - cy.step('create first list') - cy.getDataCy('create-list').click(); - cy.getDataCy('add-list-input').type(`${list1}{enter}`); - cy.getDataCy('list').should('have.length', 1); - - cy.step('create second list') - cy.getDataCy('add-list-input').type(`${list2}{enter}`); - cy.getDataCy('list').should('have.length', 2); - - cy.step('reorder lists') - cy.getDataCy('list-name').eq(0).as('list1').should('have.value', list1); - cy.getDataCy('list-name').eq(1).as('list2').should('have.value', list2); - cy.getDataCy('list').eq(0).drag('[data-cy=list-placeholder]:nth-child(2)', { force: true }); - cy.get('@list2').should('have.value', list1); - cy.get('@list1').should('have.value', list2); - -}); diff --git a/cypress/e2e/login.spec.ts b/cypress/e2e/login.spec.ts deleted file mode 100644 index 4bb20c282..000000000 --- a/cypress/e2e/login.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -describe('Login', () => { - - const user = { - email: 'filip@example.com', - password: 'Asdf.1234#' - } - - beforeEach(() => { - - cy.request('POST', '/api/reset') - cy.signupApi({ login: false, ...user }) - - }); - - it('logs in a user (click)', () => { - - cy.visit('/') - - cy.getDataCy('login-menu') - .should('be.visible') - .click() - - cy.location('pathname') - .should('eq', '/login') - - cy.getDataCy('login-email') - .type(user.email) - - cy.getDataCy('login-password') - .type('invalid{enter}') - - cy.getDataCy('notification-message') - .should('contain.text', 'Incorrect password') - - cy.location('pathname') - .should('eq', '/') - - cy.go('back') - - cy.getDataCy('login-password') - .clear() - .type(user.password) - - cy.getDataCy('login-submit') - .click() - - cy.location('pathname') - .should('eq', '/') - - cy.getCookie('auth_token') - .should('exist') - - cy.getDataCy('logged-user') - .click() - - cy.getDataCy('notification-message') - .should('contain.text', 'User was logged out') - - }); - - it('logs in a user (enter)', () => { - - cy.visit('/') - - cy.getDataCy('login-menu') - .should('be.visible') - .click() - - cy.location('pathname') - .should('eq', '/login') - - cy.getDataCy('login-email') - .type(user.email) - - cy.getDataCy('login-password') - .type(`${user.password}{enter}`) - - cy.location('pathname') - .should('eq', '/') - - cy.getCookie('auth_token') - .should('exist') - - cy.getDataCy('logged-user') - .click() - - cy.getDataCy('notification-message') - .should('contain.text', 'User was logged out') - - }); - - it('shows error on invalid login', () => { - - cy.setCookie('auth_token', 'invalid') - - cy.visit('/login') - - cy.getDataCy('notification-message') - .should('contain.text', 'Invalid authorization') - - }) - -}); \ No newline at end of file diff --git a/cypress/e2e/main.spec.ts b/cypress/e2e/main.spec.ts deleted file mode 100644 index 525af1410..000000000 --- a/cypress/e2e/main.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -describe('main page', () => { - - beforeEach(() => { - cy.addBoardApi('new board') - }); - - it('has 404 page', function() { - - const boardId = this.board.id - - cy.visit('/board/9999999999'); - cy.getDataCy('board-list-error-message').should('be.visible'); - - cy.contains('Go back home').click() - cy.location('pathname').should('eq', '/') - - cy.visit(`/board/${boardId}?card=1`); - cy.getDataCy('notification-message').should('contain.text', 'Card with id: 1 was not found') - - }); - - -}); diff --git a/cypress/e2e/pricing.spec.ts b/cypress/e2e/pricing.spec.ts deleted file mode 100644 index 40ef59d02..000000000 --- a/cypress/e2e/pricing.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -it('shows pricing', () => { - - cy.intercept({ - method: 'GET', - url: '/api/location', - times: 1 - }, { - location: 'us', - currency: 'USD', - discountEligible: false - }).as('locationUS') - - cy.visit('/pricing') - cy.wait('@locationUS') - - cy.getDataCy('plan-item').eq(1).should('have.class', 'border-blue6') - cy.getDataCy('plan-item').eq(0).click().should('have.class', 'border-blue6') - cy.getDataCy('plan-item').eq(1).should('not.have.class', 'border-blue6') - - // USD - cy.getDataCy('plan-price').should('contain', '$') - - // GBP - cy.intercept({ - method: 'GET', - url: '/api/location', - times: 1 - }, { - location: 'uk', - currency: 'GBP', - discountEligible: false - }).as('locationUK') - - - cy.reload() - cy.wait('@locationUK') - - cy.getDataCy('plan-price').should('contain', '£') - - // EUR - cy.intercept({ - method: 'GET', - url: '/api/location', - times: 1 - }, { - location: 'sk', - currency: 'EUR', - discountEligible: true, - discountAmount: 20 - }).as('locationEU') - - - cy.reload() - cy.wait('@locationEU') - - cy.getDataCy('plan-price').should('contain', '€') - cy.getDataCy('discount').should('be.visible').and('contain', '20%') - -}); - -it('shows map', () => { - - cy.visit('/pricing', { - onBeforeLoad (win) { - // e.g., force Barcelona geolocation - const latitude = 41.38879; - const longitude = 2.15899; - cy.stub(win.navigator.geolocation, 'getCurrentPosition').callsFake((cb) => { - return cb({ coords: { latitude, longitude } }); - }); - } - }) - - cy.getDataCy('find-location') - .click() - - cy.get('#map') - .should('be.visible') - -}) \ No newline at end of file diff --git a/cypress/e2e/search.spec.ts b/cypress/e2e/search.spec.ts deleted file mode 100644 index 4bb30e2f5..000000000 --- a/cypress/e2e/search.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -beforeEach(() => { - cy.request('POST', '/api/reset'); - cy.addBoardApi('new board').its('id').as('boardId') - .addListApi({ name: 'new list' }) - .addCardApi({ name: 'new card' }).its('id').as('cardId'); -}); - -it('performs a card search', function() { - - cy.visit('/') - - cy.window().invoke('store').invoke('toggleSearch', true) - - cy.getDataCy('search-input').type('new card') - cy.getDataCy('result-item').contains('new card').click() - - cy.location('href').should('include', `/board/${this.boardId}?card=${this.cardId}`) - - cy.window().invoke('store').invoke('toggleSearch', true) - cy.getDataCy('search-input').type('n') - cy.getDataCy('result-item').should('be.visible') - cy.getDataCy('search-input').type('{backspace}') - -}); \ No newline at end of file diff --git a/cypress/e2e/signup.spec.ts b/cypress/e2e/signup.spec.ts deleted file mode 100644 index f4d2abe04..000000000 --- a/cypress/e2e/signup.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -describe('Signup', () => { - - beforeEach(() => { - cy.request('POST', '/api/reset') - }); - - it('sign up a user (click)', () => { - - const user = { - email: 'filip@example.com', - password: 'Asdf.1234#' - } - - cy.visit('/signup') - - cy.getDataCy('signup-email') - .type(user.email) - - cy.getDataCy('signup-password') - .type(user.password) - - cy.getDataCy('signup-submit') - .click() - - cy.location('pathname') - .should('eq', '/') - - cy.getCookie('auth_token') - .should('exist') - - }); - - it('sign up a user (enter)', () => { - - const user = { - email: 'filip@example.com', - password: 'Asdf.1234#' - } - - cy.visit('/signup') - - cy.getDataCy('signup-email') - .type(user.email) - - cy.getDataCy('signup-password') - .type(`${user.password}{enter}`) - - cy.location('pathname') - .should('eq', '/') - - cy.getCookie('auth_token') - .should('exist') - - }); -}); \ No newline at end of file diff --git a/cypress/e2e/specs/boards.spec.ts b/cypress/e2e/specs/boards.spec.ts new file mode 100644 index 000000000..aa6b821c7 --- /dev/null +++ b/cypress/e2e/specs/boards.spec.ts @@ -0,0 +1,84 @@ +beforeEach(() => { + //seed the database with boards + cy.fixture('users').then((fixture) => { + cy.request('POST', '/api/signup', fixture[0]).then(function (response) { + const auth = response.body.accessToken; + cy.seedData('boards', undefined, auth); + }); + }); + + //Given I am logged in as a valid user + cy.visit('/login'); + cy.loginAs('Existinguser@test.com', 'Existinguser123'); +}); + +describe('Boards control: as a valid user', () => { + it('I can create a board', () => { + //Given I click on the create board button + cy.get('[data-cy="create-board"]').click(); + //When I enter the name of the board in the title field + cy.get('[data-cy="new-board-input"]').type('New Board'); + cy.get('[data-cy="new-board-create"]').click(); + //Then I should see the newly created board + cy.get('[data-cy="board-title"]').should('have.text', 'New Board'); + cy.url().should('include', '/board/'); + }); + + it('I can delete a board', () => { + //Given I navigate to a board + cy.get('[data-cy="board-item"]').filter(':contains("Portfolio Kanban")').click(); + //When I click on the delete board button in the options menu + cy.get('[data-cy="board-options"]').click(); + cy.get('[data-cy="delete-board"]').click(); + //Then I should see the board is deleted + cy.notificationEquals('Board was deleted'); + }); + + it('I can favorite a board', () => { + //Given I navigate to a board + cy.get('[data-cy="board-item"]').filter(':contains("Portfolio Kanban")').click(); + //When I click on the favorite button + cy.get('[data-cy="star"]').click(); + //Then I should see the board is favorited + cy.get('.text-yellow-300').should('exist'); + cy.get('[data-cy="home"]').click(); + cy.get('[data-cy="starred-section"]').filter(':contains("Portfolio Kanban")').should('exist'); + }); + + it('I can unfavorite a board', () => { + //Given I have favorited a board + cy.log('Setting up the test') + cy.fixture('users').then((fixture) => { + cy.request('POST', '/api/login', fixture[0]).then(function (response) { + const auth = response.body.accessToken; + cy.request({ + method: 'PATCH', + url: '/api/boards/1', + headers: { authorization: `Bearer ${auth}` }, + body: { "starred": true }, + }) + }); + }); + cy.reload(); + //And I navigate to the board + cy.get('[data-cy="starred-section"]').find('[data-cy="board-item"]').filter(':contains("Portfolio Kanban")').click(); + //When I click on the favorite button + cy.get('[data-cy="star"]').click(); + //Then I should see the board is unfavorited + cy.get('[data-cy="star"]').filter('.text-white').should('exist'); + cy.get('[data-cy="home"]').click(); + cy.get('[data-cy="starred-section"]').should('not.exist'); + cy.get('[data-cy="all-boards"]').filter(':contains("Portfolio Kanban")').should('exist'); + }); + + it('I can modify the name of a board', () => { + //Given I am on a board page + cy.get('[data-cy="board-item"]').filter(':contains("Portfolio Kanban")').click(); + //When I make a change to the board name + cy.get('input[name="board-title"]').click().clear().type('RotaMaster Kanban').type('{enter}'); + //Then I should see the board name is updated + cy.get('[data-cy="board-title"]').should('have.text', 'RotaMaster Kanban'); + cy.get('[data-cy="home"]').click(); + cy.get('[data-cy="board-item"]').filter(':contains("RotaMaster Kanban")').should('exist'); + }); +}); \ No newline at end of file diff --git a/cypress/e2e/specs/cards.spec.ts b/cypress/e2e/specs/cards.spec.ts new file mode 100644 index 000000000..806ae881e --- /dev/null +++ b/cypress/e2e/specs/cards.spec.ts @@ -0,0 +1,96 @@ +beforeEach(() => { + //seed the database with boards, lists and cards + cy.fixture('users').then((fixture: any) => { + cy.request('POST', '/api/signup', fixture[0]).then(function (response) { + const auth = response.body.accessToken; + cy.seedData('boards', undefined, auth); + cy.seedData('lists', undefined, auth); + cy.seedData('cards', undefined, auth); + }); + }); + + // Given I am logged in as a valid user + cy.visit('/login'); + cy.loginAs('Existinguser@test.com', 'Existinguser123'); +}); + +describe('Cards: as a valid user', () => { + it('I can create a card', () => { + //Given I am on a board page + cy.visit('/board/1'); + //And I Click on the new card button + cy.get('[data-cy="new-card"]').first().click(); + //When I enter the name of the card in the title field + cy.get('[data-cy="new-card-input"]').type('New Card').type('{enter}'); + //Then I should see the newly created card + cy.get('[data-cy="card"]').find('[data-cy="card-text"]').eq(1).should('have.text', 'New Card'); + }); + + it('I can delete a card', () => { + //Given I am on a cards detail page + cy.visit('/board/1?card=1'); + //When I click on the delete card button + cy.get('[data-cy="card-detail-delete"]').click(); + //Then I should see the card is deleted + cy.get('[data-cy="card"]').contains('Create a Spec document').should('not.exist'); + }); + + it('I can change the title of a card', () => { + //Given I am on a cards detail page + cy.visit('/board/1?card=1'); + //When I change the title of the card + const newTitle = 'New Card Title'; + cy.get('[data-cy="card-detail-title"]').click().clear().type(newTitle).type('{enter}'); + //Then I should see the card has been renamed + cy.get('[data-cy="notification-message"]').should('have.text', 'Card was renamed'); + cy.get('[data-cy="cancel"]').click(); + cy.get('[data-cy="card-text"]').should('contain', newTitle); + }); + + it('I can add a description to a card', () => { + //Given I am on a cards detail page + cy.visit('/board/1?card=1'); + //When I add a description to the card + cy.get('[data-cy="card-description"]').click().clear().type('This is a description').type('{enter}'); + //Then I should see the description has been added + cy.notificationEquals('Description was changed'); + }); + + it('I can see card has the correct details', () => { + //Given a detailed card exists + cy.fixture('cards').then((fixture) => { + cy.request('PATCH', '/api/cards/2', fixture[1]).then((response: any) => { + let card = response.body; + let boardId = parseInt(response.body.boardId); + let id = parseInt(response.body.id); + //When I view the card + cy.visit(`/board/${boardId}?card=${id}`); + //Then I should see the correct details + cy.get('[data-cy="card-detail-title"]').should('have.value', card.name); + cy.get('[data-cy="card-description"]').should('have.value', card.description); + }); + }); + }); + + it('I can move a card', () => { + //Given I am on a board page + cy.visit('/board/1'); + //And I have two lists with cards + cy.get('[data-cy="card-list"]').first().find('[data-cy="card"]').should('exist'); + cy.get('[data-cy="card-list"]').eq(0).find('[data-cy="card"]').as('firstCard') + cy.get('[data-cy="card-list"]').eq(1).as('secondList'); + cy.get('@firstCard').should('exist'); + //When I drag and drop a card from one list to another + cy.get('@firstCard').drag('@secondList'); + //Then I should see the card has been moved + cy.get('@firstCard').should('not.exist'); + cy.get('@secondList').find('[data-cy="card"]') + .should('contain', 'Create a Spec document') + .should('have.length', 2); + }); + + it('I can add an image to a card', () => { + + }); +}); + diff --git a/cypress/e2e/specs/lists.spec.ts b/cypress/e2e/specs/lists.spec.ts new file mode 100644 index 000000000..18f2153f4 --- /dev/null +++ b/cypress/e2e/specs/lists.spec.ts @@ -0,0 +1,67 @@ +beforeEach(() => { + //seed the database with boards and lists + cy.fixture('users').then((fixture) => { + cy.request('POST', '/api/signup', fixture[0]).then(function (response) { + const auth = response.body.accessToken; + cy.seedData('boards', undefined, auth); + cy.seedData('lists'); + }); + }); + + //Given I am logged in as a valid user + cy.visit('/login'); + cy.loginAs('Existinguser@test.com', 'Existinguser123'); + //And I am on the board page + cy.visit('/board/1'); +}); + +describe('Lists: as a valid user', () => { + it('I can create a list', () => { + //Given I click on the create list button + cy.get('[data-cy="create-list"]').click() + //When I enter the name of the list in the input field + cy.get('[data-cy="add-list-input"]').type('New List').type('{enter}'); + + //Then I should see the newly created list + cy.get('[data-cy="list-name"]').withValue('New List').should('exist'); + }); + + it('I can delete a list', () => { + //Given I am on the board page + //When I click on the delete list button in the options menu + cy.get('[data-cy="list-name"]').withValue('Delete this list').siblings('[data-cy="list-options"]').click(); + cy.get('[data-cy="delete-list"]').click(); + + //Then I should see the list is deleted + cy.get('[data-cy="list-name"]').withValue('Delete this list').should('not.exist'); + }); + + it('I can modify the name of a list', () => { + //Given I am on the board page + //And I click on the name of the list + //When I enter the new name of the list in the input field + cy.get('[data-cy="list-name"]').withValue('Move this list').click() + .clear().type('List Renamed').type('{enter}'); + + //Then I should see the list is renamed + cy.get('[data-cy="list-name"]').withValue('List Renamed').should('exist'); + + }); + + it('I can drag and drop a list', () => { + //Given I am on the board page + //And I have two lists + cy.get('[data-cy="list-name"]').first().as('firstList'); + cy.get('[data-cy="list-name"]').last().as('lastList'); + + cy.get('@firstList').should('have.value', 'Move this list'); + + //When I drag the first list to the right of the second list + cy.get('@firstList').drag('@lastList'); + + //Then I should see the lists are swapped + cy.get('@lastList').should('have.value', 'Move this list'); + cy.get('@firstList').should('have.value', 'Delete this list'); + }); + +}); \ No newline at end of file diff --git a/cypress/e2e/specs/login.spec.ts b/cypress/e2e/specs/login.spec.ts new file mode 100644 index 000000000..95ef40573 --- /dev/null +++ b/cypress/e2e/specs/login.spec.ts @@ -0,0 +1,53 @@ +describe('Login :', () => { + it('as a valid user, I can login', () => { + // create a new user + cy.fixture('users').then((fixture) => { cy.request('POST', '/api/signup', fixture[0])}); + //Given I am on the login page + cy.get('[data-cy="login-menu"').click(); + //When I login with a valid email and password + cy.loginAs('Existinguser@test.com', 'Existinguser123'); + //Then I be logged in yar + cy.notificationEquals('User is logged in'); + }); + + it('as a invalid user, I can not login', () => { + //Given I am on the login page + cy.get('[data-cy="login-menu"').click(); + //When I login with an invalid email and password + cy.loginAs('Notrealuser@test.com', 'Notrealuser123'); + //Then I should the "Cannot find user" error message + cy.notificationEquals('Cannot find user'); + }); +}); + +describe('Register :', () => { + it('as a new user, I can register', () => { + //Given I am on the login page + cy.visit('/login'); + //And I click on the "Sign up here" link + cy.get('a').contains('Sign up here').click(); + //When I register with a new email and password + const newUser = `newuser${Math.floor(Math.random() * 1000)}`; + cy.get('input[name="email"]').type(newUser + '@test.com'); + cy.get('input[name="password"]').type(newUser); + cy.get('[data-cy="signup-submit"]').click(); + //Then I should see the "User is logged in" message + cy.notificationEquals('User is logged in'); + //And my user is created + }); + + it('as a new user, I can not register with invalid password', () => { + //Given I am on the login page + cy.visit('/login'); + //And I click on the "Sign up here" link + cy.get('a').contains('Sign up here').click(); + //When I register with a invalid password + // create a new user with a random number + const newUser = `newuser${Math.floor(Math.random() * 1000)}`; + cy.get('input[name="email"]').type(newUser + '@test.com'); + cy.get('input[name="password"]').type('a'); + cy.get('[data-cy="signup-submit"]').click(); + //Then I should see the "Password is too short" message + cy.notificationEquals('Password is too short'); + }); +}); \ No newline at end of file diff --git a/cypress/e2e/tools.spec.ts b/cypress/e2e/tools.spec.ts deleted file mode 100644 index 3ca487a1d..000000000 --- a/cypress/e2e/tools.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -describe('Tools', () => { - - const body = { - email: 'filip@example.com', password: 'Asdf.1234#' - } - - beforeEach(() => { - - cy.request('POST', '/api/reset') - cy.signupApi(body) - cy.addBoardApi('new board') - cy.addListApi({name: 'new list'}) - cy.addCardApi({name: 'new card'}) - - }); - - it('show tools', function() { - - const boardId = this.board.id - const { accessToken } = this.user - - cy.intercept('DELETE', '/api/boards').as('boards') - cy.intercept('DELETE', '/api/lists').as('lists') - cy.intercept('DELETE', '/api/cards').as('cards') - cy.intercept('DELETE', '/api/users').as('users') - - cy.visit(`/board/${boardId}`) - - cy.getDataCy('card').should('be.visible') - cy.getDataCy('list').should('be.visible') - - cy.window().trigger('keydown', { keyCode: 113, which: 113 }) - - cy.getDataCy('api-tools') - .should('be.visible') - - // deletes a user - cy.contains('Users').click() - - cy.wait('@users') - - cy - .request({ - body, - failOnStatusCode: false, - headers: { - authorization: `Bearer ${accessToken}` - }, - method: 'POST', - url: '/api/login' - }).its('body').should('eq', 'Cannot find user') - - // deletes cards - cy.contains('Cards').click() - cy.getDataCy('card').should('not.exist') - - cy.wait('@cards') - - // deletes lists - cy.contains('Lists').click() - cy.getDataCy('list').should('not.exist') - - cy.wait('@lists') - - // deletes boards - cy.contains('Boards').click() - cy.location('pathname').should('eq', '/') - - cy.wait('@boards') - - cy.getDataCy('first-board').should('be.visible') - - }); - - it('resets all', function() { - - const boardId = this.board.id - const { accessToken } = this.user - - cy.intercept('POST', '/api/reset').as('reset') - - cy.visit(`/board/${boardId}`) - - cy.window().invoke('store').invoke('toggleTools', true) - - cy.getDataCy('api-tools') - .should('be.visible') - - // deletes a user - cy.contains('All').click() - - cy.wait('@reset') - - cy.location('pathname').should('eq', '/') - - cy.getDataCy('first-board').should('be.visible') - - cy - .request({ - body, - failOnStatusCode: false, - headers: { - authorization: `Bearer ${accessToken}` - }, - method: 'POST', - url: '/api/login' - }).its('body').should('eq', 'Cannot find user') - - - cy - .request({ - headers: { - accept: 'application/json' - }, - method: 'GET', - url: '/api/lists' - }).its('body').should('be.empty') - - cy - .request({ - headers: { - accept: 'application/json' - }, - method: 'GET', - url: '/api/cards' - }).its('body').should('be.empty') - - }) - -}); - diff --git a/cypress/fixtures/boards.json b/cypress/fixtures/boards.json new file mode 100644 index 000000000..fdce16898 --- /dev/null +++ b/cypress/fixtures/boards.json @@ -0,0 +1,8 @@ +[ + { + "name": "Portfolio Kanban", + "user": 1, + "starred": false, + "id": 1 + } +] \ No newline at end of file diff --git a/cypress/fixtures/cards.json b/cypress/fixtures/cards.json new file mode 100644 index 000000000..f6020d6e2 --- /dev/null +++ b/cypress/fixtures/cards.json @@ -0,0 +1,24 @@ +[ + { + "order": 0, + "boardId": 1, + "listId": 1, + "name": "Create a Spec document", + "created": "2024-03-12", + "deadline": "2024-02-24", + "description": "Create a spec document in Obsidian with the following sections, Phase 1 and Phase 2", + "completed": true, + "id": 1 + }, + { + "order": 0, + "boardId": 1, + "listId": 2, + "name": "Detailed Design Document", + "created": "2024-03-12", + "deadline": "2024-02-24", + "description": "Create a detailed design document", + "completed": true, + "id": 2 + } +] \ No newline at end of file diff --git a/cypress/fixtures/cypressLogo.png b/cypress/fixtures/cypressLogo.png deleted file mode 100755 index 10a014648..000000000 Binary files a/cypress/fixtures/cypressLogo.png and /dev/null differ diff --git a/cypress/fixtures/lists.json b/cypress/fixtures/lists.json new file mode 100644 index 000000000..d1b64d01b --- /dev/null +++ b/cypress/fixtures/lists.json @@ -0,0 +1,16 @@ + [ + { + "boardId": 1, + "name": "Move this list", + "order": 0, + "created": "2024-03-12", + "id": 1 + }, + { + "boardId": 1, + "name": "Delete this list", + "order": 1, + "created": "2024-03-12", + "id": 2 + } + ] \ No newline at end of file diff --git a/cypress/fixtures/users.json b/cypress/fixtures/users.json new file mode 100644 index 000000000..9e54f31f6 --- /dev/null +++ b/cypress/fixtures/users.json @@ -0,0 +1,6 @@ +[ + { + "email": "Existinguser@test.com", + "password": "Existinguser123" + } +] \ No newline at end of file diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts deleted file mode 100644 index f8f9407cd..000000000 --- a/cypress/plugins/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import 'dotenv/config' - -module.exports = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { - - - require('@cypress/code-coverage/task')(on, config); - - config.env.googleEnabled = process.env.VUE_APP_GOOGLE_ENABLED - config.env.googleRefreshToken = process.env.GOOGLE_REFRESH_TOKEN - config.env.googleClientId = process.env.VUE_APP_GOOGLE_CLIENT_ID - config.env.googleClientSecret = process.env.VUE_APP_GOOGLE_CLIENT_SECRET - - return config; -}; \ No newline at end of file diff --git a/cypress/support/@types/selectors.d.ts b/cypress/support/@types/selectors.d.ts deleted file mode 100644 index 7bc53b4cc..000000000 --- a/cypress/support/@types/selectors.d.ts +++ /dev/null @@ -1,75 +0,0 @@ -export type Selectors = -| '404' -| 'add-list-input' -| 'api-tools' -| 'board-detail' -| 'board-dropdown' -| 'board-item' -| 'board-list' -| 'board-list-error-message' -| 'board-options' -| 'board-title' -| 'calendar-button' -| 'calendar-dropdown' -| 'cancel' -| 'card' -| 'card-add' -| 'card-checkbox' -| 'card-description' -| 'card-detail' -| 'card-detail-backdrop' -| 'card-detail-deadline' -| 'card-detail-delete' -| 'card-detail-title' -| 'card-edit' -| 'card-list' -| 'card-list-name' -| 'card-text' -| 'copy-properties' -| 'create-board' -| 'create-list' -| 'day' -| 'delete-board' -| 'delete-list' -| 'discount' -| 'due-date' -| 'error-icon' -| 'find-location' -| 'first-board' -| 'footer-link' -| 'google-button' -| 'header-month' -| 'header-year' -| 'home' -| 'image-attachment' -| 'image-delete' -| 'info-icon' -| 'list' -| 'list-dropdown' -| 'list-name' -| 'list-options' -| 'list-placeholder' -| 'loading' -| 'logged-user' -| 'login-email' -| 'login-menu' -| 'login-password' -| 'login-submit' -| 'month' -| 'new-board-create' -| 'new-board-input' -| 'new-card' -| 'new-card-input' -| 'notification-message' -| 'plan-item' -| 'plan-price' -| 'result-item' -| 'search-input' -| 'signup-email' -| 'signup-password' -| 'signup-submit' -| 'star' -| 'starred-boards' -| 'trello-logo' -| 'upload-image' -| 'year' diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 000000000..1e974a960 --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,77 @@ +Cypress.Commands.add('loginAs', (email: string, password: string) => { + cy.log(`Logging in as ${email} with password ${password}`); + cy.get('input[name="email"]').type(email); + cy.get('input[name="password"]').type(password); + cy.get('[data-cy="login-submit"]').click(); +}); + +Cypress.Commands.add('withValue', { prevSubject: true }, (element, value) => { + return element.filter((_:number, el:any) => { + return Cypress.$(el).val() === value; + }); + }); + +Cypress.Commands.add('notificationEquals', (value) => {cy.get('[data-cy="notification-message"]').should('have.text', value)}); + +Cypress.Commands.add('seedData', (fixtureName: string, index?: number, auth?: string) => { + cy.log(`Seeding data from ${fixtureName}`); + if (!auth) { + cy.log('No auth token provided, seeding data as guest'); + if (index == undefined) { + cy.log('Seeding all data from fixture'); + cy.fixture(fixtureName).then((fixture) => { + for (let i = 0; i < fixture.length; i++) { + cy.request({ + method: 'POST', + url: `/api/${fixtureName}`, + body: fixture[i] + }); + } + }); + } else { + cy.log(`Seeding data from fixture at index ${index}`); + cy.fixture(fixtureName).then((fixture) => { + cy.request({ + method: 'POST', + url: `/api/${fixtureName}`, + body: fixture[index] + }); + }); + } + } + else { + cy.log('Auth token provided, seeding data as user'); + cy.fixture(fixtureName).then((fixture) => { + if (index == undefined) { + cy.log('Seeding all data from fixture'); + for (let i = 0; i < fixture.length; i++) { + cy.request({ + method: 'POST', + url: `/api/${fixtureName}`, + headers: { authorization: `Bearer ${auth}` }, + body: fixture[i] + }); + } + } else { + cy.log(`Seeding data from fixture at index ${index}`); + cy.request({ + method: 'POST', + url: `/api/${fixtureName}`, + headers: { authorization: `Bearer ${auth}` }, + body: fixture[index] + }); + } + }); + }; +}); + + + +declare namespace Cypress { + interface Chainable { + loginAs(email: string, password: string): Chainable + seedData(fixtureName: string, index?: number, auth?: string): Chainable + withValue(value: string): Chainable + notificationEquals(value: string): Chainable + } +} \ No newline at end of file diff --git a/cypress/support/commands/addBoardApi.ts b/cypress/support/commands/addBoardApi.ts deleted file mode 100644 index 79faa1d2c..000000000 --- a/cypress/support/commands/addBoardApi.ts +++ /dev/null @@ -1,24 +0,0 @@ -export {} -declare global { - namespace Cypress { - interface Chainable { - addBoardApi: typeof addBoardApi; - } - } -} - -/** - * Creates a new board using the API - * @param name name of the board - * @example - * cy.addBoardApi('new board') - * - */ - -export const addBoardApi = function(this: any, name: string): Cypress.Chainable { - - return cy - .request('POST', '/api/boards', { name }) - .its('body', { log: false }).as('board'); - -}; \ No newline at end of file diff --git a/cypress/support/commands/addCardApi.ts b/cypress/support/commands/addCardApi.ts deleted file mode 100644 index 36b19701a..000000000 --- a/cypress/support/commands/addCardApi.ts +++ /dev/null @@ -1,29 +0,0 @@ -export {} -declare global { - namespace Cypress { - interface Chainable { - addCardApi: typeof addCardApi; - } - } -} - -/** - * Creates a new card using the API. By default, the card is added to the first list of the first board. - * @param name name of the card - * @param boardIndex index number from this.boards - * @param listIndex index number from this.lists - * @example - * cy.addCardApi({ name: 'new card', boardIndex: 0, listIndex: 0 }) - */ -export const addCardApi = function(this: any, { name, boardAlias = 'board', listAlias = 'list', cardAlias = 'card' }: { name: string, boardAlias?: string, listAlias?: string, cardAlias?: string }): Cypress.Chainable { - - return cy - .request('POST', '/api/cards', { - boardId: this[boardAlias].id, - listId: this[listAlias].id, - name, - order: 0 - }) - .its('body', { log: false }).as(cardAlias); - -} \ No newline at end of file diff --git a/cypress/support/commands/addListApi.ts b/cypress/support/commands/addListApi.ts deleted file mode 100644 index 659ffc178..000000000 --- a/cypress/support/commands/addListApi.ts +++ /dev/null @@ -1,24 +0,0 @@ -export {} -declare global { - namespace Cypress { - interface Chainable { - addListApi: typeof addListApi; - } - } -} - -/** - * Creates a new list using the API. By default, the card is added to the first board. - * @param name name of the card - * @param boardIndex index number from this.boards - * @example - * cy.addListApi({ name: 'new card', boardIndex: 0 }) - */ -export const addListApi = function(this: any, { name, boardAlias = 'board', listAlias = 'list' }: { name: string, boardAlias?: string, listAlias?: string}): Cypress.Chainable { - - return cy.request('POST', '/api/lists', { - boardId: this[boardAlias].id, - name, - }).its('body', { log: false }).as(listAlias); - -} \ No newline at end of file diff --git a/cypress/support/commands/getDataCy.ts b/cypress/support/commands/getDataCy.ts deleted file mode 100644 index 017ccd8fb..000000000 --- a/cypress/support/commands/getDataCy.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Selectors } from '../@types/selectors'; - -declare global { - namespace Cypress { - interface Chainable { - getDataCy: typeof getDataCy - } - } -} - -/** - * Gets element using data-cy selector - * @param input data-cy attribute value - * @example - * // this command - * cy.getDataCy('header') - * // will select this element - *
- *
- * - */ -export const getDataCy = function( - input: Selectors -) { - - Cypress.log({ - consoleProps() { - return { - selector: input, - }; - }, - displayName: 'getDataCy', - name: 'Get by [data-cy] attribute', - }); - - return cy.get(`[data-cy='${input}']`); - -}; \ No newline at end of file diff --git a/cypress/support/commands/googleLogin.ts b/cypress/support/commands/googleLogin.ts deleted file mode 100644 index e430f72ea..000000000 --- a/cypress/support/commands/googleLogin.ts +++ /dev/null @@ -1,35 +0,0 @@ -export { } -declare global { - namespace Cypress { - interface Chainable { - googleLogin: typeof googleLogin; - } - } -} - -/** - * Performs a Google SSO login - * @example - * cy.googleLogin() - * - */ - -export const googleLogin = function (this: any): Cypress.Chainable { - - return cy.request({ - method: 'POST', - url: 'https://www.googleapis.com/oauth2/v4/token', - body: { - grant_type: 'refresh_token', - client_id: Cypress.env('googleClientId'), - client_secret: Cypress.env('googleClientSecret'), - refresh_token: Cypress.env('googleRefreshToken'), - }, - }).then(({ body }) => { - const { id_token } = body - cy.request('POST', '/api/login', { jwt: id_token }) - .then(({ body: { accessToken } }) => { - cy.setCookie('auth_token', accessToken) - }) - }) -} \ No newline at end of file diff --git a/cypress/support/commands/googleSignup.ts b/cypress/support/commands/googleSignup.ts deleted file mode 100644 index 0250936ff..000000000 --- a/cypress/support/commands/googleSignup.ts +++ /dev/null @@ -1,35 +0,0 @@ -export { } -declare global { - namespace Cypress { - interface Chainable { - googleSignup: typeof googleSignup; - } - } -} - -/** - * Performs a Google SSO login - * @example - * cy.googleSignup() - * - */ - -export const googleSignup = function (this: any): Cypress.Chainable { - - return cy.request({ - method: 'POST', - url: 'https://www.googleapis.com/oauth2/v4/token', - body: { - grant_type: 'refresh_token', - client_id: Cypress.env('googleClientId'), - client_secret: Cypress.env('googleClientSecret'), - refresh_token: Cypress.env('googleRefreshToken'), - }, - }).then(({ body }) => { - const { id_token } = body - cy.request('POST', '/api/signup', { jwt: id_token }) - .then(({ body: { accessToken } }) => { - cy.setCookie('auth_token', accessToken) - }) - }) -} \ No newline at end of file diff --git a/cypress/support/commands/signupApi.ts b/cypress/support/commands/signupApi.ts deleted file mode 100644 index 3991cb55b..000000000 --- a/cypress/support/commands/signupApi.ts +++ /dev/null @@ -1,28 +0,0 @@ -export { } -declare global { - namespace Cypress { - interface Chainable { - signupApi: typeof signupApi; - } - } -} - -/** - * Creates a new user using the API - * @param email user email - * @param password user password - * @param login defaults to true, logs in the user - * @example - * cy.signupApi({ email: 'filip@example.com', password: 'nbusr123', login: false }) - */ -export const signupApi = function (this: any, { email, password, login = true }: { email: string, password: string, login?: boolean }) { - - cy - .request('POST', '/api/signup', { - email, password - }).then(({ body }) => { - if (login) cy.setCookie('auth_token', body.accessToken) - cy.wrap(body).as('user'); - }); - -}; \ No newline at end of file diff --git a/cypress/support/commands/step.ts b/cypress/support/commands/step.ts deleted file mode 100644 index 2e0197c25..000000000 --- a/cypress/support/commands/step.ts +++ /dev/null @@ -1,29 +0,0 @@ -export {} -declare global { - namespace Cypress { - interface Chainable { - step: typeof step; - } - } -} - -/** - * Creates a test step - */ -export const step = (msg: string, options?: { section: boolean }) => { - - let logMessage = `${window.logCalls}. ${msg}`; - - if (options?.section) { - window.logCalls = 0; - logMessage = `\n--- ${msg} ---\n`; - } - - Cypress.log({ - displayName: logMessage.toUpperCase(), - message: '\n', - }); - - window.testFlow.push(logMessage); - window.logCalls++; -}; diff --git a/cypress/support/common.ts b/cypress/support/common.ts deleted file mode 100644 index c244f196a..000000000 --- a/cypress/support/common.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { getDataCy } from '@commands/getDataCy' - -Cypress.Commands.add('getDataCy', getDataCy); \ No newline at end of file diff --git a/cypress/support/component-index.html b/cypress/support/component-index.html deleted file mode 100644 index ac6e79fd8..000000000 --- a/cypress/support/component-index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - Components App - - -
- - \ No newline at end of file diff --git a/cypress/support/component.ts b/cypress/support/component.ts deleted file mode 100644 index b27a3cb44..000000000 --- a/cypress/support/component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { mount } from 'cypress/vue' -import { createPinia } from 'pinia' -import { createMemoryHistory, createRouter } from 'vue-router' -import { routes } from '@/router/routes'; -import { useStore } from '@/store/store'; -import VueClickAway from 'vue3-click-away'; -import './common' -import '@/index.css'; - -type MountParams = Parameters -type OptionsParam = MountParams[1] - -declare global { - namespace Cypress { - interface Chainable { - mount( - component: any, - options?: OptionsParam & { store?: any } - ): Chainable - } - } -} - -// solution #1 👇 - does not throw error, but doesn’t show component -Cypress.Commands.add('mount', (component, options = {}) => { - - let pinia = createPinia() - let router = createRouter({ - routes, - history: createMemoryHistory() - }) - - // define default route - router.push('/') - - let store = options.store || useStore(pinia) - - options = { - global: { - plugins: [store, router, VueClickAway] - }, - ...options - } - - return mount(component, options) -}) - diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index 498be3e8a..e0ab74b65 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -1,41 +1,40 @@ -import '@4tw/cypress-drag-drop'; -import '@cypress/code-coverage/support'; -import 'cypress-real-events/support'; +import './commands' +import '@4tw/cypress-drag-drop' +import '@cypress/grep' -import './common' - -import { addBoardApi } from '@commands/addBoardApi' -import { addCardApi } from '@commands/addCardApi' -import { addListApi } from '@commands/addListApi' -import { googleLogin } from '@commands/googleLogin' -import { googleSignup } from '@commands/googleSignup' -import { signupApi } from '@commands/signupApi' -import { step } from '@commands/step' +before(() => { +// cy.log('clearing the database') +// cy.request('POST', '/api/reset') +// +// cy.log('Seeding the database') +// cy.request('POST', '/api/reset') +// cy.fixture('users').then((fixture) => { +// cy.request('POST', '/api/signup', fixture[0]).then(function (response) { +// const auth = response.body.accessToken; +// cy.seedData('boards', undefined, auth); +// cy.seedData('lists'); +// cy.seedData('cards'); +// }); +// }); +// cy.log('Starting E2E tests') +}); -Cypress.Commands.add('addBoardApi', addBoardApi); -Cypress.Commands.add('addCardApi', addCardApi); -Cypress.Commands.add('addListApi', addListApi); -Cypress.Commands.add('googleLogin', googleLogin); -Cypress.Commands.add('googleSignup', googleSignup); -Cypress.Commands.add('signupApi', signupApi); -Cypress.Commands.add('step', step); +beforeEach(() => { + // clear the database + cy.request('POST', '/api/reset') + cy.visit('') -declare global { - interface Window { - logCalls: number; - testFlow: string[]; - } -} -beforeEach(function () { - window.logCalls = 1; - window.testFlow = []; -}); -Cypress.on('fail', (err) => { - if (window.testFlow.length) { - err.message += `${'\n\n' + 'Test steps were:\n\n'}${window.testFlow.join('\n')}`; - } - throw err; -}); +// cy.log('Setting up the test') +// cy.request('POST', '/api/reset') +// cy.fixture('users').then((fixture) => { +// cy.request('POST', '/api/signup', fixture[0]).then(function (response) { +// const auth = response.body.accessToken; +// cy.seedData('boards', 0, auth); +// cy.seedData('lists'); +// cy.seedData('cards'); +// }); +// }); +}) diff --git a/package-lock.json b/package-lock.json index 8b6639642..50cc3f16b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "@4tw/cypress-drag-drop": "^2.1.0", "@cypress/code-coverage": "^3.10.0", "@cypress/vite-dev-server": "^2.2.1", "@cypress/vue": "^3.0.5", @@ -66,9 +65,11 @@ "vuedraggable": "^4.1.0" }, "devDependencies": { + "@4tw/cypress-drag-drop": "^2.2.5", + "@cypress/grep": "^4.0.1", "@cypress/skip-test": "^2.6.1", "@types/leaflet": "^1.7.11", - "cypress": "^10.4.0", + "cypress": "^13.7.0", "eslint": "^8.5.0", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-no-only-tests": "^2.6.0", @@ -83,11 +84,12 @@ } }, "node_modules/@4tw/cypress-drag-drop": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.1.0.tgz", - "integrity": "sha512-HbPBcx2KBdxL3hhyTlZYsF7XSkHkEKAcmDGfvzXp0sBs2sA/NkHE9CMqHz52liLnnVgKEkVkzcoVnyatCc9J5w==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.2.5.tgz", + "integrity": "sha512-3ghTmzhOmUqeN6U3QmUnKRUxI7OMLbJA4hHUY/eS/FhWJgxbiGgcaELbolWnBAOpajPXcsNQGYEj9brd59WH6A==", + "dev": true, "peerDependencies": { - "cypress": "^2.1.0 || ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + "cypress": "2 - 13" } }, "node_modules/@achrinza/node-ipc": { @@ -293,6 +295,15 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-simple-access": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz", @@ -422,9 +433,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", - "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -432,6 +443,21 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/template": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", @@ -519,15 +545,29 @@ "cypress": "*" } }, + "node_modules/@cypress/grep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cypress/grep/-/grep-4.0.1.tgz", + "integrity": "sha512-i3mWy4mG6nxF7m93W0nzsMZkl0PflGa4+SygA9P92tELayYYAaRKlr07I4fo5PnwoPk1H9IEbXoMFJkhfTMxtg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "find-test-names": "^1.19.0", + "globby": "^11.0.4" + }, + "peerDependencies": { + "cypress": ">=10" + } + }, "node_modules/@cypress/mount-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@cypress/mount-utils/-/mount-utils-1.0.2.tgz", "integrity": "sha512-Fn3fdTiyayHoy8Ol0RSu4MlBH2maQ2ZEXeEVKl/zHHXEQpld5HX3vdNLhK5YLij8cLynA4DxOT/nO9iEnIiOXw==" }, "node_modules/@cypress/request": { - "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", - "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", + "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -543,9 +583,9 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "~6.5.2", + "qs": "6.10.4", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", + "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -1679,6 +1719,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/addressparser": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", @@ -1987,9 +2036,9 @@ } }, "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "node_modules/axios": { @@ -2662,9 +2711,9 @@ } }, "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, "engines": { "node": ">= 6" @@ -3027,30 +3076,29 @@ "dev": true }, "node_modules/cypress": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", - "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.0.tgz", + "integrity": "sha512-UimjRSJJYdTlvkChcdcfywKJ6tUYuwYuk/n1uMMglrvi+ZthNhoRYcxnWgTqUtkl17fXrPAsD5XT2rcQYN1xKA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@cypress/request": "^2.88.10", + "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", "cli-cursor": "^3.1.0", "cli-table3": "~0.6.1", - "commander": "^5.1.0", + "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", - "debug": "^4.3.2", + "debug": "^4.3.4", "enquirer": "^2.3.6", "eventemitter2": "6.4.7", "execa": "4.1.0", @@ -3059,18 +3107,19 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.6", + "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", - "semver": "^7.3.2", + "semver": "^7.5.3", "supports-color": "^8.1.1", "tmp": "~0.2.1", "untildify": "^4.0.0", @@ -3080,7 +3129,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": ">=12.0.0" + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, "node_modules/cypress-real-events": { @@ -5452,6 +5501,25 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-test-names": { + "version": "1.28.18", + "resolved": "https://registry.npmjs.org/find-test-names/-/find-test-names-1.28.18.tgz", + "integrity": "sha512-hhnGdkWK+qEA5Z02Tu0OqGQIUjFZNyOCE4WaJpbhW4hAF1+NZ7OCr0Bss9RCaj7BBtjoIjkU93utobQ8pg2iVg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.0", + "@babel/plugin-syntax-jsx": "^7.22.5", + "acorn-walk": "^8.2.0", + "debug": "^4.3.3", + "globby": "^11.0.4", + "simple-bin-help": "^1.8.0" + }, + "bin": { + "find-test-names": "bin/find-test-names.js", + "print-tests": "bin/print-tests.js", + "update-test-count": "bin/update-test-count.js" + } + }, "node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -7745,9 +7813,12 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp": { "version": "0.5.6", @@ -8855,6 +8926,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -8894,9 +8974,9 @@ "dev": true }, "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "node_modules/pump": { @@ -8929,14 +9009,26 @@ } }, "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", + "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9169,6 +9261,12 @@ "node": ">=0.10.5" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -9326,9 +9424,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -9490,6 +9588,15 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-bin-help": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/simple-bin-help/-/simple-bin-help-1.8.0.tgz", + "integrity": "sha512-0LxHn+P1lF5r2WwVB/za3hLRIsYoLaNq1CXqjbrs3ZvLuvlWnRKrUjEWzV7umZL7hpQ7xULiQMV+0iXdRa5iFg==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9648,9 +9755,9 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -10092,16 +10199,27 @@ } }, "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { - "node": ">=0.8" + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" } }, "node_modules/tr46": { @@ -10166,7 +10284,7 @@ "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "dependencies": { "safe-buffer": "^5.0.1" @@ -10178,7 +10296,7 @@ "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "node_modules/type-check": { @@ -10363,6 +10481,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", @@ -10415,7 +10543,7 @@ "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "engines": [ "node >=0.6.0" @@ -11082,9 +11210,10 @@ }, "dependencies": { "@4tw/cypress-drag-drop": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.1.0.tgz", - "integrity": "sha512-HbPBcx2KBdxL3hhyTlZYsF7XSkHkEKAcmDGfvzXp0sBs2sA/NkHE9CMqHz52liLnnVgKEkVkzcoVnyatCc9J5w==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@4tw/cypress-drag-drop/-/cypress-drag-drop-2.2.5.tgz", + "integrity": "sha512-3ghTmzhOmUqeN6U3QmUnKRUxI7OMLbJA4hHUY/eS/FhWJgxbiGgcaELbolWnBAOpajPXcsNQGYEj9brd59WH6A==", + "dev": true }, "@achrinza/node-ipc": { "version": "9.2.6", @@ -11237,6 +11366,12 @@ "@babel/types": "^7.18.0" } }, + "@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "dev": true + }, "@babel/helper-simple-access": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz", @@ -11335,9 +11470,18 @@ } }, "@babel/parser": { - "version": "7.18.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz", - "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==" + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==" + }, + "@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } }, "@babel/template": { "version": "7.16.7", @@ -11410,15 +11554,26 @@ "nyc": "15.1.0" } }, + "@cypress/grep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cypress/grep/-/grep-4.0.1.tgz", + "integrity": "sha512-i3mWy4mG6nxF7m93W0nzsMZkl0PflGa4+SygA9P92tELayYYAaRKlr07I4fo5PnwoPk1H9IEbXoMFJkhfTMxtg==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "find-test-names": "^1.19.0", + "globby": "^11.0.4" + } + }, "@cypress/mount-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@cypress/mount-utils/-/mount-utils-1.0.2.tgz", "integrity": "sha512-Fn3fdTiyayHoy8Ol0RSu4MlBH2maQ2ZEXeEVKl/zHHXEQpld5HX3vdNLhK5YLij8cLynA4DxOT/nO9iEnIiOXw==" }, "@cypress/request": { - "version": "2.88.10", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", - "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", + "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -11434,9 +11589,9 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "~6.5.2", + "qs": "6.10.4", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", + "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -12290,6 +12445,12 @@ } } }, + "acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true + }, "addressparser": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", @@ -12503,9 +12664,9 @@ "dev": true }, "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, "axios": { @@ -12998,9 +13159,9 @@ } }, "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true }, "common-tags": { @@ -13279,29 +13440,28 @@ "dev": true }, "cypress": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-10.11.0.tgz", - "integrity": "sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.0.tgz", + "integrity": "sha512-UimjRSJJYdTlvkChcdcfywKJ6tUYuwYuk/n1uMMglrvi+ZthNhoRYcxnWgTqUtkl17fXrPAsD5XT2rcQYN1xKA==", "dev": true, "requires": { - "@cypress/request": "^2.88.10", + "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", "cli-cursor": "^3.1.0", "cli-table3": "~0.6.1", - "commander": "^5.1.0", + "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", - "debug": "^4.3.2", + "debug": "^4.3.4", "enquirer": "^2.3.6", "eventemitter2": "6.4.7", "execa": "4.1.0", @@ -13310,18 +13470,19 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", "lodash": "^4.17.21", "log-symbols": "^4.0.0", - "minimist": "^1.2.6", + "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", + "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", - "semver": "^7.3.2", + "semver": "^7.5.3", "supports-color": "^8.1.1", "tmp": "~0.2.1", "untildify": "^4.0.0", @@ -15032,6 +15193,20 @@ "pkg-dir": "^4.1.0" } }, + "find-test-names": { + "version": "1.28.18", + "resolved": "https://registry.npmjs.org/find-test-names/-/find-test-names-1.28.18.tgz", + "integrity": "sha512-hhnGdkWK+qEA5Z02Tu0OqGQIUjFZNyOCE4WaJpbhW4hAF1+NZ7OCr0Bss9RCaj7BBtjoIjkU93utobQ8pg2iVg==", + "dev": true, + "requires": { + "@babel/parser": "^7.23.0", + "@babel/plugin-syntax-jsx": "^7.22.5", + "acorn-walk": "^8.2.0", + "debug": "^4.3.3", + "globby": "^11.0.4", + "simple-bin-help": "^1.8.0" + } + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -16741,9 +16916,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "mkdirp": { "version": "0.5.6", @@ -17526,6 +17701,12 @@ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -17556,9 +17737,9 @@ "dev": true }, "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, "pump": { @@ -17585,9 +17766,18 @@ } }, "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", + "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, "queue-microtask": { @@ -17757,6 +17947,12 @@ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -17855,9 +18051,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "requires": { "lru-cache": "^6.0.0" } @@ -17992,6 +18188,12 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "simple-bin-help": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/simple-bin-help/-/simple-bin-help-1.8.0.tgz", + "integrity": "sha512-0LxHn+P1lF5r2WwVB/za3hLRIsYoLaNq1CXqjbrs3ZvLuvlWnRKrUjEWzV7umZL7hpQ7xULiQMV+0iXdRa5iFg==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -18125,9 +18327,9 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -18458,13 +18660,23 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } } }, "tr46": { @@ -18524,7 +18736,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, "requires": { "safe-buffer": "^5.0.1" @@ -18533,7 +18745,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, "type-check": { @@ -18661,6 +18873,16 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", @@ -18701,7 +18923,7 @@ "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, "requires": { "assert-plus": "^1.0.0", diff --git a/package.json b/package.json index 0859c5758..075687bb2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "postinstall": "echo \"Trello app was installed sucessfully ✅\nYou can now run it by typing:\nnpm start\"" }, "dependencies": { - "@4tw/cypress-drag-drop": "^2.1.0", "@cypress/code-coverage": "^3.10.0", "@cypress/vite-dev-server": "^2.2.1", "@cypress/vue": "^3.0.5", @@ -66,9 +65,11 @@ "vuedraggable": "^4.1.0" }, "devDependencies": { + "@4tw/cypress-drag-drop": "^2.2.5", + "@cypress/grep": "^4.0.1", "@cypress/skip-test": "^2.6.1", "@types/leaflet": "^1.7.11", - "cypress": "^10.4.0", + "cypress": "^13.7.0", "eslint": "^8.5.0", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-no-only-tests": "^2.6.0", diff --git a/src/components/board/BoardDetail.vue b/src/components/board/BoardDetail.vue index 93c883542..708d3f830 100644 --- a/src/components/board/BoardDetail.vue +++ b/src/components/board/BoardDetail.vue @@ -35,14 +35,14 @@ >
-