Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

feat(incident): add the ability to document notes on a reported incident #2907

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b4401e5
feat(lab): link visit to labs
DrewGregory Nov 6, 2020
86b1eaa
feat(incident): add ability to document notes
DrewGregory Dec 14, 2020
e214866
feat(incident):added incident note table
avigpt Dec 18, 2020
87cf4fb
started new note modal work
avigpt Dec 18, 2020
1033f16
feat(incident): note modal generalizations
DrewGregory Dec 18, 2020
296e42c
feat(incident): note modal generalizations
DrewGregory Dec 18, 2020
558f379
finish NewNoteModal refactor, begin react query for incident notes
DrewGregory Dec 21, 2020
06954fb
feat(incident): use react-query to save/view notes
DrewGregory Dec 23, 2020
71ada1a
feat(incident): add no note support
avigpt Dec 23, 2020
8b24012
merge(labrequest): fixed merge conflicts
avigpt Dec 23, 2020
287251e
Merge branch 'master' into incident-notes
avigpt Dec 23, 2020
1fb7464
feat(incident): button formatting
avigpt Dec 23, 2020
1aa28fc
feat(incident): beginnings of editing notes support
DrewGregory Jan 3, 2021
b4e2ca6
feat(incident) editing and deleting notes
avigpt Jan 4, 2021
09998a1
fixed spacing
avigpt Jan 5, 2021
86512a2
feat(incident) moved hooks to top of file
avigpt Jan 5, 2021
bf58189
wip: debug react hooks
DrewGregory Jan 6, 2021
fb875b2
test(incident): fixed view incident test
DrewGregory Jan 6, 2021
52e0f99
test(incident): fixed view incident test
DrewGregory Jan 6, 2021
73606bb
test(incident): NewNoteModal
DrewGregory Jan 7, 2021
064b772
(test) fixed merged conflicts
avigpt Jan 12, 2021
b1907fa
(test) fixed some view incident details tests
avigpt Jan 12, 2021
cda6b51
(test) fixed incident details and migrated some tests to view incident
avigpt Jan 12, 2021
bae04ca
(tests) fixed view incident tests, started fixing new note modal tests
avigpt Jan 14, 2021
3ba517d
(tests) fixed existing tests, skeleton for new ones
avigpt Jan 17, 2021
85280c9
new note modal tests
DrewGregory Jan 22, 2021
de69d9d
beginnings of tests
DrewGregory Jan 24, 2021
14b29b0
Completed NotesTable tests
jameszheng405 Feb 2, 2021
bddaeda
feat(incident): tests, almost done
avigpt Feb 6, 2021
0188b72
tests: fixed merge conflicts
avigpt Feb 6, 2021
802834d
feat(incident) tests: merge resolution
avigpt Feb 6, 2021
3aee48e
test(notes): fix last incident test
DrewGregory Feb 9, 2021
6603ed8
remove dummy validation
DrewGregory Feb 9, 2021
18e8fdf
revert package.json
DrewGregory Feb 9, 2021
25ed56f
test(incident): resolve anthony's comments on PR
avigpt Mar 17, 2021
0aa0eaf
merge
DrewGregory May 1, 2021
8164f2e
merge conflicts
DrewGregory May 1, 2021
62e4759
Removed a duplicate updateTitle in ViewIncident
jrmkim50 May 1, 2021
eed0032
Removed a duplicate updateTitle in ViewIncident
jrmkim50 May 1, 2021
10b4092
ViewIncidentTest edit note test in progress right now
jrmkim50 May 1, 2021
1e6b05a
Merged incident-notes with most recent master branch
anthonyaperez Feb 19, 2022
e289b8e
Second merge
anthonyaperez Feb 19, 2022
1d221fa
feat(incident): added editable and deletable notes on reported incident
anthonyaperez Apr 16, 2022
b048957
fix(incidents): resolved linter errors
anthonyaperez Apr 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Dependabot PR merge

on:
pull_request:

jobs:
auto-merge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
target: minor
github-token: ${{ secrets.BOT_PAT }}
83 changes: 83 additions & 0 deletions src/__tests__/incidents/hooks/useAddIncidentNote.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import useAddIncidentNote from '../../../incidents/hooks/useAddIncidentNote'
import IncidentRepository from '../../../shared/db/IncidentRepository'
import Incident from '../../../shared/model/Incident'
import Note from '../../../shared/model/Note'
import executeMutation from '../../test-utils/use-mutation.util'

