Skip to content

Commit

Permalink
feat: add job edit page
Browse files Browse the repository at this point in the history
Adds the job edit page and ensures it aligns with the designs. Test
coverage has also been improved and the test approach has been aligned
across the entire suite of tests.

feat: add job edit page

fix: use hook without refetch

fix: use initial values for edit form

fix: use different label for edit form

feat: add update job hook

refactor: use simple hooks directly

fix: fetch only whitelisted fields

fix: disallow editing of system jobs

fix: use flyoutmenu

fix: fix data for user settings

fix: move sync call to setstate to async

chore(dependency): update ui

chore(dependency): remove react resolutions

fix(i18n): change namespace separator for string with colon character

test(coverage): start improving test coverage

fix(forms): format number values to string

test: add tests

test: add form fields tests

test: add remaining form fields tests

chore: update comment

test: add initial jobaddform test

test: add job form tests

chore: remove comment

test: add form tests

test: update component tests

test(hooks): add use update job test

test(config): update test coverage settings to ignore translations

refactor: simplify hooks for retrieving parameters

test(services): update service testing coverage

test: add minimal test for not authorized route

test: add tests for job list selectors

test(joblist): add tests for job list container

test(joblist): add tests for remaining job list components

test: add remaining tests

test(joblist): use data-test attribute for new job button test

refactor: deduplicate job forms

test: simplify test cleanup

test: silence react act warnings caused by popper

test: use uniform approach for tests

test: update remaining tests

test: fix job form test

chore: remove enzyme-to-json
  • Loading branch information
ismay committed Jan 13, 2021
1 parent 0265476 commit aa51d10
Show file tree
Hide file tree
Showing 137 changed files with 3,167 additions and 1,540 deletions.
11 changes: 7 additions & 4 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2020-06-25T09:59:16.015Z\n"
"PO-Revision-Date: 2020-06-25T09:59:16.015Z\n"
"POT-Creation-Date: 2020-07-22T08:26:40.694Z\n"
"PO-Revision-Date: 2020-07-22T08:26:40.694Z\n"

msgid "Checking permissions"
msgstr ""
Expand All @@ -32,7 +32,7 @@ msgstr ""
msgid "Name"
msgstr ""

msgid "Loading job types"
msgid "Loading"
msgstr ""

msgid "Job type"
Expand All @@ -41,7 +41,7 @@ msgstr ""
msgid "No options available"
msgstr ""

msgid "Save job"
msgid "Save"
msgstr ""

msgid "Cancel"
Expand Down Expand Up @@ -95,6 +95,9 @@ msgstr ""
msgid "About job configuration"
msgstr ""

msgid "Job: {{ JOBNAME }}"
msgstr ""

msgid "Edit"
msgstr ""

Expand Down
13 changes: 6 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@dhis2/app-runtime": "^2.2.2",
"@dhis2/d2-i18n": "^1.0.6",
"@dhis2/prop-types": "2.0.0",
"@dhis2/ui": "^5.0.5",
"@dhis2/ui": "^5.0.7",
"cronstrue": "^1.82.0",
"history": "^4.9.0",
"moment": "^2.24.0",
Expand All @@ -31,7 +31,6 @@
"@dhis2/cli-style": "^7.1.0-alpha.9",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
"enzyme-to-json": "^3.3.5",
"eslint-plugin-compat": "^3.1.2",
"eslint-plugin-import": "^2.18.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
Expand All @@ -56,13 +55,13 @@
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.js"
],
"snapshotSerializers": [
"enzyme-to-json/serializer"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!src/{index.js,serviceWorker.js,setupTests.js}",
"!<rootDir>/node_modules/"
"!src/{index.js,serviceWorker.js,setupTests.js}"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"/src/locales/"
],
"moduleNameMapper": {
"\\.css$": "identity-obj-proxy"
Expand Down
3 changes: 2 additions & 1 deletion src/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ module.exports = {
'**/hooks/*',
'**/pages/*',
'**/services/*',
'cronstrue/*'
'cronstrue/*',
'test/*',
],
},
],
Expand Down
6 changes: 2 additions & 4 deletions src/components/App/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import { shallow } from 'enzyme'
import App from './App'

