diff --git a/app/frontend/src/components/designer/FormViewer.vue b/app/frontend/src/components/designer/FormViewer.vue index 42c6714e2..5d1025209 100644 --- a/app/frontend/src/components/designer/FormViewer.vue +++ b/app/frontend/src/components/designer/FormViewer.vue @@ -128,7 +128,9 @@ const { authenticated, keycloak, tokenParsed, user } = storeToRefs(authStore); const { downloadedFile, isRTL } = storeToRefs(formStore); const formScheduleExpireMessage = computed(() => - t('trans.formViewer.formScheduleExpireMessage') + form?.value?.schedule?.message + ? form.value.schedule.message + : t('trans.formViewer.formScheduleExpireMessage') ); const formUnauthorizedMessage = computed(() => diff --git a/app/frontend/tests/unit/components/designer/FormViewer.spec.js b/app/frontend/tests/unit/components/designer/FormViewer.spec.js index b1bc4b02c..6f9cabb9f 100644 --- a/app/frontend/tests/unit/components/designer/FormViewer.spec.js +++ b/app/frontend/tests/unit/components/designer/FormViewer.spec.js @@ -170,7 +170,7 @@ describe('FormViewer.vue', () => { getDispositionSpy.mockImplementation(() => {}); }); - it('formScheduleExpireMessage returns the formScheduleExpireMessage translation', async () => { + it('formScheduleExpireMessage returns the formScheduleExpireMessage translation or custom message', async () => { const wrapper = shallowMount(FormViewer, { props: { formId: formId, @@ -187,9 +187,19 @@ describe('FormViewer.vue', () => { await flushPromises(); + wrapper.vm.form = {}; + expect(wrapper.vm.formScheduleExpireMessage).toEqual( 'trans.formViewer.formScheduleExpireMessage' ); + + wrapper.vm.form = { + schedule: { + message: 'custom message', + }, + }; + + expect(wrapper.vm.formScheduleExpireMessage).toEqual('custom message'); }); it('formUnauthorizedMessage returns the formUnauthorizedMessage translation', async () => { diff --git a/app/package-lock.json b/app/package-lock.json index e0ebf463b..67843b26e 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4888,9 +4888,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -5398,9 +5398,9 @@ "dev": true }, "node_modules/eslint-config-esnext/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -5866,9 +5866,9 @@ "dev": true }, "node_modules/eslint-config-node/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -6348,9 +6348,9 @@ "dev": true }, "node_modules/eslint-config-react-native/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -6831,9 +6831,9 @@ "dev": true }, "node_modules/eslint-config-recommended/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "dependencies": { "nice-try": "^1.0.4", @@ -17082,9 +17082,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -17496,9 +17496,9 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -17852,9 +17852,9 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -18217,9 +18217,9 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -18585,9 +18585,9 @@ "dev": true }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "requires": { "nice-try": "^1.0.4", diff --git a/app/src/forms/common/utils.js b/app/src/forms/common/utils.js index 456b8ab88..825a18350 100644 --- a/app/src/forms/common/utils.js +++ b/app/src/forms/common/utils.js @@ -3,6 +3,7 @@ const falsey = require('falsey'); const moment = require('moment'); const clone = require('lodash/clone'); const _ = require('lodash'); +const { ScheduleType } = require('./constants'); const setupMount = (type, app, routes) => { const p = `/${type}`; @@ -209,109 +210,98 @@ const checkIsFormExpired = (formSchedule = {}) => { message: '', }; - if (formSchedule && formSchedule.enabled) { - //Check if Form open date is in past or Is form already started for submission - if (formSchedule.openSubmissionDateTime) { - let startDate = moment(formSchedule.openSubmissionDateTime).format('YYYY-MM-DD HH:MM:SS'); - let closingDate = null; - if (formSchedule.scheduleType === 'closingDate' && formSchedule.closeSubmissionDateTime) { - closingDate = moment(formSchedule.closeSubmissionDateTime).format('YYYY-MM-DD HH:MM:SS'); - } - let isFormStartedAlready = moment().diff(startDate, 'seconds'); //If a positive number it means form get started - if (isFormStartedAlready >= 0) { - //Form have valid past open date for scheduling so lets check for the next conditions - if (isFormStartedAlready && formSchedule.enabled && formSchedule.scheduleType !== 'manual') { - if (formSchedule.closingMessageEnabled) { - if (formSchedule.closingMessage) { - result = { ...result, message: formSchedule.closingMessage }; - } else { - result = { ...result, message: 'Something went wrong.' }; - } - } else { - result = { ...result, message: 'The form submission period has expired.' }; - } + if (formSchedule && formSchedule.enabled && formSchedule.openSubmissionDateTime) { + // The start date is the date that the form should be scheduled to be open to allow submissions + let startDate = moment(formSchedule.openSubmissionDateTime).startOf('day'); + // The closing date is the date that the form should be scheduled to be closed + let closingDate = null; + if (formSchedule.scheduleType === ScheduleType.CLOSINGDATE && formSchedule.closeSubmissionDateTime) { + closingDate = moment(formSchedule.closeSubmissionDateTime).endOf('day'); + } - let closeDate = - formSchedule.scheduleType === 'period' - ? getCalculatedCloseSubmissionDate( - startDate, - formSchedule.keepOpenForTerm, - formSchedule.keepOpenForInterval, - formSchedule.allowLateSubmissions.enabled ? formSchedule.allowLateSubmissions.forNext.term : 0, - formSchedule.allowLateSubmissions.forNext.intervalType, - formSchedule.repeatSubmission.everyTerm, - formSchedule.repeatSubmission.everyIntervalType, - formSchedule.repeatSubmission.repeatUntil, - formSchedule.scheduleType, - formSchedule.closeSubmissionDateTime - ) - : closingDate; //moment(formSchedule.closeSubmissionDateTime).format('YYYY-MM-DD HH:MM:SS'); - let isBetweenStartAndCloseDate = moment().isBetween(startDate, closeDate); + const currentMoment = moment(); - if (isBetweenStartAndCloseDate) { - /** Check if form is Repeat enabled - start */ - /** Check if form is Repeat enabled and alow late submition - start */ - if (formSchedule.repeatSubmission.enabled) { - let availableDates = getSubmissionPeriodDates( + let isFormStartedAlready = currentMoment.diff(startDate, 'seconds'); //If a positive number it means form get started + + if (isFormStartedAlready >= 0) { + // The manual submission period does not have a custom closing message + if (formSchedule.scheduleType !== ScheduleType.MANUAL) { + if (formSchedule.closingMessageEnabled && formSchedule.closingMessage) { + result = { ...result, message: formSchedule.closingMessage }; + } + + let closeDate = + formSchedule.scheduleType === ScheduleType.PERIOD + ? getCalculatedCloseSubmissionDate( + startDate, formSchedule.keepOpenForTerm, formSchedule.keepOpenForInterval, - startDate, - formSchedule.repeatSubmission.everyTerm, - formSchedule.repeatSubmission.everyIntervalType, formSchedule.allowLateSubmissions.enabled ? formSchedule.allowLateSubmissions.forNext.term : 0, formSchedule.allowLateSubmissions.forNext.intervalType, - formSchedule.repeatSubmission.repeatUntil - ); - for (let i = 0; i < availableDates.length; i++) { - //Check if today is the day when a submitter can submit the form for given period of repeat submission - let repeatIsBetweenStartAndCloseDate = moment().isBetween(availableDates[i].startDate, availableDates[i].closeDate); - - if (repeatIsBetweenStartAndCloseDate) { - result = { ...result, expire: false }; //Form is available for given period to be submit. + formSchedule.repeatSubmission.everyTerm, + formSchedule.repeatSubmission.everyIntervalType, + formSchedule.repeatSubmission.repeatUntil, + formSchedule.scheduleType, + formSchedule.closeSubmissionDateTime + ) + : closingDate; + let isBetweenStartAndCloseDate = currentMoment.isBetween(startDate, closeDate); + if (isBetweenStartAndCloseDate) { + if (formSchedule.repeatSubmission.enabled) { + // These are the available submission periods that a user can submit + let availableDates = getSubmissionPeriodDates( + formSchedule.keepOpenForTerm, + formSchedule.keepOpenForInterval, + startDate, + formSchedule.repeatSubmission.everyTerm, + formSchedule.repeatSubmission.everyIntervalType, + formSchedule.allowLateSubmissions.enabled ? formSchedule.allowLateSubmissions.forNext.term : 0, + formSchedule.allowLateSubmissions.forNext.intervalType, + formSchedule.repeatSubmission.repeatUntil + ); + for (let i = 0; i < availableDates.length; i++) { + // Check if today is the day when a submitter can submit the form for given period of repeat submission + let repeatIsBetweenStartAndCloseDate = moment().isBetween(availableDates[i].startDate, availableDates[i].closeDate); + + if (repeatIsBetweenStartAndCloseDate) { + result = { ...result, expire: false }; //Form is available for given period to be submit. + break; + } else if (formSchedule.allowLateSubmissions.enabled) { + result = { ...result, expire: true }; + let isallowLateSubmissions = moment().isBetween(availableDates[i].startDate, availableDates[i].graceDate); + if (isallowLateSubmissions) { + //If late submission is allowed for the given repeat submission period then stop checking for other dates + result = { + ...result, + expire: true, + allowLateSubmissions: isallowLateSubmissions, + }; break; - } else if (formSchedule.allowLateSubmissions.enabled) { - result = { ...result, expire: true }; - /** Check if form is alow late submition - start */ - let isallowLateSubmissions = moment().isBetween(availableDates[i].startDate, availableDates[i].graceDate); - if (isallowLateSubmissions) { - //If late submission is allowed for the given repeat submission period then stop checking for other dates - result = { - ...result, - expire: true, - allowLateSubmissions: isallowLateSubmissions, - }; - break; - } - /** Check if form is alow late submition - end */ - } else { - result = { ...result, expire: true, allowLateSubmissions: false }; } + } else { + result = { ...result, expire: true, allowLateSubmissions: false }; } } - /** Check if form is Repeat enabled and alow late submition - end */ - /** Check if form is Repeat enabled - end */ + } + } else { + // Block form submission but check if the designer allowed for late submissions + if (formSchedule.allowLateSubmissions.enabled) { + result = { + ...result, + expire: true, + allowLateSubmissions: isEligibleLateSubmission(closeDate, formSchedule.allowLateSubmissions.forNext.term, formSchedule.allowLateSubmissions.forNext.intervalType), + }; } else { - //if close date not valid or not-in future OR close date not in between start and Today then block formSubmission but check the late submission if allowed - - if (formSchedule.allowLateSubmissions.enabled) { - /** Check if form is alow late submition - start */ - result = { - ...result, - expire: true, - allowLateSubmissions: isEligibleLateSubmission(closeDate, formSchedule.allowLateSubmissions.forNext.term, formSchedule.allowLateSubmissions.forNext.intervalType), - }; - /** Check if form is alow late submition - end */ - } else { - result = { ...result, expire: true, allowLateSubmissions: false }; - } + result = { ...result, expire: true, allowLateSubmissions: formSchedule.allowLateSubmissions.enabled }; } } - } else { - //Form schedule open date is in the future so form will not be available for submission - result = { ...result, expire: true, allowLateSubmissions: false, message: 'This form is not yet available for submission.' }; } + } else { + // The open submission date time is a future date time, so the form is not yet available for submission + result = { ...result, expire: true, allowLateSubmissions: formSchedule.allowLateSubmissions.enabled, message: 'This form is not yet available for submission.' }; } } + return result; }; @@ -361,12 +351,12 @@ const getSubmissionPeriodDates = ( let graceDate = null; calculatedCloseDate.add(keepOpenForTerm, keepOpenForInterval); - if (allowLateTerm && allowLateInterval) graceDate = calculatedCloseDate.clone().add(allowLateTerm, allowLateInterval).format('YYYY-MM-DD HH:MM:SS'); + if (allowLateTerm && allowLateInterval) graceDate = calculatedCloseDate.clone().add(allowLateTerm, allowLateInterval); // Always push through the first submission period submissionPeriodDates.push({ - startDate: openSubmissionDate.clone().format('YYYY-MM-DD HH:MM:SS'), - closeDate: calculatedCloseDate.format('YYYY-MM-DD HH:MM:SS'), + startDate: openSubmissionDate.clone(), + closeDate: calculatedCloseDate, graceDate: graceDate, }); @@ -383,12 +373,12 @@ const getSubmissionPeriodDates = ( calculatedCloseDate = openSubmissionDate.clone().add(keepOpenForTerm, keepOpenForInterval); // If late submissions are enabled, set the grace period equal to the closing date // with the addition of the late period - if (allowLateTerm && allowLateInterval) graceDate = calculatedCloseDate.clone().add(allowLateTerm, allowLateInterval).format('YYYY-MM-DD HH:MM:SS'); + if (allowLateTerm && allowLateInterval) graceDate = calculatedCloseDate.clone().add(allowLateTerm, allowLateInterval); // Add the calculated dates to the submission period array submissionPeriodDates.push({ - startDate: openSubmissionDate.clone().format('YYYY-MM-DD HH:MM:SS'), - closeDate: calculatedCloseDate.format('YYYY-MM-DD HH:MM:SS'), + startDate: openSubmissionDate.clone(), + closeDate: calculatedCloseDate, graceDate: graceDate, }); @@ -432,13 +422,13 @@ const getCalculatedCloseSubmissionDate = ( repeatSubmissionUntil = moment(repeatSubmissionUntil); if (!allowLateTerm && !repeatSubmissionTerm) { - calculatedCloseDate = openDate.add(keepOpenForTerm, keepOpenForInterval).format('YYYY-MM-DD HH:MM:SS'); + calculatedCloseDate = openDate.add(keepOpenForTerm, keepOpenForInterval); } else { if (repeatSubmissionTerm && repeatSubmissionInterval && repeatSubmissionUntil) { calculatedCloseDate = repeatSubmissionUntil; } if (allowLateTerm && allowLateInterval) { - calculatedCloseDate = calculatedCloseDate.add(keepOpenForTerm, keepOpenForInterval).add(allowLateTerm, allowLateInterval).format('YYYY-MM-DD HH:MM:SS'); + calculatedCloseDate = calculatedCloseDate.add(keepOpenForTerm, keepOpenForInterval).add(allowLateTerm, allowLateInterval); } } @@ -622,7 +612,7 @@ const validateScheduleObject = (schedule = {}) => { let schType = schedule.scheduleType; let openSubmissionDateTime = schedule.openSubmissionDateTime; if (isDateValid(openSubmissionDateTime)) { - if (schType === 'closingDate') { + if (schType === ScheduleType.CLOSINGDATE) { if (!isDateValid(schedule.closeSubmissionDateTime)) { result = { message: 'Invalid closed submission date.', @@ -646,7 +636,7 @@ const validateScheduleObject = (schedule = {}) => { }; return result; } - } else if (schType === 'period') { + } else if (schType === ScheduleType.PERIOD) { if (!isLateSubmissionObjValid(schedule)) { result = { message: 'Invalid late submission data.', @@ -681,7 +671,7 @@ const validateScheduleObject = (schedule = {}) => { return result; } } else { - if (schType !== 'manual') { + if (schType !== ScheduleType.MANUAL) { result = { message: 'Invalid schedule type.', status: 'error', diff --git a/app/tests/unit/forms/common/utils.spec.js b/app/tests/unit/forms/common/utils.spec.js index 47c32ddb7..8b5e310b5 100644 --- a/app/tests/unit/forms/common/utils.spec.js +++ b/app/tests/unit/forms/common/utils.spec.js @@ -1,6 +1,8 @@ const config = require('config'); +const moment = require('moment'); -const { getBaseUrl, queryUtils, typeUtils, validateScheduleObject } = require('../../../../src/forms/common/utils'); +const { checkIsFormExpired, getBaseUrl, queryUtils, typeUtils, validateScheduleObject } = require('../../../../src/forms/common/utils'); +const { ScheduleType } = require('../../../../src/forms/common/constants'); jest.mock('config'); @@ -258,4 +260,210 @@ describe('Test Schedule object validation Utils functions', () => { expect(result).toBeDefined(); expect(result).toHaveProperty('status', 'success'); }); + + it('checkIsFormExpired should return the default result object { allowLateSubmission: false, expire: false, message: "" }', () => { + expect(checkIsFormExpired()).toEqual({ allowLateSubmissions: false, expire: false, message: '' }); + }); + + it('checkIsFormExpired should return a message that the form is not available yet if the open time is a future date { ...result, expire: true, allowLateSubmissions: false, message: "This form is not yet available for submission."', () => { + expect( + checkIsFormExpired({ + enabled: true, + allowLateSubmissions: { + enabled: false, + }, + openSubmissionDateTime: moment().add(1, 'days').format('YYYY-MM-DD'), + }) + ).toEqual({ + allowLateSubmissions: false, + expire: true, + message: 'This form is not yet available for submission.', + }); + }); + + it('checkIsFormExpired should return a valid object for a manual schedule with a valid schedule ', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: ScheduleType.MANUAL, + allowLateSubmissions: { + enabled: false, + }, + openSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + }) + ).toEqual({ + allowLateSubmissions: false, + expire: false, + message: '', + }); + }); + + it('checkIsFormExpired should append a closing message if it is enabled and a valid object for a valid schedule', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: ScheduleType.CLOSINGDATE, + closingMessageEnabled: true, + closingMessage: 'closing message', + allowLateSubmissions: { + enabled: false, + }, + repeatSubmission: { + enabled: false, + }, + openSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: moment().add(1, 'days').format('YYYY-MM-DD'), + }) + ).toEqual({ + allowLateSubmissions: false, + expire: false, + message: 'closing message', + }); + }); + + it('checkIsFormExpired should return an expired object for a late schedule with no late submissions', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: ScheduleType.CLOSINGDATE, + allowLateSubmissions: { + enabled: false, + }, + repeatSubmission: { + enabled: false, + }, + openSubmissionDateTime: moment().subtract(2, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + }) + ).toEqual({ + allowLateSubmissions: false, + expire: true, + message: '', + }); + }); + + it('checkIsFormExpired should return an expired object but have allowLateSubmissions to be true for a late schedule with late submissions', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: ScheduleType.CLOSINGDATE, + allowLateSubmissions: { + enabled: true, + forNext: { + term: '1', + intervalType: 'days', + }, + }, + repeatSubmission: { + enabled: false, + }, + openSubmissionDateTime: moment().subtract(2, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + }) + ).toEqual({ + allowLateSubmissions: true, + expire: true, + message: '', + }); + }); + + it('checkIsFormExpired for a period of 1 days should return an unexpired object in a valid schedule', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: 'period', + keepOpenForTerm: '1', + repeatSubmission: { + enabled: true, + everyTerm: '1', + repeatUntil: moment().add(1, 'month').format('YYYY-MM-DD'), + everyIntervalType: 'weeks', + }, + keepOpenForInterval: 'days', + allowLateSubmissions: { enabled: true, forNext: { term: '1', intervalType: 'days' } }, + closingMessageEnabled: false, + openSubmissionDateTime: moment().format('YYYY-MM-DD'), + closeSubmissionDateTime: null, + }) + ).toEqual({ + allowLateSubmissions: false, + expire: false, + message: '', + }); + }); + + it('checkIsFormExpired for a period on an expired day with late submissions should allow it', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: 'period', + keepOpenForTerm: '1', + repeatSubmission: { + enabled: true, + everyTerm: '1', + repeatUntil: moment().add(1, 'month').format('YYYY-MM-DD'), + everyIntervalType: 'weeks', + }, + keepOpenForInterval: 'days', + allowLateSubmissions: { enabled: true, forNext: { term: '1', intervalType: 'days' } }, + closingMessageEnabled: false, + openSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: null, + }) + ).toEqual({ + allowLateSubmissions: true, + expire: true, + message: '', + }); + }); + + it('checkIsFormExpired for a period on an expired day with no late submissions should disallow it', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: 'period', + keepOpenForTerm: '1', + repeatSubmission: { + enabled: true, + everyTerm: '1', + repeatUntil: moment().add(1, 'month').format('YYYY-MM-DD'), + everyIntervalType: 'weeks', + }, + keepOpenForInterval: 'days', + allowLateSubmissions: { enabled: false, forNext: { term: '1', intervalType: 'days' } }, + closingMessageEnabled: false, + openSubmissionDateTime: moment().subtract(1, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: null, + }) + ).toEqual({ + allowLateSubmissions: false, + expire: true, + message: '', + }); + }); + + it('checkIsFormExpired for a period of 1 days should return an expired object in an invalid schedule', () => { + expect( + checkIsFormExpired({ + enabled: true, + scheduleType: 'period', + keepOpenForTerm: '1', + repeatSubmission: { + enabled: false, + everyTerm: null, + repeatUntil: null, + everyIntervalType: null, + }, + keepOpenForInterval: 'days', + allowLateSubmissions: { enabled: false, forNext: { term: '1', intervalType: 'days' } }, + closingMessageEnabled: false, + openSubmissionDateTime: moment().subtract(5, 'days').format('YYYY-MM-DD'), + closeSubmissionDateTime: null, + }) + ).toEqual({ + allowLateSubmissions: false, + expire: true, + message: '', + }); + }); }); diff --git a/tests/functional/cypress/e2e/form-draft-submission-management.cy.js b/tests/functional/cypress/e2e/form-draft-submission-management.cy.js index a9b608b25..027c6626b 100644 --- a/tests/functional/cypress/e2e/form-draft-submission-management.cy.js +++ b/tests/functional/cypress/e2e/form-draft-submission-management.cy.js @@ -138,6 +138,7 @@ it('Verify draft submission', () => { cy.get('.v-card-actions > .v-btn > .v-btn__content > span').click(); cy.waitForLoad(); // Edit draft submission + cy.wait(4000); cy.get('.mt-6 > :nth-child(1) > .v-btn > .v-btn__content > span').click(); cy.get('.mdi-pencil').click(); cy.waitForLoad(); diff --git a/tests/functional/cypress/e2e/form-submission-assign-revise-status.cy.js b/tests/functional/cypress/e2e/form-submission-assign-revise-status.cy.js new file mode 100644 index 000000000..99d97a6de --- /dev/null +++ b/tests/functional/cypress/e2e/form-submission-assign-revise-status.cy.js @@ -0,0 +1,192 @@ +import 'cypress-keycloak-commands'; +import 'cypress-drag-drop'; +import { formsettings } from '../support/login.js'; + +const depEnv = Cypress.env('depEnv'); + + +Cypress.Commands.add('waitForLoad', () => { + const loaderTimeout = 60000; + + cy.get('.nprogress-busy', { timeout: loaderTimeout }).should('not.exist'); +}); + + + +describe('Form Designer', () => { + + beforeEach(()=>{ + + + + cy.on('uncaught:exception', (err, runnable) => { + // Form.io throws an uncaught exception for missing projectid + // Cypress catches it as undefined: undefined so we can't get the text + console.log(err); + return false; + }); + }); + it('Visits the form settings page', () => { + + + cy.viewport(1000, 1100); + cy.waitForLoad(); + + formsettings(); + + + }); +// Publish a simple form +it('Verify draft submission', () => { + cy.viewport(1000, 1100); + cy.waitForLoad(); + + cy.get('button').contains('Basic Fields').click(); + cy.get('div.formio-builder-form').then($el => { + const coords = $el[0].getBoundingClientRect(); + cy.get('span.btn').contains('Text Field') + + .trigger('mousedown', { which: 1}, { force: true }) + .trigger('mousemove', coords.x, -50, { force: true }) + .trigger('mouseup', { force: true }); + cy.get('button').contains('Save').click(); + }); + cy.intercept('GET', `/${depEnv}/api/v1/forms/*`).as('getForm'); + // Form saving + let savedButton = cy.get('[data-cy=saveButton]'); + expect(savedButton).to.not.be.null; + savedButton.trigger('click'); + cy.waitForLoad(); + + + // Go to My forms + cy.wait('@getForm').then(()=>{ + let userFormsLinks = cy.get('[data-cy=userFormsLinks]'); + expect(userFormsLinks).to.not.be.null; + userFormsLinks.trigger('click'); + }); + // Filter the newly created form + cy.location('search').then(search => { + //let pathName = fullUrl.pathname + let arr = search.split('='); + let arrayValues = arr[1].split('&'); + cy.log(arrayValues[0]); + cy.visit(`/${depEnv}/form/manage?f=${arrayValues[0]}`); + cy.waitForLoad(); + + + //Publish the form + cy.get('.v-label > span').click(); + + cy.get('span').contains('Publish Version 1'); + + cy.contains('Continue').should('be.visible'); + cy.contains('Continue').trigger('click'); + //Share link verification + let shareFormButton = cy.get('[data-cy=shareFormButton]'); + expect(shareFormButton).to.not.be.null; + shareFormButton.trigger('click').then(()=>{ + //let shareFormLinkButton = cy.get('[data-cy=shareFormLinkButtonss]'); + let shareFormLinkButton=cy.get('.mx-2'); + expect(shareFormLinkButton).to.not.be.null; + shareFormLinkButton.trigger('click'); + cy.get('.mx-2 > .v-btn').click(); + }) + //Draft submission and verification + cy.visit(`/${depEnv}/form/submit?f=${arrayValues[0]}`); + cy.waitForLoad(); + cy.get('button').contains('Submit').should('be.visible'); + cy.waitForLoad(); + cy.waitForLoad(); + cy.contains('Text Field').click(); + cy.contains('Text Field').type('Alex'); + cy.get('.mt-6 > :nth-child(1) > .v-btn > .v-btn__content > span').click(); + cy.get('div > .bg-primary').click(); + cy.get('.v-data-table__tr > :nth-child(4)').contains('DRAFT'); + }); + +}); +it('Submission revise status Assignment', () => { + cy.viewport(1000, 1100); + cy.wait(4000); + cy.location('search').then(search => { + //let pathName = fullUrl.pathname + let arr = search.split('='); + let arrayValues = arr[1].split('&'); + cy.log(arrayValues[0]); + //Manage members for draft management + cy.get('.mdi-pencil').click(); + cy.get('.mdi-content-save').click(); + cy.get('.v-alert__content > div').contains('Draft Saved'); + cy.get(':nth-child(2) > :nth-child(4) > :nth-child(1) > .v-btn').click(); + cy.get('form > .v-input > .v-input__control > .v-field > .v-field__field > .v-field__input').click(); + cy.get('form > .v-input > .v-input__control > .v-field > .v-field__field > .v-field__input').type('NIM'); + cy.contains('John, Nimya 1 CITZ:EX (nimya.1.john@gov.bc.ca)').click(); + cy.get('.v-col-3').click(); + cy.get('tbody > :nth-child(2) > :nth-child(1)').contains('John, Nimya 1 CITZ:EX').should('be.visible'); + cy.get(':nth-child(1) > :nth-child(4) > .v-btn > .v-btn__content > .mdi-minus').should('not.be.enabled'); + cy.wait(4000); + + cy.get('.v-card-actions > .v-btn > .v-btn__content > span').click(); + cy.waitForLoad(); + // Edit draft submission + cy.get('.mt-6 > :nth-child(1) > .v-btn > .v-btn__content > span').click(); + cy.get('.mdi-pencil').click(); + cy.waitForLoad(); + + //Form submission + cy.contains('Text Field').click(); + cy.contains('Text Field').type('{selectall}{backspace}'); + cy.contains('Text Field').type('Nancy'); + cy.get('button').contains('Submit').click(); + cy.waitForLoad(); + cy.get('[data-test="continue-btn-continue"]').click({force: true}); + cy.waitForLoad(); + cy.location('pathname').should('eq', `/${depEnv}/form/success`); + cy.contains('h1', 'Your form has been submitted successfully'); + cy.get('.mt-6 > :nth-child(1) > .v-btn > .v-btn__content > span').click(); + + //Assign status submission + + cy.visit(`/${depEnv}/form/manage?f=${arrayValues[0]}`); + cy.get('.mdi-list-box-outline').click(); + cy.waitForLoad(); + cy.get(':nth-child(1) > :nth-child(6) > a > .v-btn > .v-btn__content > .mdi-eye').click(); + cy.get('.status-heading > .mdi-chevron-right').click(); + cy.get('[data-test="showStatusList"] > .v-input__control > .v-field > .v-field__field > .v-field__input').click(); + cy.contains('ASSIGNED').click(); + cy.get('[data-test="canAssignToMe"] > .v-btn__content > span').should('be.visible'); + cy.get('[data-test="showAssigneeList"] > .v-input__control > .v-field > .v-field__field > .v-field__input').click(); + cy.get('[data-test="showAssigneeList"] > .v-input__control > .v-field > .v-field__field > .v-field__input').type('ch'); + cy.get('div').contains('CHEFS Testing').click(); + cy.get('[data-test="updateStatusToNew"] > .v-btn__content > span').click(); + cy.wait(4000); + cy.get('[data-test="showStatusList"] > .v-input__control > .v-field > .v-field__append-inner > .mdi-menu-down').click(); + cy.contains('REVISING').click(); + //cy.get('.v-selection-control > .v-label').click(); + cy.get('.v-chip__content').contains('chefs.testing@gov.bc.ca').should('be.visible'); + cy.get('[data-test="showRecipientEmail"] > .v-input__control > .v-field > .v-field__append-inner > .mdi-menu-down').click(); + cy.contains('John, Nimya 1 CITZ:EX (nimya.1.john@gov.bc.ca)').should('be.visible'); + cy.get('label').contains('Notify all submitters').should('be.visible'); + cy.get('[data-test="canAttachCommentToEmail"] > .v-input__control > .v-selection-control > .v-label').click(); + cy.get('textarea[rows="1"]').type('some comments'); + cy.get('button').contains('REVISE').click(); + cy.get(':nth-child(1) > .v-checkbox > .v-input__control > .v-selection-control > .v-label').click(); + cy.wait(4000); + + //Verify Edit submission button is disabled + cy.get('button[title="Edit This Submission"]').should('be.disabled'); + + //Delete form after test run + cy.visit(`/${depEnv}/form/manage?f=${arrayValues[0]}`); + cy.waitForLoad(); + cy.get('.mdi-delete').click(); + cy.get('[data-test="continue-btn-continue"]').click(); + cy.get('#logoutButton > .v-btn__content > span').click(); + }); + + +}); + + +}); \ No newline at end of file