From 00c642328f8a2fd7560f06af1892170cf1e6447c Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 08:53:38 +0200 Subject: [PATCH 1/8] test(build validation result): cover with tests (DHIS2-14384) --- .../build-validation-result.test.js | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/shared/validation/build-validation-result.test.js diff --git a/src/shared/validation/build-validation-result.test.js b/src/shared/validation/build-validation-result.test.js new file mode 100644 index 000000000..6b1e3c079 --- /dev/null +++ b/src/shared/validation/build-validation-result.test.js @@ -0,0 +1,169 @@ +import buildValidationResult from './build-validation-result.js' + +describe('buildValidationResult', () => { + it('should build a result with no violations', () => { + const validationRuleViolations = { + validationRuleViolations: [], + commentRequiredViolations: [], + } + + const validationRulesMetaData = {} + + const expected = { + validationRuleViolations: {}, + commentRequiredViolations: [], + } + + const actual = buildValidationResult( + validationRuleViolations, + validationRulesMetaData + ) + + expect(actual).toEqual(expected) + }) + + it('should contain some commentRequiredViolations', () => { + const commentRequiredViolations = [ + { + displayShortName: 'violation 1', + id: 'comment-required-violation-1', + }, + { + displayShortName: 'violation 2', + id: 'comment-required-violation-2', + }, + ] + + const validationRuleViolations = { + validationRuleViolations: [], + commentRequiredViolations, + } + + const validationRulesMetaData = {} + + const expected = { + validationRuleViolations: {}, + commentRequiredViolations: [ + { + displayShortName: 'violation 1', + id: 'comment-required-violation-1', + }, + { + displayShortName: 'violation 2', + id: 'comment-required-violation-2', + }, + ], + } + + const actual = buildValidationResult( + validationRuleViolations, + validationRulesMetaData + ) + + expect(actual).toEqual(expected) + }) + + it('should group violation by priority', () => { + const validationRuleViolations = { + validationRuleViolations: [ + { + id: 'violation 1', + name: 'Violation 1', + validationRule: { id: 'validation-rule-1' }, + }, + { + id: 'violation 2', + name: 'Violation 2', + validationRule: { id: 'validation-rule-2' }, + }, + ], + commentRequiredViolations: [], + } + + const validationRulesMetaData = { + validationRules: [ + { + id: 'validation-rule-1', + importance: 'LOW', + leftSide: { displayDescription: 'Inspection 1st year' }, + rightSide: { displayDescription: 'Inspection 2nd year' }, + operator: '>=', + displayDescription: + 'More or equal amount of inspections in first year than in second year', + displayInstruction: '@TODO', + displayName: 'Inspection 1st year >= inspection 2nd year', + }, + { + id: 'validation-rule-2', + importance: 'HIGH', + leftSide: { displayDescription: 'Inspection 2nd year' }, + rightSide: { displayDescription: 'Inspection 3rd year' }, + operator: '>=', + displayDescription: + 'More or equal amount of inspections in second year than in third year', + displayInstruction: '@TODO', + displayName: 'Inspection 2nd year >= inspection 3rd year', + }, + ], + } + + const expected = { + validationRuleViolations: { + LOW: [ + { + id: 'violation 1', + name: 'Violation 1', + validationRule: { id: 'validation-rule-1' }, + metaData: { + id: 'validation-rule-1', + importance: 'LOW', + leftSide: { + displayDescription: 'Inspection 1st year', + }, + rightSide: { + displayDescription: 'Inspection 2nd year', + }, + operator: '>=', + displayDescription: + 'More or equal amount of inspections in first year than in second year', + displayInstruction: '@TODO', + displayName: + 'Inspection 1st year >= inspection 2nd year', + }, + }, + ], + HIGH: [ + { + id: 'violation 2', + name: 'Violation 2', + validationRule: { id: 'validation-rule-2' }, + metaData: { + id: 'validation-rule-2', + importance: 'HIGH', + leftSide: { + displayDescription: 'Inspection 2nd year', + }, + rightSide: { + displayDescription: 'Inspection 3rd year', + }, + operator: '>=', + displayDescription: + 'More or equal amount of inspections in second year than in third year', + displayInstruction: '@TODO', + displayName: + 'Inspection 2nd year >= inspection 3rd year', + }, + }, + ], + }, + commentRequiredViolations: [], + } + + const actual = buildValidationResult( + validationRuleViolations, + validationRulesMetaData + ) + + expect(actual).toEqual(expected) + }) +}) From a5ee0736b05d2754fba810de5c4c43b8aa90a26c Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 10:36:40 +0200 Subject: [PATCH 2/8] refactor(use data value set): extract mapDataValuesToFormInitialValues fn --- .../map-data-values-to-form-initial-values.js | 40 +++++++++++++++++++ .../use-data-value-set/use-data-value-set.js | 24 +---------- 2 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/shared/use-data-value-set/map-data-values-to-form-initial-values.js diff --git a/src/shared/use-data-value-set/map-data-values-to-form-initial-values.js b/src/shared/use-data-value-set/map-data-values-to-form-initial-values.js new file mode 100644 index 000000000..20c035d32 --- /dev/null +++ b/src/shared/use-data-value-set/map-data-values-to-form-initial-values.js @@ -0,0 +1,40 @@ +/** + * @params {[ + * { + * dataElement: string + * categoryOptionCombo: string + * value: string | number + * } + * ]} + * @returns {{ + * [dataElementId: string]: { + * [cocId: string]: { + * dataElement: string + * categoryOptionCombo: string + * value: string | number + * } + * } + * }} + */ +export default function mapDataValuesToFormInitialValues(dataValues) { + // It's possible for the backend to return a response + // that does not have dataValues + if (!dataValues) { + return {} + } + + const formInitialValues = dataValues.reduce((acc, dataValueData) => { + if (!acc[dataValueData.dataElement]) { + acc[dataValueData.dataElement] = { + [dataValueData.categoryOptionCombo]: dataValueData, + } + } else { + acc[dataValueData.dataElement][dataValueData.categoryOptionCombo] = + dataValueData + } + + return acc + }, {}) + + return formInitialValues +} diff --git a/src/shared/use-data-value-set/use-data-value-set.js b/src/shared/use-data-value-set/use-data-value-set.js index 51e6f793c..2daff6ffb 100644 --- a/src/shared/use-data-value-set/use-data-value-set.js +++ b/src/shared/use-data-value-set/use-data-value-set.js @@ -2,31 +2,9 @@ import { useQuery, useIsMutating } from '@tanstack/react-query' import { createSelector } from 'reselect' import { defaultOnSuccess } from '../../shared/default-on-success.js' import { useIsValidSelection } from '../use-context-selection/index.js' +import mapDataValuesToFormInitialValues from './map-data-values-to-form-initial-values.js' import useDataValueSetQueryKey from './use-data-value-set-query-key.js' -// Form value object structure: { [dataElementId]: { [cocId]: value } } -function mapDataValuesToFormInitialValues(dataValues) { - // It's possible for the backend to return a response - // that does not have dataValues - if (!dataValues) { - return {} - } - - const formInitialValues = dataValues.reduce((acc, dataValueData) => { - if (!acc[dataValueData.dataElement]) { - acc[dataValueData.dataElement] = { - [dataValueData.categoryOptionCombo]: dataValueData, - } - } else { - acc[dataValueData.dataElement][dataValueData.categoryOptionCombo] = - dataValueData - } - - return acc - }, {}) - return formInitialValues -} - const select = createSelector( (data) => data, (data) => { From ad27add4bdce348f5577f41660f0e4532156cc70 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 10:37:05 +0200 Subject: [PATCH 3/8] test(map data values to form initial values): cover with tests (DHIS2-14383) --- ...data-values-to-form-initial-values.test.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/shared/use-data-value-set/map-data-values-to-form-initial-values.test.js diff --git a/src/shared/use-data-value-set/map-data-values-to-form-initial-values.test.js b/src/shared/use-data-value-set/map-data-values-to-form-initial-values.test.js new file mode 100644 index 000000000..ca3259ea6 --- /dev/null +++ b/src/shared/use-data-value-set/map-data-values-to-form-initial-values.test.js @@ -0,0 +1,43 @@ +import mapDataValuesToFormInitialValues from './map-data-values-to-form-initial-values.js' + +describe('mapDataValuesToFormInitialValues', () => { + it('should return an empty object when provided a faulty values', () => { + expect(mapDataValuesToFormInitialValues(undefined)).toEqual({}) + }) + + it('should return transform the data values from an array to an object', () => { + const dataValues = [ + { dataElement: 'de1', categoryOptionCombo: 'coc1', value: 3 }, + { dataElement: 'de2', categoryOptionCombo: 'coc2', value: 4 }, + { dataElement: 'de3', categoryOptionCombo: 'coc3', value: 5 }, + ] + + const expected = { + de1: { + coc1: { + dataElement: 'de1', + categoryOptionCombo: 'coc1', + value: 3, + }, + }, + de2: { + coc2: { + dataElement: 'de2', + categoryOptionCombo: 'coc2', + value: 4, + }, + }, + de3: { + coc3: { + dataElement: 'de3', + categoryOptionCombo: 'coc3', + value: 5, + }, + }, + } + + const actual = mapDataValuesToFormInitialValues(dataValues) + + expect(actual).toEqual(expected) + }) +}) From 3755c0b2b8aec9961af0ab5ac247471dd6e577d1 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 11:08:36 +0200 Subject: [PATCH 4/8] refactor(use is valid selection): simplify missing categoryCombo & dataSet condition --- .../use-context-selection/use-is-valid-selection.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/shared/use-context-selection/use-is-valid-selection.js b/src/shared/use-context-selection/use-is-valid-selection.js index 3fb34a2a9..46827aca8 100644 --- a/src/shared/use-context-selection/use-is-valid-selection.js +++ b/src/shared/use-context-selection/use-is-valid-selection.js @@ -13,18 +13,15 @@ export function useIsValidSelection() { const dataSet = selectors.getDataSetById(data, dataSetId) const catComboId = dataSet?.categoryCombo?.id const categoryCombo = selectors.getCategoryComboById(data, catComboId) - if ( - dataSet === undefined || - categoryCombo === null || - categoryCombo === undefined - ) { + + if (!dataSet || !categoryCombo) { return false } const selectedOptions = Object.values(attributeOptionComboSelection) // if default catCombo, no selection is needed - if (categoryCombo?.isDefault) { + if (categoryCombo.isDefault) { return true } From 22542978811b242c01a82138949534930a60f801 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 11:09:57 +0200 Subject: [PATCH 5/8] test(use is valid selection): cover with tests (DHIS2-14382) --- .../use-is-valid-selection.test.js | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 src/shared/use-context-selection/use-is-valid-selection.test.js diff --git a/src/shared/use-context-selection/use-is-valid-selection.test.js b/src/shared/use-context-selection/use-is-valid-selection.test.js new file mode 100644 index 000000000..5be37abb4 --- /dev/null +++ b/src/shared/use-context-selection/use-is-valid-selection.test.js @@ -0,0 +1,203 @@ +import { useMetadata } from '../metadata/index.js' +import { useContextSelection } from './use-context-selection.js' +import { useIsValidSelection } from './use-is-valid-selection.js' + +jest.mock('./use-context-selection.js', () => ({ + useContextSelection: jest.fn(), +})) + +jest.mock('../metadata/use-metadata.js', () => ({ + useMetadata: jest.fn(), +})) + +describe('useIsValidSelection', () => { + it('should return true for a non-default category combo', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: { + category1: 'categoryOption1', + category2: 'categoryOption2', + }, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo1' }, + }, + }, + categoryCombos: { + categoryCombo1: { + isDefault: false, + categories: ['category1', 'category2'], + }, + }, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(true) + }) + + it('should return true when the category combo is the default one', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo1' }, + }, + }, + categoryCombos: { + categoryCombo1: { + isDefault: true, + categories: ['category1'], + }, + }, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(true) + }) + + it('should return false when the categoryCombo is undefined', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo2' }, + }, + }, + categoryCombos: {}, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) + + it('should return false when the data set is undefined', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: {}, + categoryCombos: {}, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) + + it('should return false when there is no metadata', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = null + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) + + it('should return false when there is no selected data set', () => { + const contextSelection = { + orgUnitId: 'orgUnit1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo1' }, + }, + }, + categoryCombos: { + categoryCombo1: { + isDefault: false, + categories: ['category1', 'category2'], + }, + }, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) + + it('should return false when there is no selected org unit', () => { + const contextSelection = { + dataSetId: 'dataSet1', + periodId: '2023', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo1' }, + }, + }, + categoryCombos: { + categoryCombo1: { + isDefault: false, + categories: ['category1', 'category2'], + }, + }, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) + + it('should return false when there is no selected period', () => { + const contextSelection = { + dataSetId: 'dataSet1', + orgUnitId: 'orgUnit1', + attributeOptionComboSelection: {}, + } + useContextSelection.mockImplementation(() => [contextSelection]) + + const metadata = { + dataSets: { + dataSet1: { + categoryCombo: { id: 'categoryCombo1' }, + }, + }, + categoryCombos: { + categoryCombo1: { + isDefault: false, + categories: ['category1', 'category2'], + }, + }, + } + useMetadata.mockImplementation(() => ({ data: metadata })) + + expect(useIsValidSelection()).toBe(false) + }) +}) From f6739d3ded989a682cd83a86b811ffc0859d85bf Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 6 Jun 2023 11:15:31 +0200 Subject: [PATCH 6/8] refactor(update lock status from backend): move to its own file --- src/shared/locked-status/index.js | 6 ++-- .../update-lock-status-from-backend.js | 32 +++++++++++++++++++ .../locked-status/use-check-lock-status.js | 31 ------------------ 3 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 src/shared/locked-status/update-lock-status-from-backend.js diff --git a/src/shared/locked-status/index.js b/src/shared/locked-status/index.js index 557769493..d6f7b598d 100644 --- a/src/shared/locked-status/index.js +++ b/src/shared/locked-status/index.js @@ -1,8 +1,6 @@ -export { - useCheckLockStatus, - updateLockStatusFromBackend, -} from './use-check-lock-status.js' export { LockedContext } from './locked-info-context.js' export { LockedProvider } from './locked-info-provider.js' export { LockedStates } from './locked-states.js' +export { useCheckLockStatus } from './use-check-lock-status.js' export { useLockedContext } from './use-locked-context.js' +export { updateLockStatusFromBackend } from './update-lock-status-from-backend.js' diff --git a/src/shared/locked-status/update-lock-status-from-backend.js b/src/shared/locked-status/update-lock-status-from-backend.js new file mode 100644 index 000000000..e8ffe7898 --- /dev/null +++ b/src/shared/locked-status/update-lock-status-from-backend.js @@ -0,0 +1,32 @@ +import { LockedStates } from './locked-states.js' + +export const updateLockStatusFromBackend = ( + frontEndLockStatus, + backEndLockStatus, + setLockStatus +) => { + // if the lock status is APPROVED, set to approved + if (backEndLockStatus === 'APPROVED') { + setLockStatus(LockedStates.LOCKED_APPROVED) + return + } + + // if the lock status is LOCKED, this is locked due to expiry days + if (backEndLockStatus === 'LOCKED') { + setLockStatus(LockedStates.LOCKED_EXPIRY_DAYS) + return + } + + // a lock status of 'OPEN' from the backend could mean either that the form is open OR + // that the form should be locked due to data input period, OR + // that the form should be locked because an organisation unit is out of range, SO + // set to OPEN unless frontend check has identified that data input period as out-of-bounds + if ( + ![ + LockedStates.LOCKED_DATA_INPUT_PERIOD, + LockedStates.LOCKED_ORGANISATION_UNIT, + ].includes(frontEndLockStatus) + ) { + setLockStatus(LockedStates.OPEN) + } +} diff --git a/src/shared/locked-status/use-check-lock-status.js b/src/shared/locked-status/use-check-lock-status.js index bc007debc..8e5022ec8 100644 --- a/src/shared/locked-status/use-check-lock-status.js +++ b/src/shared/locked-status/use-check-lock-status.js @@ -143,34 +143,3 @@ export const useCheckLockStatus = () => { currentDayString, ]) } - -export const updateLockStatusFromBackend = ( - frontEndLockStatus, - backEndLockStatus, - setLockStatus -) => { - // if the lock status is APPROVED, set to approved - if (backEndLockStatus === 'APPROVED') { - setLockStatus(LockedStates.LOCKED_APPROVED) - return - } - - // if the lock status is LOCKED, this is locked due to expiry days - if (backEndLockStatus === 'LOCKED') { - setLockStatus(LockedStates.LOCKED_EXPIRY_DAYS) - return - } - - // a lock status of 'OPEN' from the backend could mean either that the form is open OR - // that the form should be locked due to data input period, OR - // that the form should be locked because an organisation unit is out of range, SO - // set to OPEN unless frontend check has identified that data input period as out-of-bounds - if ( - ![ - LockedStates.LOCKED_DATA_INPUT_PERIOD, - LockedStates.LOCKED_ORGANISATION_UNIT, - ].includes(frontEndLockStatus) - ) { - setLockStatus(LockedStates.OPEN) - } -} From 3b00623021b5b6d4bd0590b2797a6c73e51111f1 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 7 Jun 2023 16:08:39 +0200 Subject: [PATCH 7/8] refactor(use check lock status): change org unit checker fn name to reflect the check --- .../locked-status/use-check-lock-status.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/shared/locked-status/use-check-lock-status.js b/src/shared/locked-status/use-check-lock-status.js index 8e5022ec8..309c22bc2 100644 --- a/src/shared/locked-status/use-check-lock-status.js +++ b/src/shared/locked-status/use-check-lock-status.js @@ -6,7 +6,7 @@ import { usePeriodId, useDataSetId, useOrgUnitId, -} from '../use-context-selection/use-context-selection.js' +} from '../use-context-selection/index.js' import { useDataValueSet } from '../use-data-value-set/use-data-value-set.js' import { useOrgUnit } from '../use-org-unit/use-organisation-unit.js' import { LockedStates, BackendLockStatusMap } from './locked-states.js' @@ -38,7 +38,17 @@ const isDataInputPeriodLocked = ({ ) } -const isOrgUnitLocked = ({ +/** + * An org unit is locked not based on the current date, + * but based on the selected period. + * + * -> If org unit's close date is before the period's end date, + * then the user is not allowed to modify data. + * + * -> If the org unit's open date is after the period's start date, + * then the user is not allowed to modify data. + */ +const isOrgUnitTimeConstraintWithinDataInputPeriodConstraint = ({ orgUnitOpeningDateString, orgUnitClosedDateString, selectedPeriod, @@ -61,7 +71,7 @@ const isOrgUnitLocked = ({ // if orgUnitOpeningDate exists, it must be earlier than the periodStartDate if (orgUnitOpeningDateString) { const orgUnitOpeningDate = new Date(orgUnitOpeningDateString) - if (!(orgUnitOpeningDate <= periodStartDate)) { + if (orgUnitOpeningDate > periodStartDate) { return true } } @@ -69,7 +79,7 @@ const isOrgUnitLocked = ({ // if orgUnitClosedDate exists, it must be after the periodEndDate if (orgUnitClosedDateString) { const orgUnitClosedDate = new Date(orgUnitClosedDateString) - if (!(orgUnitClosedDate >= periodEndDate)) { + if (orgUnitClosedDate < periodEndDate) { return true } } @@ -112,7 +122,7 @@ export const useCheckLockStatus = () => { } if ( - isOrgUnitLocked({ + isOrgUnitTimeConstraintWithinDataInputPeriodConstraint({ orgUnitOpeningDateString, orgUnitClosedDateString, selectedPeriod, From 7bb59c2d04cece2ec23341c6d04afe180ed2df7f Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 7 Jun 2023 16:10:24 +0200 Subject: [PATCH 8/8] test(use check lock status): cover with tests (DHIS2-14381j) --- .../use-check-lock-status.test.js | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/shared/locked-status/use-check-lock-status.test.js diff --git a/src/shared/locked-status/use-check-lock-status.test.js b/src/shared/locked-status/use-check-lock-status.test.js new file mode 100644 index 000000000..87a40a387 --- /dev/null +++ b/src/shared/locked-status/use-check-lock-status.test.js @@ -0,0 +1,151 @@ +import { renderHook } from '@testing-library/react-hooks' +import { useClientServerDate } from '../date/index.js' +import { useMetadata } from '../metadata/index.js' +import { + usePeriodId, + useDataSetId, + useOrgUnitId, +} from '../use-context-selection/index.js' +import { useDataValueSet } from '../use-data-value-set/use-data-value-set.js' +import { useOrgUnit } from '../use-org-unit/use-organisation-unit.js' +import { LockedStates } from './locked-states.js' +import { useCheckLockStatus } from './use-check-lock-status.js' +import { useLockedContext } from './use-locked-context.js' + +jest.mock('../date/use-client-server-date.js', () => ({ + __esModule: true, + default: jest.fn(), +})) + +jest.mock('../metadata/use-metadata.js', () => ({ + useMetadata: jest.fn(), +})) + +jest.mock('../use-context-selection/use-context-selection.js', () => ({ + useDataSetId: jest.fn(), + useOrgUnitId: jest.fn(), + usePeriodId: jest.fn(), +})) + +jest.mock('./use-locked-context.js', () => ({ + useLockedContext: jest.fn(), +})) + +jest.mock('../use-data-value-set/use-data-value-set.js', () => ({ + useDataValueSet: jest.fn(), +})) + +jest.mock('../use-org-unit/use-organisation-unit.js', () => ({ + useOrgUnit: jest.fn(), +})) + +describe('useCheckLockStatus', () => { + useDataSetId.mockImplementation(() => ['dataSet1']) + useOrgUnitId.mockImplementation(() => ['orgUnit1']) + usePeriodId.mockImplementation(() => ['202301']) + useOrgUnit.mockImplementation(() => ({ + data: { + id: 'orgUnit1', + displayName: 'Org unit 1', + path: '/orgUnit1', + openingDate: '2015-01-01', + closedDate: '2023-01-18', + }, + })) + useMetadata.mockImplementation(() => ({ + data: { + dataSets: { + dataSet1: { + id: 'dataSet1', + dataInputPeriods: [ + { + period: { id: '202301', name: '202301' }, + // This defines in which time frame you're allowed + // to enter data for this period + openingDate: '2022-07-01T00:00:00.000', + closingDate: '2023-01-28T00:00:00.000', + }, + ], + }, + }, + }, + })) + useDataValueSet.mockImplementation(() => ({ + loading: false, + error: null, + data: null, + })) + + const setLockStatus = jest.fn() + useLockedContext.mockImplementation(() => ({ setLockStatus })) + + afterEach(() => { + setLockStatus.mockClear() + }) + + it('should set the lock status to LOCKED_DATA_INPUT_PERIOD', () => { + useClientServerDate.mockImplementation(() => ({ + serverDate: new Date('2023-01-30'), + clientDate: new Date('2023-01-30'), + })) + + renderHook(useCheckLockStatus) + + expect(setLockStatus).toHaveBeenCalledWith( + LockedStates.LOCKED_DATA_INPUT_PERIOD + ) + }) + + it('should set the lock status to LOCKED_ORGANISATION_UNIT', () => { + useClientServerDate.mockImplementation(() => ({ + serverDate: new Date('2023-01-22'), + clientDate: new Date('2023-01-22'), + })) + + renderHook(useCheckLockStatus) + + expect(setLockStatus).toHaveBeenCalledWith( + LockedStates.LOCKED_ORGANISATION_UNIT + ) + }) + + it('should set the lock status to OPEN', () => { + usePeriodId.mockImplementation(() => ['202301']) + + useOrgUnit.mockImplementation(() => ({ + data: { + id: 'orgUnit1', + displayName: 'Org unit 1', + path: '/orgUnit1', + openingDate: '2015-01-01', + closedDate: '2023-02-01', + }, + })) + + useMetadata.mockImplementation(() => ({ + data: { + dataSets: { + dataSet1: { + id: 'dataSet1', + dataInputPeriods: [ + { + period: { id: '202301', name: '202301' }, + openingDate: '2022-07-01T00:00:00.000', + closingDate: '2023-01-31T00:00:00.000', + }, + ], + }, + }, + }, + })) + + useClientServerDate.mockImplementation(() => ({ + serverDate: new Date('2022-12-10'), + clientDate: new Date('2022-12-10'), + })) + + renderHook(useCheckLockStatus) + + expect(setLockStatus).toHaveBeenCalledWith(LockedStates.OPEN) + }) +})