import {createFakeUserAndLogin} from '../../support/authenticateUser'

import {TaskFactory} from '../../factories/task'
import {ProjectFactory} from '../../factories/project'
import {TaskCommentFactory} from '../../factories/task_comment'
import {UserFactory} from '../../factories/user'
import {UserProjectFactory} from '../../factories/users_project'
import {TaskAssigneeFactory} from '../../factories/task_assignee'
import {LabelFactory} from '../../factories/labels'
import {LabelTaskFactory} from '../../factories/label_task'
import {BucketFactory} from '../../factories/bucket'

import {TaskAttachmentFactory} from '../../factories/task_attachments'
import {TaskReminderFactory} from '../../factories/task_reminders'

function addLabelToTaskAndVerify(labelTitle: string) {
	cy.get('.task-view .action-buttons .button')
		.contains('Add Labels')
		.click()
	cy.get('.task-view .details.labels-list .multiselect input')
		.type(labelTitle)
	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', labelTitle)
}

function uploadAttachmentAndVerify(taskId: number) {
	cy.intercept(`${Cypress.env('API_URL')}/tasks/${taskId}/attachments`).as('uploadAttachment')
	cy.get('.task-view .action-buttons .button')
		.contains('Add Attachments')
		.click()
	cy.get('input[type=file]#files', {timeout: 1000})
		.selectFile('cypress/fixtures/image.jpg', {force: true}) // The input is not visible, but on purpose
	cy.wait('@uploadAttachment')

	cy.get('.attachments .attachments .files a.attachment')
		.should('exist')
}