describe('use add incident note', () => {
beforeEach(() => {
jest.resetAllMocks()
})

it('should add a new note', async () => {
const expectedNote = {
id: '5678',
text: 'some text',
} as Note

const givenIncident = {
id: '1234',
date: new Date().toISOString(),
notes: [] as Note[],
} as Incident

const expectedIncident = { ...givenIncident, notes: [expectedNote] } as Incident
jest.spyOn(IncidentRepository, 'find').mockResolvedValue(givenIncident)
jest.spyOn(IncidentRepository, 'saveOrUpdate').mockResolvedValue(expectedIncident)

const result = await executeMutation(() => useAddIncidentNote(), {
incidentId: givenIncident.id,
note: expectedNote,
})

expect(IncidentRepository.find).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).toHaveBeenCalledWith(
expect.objectContaining({
notes: [expect.objectContaining({ text: expectedNote.text })],
}),
)
expect(result).toEqual([expectedNote])
})

it('should edit an existing note', async () => {
const givenNote = {
id: '5678',
text: 'some new text',
} as Note

const expectedNote = {
id: '5678',
text: 'some edited text',
} as Note

const givenIncident = {
id: '1234',
date: new Date().toISOString(),
notes: [givenNote] as Note[],
} as Incident

const expectedIncident = { ...givenIncident, notes: [expectedNote] } as Incident
jest.spyOn(IncidentRepository, 'find').mockResolvedValue(givenIncident)
jest.spyOn(IncidentRepository, 'saveOrUpdate').mockResolvedValue(expectedIncident)

const result = await executeMutation(() => useAddIncidentNote(), {
incidentId: givenIncident.id,
note: expectedNote,
})

expect(IncidentRepository.find).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).toHaveBeenCalledWith(
expect.objectContaining({
notes: [expect.objectContaining({ text: expectedNote.text })],
}),
)
expect(IncidentRepository.saveOrUpdate).not.toHaveBeenCalledWith(
expect.objectContaining({
notes: [expect.objectContaining({ text: givenNote.text })],
}),
)
expect(result).toEqual([expectedNote])
})
})
62 changes: 62 additions & 0 deletions src/__tests__/incidents/hooks/useDeleteIncidentNote.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import useDeleteIncidentNote from '../../../incidents/hooks/useDeleteIncidentNote'
import * as validateNote from '../../../patients/util/validate-note'
import IncidentRepository from '../../../shared/db/IncidentRepository'
import Incident from '../../../shared/model/Incident'
import Note from '../../../shared/model/Note'
import { expectOneConsoleError } from '../../test-utils/console.utils'
import executeMutation from '../../test-utils/use-mutation.util'

describe('use delete incident note', () => {
beforeEach(() => {
jest.resetAllMocks()
})

it('should delete an existing note', async () => {
const givenNote = {
id: '5678',
text: 'some text',
} as Note

const givenIncident = {
id: '1234',
date: new Date().toISOString(),
notes: [givenNote] as Note[],
} as Incident

const expectedIncident = { ...givenIncident, notes: [] } as Incident
jest.spyOn(IncidentRepository, 'find').mockResolvedValue(givenIncident)
jest.spyOn(IncidentRepository, 'saveOrUpdate').mockResolvedValue(expectedIncident)

const result = await executeMutation(() => useDeleteIncidentNote(), {
incidentId: givenIncident.id,
note: givenNote,
})

expect(IncidentRepository.find).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).toHaveBeenCalledTimes(1)
expect(IncidentRepository.saveOrUpdate).not.toHaveBeenCalledWith(
expect.objectContaining({
notes: [expect.objectContaining({ text: givenNote.text })],
}),
)
expect(result).toEqual([])
})

it('should throw an error if note validation fails', async () => {
const expectedError = { nameError: 'some error' }
expectOneConsoleError(expectedError)
jest.spyOn(validateNote, 'default').mockReturnValue(expectedError)
jest.spyOn(IncidentRepository, 'saveOrUpdate')

try {
await executeMutation(() => useDeleteIncidentNote(), {
incidentId: '1234',
note: {} as Note,
})
} catch (e) {
expect(e).toEqual(expectedError)
}

expect(IncidentRepository.saveOrUpdate).not.toHaveBeenCalled()
})
})
52 changes: 52 additions & 0 deletions src/__tests__/incidents/view/NotesTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { fireEvent, render, screen } from '@testing-library/react'
import React from 'react'

import NotesTable from '../../../incidents/view/NotesTable'
import Note from '../../../shared/model/Note'

const mockNote = {
id: '1234',
date: new Date().toISOString(),
text: 'some text',
givenBy: 'some user',
} as Note

const setup = (notes: Note[] = [mockNote]) => {
const onEditSpy = jest.fn()
const onDeleteSpy = jest.fn()

render(<NotesTable onEditNote={onEditSpy} onDeleteNote={onDeleteSpy} notes={notes} />)

return { onEditSpy, onDeleteSpy }
}

