Skip to content

Commit

Permalink
Optionally add reason when completing action (#543)
Browse files Browse the repository at this point in the history
Optionally add reason when completing action
  • Loading branch information
denektenina authored Sep 2, 2021
1 parent 8f440e7 commit 0387b14
Show file tree
Hide file tree
Showing 13 changed files with 485 additions and 134 deletions.
101 changes: 96 additions & 5 deletions frontend/cypress/integration/actions_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('Actions', () => {
const actionProgression = faker.random.arrayElement([Progression.Workshop, Progression.FollowUp])

beforeEach(() => {
;({ seed, existingAction, existingNotes } = createEditSeed())
;({ seed, existingAction, existingNotes } = createEvaluationWithActionsWithNotes({ title: 'Just some random action' }))
editor = faker.random.arrayElement(seed.participants)
;({ updatedAction, newNotes } = createEditTestData(seed, editor, existingAction))

Expand All @@ -106,8 +106,6 @@ describe('Actions', () => {
editActionDialog.notesInput().replace(note.text)
editActionDialog.addNoteButton().click()
})
editActionDialog.completedSwitch().click({ force: true })
editActionDialog.onHoldSwitch().click({ force: true })

editActionDialog.assertSaved()
editActionDialog.close()
Expand Down Expand Up @@ -244,6 +242,85 @@ describe('Actions', () => {
})
})
})

context('Complete action', () => {
let seed: EvaluationSeed
let editor: Participant
let existingAction: Action
let existingNotes: Note[]
let updatedAction: Action
let newNote: Note

const actionProgression = faker.random.arrayElement([Progression.Workshop, Progression.FollowUp])

beforeEach(() => {
;({ seed, existingAction, existingNotes } = createEvaluationWithActionsWithNotes({ completed: false }))
editor = faker.random.arrayElement(seed.participants)
;({ updatedAction, newNote } = createCompleteActionData(seed, editor, existingAction))

seed.plant().then(() => {
cy.visitEvaluation(seed.evaluationId, editor.user)
evaluationPage.progressionStepLink(actionProgression).click()
})
})

function checkView() {
editActionDialog.actionCompletedText().should('not.exist')
editActionDialog.completeActionButton().should('exist')
editActionDialog.completedReasonInput().should('not.exist')
editActionDialog.completeActionConfirmButton().should('not.exist')
editActionDialog.completeActionCancelButton().should('not.exist')
}

function openConfirmCompleteView() {
editActionDialog.completeActionButton().click()
editActionDialog.completeActionButton().should('be.disabled')
editActionDialog.completedReasonInput().should('exist')
editActionDialog.completeActionConfirmButton().should('exist')
editActionDialog.completeActionCancelButton().should('exist')
}

function confirmAndCheckCompleted() {
editActionDialog.completeActionConfirmButton().click()
editActionDialog.assertSaved()
editActionDialog.actionCompletedText().should('exist')
editActionDialog.close()
}

function testCacheInReopenedActionView() {
cy.testCacheAndDB(() => {
evaluationPage.progressionStepLink(actionProgression).click()
actionsGrid.actionLink(existingAction.questionOrder, updatedAction.title).click()
editActionDialog.assertActionValues(updatedAction, existingNotes.concat([newNote]))
})
}

it('Action can be completed without writing a reason', () => {
actionsGrid.actionLink(existingAction.questionOrder, existingAction.title).click()
checkView()
openConfirmCompleteView()
confirmAndCheckCompleted()
testCacheInReopenedActionView()
})

it('Action can be completed with a reason', () => {
actionsGrid.actionLink(existingAction.questionOrder, existingAction.title).click()
checkView()
openConfirmCompleteView()
editActionDialog.completedReasonInput().replace('Closed because of a good reason')
confirmAndCheckCompleted()
testCacheInReopenedActionView()
})

it('Completing Action can be cancelled', () => {
actionsGrid.actionLink(existingAction.questionOrder, existingAction.title).click()
checkView()
openConfirmCompleteView()
editActionDialog.completeActionCancelButton().click()
checkView()
editActionDialog.assertNoClosingMessageInNotes(existingNotes)
})
})
})

