From c151f66b70271331b04e193ba1c034c01daa4a84 Mon Sep 17 00:00:00 2001 From: Angelina Uno-Antonison Date: Tue, 15 Oct 2024 13:04:38 -0500 Subject: [PATCH] Linting and unit testing pass; moving on to system testing --- frontend/src/requests.js | 3 - frontend/src/stores/analysisStore.js | 3 +- frontend/src/views/AnalysisView.vue | 83 +- frontend/test/__mocks__/requests.js | 14 +- frontend/test/views/AnalysisView.spec.js | 1169 +++++++++++----------- 5 files changed, 698 insertions(+), 574 deletions(-) diff --git a/frontend/src/requests.js b/frontend/src/requests.js index 447e800f..a21ea94e 100644 --- a/frontend/src/requests.js +++ b/frontend/src/requests.js @@ -162,7 +162,4 @@ export default { async putForm(url, data) { return await sendFormData('PUT', url, data); }, - async deleteForm(url, data) { - return await sendFormData('DELETE', url, data); - }, }; diff --git a/frontend/src/stores/analysisStore.js b/frontend/src/stores/analysisStore.js index c1ede027..69d51aea 100644 --- a/frontend/src/stores/analysisStore.js +++ b/frontend/src/stores/analysisStore.js @@ -113,9 +113,10 @@ export const analysisStore = reactive({ this.analysis_name, attachmentToDelete.attachment_id, ); - const attachmentIndex = this.attachments.findIndex((attachment) => { + const attachmentIndex = this.analysis.supporting_evidence_files.findIndex((attachment) => { return attachment.name == attachmentToDelete.name; }); + this.analysis.supporting_evidence_files.splice(attachmentIndex, 1); }, diff --git a/frontend/src/views/AnalysisView.vue b/frontend/src/views/AnalysisView.vue index 4afa163b..ea48d73d 100644 --- a/frontend/src/views/AnalysisView.vue +++ b/frontend/src/views/AnalysisView.vue @@ -132,7 +132,7 @@ const menuActions = computed(() => { operation: addSupportingEvidence, }); - if ( authStore.hasWritePermissions() ) { + if ( !authStore.hasWritePermissions() ) { return actionChoices; } @@ -359,6 +359,14 @@ async function updateSectionImage(fileId, sectionName, field) { } } +/** + * * COME BACK + * Adds an attachment to a specified section and field. + * + * @param {string} section - The section to which the attachment is added. + * @param {string} field - The field within the section for the attachment. + * @param {Object} evidence - The evidence to be attached. + */ async function addSectionAttachment(section, field, evidence) { const includeComments = true; const includeName = true; @@ -386,6 +394,14 @@ async function addSectionAttachment(section, field, evidence) { console.error('Updating the analysis did not work'); } } + +/** + * Removes the specified attachment from the section and field + * + * @param {string} section - The section name to which the attachment is added. + * @param {string} field - The field name within the section for the attachment. + * @param {Object} attachment - The attachment to add + */ async function removeSectionAttachment(section, field, attachment) { const confirmedDelete = await notificationDialog .title(`Remove attachment`) @@ -410,6 +426,15 @@ async function removeSectionAttachment(section, field, attachment) { } } +/** + * Handles changes to section attachments based on the provided operation. + * + * @param {Object} contentRow - The content row containing the operation, header, field, and value. + * @param {string} contentRow.operation - String that indicates the type of operation 'attach' or 'delete' + * @param {string} contentRow.header - The name of the section the attachment is in + * @param {string} contentRow.header - The name of the field being operated on within a section + * @param {Object} contentRow.value - the modified content + */ async function fieldSectionAttachmentChanged(contentRow) { const operations = { 'attach': addSectionAttachment, @@ -424,6 +449,9 @@ async function fieldSectionAttachmentChanged(contentRow) { operations[contentRow.operation](contentRow.header, contentRow.field, contentRow.value); } +/** + * Prompts input dialog to add new attachment to the Analysis. + */ async function addSupportingEvidence() { const includeComments = true; const includeName = true; @@ -445,6 +473,11 @@ async function addSupportingEvidence() { } } +/** + * Prompts input dialog to modify existing attachment in the Analysis. + * + * @param {Object} attachment Attachment to edit + */ async function editSupportingEvidence(attachment) { const updatedAttachment = await inputDialog .confirmText('Update') @@ -463,6 +496,11 @@ async function editSupportingEvidence(attachment) { } } +/** + * Prompts input dialog to remove existing attachment in the Analysis. + * + * @param {Object} attachmentToDelete to remove + */ async function removeSupportingEvidence(attachmentToDelete) { const confirmedDelete = await notificationDialog .title('Delete Supporting Information?') @@ -481,6 +519,11 @@ async function removeSupportingEvidence(attachmentToDelete) { } } +/** + * Downloads an attachment by its ID within the analysis. + * + * @param {Object} attachmentToDownload to download + */ function downloadSupportingEvidence(attachmentToDownload) { Analyses.downloadSupportingEvidence( attachmentToDownload.attachment_id, @@ -488,10 +531,16 @@ function downloadSupportingEvidence(attachmentToDownload) { ); } +/** + * Logs user out of Rosalution + */ function onLogout() { router.push({name: 'logout'}); } +/** + * Attaches Monday 3rd Party linkout for C-PAM Case management + */ async function addMondayLink() { const includeComments = false; const includeName = false; @@ -518,6 +567,9 @@ async function addMondayLink() { } } +/** + * Attaches Phenotips linkout for C-PAM Case management + */ async function addPhenotipsLink() { const includeComments = false; const includeName = false; @@ -544,6 +596,11 @@ async function addPhenotipsLink() { } } +/** + * Updates the Analysis with a new event. + * + * @param {string} eventType The type of event to push to Analysis + */ async function pushAnalysisEvent(eventType) { try { const updatedAnalysis = await Analyses.pushAnalysisEvent(analysisName, eventType); @@ -554,16 +611,32 @@ async function pushAnalysisEvent(eventType) { } } +/** + * Adds a new discussion post to the analysis. + * + * @param {string} newPostContent - The content of the new discussion post. + */ async function addDiscussionPost(newPostContent) { const discussions = await Analyses.postNewDiscussionThread(analysisName, newPostContent); analysisStore.analysis.discussions = discussions; } +/** + * Edits a discussion post in the analysis. + * + * @param {string} postId - The identifier of the post to edit + * @param {string} postContent - The content of the updated discussion post. + */ async function editDiscussionPost(postId, postContent) { const discussions = await Analyses.editDiscussionThreadById(analysisName, postId, postContent); analysisStore.analysis.discussions = discussions; } +/** + * Prompts to delete a discussion post in the analysis. + * + * @param {string} postId - The identifier of the post to delete + */ async function deleteDiscussionPost(postId) { const confirmedDelete = await notificationDialog .title(`Remove Discussion Post`) @@ -581,10 +654,18 @@ async function deleteDiscussionPost(postId) { } } +/** + * Toast to indicate text to the clipboard. + * + * @param {string} copiedText Text that was copied to the browser clipboard + */ function copyToClipboard(copiedText) { toast.success(`Copied ${copiedText} to clipboard!`); } +/** + * When view is mounted, queries the analysis' state. + */ onMounted(async () => { await analysisStore.getAnalysis(props.analysis_name); }); diff --git a/frontend/test/__mocks__/requests.js b/frontend/test/__mocks__/requests.js index 27c00674..6137cd60 100644 --- a/frontend/test/__mocks__/requests.js +++ b/frontend/test/__mocks__/requests.js @@ -1,17 +1,29 @@ export default { async get(url) { + }, + async getImage(url) { + + }, + async getDownload(url, data) { + }, async post(url, data) { + }, + async put(url, data) { + }, async postLogin(url, data) { + }, + async delete(url) { + }, async postForm(url, data) { }, - async delete(url) { + async putForm(url, data) { }, }; diff --git a/frontend/test/views/AnalysisView.spec.js b/frontend/test/views/AnalysisView.spec.js index dcc87fcf..68a58869 100644 --- a/frontend/test/views/AnalysisView.spec.js +++ b/frontend/test/views/AnalysisView.spec.js @@ -19,9 +19,9 @@ import toast from '@/toast.js'; import AnalysisView from '@/views/AnalysisView.vue'; import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'; +import {useRouter} from 'vue-router'; - -vi.mock( import('vue-router'), async (importOriginal) => { +vi.mock(import('vue-router'), async (importOriginal) => { const mod = await importOriginal(); return { ...mod, @@ -72,7 +72,7 @@ async function triggerAction(wrapper, actionText) { * @param {object} mockedData The mocked data to be used * @return {Promise} A promise that resolves with the mocked Vue wrapper */ -async function getMockedWrapper(latestStatus, mockedData) { +async function getWrapperWithLatestLatestStatus(latestStatus, mockedData) { const analysisData = fixtureData(); analysisData.latest_status = latestStatus; mockedData.returns(analysisData); @@ -83,34 +83,64 @@ async function getMockedWrapper(latestStatus, mockedData) { describe('AnalysisView', () => { let mockedData; - let mockAuthStore; let wrapper; let sandbox; + let mockVueRouterPush; + + let mockAnalysisEventPush; + + let attachSectionImageMock; + let updateSectionImageMock; + let removeSectionAttachmentMock; + let attachSectionAttachmentMock; + + let attachAttachmentMock; + let removeAttachmentMock; + + let postNewDiscussionThreadMock; + let deleteDiscussionThreadByIdMock; + let editDiscussionThreadByIdMock; + + let mockedAttachThirdPartyLink; + let updateAnalysisSectionsMock; + beforeEach(() => { sandbox = sinon.createSandbox(); mockedData = sandbox.stub(Analyses, 'getAnalysis'); mockedData.returns(fixtureData()); - mockAuthStore = sandbox.stub(); + authStore.hasWritePermissions = sandbox.fake.returns(true); + + mockVueRouterPush = sandbox.fake.returns(true); + useRouter.mockImplementation(() => { + return { + push: () => { + mockVueRouterPush(); + }, + }; + }); + + + mockAnalysisEventPush = sandbox.stub(Analyses, 'pushAnalysisEvent'); - // attachSectionImageMock = sandbox.stub(Analyses, 'attachSectionImage'); - // updateSectionImageMock = sandbox.stub(Analyses, 'updateSectionImage'); - // removeSectionAttachment = sandbox.stub(Analyses, 'removeSectionAttachment'); - // mockedAttachSectionSupportingEvidence = sandbox.stub(Analyses, 'attachSectionSupportingEvidence'); - // mockedAttachSupportingEvidence = sandbox.stub(Analyses, 'attachSupportingEvidence'); - // mockedRemoveSupportingEvidence = sandbox.stub(Analyses, 'removeSupportingEvidence'); - // mockedAttachThirdPartyLink = sandbox.stub(Analyses, 'attachThirdPartyLink'); + attachSectionImageMock = sandbox.stub(Analyses, 'attachSectionImage'); + updateSectionImageMock = sandbox.stub(Analyses, 'updateSectionImage'); + removeSectionAttachmentMock = sandbox.stub(Analyses, 'removeSectionAttachment'); + attachSectionAttachmentMock = sandbox.stub(Analyses, 'attachSectionSupportingEvidence'); - // markReadyMock = sandbox.stub(Analyses, 'pushAnalysisEvent'); + attachAttachmentMock = sandbox.stub(Analyses, 'attachSupportingEvidence'); + removeAttachmentMock = sandbox.stub(Analyses, 'removeSupportingEvidence'); - // updateAnalysisSectionsMock = sandbox.stub(Analyses, 'updateAnalysisSections'); + mockedAttachThirdPartyLink = sandbox.stub(Analyses, 'attachThirdPartyLink'); - // postNewDiscussionThreadMock = sandbox.stub(Analyses, 'postNewDiscussionThread'); - // deleteDiscussionThreadByIdMock = sandbox.stub(Analyses, 'deleteDiscussionThreadById'); - // editDiscussionThreadByIdMock = sandbox.stub(Analyses, 'editDiscussionThreadById'); + updateAnalysisSectionsMock = sandbox.stub(Analyses, 'updateAnalysisSections'); + + postNewDiscussionThreadMock = sandbox.stub(Analyses, 'postNewDiscussionThread'); + deleteDiscussionThreadByIdMock = sandbox.stub(Analyses, 'deleteDiscussionThreadById'); + editDiscussionThreadByIdMock = sandbox.stub(Analyses, 'editDiscussionThreadById'); // mockAuthWritePermissions = sandbox.stub(authStore, 'hasWritePermissions'); // mockAuthWritePermissions.returns(true); @@ -127,622 +157,622 @@ describe('AnalysisView', () => { expect(appContent.exists()).to.be.true; }); - // it('should display a toast when a copy text to clipboard button', async () => { - // const geneBox = wrapper.getComponent(GeneBox); - - // geneBox.vm.$emit('clipboard-copy', 'NM_001017980.3:c.164G>T'); - // await wrapper.vm.$nextTick(); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('success'); - // expect(toast.state.message).to.equal('Copied NM_001017980.3:c.164G>T to clipboard!'); - // }); - - // describe('the header', () => { - // it('contains a header element', () => { - // const appHeader = wrapper.find('app-header'); - // expect(appHeader.exists()).to.be.true; - // }); - - // it('should logout on the header logout event', async () => { - // const headerComponent = wrapper.getComponent( - // '[data-test=analysis-view-header]', - // ); - // headerComponent.vm.$emit('logout'); - // await headerComponent.vm.$nextTick(); - - // expect(wrapper.vm.$router.push.called).to.be.true; - // }); - - // it('provides the expected headings of sections to be used as anchors', () => { - // const headerComponent = wrapper.get('[data-test="analysis-view-header"]'); - // expect(headerComponent.attributes('sectionanchors')).to.equal( - // 'Brief,Medical Summary,Mus musculus (Mouse) Model System,Pedigree,' + - // 'Case Information,Discussion,Supporting Evidence', - // ); - // }); - - // it('should mark an analysis as ready', async () => { - // const wrapper = await getMockedWrapper('Preparation', mockedData); - - // await triggerAction(wrapper, 'Mark Ready'); - - // expect(markReadyMock.called).to.be.true; - // }); - - // it('should display success toast with correct message when marking analysis as ready', async () => { - // const wrapper = await getMockedWrapper('Preparation', mockedData); - - // await triggerAction(wrapper, 'Mark Ready'); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('success'); - // expect(toast.state.message).to.equal('Analysis event \'ready\' successful.'); - // }); - - // it('should display error toast with correct message when marking analysis as ready fails', async () => { - // const wrapper = await getMockedWrapper('Preparation', mockedData); - // const error = new Error('Error updating the event \'ready\'.'); - // markReadyMock.throws(error); - - // try { - // await triggerAction(wrapper, 'Mark Ready'); - // } catch (error) { - // console.error(error); - // } - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('error'); - // expect(toast.state.message).to.equal('Error updating the event \'ready\'.'); - // }); - - // it('should display info toast with correct message when marking analysis as active', async () => { - // const wrapper = await getMockedWrapper('Ready', mockedData); - - // await triggerAction(wrapper, 'Mark Active'); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('success'); - // expect(toast.state.message).to.equal('Analysis event \'opened\' successful.'); - // }); - - // it('should display info toast with correct message when entering edit mode', async () => { - // const wrapper = getMountedComponent(); - - // await triggerAction(wrapper, 'Edit'); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('success'); - // expect(toast.state.message).to.equal('Edit mode has been enabled.'); - // }); - // it('should display info toast with correct message when exiting edit mode', async () => { - // const wrapper = getMountedComponent(); - // await wrapper.setData({edit: true}); - // await triggerAction(wrapper, 'Edit'); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('info'); - // expect(toast.state.message).to.equal('Edit mode has been disabled and changes have not been saved.'); - // }); - // }); - - // describe('third party links', () => { - // it('should render the input dialog when the attach monday link menu action is clicked', async () => { - // const headerComponent = wrapper.getComponent( - // '[data-test=analysis-view-header]', - // ); - // const actionsProps = headerComponent.props('actions'); - - // for (const action of actionsProps) { - // if (action.text === 'Attach Monday.com') { - // action.operation(); - // } - // } - - // const mondayDialog = wrapper.findComponent(InputDialog); - // expect(mondayDialog.exists()).to.be.true; - // }); - - // it('should add a link to monday_com', async () => { - // const newAttachmentData = { - // data: 'https://monday.com', - // type: 'link', - // }; - // const analysisWithMondayLink = fixtureData(); - // analysisWithMondayLink['monday_com'] = 'https://monday.com'; - // mockedAttachThirdPartyLink.returns(analysisWithMondayLink); - - // wrapper = getMountedComponent(); - // const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); - // const actionsProps = headerComponent.props('actions'); - - // for (const action of actionsProps) { - // if (action.text === 'Attach Monday.com') { - // action.operation(); - // } - // } + it('should display a toast when a copy text to clipboard button', async () => { + const geneBox = wrapper.getComponent(GeneBox); - // inputDialog.confirmation(newAttachmentData); + geneBox.vm.$emit('clipboard-copy', 'NM_001017980.3:c.164G>T'); + await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('success'); + expect(toast.state.message).to.equal('Copied NM_001017980.3:c.164G>T to clipboard!'); + }); - // expect(mockedAttachThirdPartyLink.called).to.be.true; - // }); + describe('the header', () => { + it('contains a header element', () => { + const appHeader = wrapper.find('app-header'); + expect(appHeader.exists()).to.be.true; + }); + + it('should logout on the header logout event', async () => { + const headerComponent = wrapper.getComponent( + '[data-test=analysis-view-header]', + ); + headerComponent.vm.$emit('logout'); + await headerComponent.vm.$nextTick(); + + expect(mockVueRouterPush.called).to.be.true; + }); + + it('provides the expected headings of sections to be used as anchors', () => { + const headerComponent = wrapper.get('[data-test="analysis-view-header"]'); + expect(headerComponent.attributes('sectionanchors')).to.equal( + 'Brief,Medical Summary,Mus musculus (Mouse) Model System,Pedigree,' + + 'Case Information,Discussion,Supporting Evidence', + ); + }); + + it('should mark an analysis as ready', async () => { + const modifiedWrapper = await getWrapperWithLatestLatestStatus('Preparation', mockedData); + + await triggerAction(modifiedWrapper, 'Mark Ready'); + + expect(mockAnalysisEventPush.called).to.be.true; + }); + + it('should display success toast with correct message when marking analysis as ready', async () => { + const modifiedWrapper = await getWrapperWithLatestLatestStatus('Preparation', mockedData); + + await triggerAction(modifiedWrapper, 'Mark Ready'); + + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('success'); + expect(toast.state.message).to.equal('Analysis event \'ready\' successful.'); + }); + + it('should display error toast with correct message when marking analysis as ready fails', async () => { + const modifiedWrapper = await getWrapperWithLatestLatestStatus('Preparation', mockedData); + const error = new Error('Error updating the event \'ready\'.'); + mockAnalysisEventPush.throws(error); + + try { + await triggerAction(modifiedWrapper, 'Mark Ready'); + } catch (error) { + console.error(error); + } + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('error'); + expect(toast.state.message).to.equal('Error updating the event \'ready\'.'); + }); + + it('should display info toast with correct message when marking analysis as active', async () => { + const modifiedWrapper = await getWrapperWithLatestLatestStatus('Ready', mockedData); + + await triggerAction(modifiedWrapper, 'Mark Active'); + + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('success'); + expect(toast.state.message).to.equal('Analysis event \'opened\' successful.'); + }); + + it('should display info toast with correct message when entering edit mode', async () => { + const initialViewWrapper = getMountedComponent(); + + await triggerAction(initialViewWrapper, 'Edit'); + + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('success'); + expect(toast.state.message).to.equal('Edit mode has been enabled.'); + }); + it('should display info toast with correct message when exiting edit mode', async () => { + const initialViewWrapper = getMountedComponent(); + + // Places the Initial wrapper into the 'Edit' mode + await triggerAction(initialViewWrapper, 'Edit'); + + // Disabling the 'edit mode and notifying edits haven't been made + await triggerAction(initialViewWrapper, 'Edit'); + + await wrapper.vm.$nextTick(); + + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('info'); + expect(toast.state.message).to.equal('Edit mode has been disabled and changes have not been saved.'); + }); + }); + + describe('third party links', () => { + it('should render the input dialog when the attach monday link menu action is clicked', async () => { + const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); + const actionsProps = headerComponent.props('actions'); + + for (const action of actionsProps) { + if (action.text === 'Attach Monday.com') { + action.operation(); + } + } + + const mondayDialog = wrapper.findComponent(InputDialog); + expect(mondayDialog.exists()).to.be.true; + }); + + it('should add a link to monday_com', async () => { + const newAttachmentData = { + data: 'https://monday.com', + type: 'link', + }; + const analysisWithMondayLink = fixtureData({['monday_com']: 'https://monday.com'}); + mockedAttachThirdPartyLink.returns(analysisWithMondayLink); + + wrapper = getMountedComponent(); + const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); + const actionsProps = headerComponent.props('actions'); + + for (const action of actionsProps) { + if (action.text === 'Attach Monday.com') { + action.operation(); + } + } + + inputDialog.confirmation(newAttachmentData); + + await wrapper.vm.$nextTick(); + + expect(mockedAttachThirdPartyLink.called).to.be.true; + }); + + it('should render the input dialog when the attach phenotips link menu action is clicked', async () => { + const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); + const actionsProps = headerComponent.props('actions'); + + for (const action of actionsProps) { + if (action.text === 'Connect PhenoTips') { + action.operation(); + } + } + + const phenotipsDialog = wrapper.findComponent(InputDialog); + expect(phenotipsDialog.exists()).to.be.true; + }); + + it('should add a link to phenotips_com', async () => { + const analysisWithPhenotipsLink = fixtureData({['phenotips_com']: 'https://phenotips.com'}); + mockedAttachThirdPartyLink.returns(analysisWithPhenotipsLink); + + wrapper = getMountedComponent(); + const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); + const actionsProps = headerComponent.props('actions'); + + for (const action of actionsProps) { + if (action.text === 'Connect PhenoTips') { + action.operation(); + } + } + + const newAttachmentData = { + data: 'https://phenotips.com', + type: 'link', + }; + inputDialog.confirmation(newAttachmentData); + + await wrapper.vm.$nextTick(); + + expect(mockedAttachThirdPartyLink.called).to.be.true; + }); + }); - // it('should render the input dialog when the attach phenotips link menu action is clicked', async () => { - // const headerComponent = wrapper.getComponent( - // '[data-test=analysis-view-header]', - // ); - // const actionsProps = headerComponent.props('actions'); + describe('discussions', () => { + it('Should display a discussion section with three posts', () => { + const discussionSectionComponent = wrapper.getComponent(DiscussionSection); - // for (const action of actionsProps) { - // if (action.text === 'Connect PhenoTips') { - // action.operation(); - // } - // } + expect(typeof discussionSectionComponent).toBe('object'); - // const phenotipsDialog = wrapper.findComponent(InputDialog); - // expect(phenotipsDialog.exists()).to.be.true; - // }); + expect(discussionSectionComponent.props('discussions').length).to.equal(3); + }); - // it('should add a link to phenotips_com', async () => { - // const newAttachmentData = { - // data: 'https://phenotips.com', - // type: 'link', - // }; - // const analysisWithPhenotipsLink = fixtureData(); - // analysisWithPhenotipsLink['phenotips_com'] = 'https://phenotips.com'; - // mockedAttachThirdPartyLink.returns(analysisWithPhenotipsLink); + it('Should recieve an new post publish emit and add a new discussion post', async () => { + const discussionSectionComponent = wrapper.getComponent(DiscussionSection); + const newPostContent = 'Hello world'; - // wrapper = getMountedComponent(); - // const headerComponent = wrapper.getComponent('[data-test=analysis-view-header]'); - // const actionsProps = headerComponent.props('actions'); + const discussionFixtureData = fixtureData()['discussions']; - // for (const action of actionsProps) { - // if (action.text === 'Connect PhenoTips') { - // action.operation(); - // } - // } + const newDiscussionPost = { + post_id: 'e60239a34-h941-44aa-912e-912a993255fe', + author_id: 'exqkhvidr7uh2ndslsdymbzfbmqjlunk', + author_fullname: 'Variant Review Report Preparer Person', + publish_timestamp: '2023-11-01T21:13:22.687000', + content: newPostContent, + attachments: [], + thread: [], + }; - // inputDialog.confirmation(newAttachmentData); + discussionFixtureData.push(newDiscussionPost); - // await wrapper.vm.$nextTick(); + postNewDiscussionThreadMock.returns(discussionFixtureData); - // expect(mockedAttachThirdPartyLink.called).to.be.true; - // }); - // }); + expect(discussionSectionComponent.props('discussions').length).to.equal(3); - // describe('discussions', () => { - // it('Should display a discussion section with three posts', () => { - // const discussionSectionComponent = wrapper.getComponent(DiscussionSection); + discussionSectionComponent.vm.$emit('discussion:new-post', newPostContent); - // expect(typeof discussionSectionComponent).toBe('object'); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // expect(discussionSectionComponent.props('discussions').length).to.equal(3); - // }); + expect(discussionSectionComponent.props('discussions').length).to.equal(4); + }); - // it('Should recieve an new post publish emit and add a new discussion post', async () => { - // const discussionSectionComponent = wrapper.getComponent(DiscussionSection); - // const newPostContent = 'Hello world'; + it('Should receive a discussion:delete-post emit and remove the discussion post', async () => { + const discussionSectionComponent = wrapper.getComponent(DiscussionSection); - // const discussionFixtureData = fixtureData()['discussions']; + const deletePostId = '9027ec8d-6298-4afb-add5-6ef710eb5e98'; - // const newDiscussionPost = { - // post_id: 'e60239a34-h941-44aa-912e-912a993255fe', - // author_id: 'exqkhvidr7uh2ndslsdymbzfbmqjlunk', - // author_fullname: 'Variant Review Report Preparer Person', - // publish_timestamp: '2023-11-01T21:13:22.687000', - // content: newPostContent, - // attachments: [], - // thread: [], - // }; + const discussionFixtureData = fixtureData()['discussions'].filter((post) => post.post_id != deletePostId); - // discussionFixtureData.push(newDiscussionPost); + deleteDiscussionThreadByIdMock.returns(discussionFixtureData); - // postNewDiscussionThreadMock.returns(discussionFixtureData); + discussionSectionComponent.vm.$emit('discussion:delete-post', deletePostId); - // expect(discussionSectionComponent.props('discussions').length).to.equal(3); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // discussionSectionComponent.vm.$emit('discussion:new-post', newPostContent); + notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // expect(discussionSectionComponent.props('discussions').length).to.equal(4); - // }); + expect(discussionSectionComponent.props('discussions').length).toBe(2); + }); - // it('Should receive a discussion:delete-post emit and remove the discussion post', async () => { - // const discussionSectionComponent = wrapper.getComponent(DiscussionSection); + it('Should receive a discussion:edit-post emit and work to edit the selected post with new content', async () => { + const discussionSectionComponent = wrapper.getComponent(DiscussionSection); - // const deletePostId = '9027ec8d-6298-4afb-add5-6ef710eb5e98'; + const editPostId = '9027ec8d-6298-4afb-add5-6ef710eb5e98'; + const editPostContent = 'Well my mind is changed, Gundam Wing is the best anime!'; - // const discussionFixtureData = fixtureData()['discussions'].filter((post) => post.post_id != deletePostId); + const discussionFixtureData = fixtureData()['discussions']; + const postIndex = discussionFixtureData.findIndex((post) => post.post_id == editPostId); - // deleteDiscussionThreadByIdMock.returns(discussionFixtureData); + discussionFixtureData[postIndex].content = editPostContent; - // discussionSectionComponent.vm.$emit('discussion:delete-post', deletePostId); + editDiscussionThreadByIdMock.returns(discussionFixtureData); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + discussionSectionComponent.vm.$emit('discussion:edit-post', editPostId, editPostContent); - // notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + const updatedPosts = discussionSectionComponent.props('discussions'); + const updatedPostIndex = updatedPosts.findIndex((post) => post.post_id == editPostId); - // expect(discussionSectionComponent.props('discussions').length).toBe(2); - // }); + expect(updatedPosts[updatedPostIndex].content).toBe(editPostContent); + }); + }); - // it('Should receive a discussion:edit-post emit and work to edit the selected post with new content', async () => { - // const discussionSectionComponent = wrapper.getComponent(DiscussionSection); + describe('supporting evidence', () => { + describe('when adding supporting evidence as an attachment', () => { + it('displays the attachment modal when the supplemental form list requests dialog', async () => { + const supplementalComponent = + wrapper.getComponent(SupplementalFormList); + supplementalComponent.vm.$emit('open-modal'); - // const editPostId = '9027ec8d-6298-4afb-add5-6ef710eb5e98'; - // const editPostContent = 'Well my mind is changed, Gundam Wing is the best anime!'; + await wrapper.vm.$nextTick(); - // const discussionFixtureData = fixtureData()['discussions']; - // const postIndex = discussionFixtureData.findIndex((post) => post.post_id == editPostId); + const attachmentDialog = wrapper.findComponent(InputDialog); + expect(attachmentDialog.exists()).to.be.true; + }); - // discussionFixtureData[postIndex].content = editPostContent; + it('attachment dialog adds a new attachment to the analysis', async () => { + const newAttachmentData = { + name: 'fake-attachment-evidence-name', + data: 'http://sites.uab.edu/cgds', + attachment_id: 'new-failure-id', + type: 'link', + comments: '', + }; + const analysisWithNewEvidence = fixtureData(); + analysisWithNewEvidence.supporting_evidence_files.push( + newAttachmentData, + ); + attachAttachmentMock.returns(analysisWithNewEvidence.supporting_evidence_files); - // editDiscussionThreadByIdMock.returns(discussionFixtureData); + const supplementalComponent = + wrapper.getComponent(SupplementalFormList); + expect(supplementalComponent.props('attachments').length).to.equal(1); - // discussionSectionComponent.vm.$emit('discussion:edit-post', editPostId, editPostContent); + supplementalComponent.vm.$emit('open-modal'); + await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + inputDialog.confirmation(newAttachmentData); - // const updatedPosts = discussionSectionComponent.props('discussions'); - // const updatedPostIndex = updatedPosts.findIndex((post) => post.post_id == editPostId); + // Needs to cycle through updating the prop in the view and then another + // tick for vuejs to reactively update the supplemental component + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + expect(supplementalComponent.props('attachments').length).to.equal(2); + }); + }); - // expect(updatedPosts[updatedPostIndex].content).toBe(editPostContent); - // }); - // }); + describe('when removing supporting evidence', async () => { + it('prompts a confirmation when an attachment is to be deleted', async () => { + const supplementalComponent = + wrapper.getComponent(SupplementalFormList); + expect(supplementalComponent.props('attachments').length).to.equal(1); - // describe('supporting evidence', () => { - // describe('when adding supporting evidence as an attachment', () => { - // it('displays the attachment modal when the supplemental form list requests dialog', async () => { - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - // supplementalComponent.vm.$emit('open-modal'); + const fakeAttachment = {name: 'fake.txt'}; + supplementalComponent.vm.$emit('delete', fakeAttachment); - // await wrapper.vm.$nextTick(); + const confirmationDialog = wrapper.findComponent(NotificationDialog); + expect(confirmationDialog.exists()).to.be.true; + }); - // const attachmentDialog = wrapper.findComponent(InputDialog); - // expect(attachmentDialog.exists()).to.be.true; - // }); + it('can cancel deleting the attachment via the confirmation and not delete the attachment', async () => { + const fakeAttachment = {name: 'fake.txt'}; + const supplementalComponent = wrapper.getComponent(SupplementalFormList); + expect(supplementalComponent.props('attachments').length).to.equal(1); - // it('attachment dialog adds a new attachment to the analysis', async () => { - // const newAttachmentData = { - // name: 'fake-attachment-evidence-name', - // data: 'http://sites.uab.edu/cgds', - // attachment_id: 'new-failure-id', - // type: 'link', - // comments: '', - // }; - // const analysisWithNewEvidence = fixtureData(); - // analysisWithNewEvidence.supporting_evidence_files.push( - // newAttachmentData, - // ); - // mockedAttachSupportingEvidence.returns(analysisWithNewEvidence.supporting_evidence_files); + supplementalComponent.vm.$emit('delete', fakeAttachment); + notificationDialog.cancel(); - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - // expect(supplementalComponent.props('attachments').length).to.equal(1); + expect(supplementalComponent.props('attachments').length).to.equal(1); + }); - // supplementalComponent.vm.$emit('open-modal'); - // await wrapper.vm.$nextTick(); + it('should confirmation to remove the supporting evidence from the analysis', async () => { + const fakeAttachment = {name: 'fake.txt'}; + const supplementalComponent = wrapper.getComponent(SupplementalFormList); - // inputDialog.confirmation(newAttachmentData); + expect(supplementalComponent.props('attachments').length).to.equal(1); - // // Needs to cycle through updating the prop in the view and then another - // // tick for vuejs to reactively update the supplemental component - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // expect(supplementalComponent.props('attachments').length).to.equal(2); - // }); - // }); + supplementalComponent.vm.$emit('delete', fakeAttachment); - // describe('when removing supporting evidence', async () => { - // it('prompts a confirmation when an attachment is to be deleted', async () => { - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - // expect(supplementalComponent.props('attachments').length).to.equal(1); + notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // const fakeAttachment = {name: 'fake.txt'}; - // supplementalComponent.vm.$emit('delete', fakeAttachment); + expect(supplementalComponent.props('attachments').length).to.equal(0); + expect(removeAttachmentMock.called).to.be.true; + }); - // const confirmationDialog = wrapper.findComponent(NotificationDialog); - // expect(confirmationDialog.exists()).to.be.true; - // }); + it('should alert user when fails to delete', async () => { + removeAttachmentMock.throws('Failed to delete'); - // it('can cancel deleting the attachment via the confirmation and not delete the attachment', async () => { - // const fakeAttachment = {name: 'fake.txt'}; - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - // expect(supplementalComponent.props('attachments').length).to.equal(1); - - // supplementalComponent.vm.$emit('delete', fakeAttachment); - // notificationDialog.cancel(); - - // expect(supplementalComponent.props('attachments').length).to.equal(1); - // }); - - // it('should confirmation to remove the supporting evidence from the analysis', async () => { - // const fakeAttachment = {name: 'fake.txt'}; - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - - // expect(supplementalComponent.props('attachments').length).to.equal(1); - - // supplementalComponent.vm.$emit('delete', fakeAttachment); - - // notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // expect(supplementalComponent.props('attachments').length).to.equal(0); - // expect(mockedRemoveSupportingEvidence.called).to.be.true; - // }); - - // it('should alert user when fails to delete', async () => { - // mockedRemoveSupportingEvidence.throws('Failed to delete'); - - // const fakeAttachment = {name: 'fake.txt'}; - // const supplementalComponent = - // wrapper.getComponent(SupplementalFormList); - - // expect(supplementalComponent.props('attachments').length).to.equal(1); - - // supplementalComponent.vm.$emit('delete', fakeAttachment); - - // notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - - // expect(supplementalComponent.props('attachments').length).to.equal(1); - // expect(mockedRemoveSupportingEvidence.called).to.be.true; - // }); - // }); - // }); - - // describe('sections', () => { - // describe('when an image section does not have an image', () => { - // it('accepts an image render as content', async () => { - // const updatedSectionField= { - // type: 'images-dataset', - // field: 'Pedigree', - // value: [{file_id: '64a2f06a4d4d29b8dc93c2d8'}], - // }; - // attachSectionImageMock.returns(updatedSectionField); - - // const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); - // pedigreeSection.vm.$emit('attach-image', 'Pedigree'); - // await wrapper.vm.$nextTick(); - - // const fakeImage = {data: 'fakeImage.png'}; - // inputDialog.confirmation(fakeImage); + const fakeAttachment = {name: 'fake.txt'}; + const supplementalComponent = + wrapper.getComponent(SupplementalFormList); - // // Needs to cycle through updating the props in the view and then additional - // // ticks for vuejs to reactively update the supplemental component - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); + expect(supplementalComponent.props('attachments').length).to.equal(1); - // expect(reRenderedPedigreeSection.props('content').length).to.equal(1); - // }); - // }); + supplementalComponent.vm.$emit('delete', fakeAttachment); - // describe('when an image section has an image in it', () => { - // beforeEach(() => { - // const imageFieldValue = {file_id: '635a89aea7b2f21802b74539'}; - // const analysisWithNewEvidence = fixtureData(); - // analysisWithNewEvidence.sections = addSectionFieldValue('Pedigree', 'Pedigree', imageFieldValue); - // mockedData.returns(analysisWithNewEvidence); - // wrapper = getMountedComponent(); - // }); + notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); - // it.skip('updates section image content with input dialog', async () => { - // updateSectionImageMock.returns({ - // section: 'Pedigree', - // field: 'Pedigree', - // image_id: 'different-image-635a89aea7b2f21802b74539', - // }); + expect(supplementalComponent.props('attachments').length).to.equal(1); + expect(removeAttachmentMock.called).to.be.true; + }); + }); + }); - // const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); - // pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', 'Pedigree', 'Pedigree'); - // await wrapper.vm.$nextTick(); + describe('sections', () => { + describe('when an image section does not have an image', () => { + it('accepts an image render as content', async () => { + const updatedSectionField= { + type: 'images-dataset', + field: 'Pedigree', + value: [{file_id: '64a2f06a4d4d29b8dc93c2d8'}], + }; + attachSectionImageMock.returns(updatedSectionField); + + const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); + pedigreeSection.vm.$emit('attach-image', 'Pedigree'); + await wrapper.vm.$nextTick(); + + const fakeImage = {data: 'fakeImage.png'}; + inputDialog.confirmation(fakeImage); + + // Needs to cycle through updating the props in the view and then additional + // ticks for vuejs to reactively update the supplemental component + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); + + expect(reRenderedPedigreeSection.props('content').length).to.equal(1); + }); + }); + + describe('when an image section has an image in it', () => { + beforeEach(() => { + const imageFieldValue = {file_id: '635a89aea7b2f21802b74539'}; + const analysisWithNewEvidence = fixtureData(); + analysisWithNewEvidence.sections = addSectionFieldValue('Pedigree', 'Pedigree', imageFieldValue); + mockedData.returns(analysisWithNewEvidence); + wrapper = getMountedComponent(); + }); + + it.skip('updates section image content with input dialog', async () => { + updateSectionImageMock.returns({ + section: 'Pedigree', + field: 'Pedigree', + image_id: 'different-image-635a89aea7b2f21802b74539', + }); - // const fakeImageForUpdate = {data: 'fakeImage.png'}; - // inputDialog.confirmation(fakeImageForUpdate); + const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); + pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', 'Pedigree', 'Pedigree'); + await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); + const fakeImageForUpdate = {data: 'fakeImage.png'}; + inputDialog.confirmation(fakeImageForUpdate); - // const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); - // expect(updateSectionImageMock.called).to.be.true; - // expect(reRenderedPedigreeSection.props().content[0].value[0].file_id) - // .to.equal('different-image-635a89aea7b2f21802b74539'); - // }); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); - // it('notifies user when updating section image content fails', async () => { - // updateSectionImageMock.throws('failure happened'); + const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); + expect(updateSectionImageMock.called).to.be.true; + expect(reRenderedPedigreeSection.props().content[0].value[0].file_id) + .to.equal('different-image-635a89aea7b2f21802b74539'); + }); - // const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); - // pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', 'Pedigree', 'Pedigree'); - // await wrapper.vm.$nextTick(); + it('notifies user when updating section image content fails', async () => { + updateSectionImageMock.throws('failure happened'); - // const fakeImageForUpdate = {data: 'fakeImage.png'}; - // inputDialog.confirmation(fakeImageForUpdate); + const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); + pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', 'Pedigree', 'Pedigree'); + await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); - - // const failureDialog = wrapper.findComponent(NotificationDialog); - // expect(failureDialog.exists()).to.be.true; - - // expect(updateSectionImageMock.called).to.be.true; - // expect(reRenderedPedigreeSection.props('content').length).to.equal(1); - // }); - - // it('allows user to remove section image with input dialog confirmation', async () => { - // const sectionName = 'Pedigree'; - // const fieldName = 'Pedigree'; - // removeSectionAttachment.resolves(removeFieldValue('Pedigree', 'Pedigree')); - - // const pedigreeSection = wrapper.findComponent(`[id=${sectionName}]`); - // pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', sectionName, fieldName); - // await wrapper.vm.$nextTick(); - - // inputDialog.delete(); - - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const confirmationDialog = wrapper.findComponent(NotificationDialog); - // expect(confirmationDialog.exists()).to.be.true; - - // notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - - // // Neccesary to process several ticks to re-render the section - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); - - // expect(removeSectionAttachment.called).to.be.true; - // expect(reRenderedPedigreeSection.props('content')[0].value.length).to.equal(0); - // }); - - // it('notifies the user when the image content fails to be removed', async () => { - // removeSectionAttachment.throws('sad-it-did not remove'); - - // const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); - // pedigreeSection.vm.$emit('update-image', 'Pedigree'); - // await wrapper.vm.$nextTick(); - - // inputDialog.delete(); - - // await wrapper.vm.$nextTick(); - - // notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - - // // Neccesary to process several ticks to re-render the section - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const failureNotificationDialog = wrapper.findComponent(NotificationDialog); - // expect(failureNotificationDialog.exists()).to.be.true; - // }); - // }); - - // describe('when a section has a field that allows attachments', () => { - // it('may attach a link to that field', async () => { - // const sectionName = 'Mus musculus (Mouse) Model System'; - // const sectionId = 'Mus_musculus (Mouse) Model System'; - // const fieldName = 'Veterinary Pathology Imaging'; - // const newAttachmentData = { - // name: 'fake-attachment-evidence-name', - // data: 'http://sites.uab.edu/cgds', - // attachment_id: 'new-failure-id', - // type: 'link', - // comments: '', - // }; - - // mockedAttachSectionSupportingEvidence.returns(addFieldValue(sectionName, fieldName, newAttachmentData)); - - // const mouseSection = wrapper.getComponent(`[id=${sectionId}]`); - // const mouseFieldToUpdate = mouseSection.props('content').find((row) => { - // return row.field == fieldName; - // }); - - // expect(mouseFieldToUpdate.value.length).to.equal(0); - - // mouseSection.vm.$emit('update:content-row', { - // type: 'supporting-evidence', - // operation: 'attach', - // header: sectionName, - // field: fieldName, - // value: {}, - // }); - // await wrapper.vm.$nextTick(); - - // inputDialog.confirmation(newAttachmentData); - - // // Needs to cycle through updating the prop in the view and then another - // // tick for vuejs to reactively update the supplemental component - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const updatedMouseSection = wrapper.getComponent(`[id=${sectionId}]`); - // const mouseFieldUpdated = updatedMouseSection.props('content').find((row) => { - // return row.field == fieldName; - // }); - // expect(mouseFieldUpdated.value.length).to.equal(1); - // }); - - // it('removes the supporting evidence from field', async () => { - // const sectionId = 'Mus_musculus (Mouse) Model System'; - // const sectionName = 'Mus musculus (Mouse) Model System'; - // const fieldName = 'Veterinary Histology Report'; - - // removeSectionAttachment.resolves(removeFieldValue(sectionName, fieldName)); - - // const mouseSection = wrapper.getComponent(`[id=${sectionId}]`); - // const mouseFieldToUpdate = mouseSection.props('content').find((row) => { - // return row.field == fieldName; - // }); - // expect(mouseFieldToUpdate.value.length).to.equal(1); - - // mouseSection.vm.$emit('update:content-row', { - // type: 'supporting-evidence', - // operation: 'delete', - // header: sectionName, - // field: fieldName, - // value: { - // type: 'file', - // attachment_id: 'FJKLJFKLDJSKLFDS', - // }, - // }); - // await wrapper.vm.$nextTick(); - - // notificationDialog.confirmation(); - // await wrapper.vm.$nextTick(); - - // // Neccesary to process several ticks to re-render the section - // await wrapper.vm.$nextTick(); - // await wrapper.vm.$nextTick(); - - // const updatedMouseSection = wrapper.getComponent(`[id=${sectionId}]`); - // const mouseFieldUpdated = updatedMouseSection.props('content').find((row) => { - // return row.field == fieldName; - // }); - // expect(mouseFieldUpdated.value.length).to.equal(0); - // }); - // }); - // }); - - // describe('Saving and canceling analysis changes displays toasts', () => { - // beforeEach(() => { - // updateAnalysisSectionsMock.resolves([]); - // }); - - // it('should display success toast when saving analysis changes', async () => { - // const wrapper = getMountedComponent(); - // await wrapper.setData({edit: true}); - // const saveModal = wrapper.findComponent(SaveModal); - - // saveModal.vm.$emit('save'); - // await wrapper.vm.$nextTick(); - - // expect(toast.state.active).to.be.true; - // expect(toast.state.type).to.equal('success'); - // expect(toast.state.message).to.equal('Analysis updated successfully.'); - // }); - // }); + const fakeImageForUpdate = {data: 'fakeImage.png'}; + inputDialog.confirmation(fakeImageForUpdate); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); + + const failureDialog = wrapper.findComponent(NotificationDialog); + expect(failureDialog.exists()).to.be.true; + + expect(updateSectionImageMock.called).to.be.true; + expect(reRenderedPedigreeSection.props('content').length).to.equal(1); + }); + + it('allows user to remove section image with input dialog confirmation', async () => { + const sectionName = 'Pedigree'; + const fieldName = 'Pedigree'; + removeSectionAttachmentMock.resolves(removeFieldValue('Pedigree', 'Pedigree')); + + const pedigreeSection = wrapper.findComponent(`[id=${sectionName}]`); + pedigreeSection.vm.$emit('update-image', '635a89aea7b2f21802b74539', sectionName, fieldName); + await wrapper.vm.$nextTick(); + + inputDialog.delete(); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const confirmationDialog = wrapper.findComponent(NotificationDialog); + expect(confirmationDialog.exists()).to.be.true; + + notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); + + // Neccesary to process several ticks to re-render the section + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const reRenderedPedigreeSection = wrapper.findComponent('[id=Pedigree]'); + + expect(removeSectionAttachmentMock.called).to.be.true; + expect(reRenderedPedigreeSection.props('content')[0].value.length).to.equal(0); + }); + + it('notifies the user when the image content fails to be removed', async () => { + removeSectionAttachmentMock.throws('sad-it-did not remove'); + + const pedigreeSection = wrapper.findComponent('[id=Pedigree]'); + pedigreeSection.vm.$emit('update-image', 'Pedigree'); + await wrapper.vm.$nextTick(); + + inputDialog.delete(); + + await wrapper.vm.$nextTick(); + + notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); + + // Neccesary to process several ticks to re-render the section + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const failureNotificationDialog = wrapper.findComponent(NotificationDialog); + expect(failureNotificationDialog.exists()).to.be.true; + }); + }); + + describe('when a section has a field that allows attachments', () => { + it('may attach a link to that field', async () => { + const sectionName = 'Mus musculus (Mouse) Model System'; + const sectionId = 'Mus_musculus (Mouse) Model System'; + const fieldName = 'Veterinary Pathology Imaging'; + const newAttachmentData = { + name: 'fake-attachment-evidence-name', + data: 'http://sites.uab.edu/cgds', + attachment_id: 'new-failure-id', + type: 'link', + comments: '', + }; + + attachSectionAttachmentMock.returns(addFieldValue(sectionName, fieldName, newAttachmentData)); + + const mouseSection = wrapper.getComponent(`[id=${sectionId}]`); + const mouseFieldToUpdate = mouseSection.props('content').find((row) => { + return row.field == fieldName; + }); + + expect(mouseFieldToUpdate.value.length).to.equal(0); + + mouseSection.vm.$emit('update:content-row', { + type: 'supporting-evidence', + operation: 'attach', + header: sectionName, + field: fieldName, + value: {}, + }); + await wrapper.vm.$nextTick(); + + inputDialog.confirmation(newAttachmentData); + + // Needs to cycle through updating the prop in the view and then another + // tick for vuejs to reactively update the supplemental component + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const updatedMouseSection = wrapper.getComponent(`[id=${sectionId}]`); + const mouseFieldUpdated = updatedMouseSection.props('content').find((row) => { + return row.field == fieldName; + }); + expect(mouseFieldUpdated.value.length).to.equal(1); + }); + + it('removes the supporting evidence from field', async () => { + const sectionId = 'Mus_musculus (Mouse) Model System'; + const sectionName = 'Mus musculus (Mouse) Model System'; + const fieldName = 'Veterinary Histology Report'; + + removeSectionAttachmentMock.resolves(removeFieldValue(sectionName, fieldName)); + + const mouseSection = wrapper.getComponent(`[id=${sectionId}]`); + const mouseFieldToUpdate = mouseSection.props('content').find((row) => { + return row.field == fieldName; + }); + expect(mouseFieldToUpdate.value.length).to.equal(1); + + mouseSection.vm.$emit('update:content-row', { + type: 'supporting-evidence', + operation: 'delete', + header: sectionName, + field: fieldName, + value: { + type: 'file', + attachment_id: 'FJKLJFKLDJSKLFDS', + }, + }); + await wrapper.vm.$nextTick(); + + notificationDialog.confirmation(); + await wrapper.vm.$nextTick(); + + // Neccesary to process several ticks to re-render the section + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const updatedMouseSection = wrapper.getComponent(`[id=${sectionId}]`); + const mouseFieldUpdated = updatedMouseSection.props('content').find((row) => { + return row.field == fieldName; + }); + expect(mouseFieldUpdated.value.length).to.equal(0); + }); + }); + }); + + describe('Saving and canceling analysis changes displays toasts', () => { + beforeEach(() => { + updateAnalysisSectionsMock.resolves([]); + }); + + it('should display success toast when saving analysis changes', async () => { + const modifiedWrapper = getMountedComponent(); + await triggerAction(modifiedWrapper, 'Edit'); + + const saveModal = modifiedWrapper.findComponent(SaveModal); + + saveModal.vm.$emit('save'); + await modifiedWrapper.vm.$nextTick(); + await modifiedWrapper.vm.$nextTick(); + + expect(toast.state.active).to.be.true; + expect(toast.state.type).to.equal('success'); + expect(toast.state.message).to.equal('Analysis updated successfully.'); + }); + }); }); @@ -820,9 +850,11 @@ function removeFieldValue(sectionName, fieldName) { /** * Returns fixture data + * + * @param {Object} attributes from the analysis to override * @return {Object} containing analysis data for CPAM0046. */ -function fixtureData() { +function fixtureData(attributes) { return { name: 'CPAM0046', description: ': LMNA-related congenital muscular dystropy', @@ -1020,5 +1052,6 @@ function fixtureData() { comments: ' ', }, ], + ...attributes, }; }