diff --git a/snprc_scheduler/jest.config.json b/snprc_scheduler/jest.config.json new file mode 100644 index 000000000..5c4d81cd9 --- /dev/null +++ b/snprc_scheduler/jest.config.json @@ -0,0 +1,9 @@ +{ + "snapshotSerializers":[ + "enzyme-to-json/serializer" + ], + "setupFiles": [ + "raf/polyfill", + "/src/client/tests/setupTests.js" + ] +} \ No newline at end of file diff --git a/snprc_scheduler/src/client/tests/fixtures/animalData.js b/snprc_scheduler/src/client/tests/fixtures/animalData.js new file mode 100644 index 000000000..1bfc06ba7 --- /dev/null +++ b/snprc_scheduler/src/client/tests/fixtures/animalData.js @@ -0,0 +1,104 @@ + + +const animalData = [ { + "StartDate" : { + "value" : "2017/04/13 00:00:00" + }, + "RevisionNum" : { + "value" : 0 + }, + "ProjectId" : { + "value" : 1 + }, + "Id" : { + "value" : "11111", + "url" : "/labkey/ehr/SNPRC/participantView.view?participantId=17275" + }, + "Gender" : { + "value" : "F", + "url" : "/labkey/query/SNPRC/detailsQueryRow.view?schemaName=ehr_lookups&query.queryName=gender_codes&code=F", + "displayValue" : "Female" + }, + "Iacuc" : { + "value" : "1425MM", + "url" : "/labkey/ehr/SNPRC/protocolDetails.view?protocol=1425MM" + }, + "ChargeId" : { + "value" : 435 + }, + "AssignmentStatus" : { + "value" : "A", + "url" : "/labkey/query/SNPRC/recordDetails.view?schemaName=ehr_lookups&query.queryName=assignment_status&keyField=value&key=A" + }, + "EndDate" : { + "value" : null + } +}, { + "StartDate" : { + "value" : "2017/04/13 00:00:00" + }, + "RevisionNum" : { + "value" : 0 + }, + "ProjectId" : { + "value" : 1 + }, + "Id" : { + "value" : "22222", + "url" : "/labkey/ehr/SNPRC/participantView.view?participantId=31386" + }, + "Gender" : { + "value" : "M", + "url" : "/labkey/query/SNPRC/detailsQueryRow.view?schemaName=ehr_lookups&query.queryName=gender_codes&code=M", + "displayValue" : "Female" + }, + "Iacuc" : { + "value" : "1425MM", + "url" : "/labkey/ehr/SNPRC/protocolDetails.view?protocol=1425MM" + }, + "ChargeId" : { + "value" : 435 + }, + "AssignmentStatus" : { + "value" : "A", + "url" : "/labkey/query/SNPRC/recordDetails.view?schemaName=ehr_lookups&query.queryName=assignment_status&keyField=value&key=A" + }, + "EndDate" : { + "value" : null + } +}, { + "StartDate" : { + "value" : "2017/04/13 00:00:00" + }, + "RevisionNum" : { + "value" : 0 + }, + "ProjectId" : { + "value" : 1 + }, + "Id" : { + "value" : "33333", + "url" : "/labkey/ehr/SNPRC/participantView.view?participantId=33003" + }, + "Gender" : { + "value" : "M", + "url" : "/labkey/query/SNPRC/detailsQueryRow.view?schemaName=ehr_lookups&query.queryName=gender_codes&code=M", + "displayValue" : "Male" + }, + "Iacuc" : { + "value" : "1425MM", + "url" : "/labkey/ehr/SNPRC/protocolDetails.view?protocol=1425MM" + }, + "ChargeId" : { + "value" : 435 + }, + "AssignmentStatus" : { + "value" : "A", + "url" : "/labkey/query/SNPRC/recordDetails.view?schemaName=ehr_lookups&query.queryName=assignment_status&keyField=value&key=A" + }, + "EndDate" : { + "value" : null + } +} ]; + +export {animalData}; \ No newline at end of file diff --git a/snprc_scheduler/src/client/tests/fixtures/projectData.js b/snprc_scheduler/src/client/tests/fixtures/projectData.js new file mode 100644 index 000000000..ee609f0bf --- /dev/null +++ b/snprc_scheduler/src/client/tests/fixtures/projectData.js @@ -0,0 +1,78 @@ +const projectData = [{ + "endDate": "2019-12-31", + "ProjectObjectId": "55130483-F7DD-4366-8FA3-55ED58115482", + "CostAccount": "1474-001", + "active": true, + "VsNumber": null, + "description": "Test data 1", + "projectId": 1, + "revisionNum": 0, + "Iacuc": "1042PC", + "referenceId": 48, + "Veterinarian1": "vet1", + "Veterinarian2": "vet2", + "hasEvent": false, + "ProjectType": "Research", + "startDate": "2008-06-04", + "FeeSchedule": null, + "objectId": "55130483-F7DD-4366-8FA3-55ED58115482", + "ProjectItems": [{ + "superPkgId": 2361, + "_row": 1, + "description": "Research sedation", + "pkgId": 222, + "projectItemId": 79664 + }, { + "superPkgId": 1590, + "_row": 2, + "description": "I need a research package", + "pkgId": 81, + "projectItemId": 79752 + }, { + "superPkgId": 1244, + "_row": 3, + "description": "Move to location", + "pkgId": 13, + "projectItemId": 79777 + }] +}, + { + "endDate": "2019-12-31", + "ProjectObjectId": "55130483-F7DD-4366-8FA3-55ED58115482", + "CostAccount": "1474-001", + "active": true, + "VsNumber": null, + "description": "Test data 2", + "projectId": 2, + "revisionNum": 0, + "Iacuc": "1042PC", + "referenceId": 48, + "Veterinarian1": "vet11", + "Veterinarian2": "vet22", + "hasEvent": false, + "ProjectType": "Research", + "startDate": "2008-06-04", + "FeeSchedule": null, + "objectId": "55130483-F7DD-4366-8FA3-55ED58115482", + "ProjectItems": [{ + "superPkgId": 2361, + "_row": 1, + "description": "Research sedation", + "pkgId": 222, + "projectItemId": 79664 + }, { + "superPkgId": 1590, + "_row": 2, + "description": "I need a research package", + "pkgId": 81, + "projectItemId": 79752 + }, { + "superPkgId": 1244, + "_row": 3, + "description": "Move to location", + "pkgId": 13, + "projectItemId": 79777 + }] + }]; + +export {projectData}; \ No newline at end of file diff --git a/snprc_scheduler/src/client/tests/fixtures/timelineData.js b/snprc_scheduler/src/client/tests/fixtures/timelineData.js new file mode 100644 index 000000000..13569b8d3 --- /dev/null +++ b/snprc_scheduler/src/client/tests/fixtures/timelineData.js @@ -0,0 +1,89 @@ + + +// test data +const timelineData = [{ + "Description" : "Timeline #1 revision 0", + "ProjectObjectId" : "55130483-F7DD-4366-8FA3-55ED58115482", + "SchedulerNotes" : "Of all the things i've lost in life, i miss your mind the most", + "EndDate" : "2018/12/30 00:00:00", + "Created" : "2018/09/20 00:00:00", + "StartDate" : "2018/02/01 00:00:00", + "LeadTechs" : "Henry Ford, Nicoli Tesla", + "TimelineId" : 1, + "RevisionNum" : 0, + "Notes" : "Don't take any wooden nickels.", + "TimelineItems" : [ { + "StudyDay" : 0, + "ScheduleDate" : null, + "ProjectItemId" : 79777 + }, { + "StudyDay" : 1, + "ScheduleDate" : null, + "ProjectItemId" : 80069 + }, { + "StudyDay" : 2, + "ScheduleDate" : null, + "ProjectItemId" : 80220 + }, { + "StudyDay" : 2, + "ScheduleDate" : null, + "ProjectItemId" : 81070 + } ], + "TimelineAnimalItems" : [ { + "AnimalId" : "11111", + "Gender" : "F", + "AssignmentStatus" : "A", + "EndDate" : null, + "Weight" : 18.26, + "Age" : "12.1 years" + }, { + "AnimalId" : "11112", + "Gender" : "F", + "AssignmentStatus" : "A", + "EndDate" : null, + "Weight" : 12.38, + "Age" : "12.1 years" + } ] +}, + { + "Description" : "Timeline #4 new Revision 3", + "ProjectObjectId" : "55130483-F7DD-4366-8FA3-55ED58115482", + "SchedulerNotes" : "Of all the things i've lost in life, i miss my mind the most", + "EndDate" : "2018/12/30 00:00:00", + "Created" : "2018/09/20 00:00:00", + "StartDate" : "2018/02/01 00:00:00", + "LeadTechs" : "Herman Munster", + "TimelineId" : 119, + "RevisionNum" : 0, + "Notes" : "Ha ha ha.", + "TimelineItems" : [ { + "StudyDay" : 110, + "ScheduleDate" : null, + "ProjectItemId" : 79664 + }, { + "StudyDay" : 100, + "ScheduleDate" : null, + "ProjectItemId" : 79752 + }, { + "StudyDay" : 0, + "ScheduleDate" : null, + "ProjectItemId" : 79777 + } ], + "TimelineAnimalItems" : [ { + "AnimalId" : "22221", + "Gender" : "F", + "AssignmentStatus" : "A", + "EndDate" : null, + "Weight" : 18.26, + "Age" : "12.1 years" + }, { + "AnimalId" : "22222", + "Gender" : "F", + "AssignmentStatus" : "A", + "EndDate" : null, + "Weight" : 12.38, + "Age" : "12.1 years" + }] + }]; + +export {timelineData}; \ No newline at end of file diff --git a/snprc_scheduler/src/client/tests/reducers/projectReducers.test.js b/snprc_scheduler/src/client/tests/reducers/projectReducers.test.js new file mode 100644 index 000000000..2b9af2090 --- /dev/null +++ b/snprc_scheduler/src/client/tests/reducers/projectReducers.test.js @@ -0,0 +1,211 @@ +import projectReducer from '../../reducers/projectReducer'; +import {projectData} from '../fixtures/projectData'; +import {animalData} from '../fixtures/animalData'; +import {timelineData} from "../fixtures/timelineData"; + +import { + ANIMAL_LIST_FILTERED, + ANIMAL_LIST_RECEIVED, + ANIMAL_LIST_REQUEST_FAILED, + PROJECT_LIST_FILTERED, + PROJECT_LIST_RECEIVED, + PROJECT_LIST_REQUEST_FAILED, + PROJECT_LIST_SORTED, + PROJECT_SELECTED, + TIMELINE_DUPLICATED, + TIMELINE_LIST_RECEIVED, + TIMELINE_SELECTED +} from "../../actions/dataActions"; + +let projectState = { + 0: projectData[0], + 1: projectData[1], + errors: [], + allProjects: [...projectData] , + projects: [...projectData] +}; + +const animalState = { + 0: animalData[0], + 1: animalData[1], + 2: animalData[2], + errors: [], + allAnimals: [...animalData], + animals: [...animalData] +}; + +const timelineState = { + 0: timelineData[0], + 1: timelineData[1], + errors: [], + allTimelines: [...timelineData], + timelines: [...timelineData] +}; +test('make sure default state gets returns an empty error array.', () => { + const state = projectReducer(undefined, {type: '@@init'}); + expect(state).toEqual({"errors": []}); +}); + +test('Should set project list', () => { + const action = { + type: PROJECT_LIST_RECEIVED, + payload: projectData + }; + const state = projectReducer(projectData, action); + expect(state).toEqual( + { + ...projectState, + "selectedProject": {} + }); +}); + +test('Should set animal list', () => { + const action = { + type: ANIMAL_LIST_RECEIVED, + payload: animalData + }; + const state = projectReducer(animalData, action); + expect(state).toEqual( + { + ...animalState + }); +}); + +test('Should not assign projects when an error occurs', () => { + const action = { + type: PROJECT_LIST_REQUEST_FAILED, + payload: {} + }; + const state = projectReducer(projectData, action); + expect(state).toEqual( + { + ...projectState, + errors: [{}], + allProjects: [], + projects: [] + }); +}); + +test('Should not assign animals when an error occurs', () => { + const action = { + type: ANIMAL_LIST_REQUEST_FAILED, + payload: {} + }; + const state = projectReducer(animalData, action); + expect(state).toEqual( + { + ...animalState, + errors: [{}], + allAnimals: [], + animals: [] + }); +}); + +test('Should select project - SEARCH_MODE == SEARCH_MODE_SND', () => { + const action = { + type: PROJECT_SELECTED, + payload: projectData[0].projectId + }; + const state = projectReducer(projectState, action); + expect(state).toEqual( + { + ...projectState, + selectedProject: projectData[0] + }); +}); + +test('Should select project using filter', () => { + const action = { + type: PROJECT_LIST_FILTERED, + payload: projectData[0].description + }; + const state = projectReducer(projectState, action); + expect(state).toEqual( + { + ...projectState, + projects : [projectData[0]] + }); +}); + +test('Should select animal using filter (test id & gender)', () => { + // search by id + let action = { + type: ANIMAL_LIST_FILTERED, + payload: animalData[0].Id.value + }; + let state = projectReducer(animalState, action); + expect(state).toEqual( + { + ...animalState, + animals: [animalData[0]] + }); + + // search by gender + action = { + type: ANIMAL_LIST_FILTERED, + payload: 'M' + }; + state = projectReducer(animalState, action); + expect(state).toEqual( + { + ...animalState, + animals: [animalData[1], animalData[2]] + }); + +}); + +test('Should set timeline list', () => { + const action = { + type: TIMELINE_LIST_RECEIVED, + payload: timelineData + }; + const state = projectReducer(timelineData, action); + expect(state).toEqual( + { + ...timelineState, + "selectedTimeline": {} + + }); +}); + +test('Should clone timeline returning a new timeline object with TimelineId = -1, revisionNum = -1, IsDraft = true', () => { + const action = { + type: TIMELINE_DUPLICATED, + payload: timelineData[0] + }; + const state = projectReducer(timelineState, action); + expect(state).toEqual( + { + ...timelineState, + timelines: [ ...timelineData, {...timelineData[0], TimelineId: -1, revisionNum: -1, IsDraft: true} ] + + }); +}); + + +test('Should select timeline', () => { + const action = { + type: TIMELINE_SELECTED, + payload: timelineData[0] + }; + const state = projectReducer(timelineState, action); + expect(state).toEqual( + { + ...timelineState, + selectedTimeline: timelineData[0] + }); +}); + +test('Should do a descending sort on project data', () => { + const action = { + type: PROJECT_LIST_SORTED, + payload: {direction: "DESC", field: "projectId"} + }; + const state = projectReducer(projectState, action); + expect(state).toEqual( + { + ...projectState, + projects: [projectData[1], projectData[0]] + }); +}); + diff --git a/snprc_scheduler/src/client/tests/setupTests.js b/snprc_scheduler/src/client/tests/setupTests.js new file mode 100644 index 000000000..b18c48b7e --- /dev/null +++ b/snprc_scheduler/src/client/tests/setupTests.js @@ -0,0 +1,9 @@ +//import enzyme, the enzyme adapter, and call a single method to wire up enzyme to work with the adapter + +import Enzyme from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +Enzyme.configure({ + adapter: new Adapter() +}); + diff --git a/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminPermission.java b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminPermission.java new file mode 100644 index 000000000..797d2d171 --- /dev/null +++ b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminPermission.java @@ -0,0 +1,17 @@ +package org.labkey.snprc_scheduler.security; + +import org.labkey.api.security.permissions.AbstractPermission; + +/** + * Created by thawkins on 1/25/2019. + */ +public class SNPRC_schedulerAdminPermission extends AbstractPermission +{ + public SNPRC_schedulerAdminPermission() + { + super("SNPRC_schedulerAdminPermission", "This permission grants user full authority over SNPRC scheduler data."); + } + +} + + diff --git a/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminRole.java b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminRole.java new file mode 100644 index 000000000..8a2f56de9 --- /dev/null +++ b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerAdminRole.java @@ -0,0 +1,22 @@ +package org.labkey.snprc_scheduler.security; + +import org.labkey.api.security.roles.AbstractRole; + + +/** + * Created by thawkins on 1/25/2019. + */ +public class SNPRC_schedulerAdminRole extends AbstractRole +{ + public SNPRC_schedulerAdminRole() + { + super("SNPRC Schedule Admins", "Schedule Admins have full authority over SNPRC Timelines and Schedules.", + SNPRC_schedulerEditorsPermission.class, + SNPRC_schedulerReadersPermission.class, + SNPRC_schedulerReviewersPermission.class, + SNPRC_schedulerAdminPermission.class + ); + + + } +} \ No newline at end of file diff --git a/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersPermission.java b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersPermission.java new file mode 100644 index 000000000..ab14fdeba --- /dev/null +++ b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersPermission.java @@ -0,0 +1,15 @@ +package org.labkey.snprc_scheduler.security; + +import org.labkey.api.security.permissions.AbstractPermission; + +/** + * Created by thawkins on 1/25/2019. + */ +public class SNPRC_schedulerReviewersPermission extends AbstractPermission +{ + public SNPRC_schedulerReviewersPermission() + { + super("SNPRC_schedulerReviewerPermission", "This permission grants a user the ability to approve/decline SNPRC scheduler Timeline changes."); + } + +} \ No newline at end of file diff --git a/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersRole.java b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersRole.java new file mode 100644 index 000000000..b9088e902 --- /dev/null +++ b/snprc_scheduler/src/org/labkey/snprc_scheduler/security/SNPRC_schedulerReviewersRole.java @@ -0,0 +1,20 @@ +package org.labkey.snprc_scheduler.security; + +import org.labkey.api.security.roles.AbstractRole; + + +/** + * Created by thawkins on 1/25/2019. + */ +public class SNPRC_schedulerReviewersRole extends AbstractRole +{ + public SNPRC_schedulerReviewersRole() + { + super("SNPRC Schedule Reviewers", "This role is required to approve/decline changes to SNPRC Timelines and Schedules.", + SNPRC_schedulerReadersPermission.class, + SNPRC_schedulerReviewersPermission.class + ); + + + } +} \ No newline at end of file