test(Cypress): Add e2e tests for sharing basics and navigation

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl
2023-01-02 11:53:21 +01:00
parent 9cc199dc47
commit 4ab70617a5
11 changed files with 195 additions and 47 deletions

View File

@@ -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')
})
}) })

View File

@@ -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(() => {

View File

@@ -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()

View 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()
})
})
})

View File

@@ -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,
}) })
}) })

View File

@@ -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)
})

View 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>

View 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)

View 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',
},
],
},
],
}
}

View File

@@ -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>

View File

@@ -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>