describe('<App>', () => {
it('renders correctly', () => {
const wrapper = shallow(<App />)

expect(wrapper).toMatchSnapshot()
it('renders without errors', () => {
shallow(<App />)
})
})
16 changes: 0 additions & 16 deletions src/components/App/__snapshots__/App.test.js.snap

This file was deleted.

13 changes: 10 additions & 3 deletions src/components/AuthWall/AuthWall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import { PropTypes } from '@dhis2/prop-types'
import { Redirect } from 'react-router-dom'
import { CircularLoader, Layer, CenteredContent } from '@dhis2/ui'
import i18n from '@dhis2/d2-i18n'
import { useGetMe, selectors } from '../../hooks/me'
import { useDataQuery } from '@dhis2/app-runtime'
import { getAuthorized } from './selectors'

const query = {
me: {
resource: 'me',
},
}

const AuthWall = ({ children }) => {
const { loading, error, data } = useGetMe()
const { loading, error, data } = useDataQuery(query)

if (loading) {
return (
Expand All @@ -27,7 +34,7 @@ const AuthWall = ({ children }) => {
throw error
}

const isAuthorized = selectors.getAuthorized(data)
const isAuthorized = getAuthorized(data.me)

if (!isAuthorized) {
return <Redirect push to="/notauthorized" />
Expand Down
60 changes: 39 additions & 21 deletions src/components/AuthWall/AuthWall.test.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,76 @@
import React from 'react'
import { shallow } from 'enzyme'
import { useGetMe, selectors } from '../../hooks/me'
import { shallow, mount } from 'enzyme'
import { useDataQuery } from '@dhis2/app-runtime'
import expectRenderError from '../../../test/expect-render-error'
import { getAuthorized } from './selectors'
import AuthWall from './AuthWall'

jest.mock('../../hooks/me', () => ({
useGetMe: jest.fn(),
selectors: {
getAuthorized: jest.fn(),
},
jest.mock('@dhis2/app-runtime', () => ({
useDataQuery: jest.fn(),
}))

jest.mock('./selectors', () => ({
getAuthorized: jest.fn(),
}))

afterEach(() => {
jest.resetAllMocks()
})

describe('<AuthWall>', () => {
it('renders a spinner when loading', () => {
useGetMe.mockImplementationOnce(() => ({ loading: true }))
it('shows a loading message when loading', () => {
useDataQuery.mockImplementation(() => ({ loading: true }))

const wrapper = shallow(<AuthWall>Child</AuthWall>)
const wrapper = mount(<AuthWall>Child</AuthWall>)
const loadingIndicator = wrapper.find({
'data-test': 'dhis2-uicore-circularloader',
})

expect(wrapper).toMatchSnapshot()
expect(loadingIndicator).toHaveLength(1)
expect(wrapper.text()).toEqual(
expect.stringContaining('Checking permissions')
)
})

it('throws fetching errors if they occur', () => {
const props = { children: 'Child' }
const error = new Error('Something went wrong')
useGetMe.mockImplementationOnce(() => ({
const message = 'Something went wrong'
const error = new Error(message)

useDataQuery.mockImplementation(() => ({
loading: false,
error,
}))

expect(() => AuthWall(props)).toThrow(error)
expectRenderError(<AuthWall {...props} />, message)
})

it('redirects unauthorized users', () => {
useGetMe.mockImplementationOnce(() => ({
it('redirects unauthorized users to /notauthorized', () => {
useDataQuery.mockImplementation(() => ({
loading: false,
error: undefined,
data: {},
}))
selectors.getAuthorized.mockImplementationOnce(() => false)
getAuthorized.mockImplementation(() => false)

const wrapper = shallow(<AuthWall>Child</AuthWall>)
const redirect = wrapper.find('Redirect')
const props = redirect.props()

expect(wrapper).toMatchSnapshot()
expect(redirect).toHaveLength(1)
expect(props).toEqual(expect.objectContaining({ to: '/notauthorized' }))
})

it('renders the children for users that are authorized', () => {
useGetMe.mockImplementationOnce(() => ({
useDataQuery.mockImplementation(() => ({
loading: false,
error: undefined,
data: {},
}))
selectors.getAuthorized.mockImplementationOnce(() => true)
getAuthorized.mockImplementation(() => true)

const wrapper = shallow(<AuthWall>Child</AuthWall>)

expect(wrapper).toMatchSnapshot()
expect(wrapper.text()).toEqual(expect.stringContaining('Child'))
})
})
32 changes: 0 additions & 32 deletions src/components/AuthWall/__snapshots__/AuthWall.test.js.snap

This file was deleted.

13 changes: 13 additions & 0 deletions src/components/AuthWall/selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const getAuthorized = me => {
const { authorities } = me

if (!authorities) {
return false
}

const isAuthorized =
authorities.includes('ALL') ||
authorities.includes('F_SCHEDULING_ADMIN')

return isAuthorized
}
23 changes: 23 additions & 0 deletions src/components/AuthWall/selectors.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getAuthorized } from './selectors'

describe('getAuthorized', () => {
it('should return false if there are no authorities', () => {
expect(getAuthorized({})).toBe(false)
})

it('should return true if the authorities include ALL', () => {
const me = {
authorities: ['ALL'],
}

expect(getAuthorized(me)).toBe(true)
})

it('should return true if the authorities include F_SCHEDULING_ADMIN', () => {
const me = {
authorities: ['F_SCHEDULING_ADMIN'],
}

expect(getAuthorized(me)).toBe(true)
})
})
5 changes: 4 additions & 1 deletion src/components/Buttons/CronPresetButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const CronPresetButton = ({ setCron, small }) => {
</Button>
{showModal && (
<CronPresetModal
hideModal={() => setShowModal(false)}
hideModal={
/* istanbul ignore next */
() => setShowModal(false)
}
setCron={setCron}
/>
)}
Expand Down
12 changes: 4 additions & 8 deletions src/components/Buttons/CronPresetButton.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@ import { shallow, mount } from 'enzyme'
import CronPresetButton from './CronPresetButton'

describe('<CronPresetButton>', () => {
it('renders correctly', () => {
const wrapper = shallow(<CronPresetButton setCron={() => {}} />)

expect(wrapper).toMatchSnapshot()
it('renders without errors', () => {
shallow(<CronPresetButton setCron={() => {}} />)
})

it('renders small correctly', () => {
const wrapper = shallow(<CronPresetButton setCron={() => {}} small />)

expect(wrapper).toMatchSnapshot()
it('renders without errors when small', () => {
shallow(<CronPresetButton setCron={() => {}} small />)
})

it('shows the modal when button is clicked', () => {
Expand Down
8 changes: 7 additions & 1 deletion src/components/Buttons/DeleteJobButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ const DeleteJobButton = ({ id }) => {
{i18n.t('Delete')}
</Button>
{showModal && (
<DeleteJobModal id={id} hideModal={() => setShowModal(false)} />
<DeleteJobModal
id={id}
hideModal={
/* istanbul ignore next */
() => setShowModal(false)
}
/>
)}
</React.Fragment>
)
Expand Down
6 changes: 2 additions & 4 deletions src/components/Buttons/DeleteJobButton.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import { shallow, mount } from 'enzyme'
import DeleteJobButton from './DeleteJobButton'

describe('<DeleteJobButton>', () => {
it('renders correctly', () => {
const wrapper = shallow(<DeleteJobButton id="1" />)

expect(wrapper).toMatchSnapshot()
it('renders without errors', () => {
shallow(<DeleteJobButton id="1" />)
})

it('shows the modal when button is clicked', () => {
Expand Down
7 changes: 6 additions & 1 deletion src/components/Buttons/DiscardFormButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ const DiscardFormButton = ({ shouldConfirm, children, small, className }) => {
{children}
</Button>
{showModal && (
<DiscardFormModal hideModal={() => setShowModal(false)} />
<DiscardFormModal
hideModal={
/* istanbul ignore next */
() => setShowModal(false)
}
/>
)}
</React.Fragment>
)
Expand Down
Loading

0 comments on commit aa51d10

Please sign in to comment.