describe('Task', () => {
	createFakeUserAndLogin()

	let projects
	let buckets

	beforeEach(() => {
		// UserFactory.create(1)
		projects = ProjectFactory.create(1)
		buckets = BucketFactory.create(1, {
			project_id: projects[0].id,
		})
		TaskFactory.truncate()
		UserProjectFactory.truncate()
	})

	it('Should be created new', () => {
		cy.visit('/projects/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 project', () => {
		TaskFactory.create(1)

		cy.visit('/projects/1/list')
		cy.get('.project-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('/projects/1/list')
		cy.get('.tasks .task .fancycheckbox')
			.first()
			.click()
		cy.get('.global-notification')
			.should('contain', 'Success')
	})

	it('Can add a task to favorites', () => {
		TaskFactory.create(1)

		cy.visit('/projects/1/list')
		cy.get('.tasks .task .favorite')
			.first()
			.click()
		cy.get('.menu-container')
			.should('contain', 'Favorites')
	})

	it('Should show a task description icon if the task has a description', () => {
		cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
		TaskFactory.create(1, {
			description: 'Lorem Ipsum',
		})

		cy.visit('/projects/1/list')
		cy.wait('@loadTasks')

		cy.get('.tasks .task .project-task-icon')
			.should('exist')
	})

	it('Should not show a task description icon if the task has an empty description', () => {
		cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
		TaskFactory.create(1, {
			description: '',
		})

		cy.visit('/projects/1/list')
		cy.wait('@loadTasks')

		cy.get('.tasks .task .project-task-icon')
			.should('not.exist')
	})

	it('Should not show a task description icon if the task has a description containing only an empty p tag', () => {
		cy.intercept(Cypress.env('API_URL') + '/projects/1/tasks**').as('loadTasks')
		TaskFactory.create(1, {
			description: '<p></p>',
		})

		cy.visit('/projects/1/list')
		cy.wait('@loadTasks')

		cy.get('.tasks .task .project-task-icon')
			.should('not.exist')
	})

	describe('Task Detail View', () => {
		beforeEach(() => {
			TaskCommentFactory.truncate()
			LabelTaskFactory.truncate()
			TaskAttachmentFactory.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', projects[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: new Date().toISOString(),
			})
			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 project has one', () => {
			const projects = ProjectFactory.create(1, {
				id: 1,
				identifier: 'TEST',
			})
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_id: projects[0].id,
				index: 1,
			})

			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view h1.title.task-id')
				.should('contain', `${projects[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 .tiptap button.done-edit')
				.click()
			cy.get('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror')
				.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('Shows an empty editor when the description of a task is empty', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				description: '',
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .details.content.description .tiptap.ProseMirror p')
				.should('have.attr', 'data-placeholder')
			cy.get('.task-view .details.content.description .tiptap button.done-edit')
				.should('not.exist')
		})

		it('Shows a preview editor when the description of a task is not empty', () => {
			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 .tiptap.ProseMirror p')
				.should('not.have.attr', 'data-placeholder')
			cy.get('.task-view .details.content.description .tiptap button.done-edit')
				.should('exist')
		})

		it('Shows a preview editor when the description of a task contains html', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				description: '<p>Lorem Ipsum dolor sit amet</p>',
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .details.content.description .tiptap.ProseMirror p')
				.should('not.have.attr', 'data-placeholder')
			cy.get('.task-view .details.content.description .tiptap button.done-edit')
				.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 .tiptap__editor .tiptap.ProseMirror')
				.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 .tiptap__editor')
				.should('contain', 'New Comment')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Can move a task to another project', () => {
			const projects = ProjectFactory.create(2)
			BucketFactory.create(2, {
				project_id: '{increment}',
			})
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_id: projects[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(`${projects[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', projects[1].title)
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Can delete a task', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_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', `/projects/${tasks[0].project_id}/`)
		})

		it('Can add an assignee to a task', () => {
			const users = UserFactory.create(5)
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_id: 1,
			})
			UserProjectFactory.create(5, {
				project_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,
				project_id: 1,
			})
			UserProjectFactory.create(5, {
				project_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('.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,
				project_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,
				project_id: 1,
			})
			const labels = LabelFactory.create(1)
			LabelTaskFactory.truncate()

			cy.visit(`/tasks/${tasks[0].id}`)

			addLabelToTaskAndVerify(labels[0].title)
		})

		it('Can add a label to a task and it shows up on the kanban board afterwards', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_id: projects[0].id,
				bucket_id: buckets[0].id,
			})
			const labels = LabelFactory.create(1)
			LabelTaskFactory.truncate()

			cy.visit(`/projects/${projects[0].id}/kanban`)

			cy.get('.bucket .task')
				.contains(tasks[0].title)
				.click()

			addLabelToTaskAndVerify(labels[0].title)

			cy.get('.modal-content .close')
				.click()

			cy.get('.bucket .task')
				.should('contain.text', labels[0].title)
		})

		it('Can remove a label from a task', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_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.get('.task-view .details.labels-list .multiselect .input-wrapper')
				.should('be.visible')
				.should('contain', labels[0].title)
			cy.get('.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 button')
				.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')
		})

		it('Can set a due date to a specific 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-popup .flatpickr-innerContainer .flatpickr-days .flatpickr-day.today')
				.click()
			cy.get('[data-cy="closeDatepicker"]')
				.contains('Confirm')
				.click()

			const today = new Date()
			const day = today.toLocaleString('default', {day: 'numeric'})
			const month = today.toLocaleString('default', {month: 'short'})
			const year = today.toLocaleString('default', {year: 'numeric'})
			const date = `${day} ${month} ${year}, 12:00:00`
			cy.get('.task-view .columns.details .column')
				.contains('Due Date')
				.get('.date-input .datepicker-popup')
				.should('not.exist')
			cy.get('.task-view .columns.details .column')
				.contains('Due Date')
				.get('.date-input')
				.should('contain.text', date)
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Can change a due date to a specific date for a task', () => {
			const dueDate = new Date()
			dueDate.setHours(12)
			dueDate.setMinutes(0)
			dueDate.setSeconds(0)
			dueDate.setDate(1)
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
				due_date: dueDate.toISOString(),
			})
			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-popup .flatpickr-innerContainer .flatpickr-days .flatpickr-day.today')
				.click()
			cy.get('[data-cy="closeDatepicker"]')
				.contains('Confirm')
				.click()

			const today = new Date()
			const day = today.toLocaleString('default', {day: 'numeric'})
			const month = today.toLocaleString('default', {month: 'short'})
			const year = today.toLocaleString('default', {year: 'numeric'})
			const date = `${day} ${month} ${year}, 12:00:00`
			cy.get('.task-view .columns.details .column')
				.contains('Due Date')
				.get('.date-input .datepicker-popup')
				.should('not.exist')
			cy.get('.task-view .columns.details .column')
				.contains('Due Date')
				.get('.date-input')
				.should('contain.text', date)
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Can set a reminder', () => {
			TaskReminderFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Reminders')
				.click()
			cy.get('.task-view .columns.details .column button')
				.contains('Add a new reminder')
				.click()
			cy.get('.datepicker__quick-select-date')
				.contains('Tomorrow')
				.click()

			cy.get('.reminder-options-popup')
				.should('not.be.visible')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Allows to set a relative reminder when the task already has a due date', () => {
			TaskReminderFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
				due_date: (new Date()).toISOString(),
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Reminders')
				.click()
			cy.get('.task-view .columns.details .column button')
				.contains('Add a new reminder')
				.click()
			cy.get('.datepicker__quick-select-date')
				.should('not.exist')
			cy.get('.reminder-options-popup .card-content')
				.should('contain', '1 day before Due Date')
			cy.get('.reminder-options-popup .card-content')
				.contains('1 day before Due Date')
				.click()

			cy.get('.reminder-options-popup')
				.should('not.be.visible')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Allows to set a relative reminder when the task already has a start date', () => {
			TaskReminderFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
				start_date: (new Date()).toISOString(),
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Reminders')
				.click()
			cy.get('.task-view .columns.details .column button')
				.contains('Add a new reminder')
				.click()
			cy.get('.datepicker__quick-select-date')
				.should('not.exist')
			cy.get('.reminder-options-popup .card-content')
				.should('contain', '1 day before Start Date')
			cy.get('.reminder-options-popup .card-content')
				.contains('1 day before Start Date')
				.click()

			cy.get('.reminder-options-popup')
				.should('not.be.visible')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Allows to set a custom relative reminder when the task already has a due date', () => {
			TaskReminderFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
				due_date: (new Date()).toISOString(),
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Reminders')
				.click()
			cy.get('.task-view .columns.details .column button')
				.contains('Add a new reminder')
				.click()
			cy.get('.datepicker__quick-select-date')
				.should('not.exist')
			cy.get('.reminder-options-popup .card-content')
				.contains('Custom')
				.click()
			cy.get('.reminder-options-popup .card-content .reminder-period input')
				.first()
				.type('{selectall}10')
			cy.get('.reminder-options-popup .card-content .reminder-period select')
				.first()
				.select('days')
			cy.get('.reminder-options-popup .card-content button')
				.contains('Confirm')
				.click()

			cy.get('.reminder-options-popup')
				.should('not.be.visible')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Allows to set a fixed reminder when the task already has a due date', () => {
			TaskReminderFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				done: false,
				due_date: (new Date()).toISOString(),
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Reminders')
				.click()
			cy.get('.task-view .columns.details .column button')
				.contains('Add a new reminder')
				.click()
			cy.get('.datepicker__quick-select-date')
				.should('not.exist')
			cy.get('.reminder-options-popup .card-content')
				.contains('Date and time')
				.click()
			cy.get('.datepicker__quick-select-date')
				.contains('Tomorrow')
				.click()

			cy.get('.reminder-options-popup')
				.should('not.be.visible')
			cy.get('.global-notification')
				.should('contain', 'Success')
		})

		it('Can set a priority for a task', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Priority')
				.click()
			cy.get('.task-view .columns.details .column')
				.contains('Priority')
				.get('.select select')
				.select('Urgent')
			cy.get('.global-notification')
				.should('contain', 'Success')

			cy.get('.task-view .columns.details .column')
				.contains('Priority')
				.get('.select select')
				.should('have.value', '4')
		})

		it('Can set the progress for a task', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .action-buttons .button')
				.contains('Set Progress')
				.click()
			cy.get('.task-view .columns.details .column')
				.contains('Progress')
				.get('.select select')
				.select('50%')
			cy.get('.global-notification')
				.should('contain', 'Success')

			cy.wait(200)

			cy.get('.task-view .columns.details .column')
				.contains('Progress')
				.get('.select select')
				.should('be.visible')
				.should('have.value', '0.5')
		})

		it('Can add an attachment to a task', () => {
			TaskAttachmentFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			uploadAttachmentAndVerify(tasks[0].id)
		})

		it('Can add an attachment to a task and see it appearing on kanban', () => {
			TaskAttachmentFactory.truncate()
			const tasks = TaskFactory.create(1, {
				id: 1,
				project_id: projects[0].id,
				bucket_id: buckets[0].id,
			})
			const labels = LabelFactory.create(1)
			LabelTaskFactory.truncate()

			cy.visit(`/projects/${projects[0].id}/kanban`)

			cy.get('.bucket .task')
				.contains(tasks[0].title)
				.click()

			uploadAttachmentAndVerify(tasks[0].id)

			cy.get('.modal-content .close')
				.click()

			cy.get('.bucket .task .footer .icon svg.fa-paperclip')
				.should('exist')
		})

		it('Can check items off a checklist', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				description: `
<ul data-type="taskList">
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>First Item</p></div>
	</li>
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>Second Item</p></div>
	</li>
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>Third Item</p></div>
	</li>
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>Fourth Item</p></div>
	</li>
	<li data-checked="true" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>Fifth Item</p></div>
	</li>
</ul>`,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.task-view .checklist-summary')
				.should('contain.text', '1 of 5 tasks')
			cy.get('.tiptap__editor ul > li input[type=checkbox]')
				.eq(2)
				.click()

			cy.get('.task-view .details.content.description h3 span.is-small.has-text-success')
				.contains('Saved!')
				.should('exist')
			cy.get('.tiptap__editor ul > li input[type=checkbox]')
				.eq(2)
				.should('be.checked')
			cy.get('.tiptap__editor input[type=checkbox]')
				.should('have.length', 5)
			cy.get('.task-view .checklist-summary')
				.should('contain.text', '2 of 5 tasks')
		})

		it('Should use the editor to render description', () => {
			const tasks = TaskFactory.create(1, {
				id: 1,
				description: `
<h1>Lorem Ipsum</h1>
<p>Dolor sit amet</p>
<ul data-type="taskList">
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>First Item</p></div>
	</li>
	<li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label>
		<div><p>Second Item</p></div>
	</li>
</ul>`,
			})
			cy.visit(`/tasks/${tasks[0].id}`)

			cy.get('.tiptap__editor ul > li input[type=checkbox]')
				.should('exist')
			cy.get('.tiptap__editor h1')
				.contains('Lorem Ipsum')
				.should('exist')
			cy.get('.tiptap__editor p')
				.contains('Dolor sit amet')
				.should('exist')
		})

		it('Should render an image from attachment', async () => {

			TaskAttachmentFactory.truncate()

			const tasks = TaskFactory.create(1, {
				id: 1,
				description: '',
			})

			cy.readFile('cypress/fixtures/image.jpg', null).then(file => {

				const formData = new FormData()
				formData.append('files', new Blob([file]), 'image.jpg')

				cy.request({
					method: 'PUT',
					url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`,
					headers: {
						'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
						'Content-Type': 'multipart/form-data',
					},
					body: formData,
				})
					.then(({body}) => {
						const dec = new TextDecoder('utf-8')
						const {success} = JSON.parse(dec.decode(body))

						TaskFactory.create(1, {
							id: 1,
							description: `<img src="${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments/${success[0].id}" alt="test image">`,
						})

						cy.visit(`/tasks/${tasks[0].id}`)

						cy.get('.tiptap__editor img')
							.should('be.visible')
							.and(($img) => {
								// "naturalWidth" and "naturalHeight" are set when the image loads
								expect($img[0].naturalWidth).to.be.greaterThan(0)
							})

					})
			})
		})
	})
})