describe('Notes Table', () => {
it('should render a notes table if at least one note is in the list.', async () => {
setup()

expect(screen.getByRole('table')).toBeInTheDocument()
})
it('should display edit and delete buttons if notes exist', async () => {
setup()
expect(screen.getByRole('button', { name: 'actions.edit' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'actions.delete' })).toBeInTheDocument()
})

it('should display no notes message if no notes exist', async () => {
setup([])
expect(screen.getByRole('alert')).toBeInTheDocument()
expect(screen.getByText('patient.notes.warning.noNotes')).toBeInTheDocument()
})

it('calls on edit note when edit note button clicked', async () => {
const { onEditSpy } = setup()
fireEvent.click(screen.getByRole('button', { name: 'actions.edit' }))
expect(onEditSpy).toHaveBeenCalled()
})

it('calls on delete note when delete note button clicked', async () => {
const { onDeleteSpy } = setup()
fireEvent.click(screen.getByRole('button', { name: 'actions.delete' }))
expect(onDeleteSpy).toHaveBeenCalled()
})
})
103 changes: 82 additions & 21 deletions src/__tests__/incidents/view/ViewIncident.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen } from '@testing-library/react'
import { fireEvent, render, screen } from '@testing-library/react'
import { createMemoryHistory } from 'history'
import React from 'react'
import { Provider } from 'react-redux'
Expand All @@ -12,20 +12,36 @@ import { ButtonBarProvider } from '../../../page-header/button-toolbar/ButtonBar
import { TitleProvider } from '../../../page-header/title/TitleContext'
import IncidentRepository from '../../../shared/db/IncidentRepository'
import Incident from '../../../shared/model/Incident'
import Note from '../../../shared/model/Note'
import Permissions from '../../../shared/model/Permissions'
import { RootState } from '../../../shared/store'

const mockStore = createMockStore<RootState, any>([thunk])

const setup = (permissions: Permissions[], id: string | undefined) => {
jest.resetAllMocks()
jest.spyOn(breadcrumbUtil, 'default')
jest.spyOn(IncidentRepository, 'find').mockResolvedValue({
const expectedNote = {
id: '5678',
date: new Date().toISOString(),
text: 'some text',
givenBy: 'some user',
deleted: false,
} as Note

const setup = (
permissions: Permissions[] | undefined,
id: string | undefined,
notes: Note[] = [],
) => {
const expectedIncident = {
id,
date: new Date().toISOString(),
code: 'some code',
reportedOn: new Date().toISOString(),
} as Incident)
notes,
} as Incident

jest.resetAllMocks()
jest.spyOn(breadcrumbUtil, 'default')
jest.spyOn(IncidentRepository, 'find').mockResolvedValue(expectedIncident)

const history = createMemoryHistory({ initialEntries: [`/incidents/${id}`] })
const store = mockStore({
Expand All @@ -52,22 +68,67 @@ const setup = (permissions: Permissions[], id: string | undefined) => {
}
}

it('should not render ViewIncidentDetails if there are no Permissions', async () => {
setup(undefined, '1234')
describe('View Incident', () => {
it('should not render ViewIncidentDetails if there are no Permissions', async () => {
const { container } = setup(undefined, '1234')

expect(
screen.queryByRole('heading', {
name: /incidents\.reports\.dateofincident/i,
}),
).not.toBeInTheDocument()
})
expect(container.querySelector(`[class^='css-']`)).not.toBeInTheDocument()
expect(
screen.queryByRole('heading', {
name: /incidents\.reports\.dateofincident/i,
}),
).not.toBeInTheDocument()
})

it('should render tabs header', async () => {
setup([Permissions.ViewIncident], '1234')

expect(await screen.findByText('patient.notes.label')).toBeInTheDocument()
})

it('should render notes tab and add new note button when clicked', async () => {
setup([Permissions.ViewIncident, Permissions.ReportIncident], '1234')

fireEvent.click(await screen.findByText('patient.notes.label'))
expect(screen.getByRole('button', { name: 'patient.notes.new' })).toBeInTheDocument()
})

it('should not display add new note button without permission to report', async () => {
setup([Permissions.ViewIncident], '1234')

fireEvent.click(await screen.findByText('patient.notes.label'))
expect(screen.queryByRole('button', { name: 'patient.notes.new' })).not.toBeInTheDocument()
})

it('should not display modal before new note button clicked', async () => {
setup([Permissions.ViewIncident, Permissions.ReportIncident], '1234')

fireEvent.click(await screen.findByText('patient.notes.label'))
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})

it('should display modal after new note button is clicked', async () => {
setup([Permissions.ViewIncident, Permissions.ReportIncident], '1234')

fireEvent.click(await screen.findByText('patient.notes.label'))
fireEvent.click(screen.getByRole('button', { name: 'patient.notes.new' }))
expect(screen.queryByRole('dialog')).toBeInTheDocument()
})

it('should display modal when edit note is clicked', async () => {
setup([Permissions.ViewIncident, Permissions.ReportIncident], '1234', [expectedNote])

fireEvent.click(await screen.findByRole('button', { name: 'actions.edit' }))
expect(await screen.findByRole('dialog')).toBeInTheDocument()
})

it('should not ViewIncidentDetails no there is no ID', async () => {
setup([Permissions.ReportIncident, Permissions.ResolveIncident], undefined)
it('should not render ViewIncidentDetails when there is no ID', async () => {
setup([Permissions.ReportIncident, Permissions.ResolveIncident], undefined)

expect(
screen.queryByRole('heading', {
name: /incidents\.reports\.dateofincident/i,
}),
).not.toBeInTheDocument()
expect(
screen.queryByRole('heading', {
name: /incidents\.reports\.dateofincident/i,
}),
).not.toBeInTheDocument()
})
})
Loading