const createCreateSeed = () => {
Expand All @@ -262,15 +339,16 @@ const createCreateSeed = () => {
return { seed }
}

const createEditSeed = () => {
const createEvaluationWithActionsWithNotes = (actionParameters: Partial<Action>) => {
const seed = new EvaluationSeed({
progression: faker.random.arrayElement(Object.values(Progression)),
users: getUsers(faker.datatype.number({ min: 1, max: 4 })),
})

const existingAction = seed.createAction({
title: 'Just some random action',
...actionParameters,
})

const existingNotes: Note[] = Array.from({ length: faker.datatype.number({ min: 1, max: 4 }) }, () => {
return new Note({
text: faker.lorem.sentence(),
Expand Down Expand Up @@ -356,3 +434,16 @@ const createEditTestData = (seed: EvaluationSeed, editor: Participant, existingA

return { updatedAction, newNotes }
}

const createCompleteActionData = (seed: EvaluationSeed, editor: Participant, existingAction: Action) => {
const updatedAction = { ...existingAction, completed: true }

const newNote = new Note({
text: '',
action: updatedAction,
createdBy: editor,
typeName: 'ClosingRemark',
})

return { updatedAction, newNote }
}
47 changes: 34 additions & 13 deletions frontend/cypress/support/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,28 @@ export class EditActionDialog extends ActionDialog {
return cy.getByDataTestid('add_note_button')
}

notesDiv = () => {
return cy.getByDataTestid('notes_list')
completedReasonInput = () => {
return cy.get('#completed-reason')
}

completedSwitch = () => {
return this.body()
.contains('Completed')
.then($el => cy.wrap($el).find('input'))
completeActionButton = () => {
return cy.getByDataTestid('complete_action_button')
}

completeActionConfirmButton = () => {
return cy.getByDataTestid('complete_action_confirm_button')
}

completeActionCancelButton = () => {
return cy.getByDataTestid('complete_action_cancel_button')
}

notesDiv = () => {
return cy.getByDataTestid('notes_list')
}

onHoldSwitch = () => {
return this.body()
.contains('On hold')
.then($el => cy.wrap($el).find('input'))
actionCompletedText = () => {
return cy.getByDataTestid('action_completed_text')
}

assertSaved = () => {
Expand All @@ -143,14 +151,27 @@ export class EditActionDialog extends ActionDialog {
this.assignedToInput().should('have.value', action.assignedTo.user.name)
this.priorityInput().should('have.text', mapPriority(action.priority))
this.dueDateInput().should('have.value', action.dueDate.toLocaleDateString(FUSION_DATE_LOCALE))
;[...notes].reverse().forEach((note, index) => {
this.notesDiv().children().eq(index).as('note')
if (note.__typename === 'ClosingRemark') {
cy.get('@note').should('contain.text', note.createdBy.user.name + ' closed action')
} else {
cy.get('@note').should('contain.text', note.createdBy.user.name + ' wrote')
}
if (note.text) {
cy.get('@note').contains(note.text).should('exist')
}

// TODO: time not checked. Need to figure out if it can be done reasonably
})
}

assertNoClosingMessageInNotes = (notes: Note[]) => {
;[...notes].reverse().forEach((note, index) => {
this.notesDiv().children().eq(index).as('note')
cy.get('@note').should('contain.text', note.createdBy.user.name + ' wrote')
cy.get('@note').contains(note.text).should('exist')
// TODO: time not checked. Need to figure out if it can be done reasonably
})
this.completedSwitch().should(action.completed ? 'be.checked' : 'be.not.checked')
this.onHoldSwitch().should(action.onHold ? 'be.checked' : 'be.not.checked')
}
}

Expand Down
4 changes: 4 additions & 0 deletions frontend/cypress/support/mock/external/external.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,8 @@ Cypress.on('uncaught:exception', (err, runnable, promise) => {
) {
return false
}

if (err.message.includes("Cannot read properties of null (reading 'removeEventListener')")) {
return false
}
})
5 changes: 4 additions & 1 deletion frontend/cypress/support/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,20 @@ interface INote {
text: string
action: Action
createdBy: Participant
typeName?: string
}

export class Note {
text: string
action: Action
createdBy: Participant
__typename?: string

constructor({ text, action, createdBy }: INote) {
constructor({ text, action, createdBy, typeName }: INote) {
this.text = text
this.action = action
this.createdBy = createdBy
this.__typename = typeName
}
}

Expand Down
21 changes: 21 additions & 0 deletions frontend/src/api/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ fragment QuestionActions on Question {
${ACTION_FIELDS_FRAGMENT}
`

export const CLOSING_REMARK_FIELDS_FRAGMENT = gql`
fragment ClosingRemarkFields on ClosingRemark {
id
text
createDate
createdBy {
...ParticipantFields
}
}
${PARTICIPANT_FIELDS_FRAGMENT}
`

export const NOTE_FIELDS_FRAGMENT = gql`
fragment NoteFields on Note {
id
Expand All @@ -115,3 +127,12 @@ export const ACTION_NOTES_FRAGMENT = gql`
}
${NOTE_FIELDS_FRAGMENT}
`

export const ACTION_CLOSING_REMARKS_FRAGMENT = gql`
fragment ActionClosingRemarks on Action {
closingRemarks {
...ClosingRemarkFields
}
}
${CLOSING_REMARK_FIELDS_FRAGMENT}
`
Loading

0 comments on commit 0387b14

Please sign in to comment.