frontend/cypress/integration/task/task.spec.js
konrad efed128f03 fix: rely on api to properly sort tasks on home page (#1997)
This PR changes the behaviour of how tasks are sorted. Before, the frontend would sort tasks but this resulted in some cases where tasks were not sorted properly. Most of this is test code to reliably reproduce the problem and make fixing it easier.
The actual bug was in Vikunja's api, therefore I've removed all sorting of tasks in the frontend and ensured the api properly sorts tasks.

Fixes https://github.com/go-vikunja/frontend/issues/54

Depends on vikunja/api#1177

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#1997
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2022-06-01 16:59:59 +00:00

421 lines
11 KiB
JavaScript

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