test(Cypress): Add e2e tests for sharing basics and navigation
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { randUser } from '../utils/index.js'
|
import { randUser } from '../utils/index.js'
|
||||||
const user = randUser()
|
const user = randUser()
|
||||||
|
const recipient = randUser()
|
||||||
|
|
||||||
describe('Board', function() {
|
describe('Board', function() {
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
cy.createUser(user)
|
cy.createUser(user)
|
||||||
|
cy.createUser(recipient)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@@ -21,7 +23,6 @@ describe('Board', function() {
|
|||||||
}).as('createBoardRequest')
|
}).as('createBoardRequest')
|
||||||
|
|
||||||
// Click "Add board"
|
// Click "Add board"
|
||||||
cy.openLeftSidebar()
|
|
||||||
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||||
.eq(3).find('a').first().click({ force: true })
|
.eq(3).find('a').first().click({ force: true })
|
||||||
|
|
||||||
@@ -38,4 +39,18 @@ describe('Board', function() {
|
|||||||
cy.get('.app-navigation__list .app-navigation-entry__children .app-navigation-entry')
|
cy.get('.app-navigation__list .app-navigation-entry__children .app-navigation-entry')
|
||||||
.contains(board).should('be.visible')
|
.contains(board).should('be.visible')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Shows and hides the navigation', () => {
|
||||||
|
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||||
|
.contains('Upcoming cards')
|
||||||
|
.should('be.visible')
|
||||||
|
cy.openLeftSidebar()
|
||||||
|
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||||
|
.contains('Upcoming cards')
|
||||||
|
.should('not.be.visible')
|
||||||
|
cy.openLeftSidebar()
|
||||||
|
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
|
||||||
|
.contains('Upcoming cards')
|
||||||
|
.should('be.visible')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,40 +1,29 @@
|
|||||||
import { randUser } from '../utils/index.js'
|
import { randUser } from '../utils/index.js'
|
||||||
const user = randUser()
|
import { sampleBoard } from '../utils/sampleBoard'
|
||||||
|
|
||||||
const testBoardData = {
|
const user = randUser()
|
||||||
title: 'MyBoardTest',
|
const boardData = sampleBoard()
|
||||||
color: '00ff00',
|
|
||||||
stacks: [
|
|
||||||
{
|
|
||||||
title: 'TestList',
|
|
||||||
cards: [
|
|
||||||
{
|
|
||||||
title: 'Hello world',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Card', function() {
|
describe('Card', function() {
|
||||||
|
let boardId
|
||||||
before(function() {
|
before(function() {
|
||||||
cy.createUser(user)
|
cy.createUser(user)
|
||||||
cy.login(user)
|
cy.login(user)
|
||||||
cy.createExampleBoard({
|
cy.createExampleBoard({
|
||||||
user: user.userId,
|
user,
|
||||||
password: user.password,
|
board: boardData,
|
||||||
board: testBoardData,
|
}).then((board) => {
|
||||||
|
boardId = board.id
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
cy.login(user)
|
cy.login(user)
|
||||||
cy.visit('/apps/deck')
|
cy.visit(`/apps/deck/#/board/${boardId}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Can show card details modal', function() {
|
it('Can show card details modal', function() {
|
||||||
cy.openLeftSidebar()
|
cy.getNavigationEntry(boardData.title)
|
||||||
cy.getNavigationEntry(testBoardData.title)
|
|
||||||
.first().click({ force: true })
|
.first().click({ force: true })
|
||||||
|
|
||||||
cy.get('.board .stack').eq(0).within(() => {
|
cy.get('.board .stack').eq(0).within(() => {
|
||||||
@@ -48,8 +37,7 @@ describe('Card', function() {
|
|||||||
it('Can add a card', function() {
|
it('Can add a card', function() {
|
||||||
const newCardTitle = 'Write some cypress tests'
|
const newCardTitle = 'Write some cypress tests'
|
||||||
|
|
||||||
cy.openLeftSidebar()
|
cy.getNavigationEntry(boardData.title)
|
||||||
cy.getNavigationEntry(testBoardData.title)
|
|
||||||
.first().click({ force: true })
|
.first().click({ force: true })
|
||||||
|
|
||||||
cy.get('.board .stack').eq(0).within(() => {
|
cy.get('.board .stack').eq(0).within(() => {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ describe('Deck dashboard', function() {
|
|||||||
it('Can see the default "Personal Board" created for user by default', function() {
|
it('Can see the default "Personal Board" created for user by default', function() {
|
||||||
const defaultBoard = 'Personal'
|
const defaultBoard = 'Personal'
|
||||||
|
|
||||||
cy.openLeftSidebar()
|
|
||||||
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
|
||||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
|
||||||
.first()
|
.first()
|
||||||
|
|||||||
50
cypress/e2e/sharingFeatures.js
Normal file
50
cypress/e2e/sharingFeatures.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { randUser } from '../utils/index.js'
|
||||||
|
import { sampleBoard } from '../utils/sampleBoard'
|
||||||
|
const user = randUser()
|
||||||
|
const recipient = randUser()
|
||||||
|
|
||||||
|
describe('Board', function() {
|
||||||
|
before(function() {
|
||||||
|
cy.createUser(user)
|
||||||
|
cy.createUser(recipient)
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
cy.login(user)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Share a board to a user', function() {
|
||||||
|
const board = sampleBoard('Read only board')
|
||||||
|
cy.createExampleBoard({ user, board }).then((board) => {
|
||||||
|
const boardId = board.id
|
||||||
|
cy.visit(`/apps/deck/#/board/${boardId}`)
|
||||||
|
cy.get('.board-title').contains(board.title)
|
||||||
|
|
||||||
|
cy.shareBoardWithUi(recipient.userId)
|
||||||
|
|
||||||
|
cy.login(recipient)
|
||||||
|
cy.visit(`/apps/deck/#/board/${boardId}`)
|
||||||
|
cy.get('.board-title').contains(board.title)
|
||||||
|
cy.get('.button-vue[aria-label*="Add card"]')
|
||||||
|
.should('not.exist')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Share a board to a user as writable', function() {
|
||||||
|
const board = sampleBoard('Editable board')
|
||||||
|
cy.createExampleBoard({ user, board }).then((board) => {
|
||||||
|
const boardId = board.id
|
||||||
|
cy.visit(`/apps/deck/#/board/${boardId}`)
|
||||||
|
cy.get('.board-title').contains(board.title)
|
||||||
|
|
||||||
|
cy.shareBoardWithUi(recipient.userId)
|
||||||
|
cy.get(`[data-cy="acl-participant:${recipient.userId}"]`).find('[data-cy="action:permission-edit"]').click()
|
||||||
|
|
||||||
|
cy.login(recipient)
|
||||||
|
cy.visit(`/apps/deck/#/board/${boardId}`)
|
||||||
|
cy.get('.board-title').contains(board.title)
|
||||||
|
cy.get('.button-vue[aria-label*="Add card"]')
|
||||||
|
.first().click()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -16,8 +16,7 @@ describe('Stack', function() {
|
|||||||
cy.createUser(user)
|
cy.createUser(user)
|
||||||
cy.login(user)
|
cy.login(user)
|
||||||
cy.createExampleBoard({
|
cy.createExampleBoard({
|
||||||
user: user.userId,
|
user,
|
||||||
password: user.password,
|
|
||||||
board: testBoardData,
|
board: testBoardData,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -63,14 +63,15 @@ Cypress.Commands.add('deckCreateList', ({ user, password }, title) => {
|
|||||||
cy.get('#stack-add form input[type=submit]').first().click()
|
cy.get('#stack-add form input[type=submit]').first().click()
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add('createExampleBoard', ({ user, password, board }) => {
|
Cypress.Commands.add('createExampleBoard', ({ user, board }) => {
|
||||||
|
const auth = {
|
||||||
|
user: user.userId,
|
||||||
|
password: user.password,
|
||||||
|
}
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
|
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
|
||||||
auth: {
|
auth,
|
||||||
user,
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
body: { title: board.title, color: board.color ?? 'ff0000' },
|
body: { title: board.title, color: board.color ?? 'ff0000' },
|
||||||
}).then((boardResponse) => {
|
}).then((boardResponse) => {
|
||||||
expect(boardResponse.status).to.eq(200)
|
expect(boardResponse.status).to.eq(200)
|
||||||
@@ -80,10 +81,7 @@ Cypress.Commands.add('createExampleBoard', ({ user, password, board }) => {
|
|||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
|
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
|
||||||
auth: {
|
auth,
|
||||||
user,
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
body: { title: stack.title, order: 0 },
|
body: { title: stack.title, order: 0 },
|
||||||
}).then((stackResponse) => {
|
}).then((stackResponse) => {
|
||||||
const stackData = stackResponse.body
|
const stackData = stackResponse.body
|
||||||
@@ -92,15 +90,13 @@ Cypress.Commands.add('createExampleBoard', ({ user, password, board }) => {
|
|||||||
cy.request({
|
cy.request({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
|
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
|
||||||
auth: {
|
auth,
|
||||||
user,
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
body: { title: card.title },
|
body: { title: card.title },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
cy.wrap(boardData)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -109,3 +105,13 @@ Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
|
|||||||
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
|
||||||
.find('a.app-navigation-entry-link')
|
.find('a.app-navigation-entry-link')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('shareBoardWithUi', (userId) => {
|
||||||
|
cy.get('[aria-label="Open details"]').click()
|
||||||
|
cy.get('.app-sidebar').should('be.visible')
|
||||||
|
cy.get('.multiselect__input').type(`${userId}`)
|
||||||
|
cy.get('.multiselect__content .multiselect__element').first().contains(userId)
|
||||||
|
cy.get('.multiselect__input').type('{enter}')
|
||||||
|
|
||||||
|
cy.get('.shareWithList').contains(userId)
|
||||||
|
})
|
||||||
|
|||||||
12
cypress/support/component-index.html
Normal file
12
cypress/support/component-index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<title>Components App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div data-cy-root></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
27
cypress/support/component.js
Normal file
27
cypress/support/component.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// This example support/component.js is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.js using ES2015 syntax:
|
||||||
|
import './commands'
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
|
|
||||||
|
import { mount } from 'cypress/vue2'
|
||||||
|
|
||||||
|
Cypress.Commands.add('mount', mount)
|
||||||
|
|
||||||
|
// Example use:
|
||||||
|
// cy.mount(MyComponent)
|
||||||
37
cypress/utils/sampleBoard.js
Normal file
37
cypress/utils/sampleBoard.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const sampleBoard = (title = 'MyTestBoard') => {
|
||||||
|
return {
|
||||||
|
title: title,
|
||||||
|
color: '00ff00',
|
||||||
|
stacks: [
|
||||||
|
{
|
||||||
|
title: 'TestList',
|
||||||
|
cards: [
|
||||||
|
{
|
||||||
|
title: 'Hello world',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li v-for="acl in board.acl" :key="acl.id">
|
<li v-for="acl in board.acl" :key="acl.id" :data-cy="'acl-participant:' + acl.participant.uid">
|
||||||
<NcAvatar v-if="acl.type===0" :user="acl.participant.uid" />
|
<NcAvatar v-if="acl.type===0" :user="acl.participant.uid" />
|
||||||
<div v-if="acl.type===1" class="avatardiv icon icon-group" />
|
<div v-if="acl.type===1" class="avatardiv icon icon-group" />
|
||||||
<div v-if="acl.type===7" class="avatardiv icon icon-circles" />
|
<div v-if="acl.type===7" class="avatardiv icon icon-circles" />
|
||||||
@@ -41,20 +41,35 @@
|
|||||||
<span v-if="acl.type===7">{{ t('deck', '(Circle)') }}</span>
|
<span v-if="acl.type===7">{{ t('deck', '(Circle)') }}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<NcActionCheckbox v-if="!(isCurrentUser(acl.participant.uid) && acl.type === 0) && (canManage || (canEdit && canShare))" :checked="acl.permissionEdit" @change="clickEditAcl(acl)">
|
<NcActionCheckbox v-if="!(isCurrentUser(acl.participant.uid) && acl.type === 0) && (canManage || (canEdit && canShare))"
|
||||||
|
:checked="acl.permissionEdit"
|
||||||
|
data-cy="action:permission-edit"
|
||||||
|
@change="clickEditAcl(acl)">
|
||||||
{{ t('deck', 'Can edit') }}
|
{{ t('deck', 'Can edit') }}
|
||||||
</NcActionCheckbox>
|
</NcActionCheckbox>
|
||||||
<NcActions v-if="!(isCurrentUser(acl.participant.uid) && acl.type === 0)" :force-menu="true">
|
<NcActions v-if="!(isCurrentUser(acl.participant.uid) && acl.type === 0)" :force-menu="true">
|
||||||
<NcActionCheckbox v-if="canManage || canShare" :checked="acl.permissionShare" @change="clickShareAcl(acl)">
|
<NcActionCheckbox v-if="canManage || canShare"
|
||||||
|
:checked="acl.permissionShare"
|
||||||
|
data-cy="action:permission-share"
|
||||||
|
@change="clickShareAcl(acl)">
|
||||||
{{ t('deck', 'Can share') }}
|
{{ t('deck', 'Can share') }}
|
||||||
</NcActionCheckbox>
|
</NcActionCheckbox>
|
||||||
<NcActionCheckbox v-if="canManage" :checked="acl.permissionManage" @change="clickManageAcl(acl)">
|
<NcActionCheckbox v-if="canManage"
|
||||||
|
:checked="acl.permissionManage"
|
||||||
|
data-cy="action:permission-manage"
|
||||||
|
@change="clickManageAcl(acl)">
|
||||||
{{ t('deck', 'Can manage') }}
|
{{ t('deck', 'Can manage') }}
|
||||||
</NcActionCheckbox>
|
</NcActionCheckbox>
|
||||||
<NcActionCheckbox v-if="acl.type === 0 && isCurrentUser(board.owner.uid)" :checked="acl.owner" @change="clickTransferOwner(acl.participant.uid)">
|
<NcActionCheckbox v-if="acl.type === 0 && isCurrentUser(board.owner.uid)"
|
||||||
|
:checked="acl.owner"
|
||||||
|
data-cy="action:permission-owner"
|
||||||
|
@change="clickTransferOwner(acl.participant.uid)">
|
||||||
{{ t('deck', 'Owner') }}
|
{{ t('deck', 'Owner') }}
|
||||||
</NcActionCheckbox>
|
</NcActionCheckbox>
|
||||||
<NcActionButton v-if="canManage" icon="icon-delete" @click="clickDeleteAcl(acl)">
|
<NcActionButton v-if="canManage"
|
||||||
|
icon="icon-delete"
|
||||||
|
data-cy="action:acl-delete"
|
||||||
|
@click="clickDeleteAcl(acl)">
|
||||||
{{ t('deck', 'Delete') }}
|
{{ t('deck', 'Delete') }}
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
<NcActions v-if="canEdit && !showArchived && !isArchived">
|
<NcActions v-if="canEdit && !showArchived && !isArchived">
|
||||||
<NcActionButton icon="icon-add" @click.stop="showAddCard=true">
|
<NcActionButton icon="icon-add" data-cy="action:add-card" @click.stop="showAddCard=true">
|
||||||
{{ t('deck', 'Add card') }}
|
{{ t('deck', 'Add card') }}
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
</NcActions>
|
</NcActions>
|
||||||
|
|||||||
Reference in New Issue
Block a user