chore(deps): update dependency cypress to v10 (#2015)
Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2015 Co-authored-by: renovate <renovatebot@kolaente.de> Co-committed-by: renovate <renovatebot@kolaente.de>
This commit is contained in:
56
cypress/e2e/list/list-history.spec.js
Normal file
56
cypress/e2e/list/list-history.spec.js
Normal file
@ -0,0 +1,56 @@
|
||||
import {ListFactory} from '../../factories/list'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
import {prepareLists} from './prepareLists'
|
||||
|
||||
describe('List History', () => {
|
||||
prepareLists()
|
||||
|
||||
it('should show a list history on the home page', () => {
|
||||
cy.intercept(Cypress.env('API_URL') + '/namespaces*').as('loadNamespaces')
|
||||
cy.intercept(Cypress.env('API_URL') + '/lists/*').as('loadList')
|
||||
|
||||
const lists = ListFactory.create(6)
|
||||
|
||||
cy.visit('/')
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.get('body')
|
||||
.should('not.contain', 'Last viewed')
|
||||
|
||||
cy.visit(`/lists/${lists[0].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
cy.visit(`/lists/${lists[1].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
cy.visit(`/lists/${lists[2].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
cy.visit(`/lists/${lists[3].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
cy.visit(`/lists/${lists[4].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
cy.visit(`/lists/${lists[5].id}`)
|
||||
cy.wait('@loadNamespaces')
|
||||
cy.wait('@loadList')
|
||||
|
||||
// cy.visit('/')
|
||||
// cy.wait('@loadNamespaces')
|
||||
// Not using cy.visit here to work around the redirect issue fixed in #1337
|
||||
cy.get('nav.menu.top-menu a')
|
||||
.contains('Overview')
|
||||
.click()
|
||||
|
||||
cy.get('body')
|
||||
.should('contain', 'Last viewed')
|
||||
cy.get('.list-cards-wrapper-2-rows')
|
||||
.should('not.contain', lists[0].title)
|
||||
.should('contain', lists[1].title)
|
||||
.should('contain', lists[2].title)
|
||||
.should('contain', lists[3].title)
|
||||
.should('contain', lists[4].title)
|
||||
.should('contain', lists[5].title)
|
||||
})
|
||||
})
|
76
cypress/e2e/list/list-view-gantt.spec.js
Normal file
76
cypress/e2e/list/list-view-gantt.spec.js
Normal file
@ -0,0 +1,76 @@
|
||||
import {formatISO, format} from 'date-fns'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {prepareLists} from './prepareLists'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('List View Gantt', () => {
|
||||
prepareLists()
|
||||
|
||||
it('Hides tasks with no dates', () => {
|
||||
const tasks = TaskFactory.create(1)
|
||||
cy.visit('/lists/1/gantt')
|
||||
|
||||
cy.get('.gantt-chart .tasks')
|
||||
.should('not.contain', tasks[0].title)
|
||||
})
|
||||
|
||||
it('Shows tasks from the current and next month', () => {
|
||||
const now = new Date()
|
||||
const nextMonth = now
|
||||
nextMonth.setDate(1)
|
||||
nextMonth.setMonth(now.getMonth() + 1)
|
||||
|
||||
cy.visit('/lists/1/gantt')
|
||||
|
||||
cy.get('.gantt-chart .months')
|
||||
.should('contain', format(now, 'MMMM'))
|
||||
.should('contain', format(nextMonth, 'MMMM'))
|
||||
})
|
||||
|
||||
it('Shows tasks with dates', () => {
|
||||
const now = new Date()
|
||||
const tasks = TaskFactory.create(1, {
|
||||
start_date: formatISO(now),
|
||||
end_date: formatISO(now.setDate(now.getDate() + 4))
|
||||
})
|
||||
cy.visit('/lists/1/gantt')
|
||||
|
||||
cy.get('.gantt-chart .tasks')
|
||||
.should('not.be.empty')
|
||||
cy.get('.gantt-chart .tasks')
|
||||
.should('contain', tasks[0].title)
|
||||
})
|
||||
|
||||
it('Shows tasks with no dates after enabling them', () => {
|
||||
TaskFactory.create(1, {
|
||||
start_date: null,
|
||||
end_date: null,
|
||||
})
|
||||
cy.visit('/lists/1/gantt')
|
||||
|
||||
cy.get('.gantt-options .fancycheckbox')
|
||||
.contains('Show tasks which don\'t have dates set')
|
||||
.click()
|
||||
|
||||
cy.get('.gantt-chart .tasks')
|
||||
.should('not.be.empty')
|
||||
cy.get('.gantt-chart .tasks .task.nodate')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Drags a task around', () => {
|
||||
const now = new Date()
|
||||
TaskFactory.create(1, {
|
||||
start_date: formatISO(now),
|
||||
end_date: formatISO(now.setDate(now.getDate() + 4))
|
||||
})
|
||||
cy.visit('/lists/1/gantt')
|
||||
|
||||
cy.get('.gantt-chart .tasks .task')
|
||||
.first()
|
||||
.trigger('mousedown', {which: 1})
|
||||
.trigger('mousemove', {clientX: 500, clientY: 0})
|
||||
.trigger('mouseup', {force: true})
|
||||
})
|
||||
})
|
196
cypress/e2e/list/list-view-kanban.spec.js
Normal file
196
cypress/e2e/list/list-view-kanban.spec.js
Normal file
@ -0,0 +1,196 @@
|
||||
import {BucketFactory} from '../../factories/bucket'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {prepareLists} from './prepareLists'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('List View Kanban', () => {
|
||||
let buckets
|
||||
prepareLists()
|
||||
|
||||
beforeEach(() => {
|
||||
buckets = BucketFactory.create(2)
|
||||
})
|
||||
|
||||
it('Shows all buckets with their tasks', () => {
|
||||
const data = TaskFactory.create(10, {
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains(buckets[0].title)
|
||||
.should('exist')
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains(buckets[1].title)
|
||||
.should('exist')
|
||||
cy.get('.kanban .bucket')
|
||||
.first()
|
||||
.should('contain', data[0].title)
|
||||
})
|
||||
|
||||
it('Can add a new task to a bucket', () => {
|
||||
TaskFactory.create(2, {
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket')
|
||||
.contains(buckets[0].title)
|
||||
.get('.bucket-footer .button')
|
||||
.contains('Add another task')
|
||||
.click()
|
||||
cy.get('.kanban .bucket')
|
||||
.contains(buckets[0].title)
|
||||
.get('.bucket-footer .field .control input.input')
|
||||
.type('New Task{enter}')
|
||||
|
||||
cy.get('.kanban .bucket')
|
||||
.first()
|
||||
.should('contain', 'New Task')
|
||||
})
|
||||
|
||||
it('Can create a new bucket', () => {
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.get('.kanban .bucket.new-bucket .button')
|
||||
.click()
|
||||
cy.get('.kanban .bucket.new-bucket input.input')
|
||||
.type('New Bucket{enter}')
|
||||
|
||||
cy.wait(1000) // Wait for the request to finish
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains('New Bucket')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can set a bucket limit', () => {
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
|
||||
.contains('Limit: Not Set')
|
||||
.click()
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item .field input.input')
|
||||
.first()
|
||||
.type(3)
|
||||
cy.get('[data-cy="setBucketLimit"]')
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.kanban .bucket .bucket-header span.limit')
|
||||
.contains('0/3')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can rename a bucket', () => {
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .bucket-header .title')
|
||||
.first()
|
||||
.type('{selectall}New Bucket Title{enter}')
|
||||
cy.get('.kanban .bucket .bucket-header .title')
|
||||
.first()
|
||||
.should('contain', 'New Bucket Title')
|
||||
})
|
||||
|
||||
it('Can delete a bucket', () => {
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .bucket-header .dropdown.options .dropdown-trigger')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.kanban .bucket .bucket-header .dropdown.options .dropdown-menu .dropdown-item')
|
||||
.contains('Delete')
|
||||
.click()
|
||||
cy.get('.modal-mask .modal-container .modal-content .header')
|
||||
.should('contain', 'Delete the bucket')
|
||||
cy.get('.modal-mask .modal-container .modal-content .actions .button')
|
||||
.contains('Do it!')
|
||||
.click()
|
||||
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains(buckets[0].title)
|
||||
.should('not.exist')
|
||||
cy.get('.kanban .bucket .title')
|
||||
.contains(buckets[1].title)
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can drag tasks around', () => {
|
||||
const tasks = TaskFactory.create(2, {
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .tasks .task')
|
||||
.contains(tasks[0].title)
|
||||
.first()
|
||||
.drag('.kanban .bucket:nth-child(2) .tasks')
|
||||
|
||||
cy.get('.kanban .bucket:nth-child(2) .tasks')
|
||||
.should('contain', tasks[0].title)
|
||||
cy.get('.kanban .bucket:nth-child(1) .tasks')
|
||||
.should('not.contain', tasks[0].title)
|
||||
})
|
||||
|
||||
it('Should navigate to the task when the task card is clicked', () => {
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .tasks .task')
|
||||
.contains(tasks[0].title)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', `/tasks/${tasks[0].id}`, { timeout: 1000 })
|
||||
})
|
||||
|
||||
it('Should remove a task from the kanban board when moving it to another list', () => {
|
||||
const lists = ListFactory.create(2)
|
||||
BucketFactory.create(2, {
|
||||
list_id: '{increment}',
|
||||
})
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
list_id: 1,
|
||||
bucket_id: 1,
|
||||
})
|
||||
const task = tasks[0]
|
||||
cy.visit('/lists/1/kanban')
|
||||
|
||||
cy.getSettled('.kanban .bucket .tasks .task')
|
||||
.contains(task.title)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .action-buttons .button', { timeout: 3000 })
|
||||
.contains('Move')
|
||||
.click()
|
||||
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
|
||||
.type(`${lists[1].title}{enter}`)
|
||||
// The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
|
||||
// presses enter and we can't simulate pressing on enter to select the item.
|
||||
cy.get('.task-view .content.details .field .multiselect.control .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 1000 })
|
||||
.should('contain', 'Success')
|
||||
cy.go('back')
|
||||
cy.get('.kanban .bucket')
|
||||
.should('not.contain', task.title)
|
||||
})
|
||||
})
|
107
cypress/e2e/list/list-view-list.spec.js
Normal file
107
cypress/e2e/list/list-view-list.spec.js
Normal file
@ -0,0 +1,107 @@
|
||||
import {UserListFactory} from '../../factories/users_list'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {UserFactory} from '../../factories/user'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {prepareLists} from './prepareLists'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('List View List', () => {
|
||||
prepareLists()
|
||||
|
||||
it('Should be an empty list', () => {
|
||||
cy.visit('/lists/1')
|
||||
cy.url()
|
||||
.should('contain', '/lists/1/list')
|
||||
cy.get('.list-title h1')
|
||||
.should('contain', 'First List')
|
||||
cy.get('.list-title .dropdown')
|
||||
.should('exist')
|
||||
cy.get('p')
|
||||
.contains('This list is currently empty.')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Should create a new task', () => {
|
||||
const newTaskTitle = 'New task'
|
||||
|
||||
cy.visit('/lists/1')
|
||||
cy.get('.task-add textarea')
|
||||
.type(newTaskTitle+'{enter}')
|
||||
cy.get('.tasks')
|
||||
.should('contain.text', newTaskTitle)
|
||||
})
|
||||
|
||||
it('Should navigate to the task when the title is clicked', () => {
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
list_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/list')
|
||||
|
||||
cy.get('.tasks .task .tasktext')
|
||||
.contains(tasks[0].title)
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', `/tasks/${tasks[0].id}`)
|
||||
})
|
||||
|
||||
it('Should not see any elements for a list which is shared read only', () => {
|
||||
UserFactory.create(2)
|
||||
UserListFactory.create(1, {
|
||||
list_id: 2,
|
||||
user_id: 1,
|
||||
right: 0,
|
||||
})
|
||||
const lists = ListFactory.create(2, {
|
||||
owner_id: '{increment}',
|
||||
namespace_id: '{increment}',
|
||||
})
|
||||
cy.visit(`/lists/${lists[1].id}/`)
|
||||
|
||||
cy.get('.list-title a.icon')
|
||||
.should('not.exist')
|
||||
cy.get('input.input[placeholder="Add a new task..."')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('Should only show the color of a list in the navigation and not in the list view', () => {
|
||||
const lists = ListFactory.create(1, {
|
||||
hex_color: '00db60',
|
||||
})
|
||||
TaskFactory.create(10, {
|
||||
list_id: lists[0].id,
|
||||
})
|
||||
cy.visit(`/lists/${lists[0].id}/`)
|
||||
|
||||
cy.get('.menu-list li .list-menu-link .color-bubble')
|
||||
.should('have.css', 'background-color', 'rgb(0, 219, 96)')
|
||||
cy.get('.tasks-container .tasks .color-bubble')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('Should paginate for > 50 tasks', () => {
|
||||
const tasks = TaskFactory.create(100, {
|
||||
id: '{increment}',
|
||||
title: i => `task${i}`,
|
||||
list_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/list')
|
||||
|
||||
cy.get('.tasks-container .tasks')
|
||||
.should('contain', tasks[99].title)
|
||||
|
||||
cy.get('.card-content .pagination .pagination-link')
|
||||
.contains('2')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', '?page=2')
|
||||
cy.get('.tasks-container .tasks')
|
||||
.should('contain', tasks[1].title)
|
||||
cy.get('.tasks-container .tasks')
|
||||
.should('not.contain', tasks[99].title)
|
||||
})
|
||||
})
|
52
cypress/e2e/list/list-view-table.spec.js
Normal file
52
cypress/e2e/list/list-view-table.spec.js
Normal file
@ -0,0 +1,52 @@
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('List View Table', () => {
|
||||
it('Should show a table with tasks', () => {
|
||||
const tasks = TaskFactory.create(1)
|
||||
cy.visit('/lists/1/table')
|
||||
|
||||
cy.get('.list-table table.table')
|
||||
.should('exist')
|
||||
cy.get('.list-table table.table')
|
||||
.should('contain', tasks[0].title)
|
||||
})
|
||||
|
||||
it('Should have working column switches', () => {
|
||||
TaskFactory.create(1)
|
||||
cy.visit('/lists/1/table')
|
||||
|
||||
cy.get('.list-table .filter-container .items .button')
|
||||
.contains('Columns')
|
||||
.click()
|
||||
cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
||||
.contains('Priority')
|
||||
.click()
|
||||
cy.get('.list-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
|
||||
.contains('Done')
|
||||
.click()
|
||||
|
||||
cy.get('.list-table table.table th')
|
||||
.contains('Priority')
|
||||
.should('exist')
|
||||
cy.get('.list-table table.table th')
|
||||
.contains('Done')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('Should navigate to the task when the title is clicked', () => {
|
||||
const tasks = TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
list_id: 1,
|
||||
})
|
||||
cy.visit('/lists/1/table')
|
||||
|
||||
cy.get('.list-table table.table')
|
||||
.contains(tasks[0].title)
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', `/tasks/${tasks[0].id}`)
|
||||
})
|
||||
})
|
101
cypress/e2e/list/list.spec.js
Normal file
101
cypress/e2e/list/list.spec.js
Normal file
@ -0,0 +1,101 @@
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {prepareLists} from './prepareLists'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('Lists', () => {
|
||||
let lists
|
||||
prepareLists((newLists) => (lists = newLists))
|
||||
|
||||
it('Should create a new list', () => {
|
||||
cy.visit('/')
|
||||
cy.get('.namespace-title .dropdown-trigger')
|
||||
.click()
|
||||
cy.get('.namespace-title .dropdown .dropdown-item')
|
||||
.contains('New list')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/lists/new/1')
|
||||
cy.get('.card-header-title')
|
||||
.contains('New list')
|
||||
cy.get('input.input')
|
||||
.type('New List')
|
||||
cy.get('.button')
|
||||
.contains('Create')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 1000 }) // Waiting until the request to create the new list is done
|
||||
.should('contain', 'Success')
|
||||
cy.url()
|
||||
.should('contain', '/lists/')
|
||||
cy.get('.list-title h1')
|
||||
.should('contain', 'New List')
|
||||
})
|
||||
|
||||
it('Should redirect to a specific list view after visited', () => {
|
||||
cy.visit('/lists/1/kanban')
|
||||
cy.url()
|
||||
.should('contain', '/lists/1/kanban')
|
||||
cy.visit('/lists/1')
|
||||
cy.url()
|
||||
.should('contain', '/lists/1/kanban')
|
||||
})
|
||||
|
||||
it('Should rename the list in all places', () => {
|
||||
TaskFactory.create(5, {
|
||||
id: '{increment}',
|
||||
list_id: 1,
|
||||
})
|
||||
const newListName = 'New list name'
|
||||
|
||||
cy.visit('/lists/1')
|
||||
cy.get('.list-title h1')
|
||||
.should('contain', 'First List')
|
||||
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child .dropdown .dropdown-trigger')
|
||||
.click()
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child .dropdown .dropdown-content')
|
||||
.contains('Edit')
|
||||
.click()
|
||||
cy.get('#title')
|
||||
.type(`{selectall}${newListName}`)
|
||||
cy.get('footer.modal-card-foot .button')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.list-title h1')
|
||||
.should('contain', newListName)
|
||||
.should('not.contain', lists[0].title)
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child')
|
||||
.should('contain', newListName)
|
||||
.should('not.contain', lists[0].title)
|
||||
cy.visit('/')
|
||||
cy.get('.card-content')
|
||||
.should('contain', newListName)
|
||||
.should('not.contain', lists[0].title)
|
||||
})
|
||||
|
||||
it('Should remove a list', () => {
|
||||
cy.visit(`/lists/${lists[0].id}`)
|
||||
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child .dropdown .dropdown-trigger')
|
||||
.click()
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list li:first-child .dropdown .dropdown-content')
|
||||
.contains('Delete')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/settings/delete')
|
||||
cy.get('[data-cy="modalPrimary"]')
|
||||
.contains('Do it')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.namespace-container .menu.namespaces-lists .more-container .menu-list')
|
||||
.should('not.contain', lists[0].title)
|
||||
cy.location('pathname')
|
||||
.should('equal', '/')
|
||||
})
|
||||
})
|
145
cypress/e2e/list/namespaces.spec.js
Normal file
145
cypress/e2e/list/namespaces.spec.js
Normal file
@ -0,0 +1,145 @@
|
||||
import {UserFactory} from '../../factories/user'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
|
||||
describe('Namepaces', () => {
|
||||
let namespaces
|
||||
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1)
|
||||
namespaces = NamespaceFactory.create(1)
|
||||
ListFactory.create(1)
|
||||
})
|
||||
|
||||
it('Should be all there', () => {
|
||||
cy.visit('/namespaces')
|
||||
cy.get('[data-cy="namespace-title"]')
|
||||
.should('contain', namespaces[0].title)
|
||||
})
|
||||
|
||||
it('Should create a new Namespace', () => {
|
||||
const newNamespaceTitle = 'New Namespace'
|
||||
|
||||
cy.visit('/namespaces')
|
||||
cy.get('[data-cy="new-namespace"]')
|
||||
.should('contain', 'New namespace')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', '/namespaces/new')
|
||||
cy.get('.card-header-title')
|
||||
.should('contain', 'New namespace')
|
||||
cy.get('input.input')
|
||||
.type(newNamespaceTitle)
|
||||
cy.get('.button')
|
||||
.contains('Create')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.namespace-container')
|
||||
.should('contain', newNamespaceTitle)
|
||||
cy.url()
|
||||
.should('contain', '/namespaces')
|
||||
})
|
||||
|
||||
it('Should rename the namespace all places', () => {
|
||||
const newNamespaces = NamespaceFactory.create(5)
|
||||
const newNamespaceName = 'New namespace name'
|
||||
|
||||
cy.visit('/namespaces')
|
||||
|
||||
cy.get(`.namespace-container .menu.namespaces-lists .namespace-title:contains(${newNamespaces[0].title}) .dropdown .dropdown-trigger`)
|
||||
.click()
|
||||
cy.get('.namespace-container .menu.namespaces-lists .namespace-title .dropdown .dropdown-content')
|
||||
.contains('Edit')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/settings/edit')
|
||||
cy.get('#namespacetext')
|
||||
.invoke('val')
|
||||
.should('equal', newNamespaces[0].title) // wait until the namespace data is loaded
|
||||
cy.get('#namespacetext')
|
||||
.type(`{selectall}${newNamespaceName}`)
|
||||
cy.get('footer.modal-card-foot .button')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 1000 })
|
||||
.should('contain', 'Success')
|
||||
cy.get('.namespace-container .menu.namespaces-lists')
|
||||
.should('contain', newNamespaceName)
|
||||
.should('not.contain', newNamespaces[0].title)
|
||||
cy.get('[data-cy="namespaces-list"]')
|
||||
.should('contain', newNamespaceName)
|
||||
.should('not.contain', newNamespaces[0].title)
|
||||
})
|
||||
|
||||
it('Should remove a namespace when deleting it', () => {
|
||||
const newNamespaces = NamespaceFactory.create(5)
|
||||
|
||||
cy.visit('/')
|
||||
|
||||
cy.get(`.namespace-container .menu.namespaces-lists .namespace-title:contains(${newNamespaces[0].title}) .dropdown .dropdown-trigger`)
|
||||
.click()
|
||||
cy.get('.namespace-container .menu.namespaces-lists .namespace-title .dropdown .dropdown-content')
|
||||
.contains('Delete')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/settings/delete')
|
||||
cy.get('[data-cy="modalPrimary"]')
|
||||
.contains('Do it')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.namespace-container .menu.namespaces-lists')
|
||||
.should('not.contain', newNamespaces[0].title)
|
||||
})
|
||||
|
||||
it('Should not show archived lists & namespaces if the filter is not checked', () => {
|
||||
const n = NamespaceFactory.create(1, {
|
||||
id: 2,
|
||||
is_archived: true,
|
||||
}, false)
|
||||
ListFactory.create(1, {
|
||||
id: 2,
|
||||
namespace_id: n[0].id,
|
||||
}, false)
|
||||
|
||||
ListFactory.create(1, {
|
||||
id: 3,
|
||||
is_archived: true,
|
||||
}, false)
|
||||
|
||||
// Initial
|
||||
cy.visit('/namespaces')
|
||||
cy.get('.namespace')
|
||||
.should('not.contain', 'Archived')
|
||||
|
||||
// Show archived
|
||||
cy.get('[data-cy="show-archived-check"] label.check span')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('[data-cy="show-archived-check"] input')
|
||||
.should('be.checked')
|
||||
cy.get('.namespace')
|
||||
.should('contain', 'Archived')
|
||||
|
||||
// Don't show archived
|
||||
cy.get('[data-cy="show-archived-check"] label.check span')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('[data-cy="show-archived-check"] input')
|
||||
.should('not.be.checked')
|
||||
|
||||
// Second time visiting after unchecking
|
||||
cy.visit('/namespaces')
|
||||
cy.get('[data-cy="show-archived-check"] input')
|
||||
.should('not.be.checked')
|
||||
cy.get('.namespace')
|
||||
.should('not.contain', 'Archived')
|
||||
})
|
||||
})
|
16
cypress/e2e/list/prepareLists.js
Normal file
16
cypress/e2e/list/prepareLists.js
Normal file
@ -0,0 +1,16 @@
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {UserFactory} from '../../factories/user'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
|
||||
export function prepareLists(setLists = () => {}) {
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1)
|
||||
NamespaceFactory.create(1)
|
||||
const lists = ListFactory.create(1, {
|
||||
title: 'First List'
|
||||
})
|
||||
setLists(lists)
|
||||
TaskFactory.truncate()
|
||||
})
|
||||
}
|
37
cypress/e2e/misc/editor.spec.js
Normal file
37
cypress/e2e/misc/editor.spec.js
Normal file
@ -0,0 +1,37 @@
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
import {UserListFactory} from '../../factories/users_list'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('Editor', () => {
|
||||
beforeEach(() => {
|
||||
NamespaceFactory.create(1)
|
||||
const lists = ListFactory.create(1)
|
||||
TaskFactory.truncate()
|
||||
UserListFactory.truncate()
|
||||
})
|
||||
|
||||
it('Has a preview with checkable checkboxes', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
description: `# Test Heading
|
||||
* Bullet 1
|
||||
* Bullet 2
|
||||
|
||||
* [ ] Checklist
|
||||
* [x] Checklist checked
|
||||
`,
|
||||
})
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
cy.get('input[type=checkbox][data-checkbox-num=0]')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
|
||||
.contains('Saved!')
|
||||
.should('exist')
|
||||
cy.get('.preview.content')
|
||||
.should('contain', 'Test Heading')
|
||||
})
|
||||
})
|
29
cypress/e2e/misc/menu.spec.js
Normal file
29
cypress/e2e/misc/menu.spec.js
Normal file
@ -0,0 +1,29 @@
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('The Menu', () => {
|
||||
it('Is visible by default on desktop', () => {
|
||||
cy.get('.namespace-container')
|
||||
.should('have.class', 'is-active')
|
||||
})
|
||||
|
||||
it('Can be hidden on desktop', () => {
|
||||
cy.get('button.menu-show-button:visible')
|
||||
.click()
|
||||
cy.get('.namespace-container')
|
||||
.should('not.have.class', 'is-active')
|
||||
})
|
||||
|
||||
it('Is hidden by default on mobile', () => {
|
||||
cy.viewport('iphone-8')
|
||||
cy.get('.namespace-container')
|
||||
.should('not.have.class', 'is-active')
|
||||
})
|
||||
|
||||
it('Is can be shown on mobile', () => {
|
||||
cy.viewport('iphone-8')
|
||||
cy.get('button.menu-show-button:visible')
|
||||
.click()
|
||||
cy.get('.namespace-container')
|
||||
.should('have.class', 'is-active')
|
||||
})
|
||||
})
|
25
cypress/e2e/sharing/linkShare.spec.js
Normal file
25
cypress/e2e/sharing/linkShare.spec.js
Normal file
@ -0,0 +1,25 @@
|
||||
import {LinkShareFactory} from '../../factories/link_sharing'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
|
||||
describe('Link shares', () => {
|
||||
it('Can view a link share', () => {
|
||||
const lists = ListFactory.create(1)
|
||||
const tasks = TaskFactory.create(10, {
|
||||
list_id: lists[0].id
|
||||
})
|
||||
const linkShares = LinkShareFactory.create(1, {
|
||||
list_id: lists[0].id,
|
||||
right: 0,
|
||||
})
|
||||
|
||||
cy.visit(`/share/${linkShares[0].hash}/auth`)
|
||||
|
||||
cy.get('h1.title')
|
||||
.should('contain', lists[0].title)
|
||||
cy.get('input.input[placeholder="Add a new task..."')
|
||||
.should('not.exist')
|
||||
cy.get('.tasks')
|
||||
.should('contain', tasks[0].title)
|
||||
})
|
||||
})
|
128
cypress/e2e/sharing/team.spec.js
Normal file
128
cypress/e2e/sharing/team.spec.js
Normal file
@ -0,0 +1,128 @@
|
||||
import {TeamFactory} from '../../factories/team'
|
||||
import {TeamMemberFactory} from '../../factories/team_member'
|
||||
import {UserFactory} from '../../factories/user'
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('Team', () => {
|
||||
it('Creates a new team', () => {
|
||||
TeamFactory.truncate()
|
||||
cy.visit('/teams')
|
||||
|
||||
const newTeamName = 'New Team'
|
||||
|
||||
cy.get('a.button')
|
||||
.contains('Create a new team')
|
||||
.click()
|
||||
cy.url()
|
||||
.should('contain', '/teams/new')
|
||||
cy.get('.card-header-title')
|
||||
.contains('Create a new team')
|
||||
cy.get('input.input')
|
||||
.type(newTeamName)
|
||||
cy.get('.button')
|
||||
.contains('Create')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', '/edit')
|
||||
cy.get('input#teamtext')
|
||||
.should('have.value', newTeamName)
|
||||
})
|
||||
|
||||
it('Shows all teams', () => {
|
||||
TeamMemberFactory.create(10, {
|
||||
team_id: '{increment}',
|
||||
})
|
||||
const teams = TeamFactory.create(10, {
|
||||
id: '{increment}',
|
||||
})
|
||||
|
||||
cy.visit('/teams')
|
||||
|
||||
cy.get('.teams.box')
|
||||
.should('not.be.empty')
|
||||
teams.forEach(t => {
|
||||
cy.get('.teams.box')
|
||||
.should('contain', t.name)
|
||||
})
|
||||
})
|
||||
|
||||
it('Allows an admin to edit the team', () => {
|
||||
TeamMemberFactory.create(1, {
|
||||
team_id: 1,
|
||||
admin: true,
|
||||
})
|
||||
const teams = TeamFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
|
||||
cy.visit('/teams/1/edit')
|
||||
cy.get('.card input.input')
|
||||
.first()
|
||||
.type('{selectall}New Team Name')
|
||||
|
||||
cy.get('.card .button')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
cy.get('table.table td')
|
||||
.contains('Admin')
|
||||
.should('exist')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Does not allow a normal user to edit the team', () => {
|
||||
TeamMemberFactory.create(1, {
|
||||
team_id: 1,
|
||||
admin: false,
|
||||
})
|
||||
const teams = TeamFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
|
||||
cy.visit('/teams/1/edit')
|
||||
cy.get('.card input.input')
|
||||
.should('not.exist')
|
||||
cy.get('table.table td')
|
||||
.contains('Member')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Allows an admin to add members to the team', () => {
|
||||
TeamMemberFactory.create(1, {
|
||||
team_id: 1,
|
||||
admin: true,
|
||||
})
|
||||
TeamFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
const users = UserFactory.create(5)
|
||||
|
||||
cy.visit('/teams/1/edit')
|
||||
cy.get('.card')
|
||||
.contains('Team Members')
|
||||
.get('.card-content .multiselect .input-wrapper input')
|
||||
.type(users[1].username)
|
||||
cy.get('.card')
|
||||
.contains('Team Members')
|
||||
.get('.card-content .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.card')
|
||||
.contains('Team Members')
|
||||
.get('.card-content .button')
|
||||
.contains('Add to team')
|
||||
.click()
|
||||
|
||||
cy.get('table.table td')
|
||||
.contains('Admin')
|
||||
.should('exist')
|
||||
cy.get('table.table tr')
|
||||
.should('contain', users[1].username)
|
||||
.should('contain', 'Member')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
})
|
130
cypress/e2e/task/overview.spec.js
Normal file
130
cypress/e2e/task/overview.spec.js
Normal file
@ -0,0 +1,130 @@
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {seed} from '../../support/seed'
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {formatISO} from 'date-fns'
|
||||
import {UserFactory} from '../../factories/user'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
import {BucketFactory} from '../../factories/bucket'
|
||||
import {updateUserSettings} from '../../support/updateUserSettings'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
function seedTasks(numberOfTasks = 100, startDueDate = new Date()) {
|
||||
UserFactory.create(1)
|
||||
NamespaceFactory.create(1)
|
||||
const list = ListFactory.create()[0]
|
||||
BucketFactory.create(1, {
|
||||
list_id: list.id,
|
||||
})
|
||||
const tasks = []
|
||||
let dueDate = startDueDate
|
||||
for (let i = 0; i < numberOfTasks; i++) {
|
||||
const now = new Date()
|
||||
dueDate = (new Date(dueDate.valueOf())).setDate((new Date(dueDate.valueOf())).getDate() + 2)
|
||||
tasks.push({
|
||||
id: i + 1,
|
||||
list_id: list.id,
|
||||
done: false,
|
||||
created_by_id: 1,
|
||||
title: 'Test Task ' + i,
|
||||
index: i + 1,
|
||||
due_date: formatISO(dueDate),
|
||||
created: formatISO(now),
|
||||
updated: formatISO(now),
|
||||
})
|
||||
}
|
||||
seed(TaskFactory.table, tasks)
|
||||
return {tasks, list}
|
||||
}
|
||||
|
||||
describe('Home Page Task Overview', () => {
|
||||
it('Should show tasks with a near due date first on the home page overview', () => {
|
||||
const {tasks} = seedTasks()
|
||||
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.each(([task], index) => {
|
||||
expect(task.innerText).to.contain(tasks[index].title)
|
||||
})
|
||||
})
|
||||
|
||||
it('Should show overdue tasks first, then show other tasks', () => {
|
||||
const oldDate = (new Date()).setDate((new Date()).getDate() - 14)
|
||||
const {tasks} = seedTasks(100, oldDate)
|
||||
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.each(([task], index) => {
|
||||
expect(task.innerText).to.contain(tasks[index].title)
|
||||
})
|
||||
})
|
||||
|
||||
it('Should show a new task with a very soon due date at the top', () => {
|
||||
const {tasks} = seedTasks()
|
||||
const newTaskTitle = 'New Task'
|
||||
|
||||
cy.visit('/')
|
||||
|
||||
TaskFactory.create(1, {
|
||||
id: 999,
|
||||
title: newTaskTitle,
|
||||
due_date: formatISO(new Date()),
|
||||
}, false)
|
||||
|
||||
cy.visit(`/lists/${tasks[0].list_id}/list`)
|
||||
cy.get('.tasks .task')
|
||||
.first()
|
||||
.should('contain.text', newTaskTitle)
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.first()
|
||||
.should('contain.text', newTaskTitle)
|
||||
})
|
||||
|
||||
it('Should not show a new task without a date at the bottom when there are > 50 tasks', () => {
|
||||
// We're not using the api here to create the task in order to verify the flow
|
||||
const {tasks} = seedTasks()
|
||||
const newTaskTitle = 'New Task'
|
||||
|
||||
cy.visit('/')
|
||||
|
||||
cy.visit(`/lists/${tasks[0].list_id}/list`)
|
||||
cy.get('.task-add textarea')
|
||||
.type(newTaskTitle+'{enter}')
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.last()
|
||||
.should('not.contain.text', newTaskTitle)
|
||||
})
|
||||
|
||||
it('Should show a new task without a date at the bottom when there are < 50 tasks', () => {
|
||||
seedTasks(40)
|
||||
const newTaskTitle = 'New Task'
|
||||
TaskFactory.create(1, {
|
||||
id: 999,
|
||||
title: newTaskTitle,
|
||||
}, false)
|
||||
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.last()
|
||||
.should('contain.text', newTaskTitle)
|
||||
})
|
||||
|
||||
it('Should show a task without a due date added via default list at the bottom', () => {
|
||||
const {list} = seedTasks(40)
|
||||
updateUserSettings({
|
||||
default_list_id: list.id,
|
||||
})
|
||||
|
||||
const newTaskTitle = 'New Task'
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('.add-task-textarea')
|
||||
.type(`${newTaskTitle}{enter}`)
|
||||
|
||||
cy.get('[data-cy="showTasks"] .card .task')
|
||||
.last()
|
||||
.should('contain.text', newTaskTitle)
|
||||
})
|
||||
})
|
420
cypress/e2e/task/task.spec.js
Normal file
420
cypress/e2e/task/task.spec.js
Normal file
@ -0,0 +1,420 @@
|
||||
import {formatISO} from 'date-fns'
|
||||
|
||||
import {TaskFactory} from '../../factories/task'
|
||||
import {ListFactory} from '../../factories/list'
|
||||
import {TaskCommentFactory} from '../../factories/task_comment'
|
||||
import {UserFactory} from '../../factories/user'
|
||||
import {NamespaceFactory} from '../../factories/namespace'
|
||||
import {UserListFactory} from '../../factories/users_list'
|
||||
import {TaskAssigneeFactory} from '../../factories/task_assignee'
|
||||
import {LabelFactory} from '../../factories/labels'
|
||||
import {LabelTaskFactory} from '../../factories/label_task'
|
||||
import {BucketFactory} from '../../factories/bucket'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('Task', () => {
|
||||
let namespaces
|
||||
let lists
|
||||
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1)
|
||||
namespaces = NamespaceFactory.create(1)
|
||||
lists = ListFactory.create(1)
|
||||
TaskFactory.truncate()
|
||||
UserListFactory.truncate()
|
||||
})
|
||||
|
||||
it('Should be created new', () => {
|
||||
cy.visit('/lists/1/list')
|
||||
cy.get('.input[placeholder="Add a new task…"')
|
||||
.type('New Task')
|
||||
cy.get('.button')
|
||||
.contains('Add')
|
||||
.click()
|
||||
cy.get('.tasks .task .tasktext')
|
||||
.first()
|
||||
.should('contain', 'New Task')
|
||||
})
|
||||
|
||||
it('Inserts new tasks at the top of the list', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/lists/1/list')
|
||||
cy.get('.list-is-empty-notice')
|
||||
.should('not.exist')
|
||||
cy.get('.input[placeholder="Add a new task…"')
|
||||
.type('New Task')
|
||||
cy.get('.button')
|
||||
.contains('Add')
|
||||
.click()
|
||||
|
||||
cy.wait(1000) // Wait for the request
|
||||
cy.get('.tasks .task .tasktext')
|
||||
.first()
|
||||
.should('contain', 'New Task')
|
||||
})
|
||||
|
||||
it('Marks a task as done', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/lists/1/list')
|
||||
cy.get('.tasks .task .fancycheckbox label.check')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Can add a task to favorites', () => {
|
||||
TaskFactory.create(1)
|
||||
|
||||
cy.visit('/lists/1/list')
|
||||
cy.get('.tasks .task .favorite')
|
||||
.first()
|
||||
.click()
|
||||
cy.get('.menu.namespaces-lists')
|
||||
.should('contain', 'Favorites')
|
||||
})
|
||||
|
||||
describe('Task Detail View', () => {
|
||||
beforeEach(() => {
|
||||
TaskCommentFactory.truncate()
|
||||
})
|
||||
|
||||
it('Shows all task details', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
index: 1,
|
||||
description: 'Lorem ipsum dolor sit amet.'
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view h1.title.input')
|
||||
.should('contain', tasks[0].title)
|
||||
cy.get('.task-view h1.title.task-id')
|
||||
.should('contain', '#1')
|
||||
cy.get('.task-view h6.subtitle')
|
||||
.should('contain', namespaces[0].title)
|
||||
.should('contain', lists[0].title)
|
||||
cy.get('.task-view .details.content.description')
|
||||
.should('contain', tasks[0].description)
|
||||
cy.get('.task-view .action-buttons p.created')
|
||||
.should('contain', 'Created')
|
||||
})
|
||||
|
||||
it('Shows a done label for done tasks', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
index: 1,
|
||||
done: true,
|
||||
done_at: formatISO(new Date())
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .heading .is-done')
|
||||
.should('be.visible')
|
||||
.should('contain', 'Done')
|
||||
cy.get('.task-view .action-buttons p.created')
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
.should('contain', 'Done')
|
||||
})
|
||||
|
||||
it('Can mark a task as done', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
done: false,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Mark task done!')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .heading .is-done')
|
||||
.should('exist')
|
||||
.should('contain', 'Done')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.should('contain', 'Mark as undone')
|
||||
})
|
||||
|
||||
it('Shows a task identifier since the list has one', () => {
|
||||
const lists = ListFactory.create(1, {
|
||||
id: 1,
|
||||
identifier: 'TEST',
|
||||
})
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: lists[0].id,
|
||||
index: 1,
|
||||
})
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view h1.title.task-id')
|
||||
.should('contain', `${lists[0].identifier}-${tasks[0].index}`)
|
||||
})
|
||||
|
||||
it('Can edit the description', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
description: 'Lorem ipsum dolor sit amet.'
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .details.content.description .editor a')
|
||||
.click()
|
||||
cy.get('.task-view .details.content.description .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
|
||||
.type('{selectall}New Description')
|
||||
cy.get('[data-cy="saveEditor"]')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
|
||||
.contains('Saved!')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can add a new comment', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .comments .media.comment .editor .vue-easymde .EasyMDEContainer .CodeMirror-scroll')
|
||||
.should('be.visible')
|
||||
.type('{selectall}New Comment')
|
||||
cy.get('.task-view .comments .media.comment .button:not([disabled])')
|
||||
.contains('Comment')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .comments .media.comment .editor')
|
||||
.should('contain', 'New Comment')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Can move a task to another list', () => {
|
||||
const lists = ListFactory.create(2)
|
||||
BucketFactory.create(2, {
|
||||
list_id: '{increment}'
|
||||
})
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: lists[0].id,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Move')
|
||||
.click()
|
||||
cy.get('.task-view .content.details .field .multiselect.control .input-wrapper input')
|
||||
.type(`${lists[1].title}{enter}`)
|
||||
// The requests happen with a 200ms timeout. Because of that, the results are not yet there when cypress
|
||||
// presses enter and we can't simulate pressing on enter to select the item.
|
||||
cy.get('.task-view .content.details .field .multiselect.control .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.task-view h6.subtitle')
|
||||
.should('contain', namespaces[0].title)
|
||||
.should('contain', lists[1].title)
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Can delete a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.should('be.visible')
|
||||
.contains('Delete')
|
||||
.click()
|
||||
cy.get('.modal-mask .modal-container .modal-content .header')
|
||||
.should('contain', 'Delete this task')
|
||||
cy.get('.modal-mask .modal-container .modal-content .actions .button')
|
||||
.contains('Do it!')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.url()
|
||||
.should('contain', `/lists/${tasks[0].list_id}/`)
|
||||
})
|
||||
|
||||
it('Can add an assignee to a task', () => {
|
||||
const users = UserFactory.create(5)
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
UserListFactory.create(5, {
|
||||
list_id: 1,
|
||||
user_id: '{increment}',
|
||||
})
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('[data-cy="taskDetail.assign"]')
|
||||
.click()
|
||||
cy.get('.task-view .column.assignees .multiselect input')
|
||||
.type(users[1].username)
|
||||
cy.get('.task-view .column.assignees .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .column.assignees .multiselect .input-wrapper span.assignee')
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
it('Can remove an assignee from a task', () => {
|
||||
const users = UserFactory.create(2)
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
UserListFactory.create(5, {
|
||||
list_id: 1,
|
||||
user_id: '{increment}',
|
||||
})
|
||||
TaskAssigneeFactory.create(1, {
|
||||
task_id: tasks[0].id,
|
||||
user_id: users[1].id,
|
||||
})
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .column.assignees .multiselect .input-wrapper span.assignee')
|
||||
.get('a.remove-assignee')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .column.assignees .multiselect .input-wrapper span.assignee')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
it('Can add a new label to a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
LabelFactory.truncate()
|
||||
const newLabelText = 'some new label'
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add Labels')
|
||||
.should('be.visible')
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
.type(newLabelText)
|
||||
cy.get('.task-view .details.labels-list .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
|
||||
.should('exist')
|
||||
.should('contain', newLabelText)
|
||||
})
|
||||
|
||||
it('Can add an existing label to a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Add Labels')
|
||||
.click()
|
||||
cy.get('.task-view .details.labels-list .multiselect input')
|
||||
.type(labels[0].title)
|
||||
cy.get('.task-view .details.labels-list .multiselect .search-results')
|
||||
.children()
|
||||
.first()
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification', { timeout: 4000 })
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper span.tag')
|
||||
.should('exist')
|
||||
.should('contain', labels[0].title)
|
||||
})
|
||||
|
||||
it('Can remove a label from a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
list_id: 1,
|
||||
})
|
||||
const labels = LabelFactory.create(1)
|
||||
LabelTaskFactory.create(1, {
|
||||
task_id: tasks[0].id,
|
||||
label_id: labels[0].id,
|
||||
})
|
||||
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
|
||||
.should('be.visible')
|
||||
.should('contain', labels[0].title)
|
||||
cy.getSettled('.task-view .details.labels-list .multiselect .input-wrapper')
|
||||
.children()
|
||||
.first()
|
||||
.get('[data-cy="taskDetail.removeLabel"]')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.task-view .details.labels-list .multiselect .input-wrapper')
|
||||
.should('not.contain', labels[0].title)
|
||||
})
|
||||
|
||||
it('Can set a due date for a task', () => {
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
done: false,
|
||||
})
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.get('.task-view .action-buttons .button')
|
||||
.contains('Set Due Date')
|
||||
.click()
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Due Date')
|
||||
.get('.date-input .datepicker .show')
|
||||
.click()
|
||||
cy.get('.datepicker .datepicker-popup a')
|
||||
.contains('Tomorrow')
|
||||
.click()
|
||||
cy.get('[data-cy="closeDatepicker"]')
|
||||
.contains('Confirm')
|
||||
.click()
|
||||
|
||||
cy.get('.task-view .columns.details .column')
|
||||
.contains('Due Date')
|
||||
.get('.date-input .datepicker-popup')
|
||||
.should('not.exist')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
})
|
||||
})
|
58
cypress/e2e/user/login.spec.js
Normal file
58
cypress/e2e/user/login.spec.js
Normal file
@ -0,0 +1,58 @@
|
||||
import {UserFactory} from '../../factories/user'
|
||||
|
||||
const testAndAssertFailed = fixture => {
|
||||
cy.visit('/login')
|
||||
cy.get('input[id=username]').type(fixture.username)
|
||||
cy.get('input[id=password]').type(fixture.password)
|
||||
cy.get('.button').contains('Login').click()
|
||||
|
||||
cy.wait(5000) // It can take waaaayy too long to log the user in
|
||||
cy.url().should('include', '/')
|
||||
cy.get('div.message.danger').contains('Wrong username or password.')
|
||||
}
|
||||
|
||||
context('Login', () => {
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1, {
|
||||
username: 'test',
|
||||
})
|
||||
cy.visit('/', {
|
||||
onBeforeLoad(win) {
|
||||
win.localStorage.removeItem('token')
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('Should log in with the right credentials', () => {
|
||||
const fixture = {
|
||||
username: 'test',
|
||||
password: '1234',
|
||||
}
|
||||
|
||||
cy.visit('/login')
|
||||
cy.get('input[id=username]').type(fixture.username)
|
||||
cy.get('input[id=password]').type(fixture.password)
|
||||
cy.get('.button').contains('Login').click()
|
||||
cy.url().should('include', '/')
|
||||
cy.clock(1625656161057) // 13:00
|
||||
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
|
||||
})
|
||||
|
||||
it('Should fail with a bad password', () => {
|
||||
const fixture = {
|
||||
username: 'test',
|
||||
password: '123456',
|
||||
}
|
||||
|
||||
testAndAssertFailed(fixture)
|
||||
})
|
||||
|
||||
it('Should fail with a bad username', () => {
|
||||
const fixture = {
|
||||
username: 'loremipsum',
|
||||
password: '1234',
|
||||
}
|
||||
|
||||
testAndAssertFailed(fixture)
|
||||
})
|
||||
})
|
16
cypress/e2e/user/logout.spec.js
Normal file
16
cypress/e2e/user/logout.spec.js
Normal file
@ -0,0 +1,16 @@
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('Log out', () => {
|
||||
it('Logs the user out', () => {
|
||||
cy.visit('/')
|
||||
|
||||
cy.get('.navbar .user .username')
|
||||
.click()
|
||||
cy.get('.navbar .user .dropdown-menu .dropdown-item')
|
||||
.contains('Logout')
|
||||
.click()
|
||||
|
||||
cy.url()
|
||||
.should('contain', '/login')
|
||||
})
|
||||
})
|
48
cypress/e2e/user/registration.spec.js
Normal file
48
cypress/e2e/user/registration.spec.js
Normal file
@ -0,0 +1,48 @@
|
||||
// This test assumes no mailer is set up and all users are activated immediately.
|
||||
|
||||
import {UserFactory} from '../../factories/user'
|
||||
|
||||
context('Registration', () => {
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1, {
|
||||
username: 'test',
|
||||
})
|
||||
cy.visit('/', {
|
||||
onBeforeLoad(win) {
|
||||
win.localStorage.removeItem('token')
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('Should work without issues', () => {
|
||||
const fixture = {
|
||||
username: 'testuser',
|
||||
password: '123456',
|
||||
email: 'testuser@example.com',
|
||||
}
|
||||
|
||||
cy.visit('/register')
|
||||
cy.get('#username').type(fixture.username)
|
||||
cy.get('#email').type(fixture.email)
|
||||
cy.get('#password').type(fixture.password)
|
||||
cy.get('#register-submit').click()
|
||||
cy.url().should('include', '/')
|
||||
cy.clock(1625656161057) // 13:00
|
||||
cy.get('h2').should('contain', `Hi ${fixture.username}!`)
|
||||
})
|
||||
|
||||
it.only('Should fail', () => {
|
||||
const fixture = {
|
||||
username: 'test',
|
||||
password: '123456',
|
||||
email: 'testuser@example.com',
|
||||
}
|
||||
|
||||
cy.visit('/register')
|
||||
cy.get('#username').type(fixture.username)
|
||||
cy.get('#email').type(fixture.email)
|
||||
cy.get('#password').type(fixture.password)
|
||||
cy.get('#register-submit').click()
|
||||
cy.get('div.message.danger').contains('A user with this username already exists.')
|
||||
})
|
||||
})
|
47
cypress/e2e/user/settings.spec.js
Normal file
47
cypress/e2e/user/settings.spec.js
Normal file
@ -0,0 +1,47 @@
|
||||
import {UserFactory} from '../../factories/user'
|
||||
|
||||
import '../../support/authenticateUser'
|
||||
|
||||
describe('User Settings', () => {
|
||||
beforeEach(() => {
|
||||
UserFactory.create(1)
|
||||
})
|
||||
|
||||
it('Changes the user avatar', () => {
|
||||
cy.intercept(`${Cypress.env('API_URL')}/user/settings/avatar/upload`).as('uploadAvatar')
|
||||
|
||||
cy.visit('/user/settings/avatar')
|
||||
|
||||
cy.get('input[name=avatarProvider][value=upload]')
|
||||
.click()
|
||||
cy.get('input[type=file]', {timeout: 1000})
|
||||
.selectFile('cypress/fixtures/image.jpg', {force: true}) // The input is not visible, but on purpose
|
||||
cy.get('.vue-handler-wrapper.vue-handler-wrapper--south .vue-simple-handler.vue-simple-handler--south')
|
||||
.trigger('mousedown', {which: 1})
|
||||
.trigger('mousemove', {clientY: 100})
|
||||
.trigger('mouseup')
|
||||
cy.get('[data-cy="uploadAvatar"]')
|
||||
.contains('Upload Avatar')
|
||||
.click()
|
||||
|
||||
cy.wait('@uploadAvatar')
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Updates the name', () => {
|
||||
cy.visit('/user/settings/general')
|
||||
|
||||
cy.get('.general-settings .control input.input')
|
||||
.first()
|
||||
.type('Lorem Ipsum')
|
||||
cy.get('[data-cy="saveGeneralSettings"]')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
cy.get('.global-notification')
|
||||
.should('contain', 'Success')
|
||||
cy.get('.navbar .user .username')
|
||||
.should('contain', 'Lorem Ipsum')
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user