From 62b7782a1857503f5e5d81be7236adec40944f81 Mon Sep 17 00:00:00 2001 From: Daneryl Date: Fri, 16 Nov 2018 16:07:43 +0100 Subject: [PATCH 1/6] raw page with blank text not going to throw a 404 anynmore --- app/api/entities/entities.js | 3 ++- app/api/entities/specs/entities.spec.js | 34 +++++++++++++++---------- app/api/entities/specs/fixtures.js | 1 + 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/api/entities/entities.js b/app/api/entities/entities.js index 83906d636d..ac5fbade63 100644 --- a/app/api/entities/entities.js +++ b/app/api/entities/entities.js @@ -379,7 +379,8 @@ export default { if (!entity) { throw createError('entity does not exists', 404); } - if (!entity.fullText[pageNumber]) { + + if (typeof entity.fullText[pageNumber] === 'undefined') { throw createError('page does not exist', 404); } diff --git a/app/api/entities/specs/entities.spec.js b/app/api/entities/specs/entities.spec.js index 37cec31d81..efc79547a5 100644 --- a/app/api/entities/specs/entities.spec.js +++ b/app/api/entities/specs/entities.spec.js @@ -692,6 +692,14 @@ describe('entities', () => { }); }); + describe('when page is blank', () => { + it('should not throw a 404', async () => { + const pageNumber = 3; + const page = await entities.getRawPage('shared', 'en', pageNumber); + + expect(page).toBe(''); + }); + }); describe('when page do not exists', () => { it('should throw 404 error', async () => { const pageNumber = 200; @@ -715,21 +723,21 @@ describe('entities', () => { }); it('should delete the document from the search', done => entities.delete('shared') - .then(() => { - const argumnets = search.delete.calls.allArgs(); - expect(search.delete).toHaveBeenCalled(); - expect(argumnets[0][0]._id.toString()).toBe(batmanFinishesId.toString()); - done(); - }) - .catch(catchErrors(done))); + .then(() => { + const argumnets = search.delete.calls.allArgs(); + expect(search.delete).toHaveBeenCalled(); + expect(argumnets[0][0]._id.toString()).toBe(batmanFinishesId.toString()); + done(); + }) + .catch(catchErrors(done))); it('should delete the document relationships', done => entities.delete('shared') - .then(() => relationships.get({ entity: 'shared' })) - .then((refs) => { - expect(refs.length).toBe(0); - done(); - }) - .catch(catchErrors(done))); + .then(() => relationships.get({ entity: 'shared' })) + .then((refs) => { + expect(refs.length).toBe(0); + done(); + }) + .catch(catchErrors(done))); it('should delete the original file', (done) => { fs.writeFileSync(path.join(uploadDocumentsPath, '8202c463d6158af8065022d9b5014ccb.pdf')); diff --git a/app/api/entities/specs/fixtures.js b/app/api/entities/specs/fixtures.js index 3d1057f271..bd62572820 100644 --- a/app/api/entities/specs/fixtures.js +++ b/app/api/entities/specs/fixtures.js @@ -33,6 +33,7 @@ export default { fullText: { 1: 'page[[1]] 1[[1]]', 2: 'page[[2]] 2[[2]]', + 3: '', }, metadata: { property1: 'value1' From f602f9dbce56ab51d77c683663e92e01ab7415f0 Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 22 Nov 2018 16:25:41 +0100 Subject: [PATCH 2/6] indexEntities fix to index properly the entity relationships --- app/api/entities/entities.js | 2 +- app/api/entities/specs/entities.spec.js | 31 +++++++++++++++++++++---- app/api/entities/specs/fixtures.js | 20 ++++++++-------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/app/api/entities/entities.js b/app/api/entities/entities.js index 6dcc42343d..eb68e60678 100644 --- a/app/api/entities/entities.js +++ b/app/api/entities/entities.js @@ -197,7 +197,7 @@ export default { } return this.get(query, select, { skip: offset, limit }) - .then(entities => Promise.all(entities.map(entity => relationships.get({ entity: entity.sharedId, language: entity.language }) + .then(entities => Promise.all(entities.map(entity => relationships.get({ entity: entity.sharedId }) .then((relations) => { entity.relationships = relations || []; return entity; diff --git a/app/api/entities/specs/entities.spec.js b/app/api/entities/specs/entities.spec.js index 63fa6d0058..a2b8fdeb21 100644 --- a/app/api/entities/specs/entities.spec.js +++ b/app/api/entities/specs/entities.spec.js @@ -418,15 +418,38 @@ describe('entities', () => { describe('indexEntities', () => { it('should index entities based on query params passed', (done) => { - entities.indexEntities({ sharedId: 'shared' }, { title: 1 }) + entities.indexEntities({ sharedId: 'shared' }) .then(() => { const documentsToIndex = search.bulkIndex.calls.argsFor(0)[0]; expect(documentsToIndex[0].title).toBeDefined(); - expect(documentsToIndex[0].metadata).not.toBeDefined(); + expect(documentsToIndex[0].fullText).not.toBeDefined(); + expect(documentsToIndex[0].relationships.length).toBe(4); + + expect(documentsToIndex[1].title).toBeDefined(); + expect(documentsToIndex[1].fullText).not.toBeDefined(); + expect(documentsToIndex[1].relationships.length).toBe(4); + + expect(documentsToIndex[2].title).toBeDefined(); + expect(documentsToIndex[2].fullText).not.toBeDefined(); + expect(documentsToIndex[2].relationships.length).toBe(4); + done(); + }) + .catch(catchErrors(done)); + }); + + it('should index entities withh fullText', (done) => { + entities.indexEntities({ sharedId: 'shared' }, '+fullText') + .then(() => { + const documentsToIndex = search.bulkIndex.calls.argsFor(0)[0]; + expect(documentsToIndex[0].title).toBeDefined(); + expect(documentsToIndex[0].fullText).toBeDefined(); + expect(documentsToIndex[0].relationships.length).toBe(4); + expect(documentsToIndex[1].title).toBeDefined(); - expect(documentsToIndex[1].metadata).not.toBeDefined(); + expect(documentsToIndex[1].relationships.length).toBe(4); + expect(documentsToIndex[2].title).toBeDefined(); - expect(documentsToIndex[2].metadata).not.toBeDefined(); + expect(documentsToIndex[2].relationships.length).toBe(4); done(); }) .catch(catchErrors(done)); diff --git a/app/api/entities/specs/fixtures.js b/app/api/entities/specs/fixtures.js index 1be51170d4..e2bc0acbd2 100644 --- a/app/api/entities/specs/fixtures.js +++ b/app/api/entities/specs/fixtures.js @@ -107,16 +107,16 @@ export default { ] } ], connections: [ - { _id: referenceId, entity: 'shared', template: null, hub: hub1, language: 'en' }, - { entity: 'shared2', template: 'relation1', hub: hub1, language: 'en' }, - { entity: 'shared', template: null, hub: hub2, language: 'en' }, - { entity: 'source2', template: 'relation2', hub: hub2, language: 'en' }, - { entity: 'another', template: 'relation3', hub: hub3, language: 'en' }, - { entity: 'document', template: 'relation3', hub: hub3, language: 'en' }, - { entity: 'shared', template: 'relation2', hub: hub4, language: 'en' }, - { entity: 'shared1', template: 'relation2', hub: hub4, language: 'en' }, - { entity: 'shared1', template: 'relation2', hub: hub5, language: 'en' }, - { entity: 'shared', template: 'relation2', hub: hub5, language: 'en' } + { _id: referenceId, entity: 'shared', template: null, hub: hub1 }, + { entity: 'shared2', template: 'relation1', hub: hub1 }, + { entity: 'shared', template: null, hub: hub2 }, + { entity: 'source2', template: 'relation2', hub: hub2 }, + { entity: 'another', template: 'relation3', hub: hub3 }, + { entity: 'document', template: 'relation3', hub: hub3 }, + { entity: 'shared', template: 'relation2', hub: hub4 }, + { entity: 'shared1', template: 'relation2', hub: hub4 }, + { entity: 'shared1', template: 'relation2', hub: hub5 }, + { entity: 'shared', template: 'relation2', hub: hub5 } ] }; From 22188b14e8afda88475aba499fd38d276c04ab3d Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 22 Nov 2018 16:36:35 +0100 Subject: [PATCH 3/6] savePdfInfo now only sends the needed params --- app/react/Viewer/actions/documentActions.js | 83 +++++---- .../actions/specs/documentActions.spec.js | 158 +++++++++--------- 2 files changed, 118 insertions(+), 123 deletions(-) diff --git a/app/react/Viewer/actions/documentActions.js b/app/react/Viewer/actions/documentActions.js index dfe1d6caea..ebcb2af0dd 100644 --- a/app/react/Viewer/actions/documentActions.js +++ b/app/react/Viewer/actions/documentActions.js @@ -1,19 +1,20 @@ -import api from 'app/utils/api'; -import referencesAPI from 'app/Viewer/referencesAPI'; -import * as types from 'app/Viewer/actions/actionTypes'; -import * as connectionsTypes from 'app/Connections/actions/actionTypes'; +import { actions as formActions } from 'react-redux-form'; import { APIURL } from 'app/config.js'; -import { PDFUtils } from '../../PDF/'; import { actions } from 'app/BasicReducer'; -import { actions as formActions } from 'react-redux-form'; -import documents from 'app/Documents'; +import { isClient } from 'app/utils'; import { notify } from 'app/Notifications'; +import { actions as relationshipActions } from 'app/Relationships'; import { removeDocument, unselectAllDocuments } from 'app/Library/actions/libraryActions'; +import api from 'app/utils/api'; +import * as connectionsTypes from 'app/Connections/actions/actionTypes'; +import documents from 'app/Documents'; +import referencesAPI from 'app/Viewer/referencesAPI'; +import * as types from 'app/Viewer/actions/actionTypes'; + +import { PDFUtils } from '../../PDF/'; import * as selectionActions from './selectionActions'; import * as uiActions from './uiActions'; -import { isClient } from 'app/utils'; -import { actions as relationshipActions } from 'app/Relationships'; export function setDocument(document, html) { return { @@ -43,20 +44,18 @@ export function saveDocument(doc) { } }); - return function (dispatch) { - return documents.api.save(updateDoc) - .then((updatedDoc) => { - dispatch(notify('Document updated', 'success')); - dispatch({ type: types.VIEWER_UPDATE_DOCUMENT, doc }); - dispatch(formActions.reset('documentViewer.sidepanel.metadata')); - dispatch(actions.set('viewer/doc', updatedDoc)); - dispatch(relationshipActions.reloadRelationships(updatedDoc.sharedId)); - }); - }; + return dispatch => documents.api.save(updateDoc) + .then((updatedDoc) => { + dispatch(notify('Document updated', 'success')); + dispatch({ type: types.VIEWER_UPDATE_DOCUMENT, doc }); + dispatch(formActions.reset('documentViewer.sidepanel.metadata')); + dispatch(actions.set('viewer/doc', updatedDoc)); + dispatch(relationshipActions.reloadRelationships(updatedDoc.sharedId)); + }); } export function saveToc(toc) { - return function (dispatch, getState) { + return (dispatch, getState) => { const { _id, _rev, sharedId, file } = getState().documentViewer.doc.toJS(); dispatch(formActions.reset('documentViewer.sidepanel.metadata')); dispatch(actions.set('documentViewer/tocBeingEdited', false)); @@ -65,15 +64,13 @@ export function saveToc(toc) { } export function deleteDocument(doc) { - return function (dispatch) { - return documents.api.delete(doc) - .then(() => { - dispatch(notify('Document deleted', 'success')); - dispatch(resetDocumentViewer()); - dispatch(removeDocument(doc)); - dispatch(unselectAllDocuments()); - }); - }; + return dispatch => documents.api.delete(doc) + .then(() => { + dispatch(notify('Document deleted', 'success')); + dispatch(resetDocumentViewer()); + dispatch(removeDocument(doc)); + dispatch(unselectAllDocuments()); + }); } export function getDocument(id) { @@ -88,28 +85,26 @@ export function getDocument(id) { } return PDFUtils.extractPDFInfo(`${APIURL}documents/download?_id=${doc._id}`) .then((pdfInfo) => { - doc.pdfInfo = pdfInfo; - return api.post('documents/pdfInfo', doc) + const { _id, sharedId } = doc; + return api.post('documents/pdfInfo', { _id, sharedId, pdfInfo }) .then(res => res.json); }); }); } export function loadTargetDocument(id) { - return function (dispatch) { - return Promise.all([ + return dispatch => Promise.all([ getDocument(id), referencesAPI.get(id) - ]) - .then(([targetDoc, references]) => { - dispatch(actions.set('viewer/targetDoc', targetDoc)); - dispatch(actions.set('viewer/targetDocReferences', references)); - }); - }; + ]) + .then(([targetDoc, references]) => { + dispatch(actions.set('viewer/targetDoc', targetDoc)); + dispatch(actions.set('viewer/targetDocReferences', references)); + }); } export function cancelTargetDocument() { - return function (dispatch) { + return (dispatch) => { dispatch({ type: connectionsTypes.CANCEL_RANGED_CONNECTION }); dispatch(actions.unset('viewer/targetDoc')); dispatch(actions.unset('viewer/targetDocReferences')); @@ -119,7 +114,7 @@ export function cancelTargetDocument() { } export function editToc(toc) { - return function (dispatch) { + return (dispatch) => { dispatch(actions.set('documentViewer/tocBeingEdited', true)); dispatch(formActions.load('documentViewer.tocForm', toc)); dispatch(uiActions.openPanel('viewMetadataPanel')); @@ -128,7 +123,7 @@ export function editToc(toc) { } export function removeFromToc(tocElement) { - return function (dispatch, getState) { + return (dispatch, getState) => { const state = getState(); let toc = state.documentViewer.tocForm; @@ -139,7 +134,7 @@ export function removeFromToc(tocElement) { } export function indentTocElement(tocElement, indentation) { - return function (dispatch, getState) { + return (dispatch, getState) => { const state = getState(); const toc = state.documentViewer.tocForm.map((_entry) => { const entry = Object.assign({}, _entry); @@ -154,7 +149,7 @@ export function indentTocElement(tocElement, indentation) { } export function addToToc(textSelectedObject) { - return function (dispatch, getState) { + return (dispatch, getState) => { const state = getState(); let toc = state.documentViewer.tocForm.concat(); if (!toc.length) { diff --git a/app/react/Viewer/actions/specs/documentActions.spec.js b/app/react/Viewer/actions/specs/documentActions.spec.js index 3cab01e14c..7caeeaa5ca 100644 --- a/app/react/Viewer/actions/specs/documentActions.spec.js +++ b/app/react/Viewer/actions/specs/documentActions.spec.js @@ -5,15 +5,15 @@ import backend from 'fetch-mock'; import Immutable from 'immutable'; import api from 'app/utils/api'; -import {PDFUtils} from '../../../PDF/'; -import {mockID} from 'shared/uniqueID.js'; +import { PDFUtils } from '../../../PDF/'; +import { mockID } from 'shared/uniqueID.js'; import documents from 'app/Documents'; -import {APIURL} from 'app/config.js'; +import { APIURL } from 'app/config.js'; import * as notificationsTypes from 'app/Notifications/actions/actionTypes'; import * as actions from '../documentActions'; import * as types from '../actionTypes'; -import {actions as formActions} from 'react-redux-form'; -import {actions as relationshipActions} from 'app/Relationships'; +import { actions as formActions } from 'react-redux-form'; +import { actions as relationshipActions } from 'app/Relationships'; const middlewares = [thunk]; const mockStore = configureMockStore(middlewares); @@ -21,41 +21,41 @@ const mockStore = configureMockStore(middlewares); describe('documentActions', () => { describe('setDocument()', () => { it('should return a SET_REFERENCES type action with the document', () => { - let action = actions.setDocument('document', 'html'); - expect(action).toEqual({type: types.SET_DOCUMENT, document: 'document', html: 'html'}); + const action = actions.setDocument('document', 'html'); + expect(action).toEqual({ type: types.SET_DOCUMENT, document: 'document', html: 'html' }); }); }); describe('resetDocumentViewer()', () => { it('should return a RESET_DOCUMENT_VIEWER', () => { - let action = actions.resetDocumentViewer(); - expect(action).toEqual({type: types.RESET_DOCUMENT_VIEWER}); + const action = actions.resetDocumentViewer(); + expect(action).toEqual({ type: types.RESET_DOCUMENT_VIEWER }); }); }); describe('loadDefaultViewerMenu()', () => { it('should return a LOAD_DEFAULT_VIEWER_MENU', () => { - let action = actions.loadDefaultViewerMenu(); - expect(action).toEqual({type: types.LOAD_DEFAULT_VIEWER_MENU}); + const action = actions.loadDefaultViewerMenu(); + expect(action).toEqual({ type: types.LOAD_DEFAULT_VIEWER_MENU }); }); }); describe('openReferencePanel()', () => { it('should return a OPEN_REFERENCE_PANEL', () => { - let action = actions.loadDefaultViewerMenu(); - expect(action).toEqual({type: types.LOAD_DEFAULT_VIEWER_MENU}); + const action = actions.loadDefaultViewerMenu(); + expect(action).toEqual({ type: types.LOAD_DEFAULT_VIEWER_MENU }); }); }); describe('addToToc', () => { it('should populate doc form, and add the selected text to its correct place', () => { - spyOn(formActions, 'load').and.returnValue({type: 'loadAction'}); - let reference = {sourceDocument: '123', sourceRange: {start: 12, end: 23, text: 'Chapter 1'}}; - let chapter1 = {range: {start: 12, end: 23}, label: 'Chapter 1', indentation: 0}; - let chapter2 = {range: {start: 22, end: 43}, label: 'Chapter 2', indentation: 0}; + spyOn(formActions, 'load').and.returnValue({ type: 'loadAction' }); + const reference = { sourceDocument: '123', sourceRange: { start: 12, end: 23, text: 'Chapter 1' } }; + const chapter1 = { range: { start: 12, end: 23 }, label: 'Chapter 1', indentation: 0 }; + const chapter2 = { range: { start: 22, end: 43 }, label: 'Chapter 2', indentation: 0 }; const expectedActions = [ - {type: 'documentViewer/tocBeingEdited/SET', value: true}, - {type: 'loadAction'}, - {type: types.OPEN_PANEL, panel: 'viewMetadataPanel'}, - {type: 'viewer.sidepanel.tab/SET', value: 'toc'} + { type: 'documentViewer/tocBeingEdited/SET', value: true }, + { type: 'loadAction' }, + { type: types.OPEN_PANEL, panel: 'viewMetadataPanel' }, + { type: 'viewer.sidepanel.tab/SET', value: 'toc' } ]; const store = mockStore({ @@ -75,15 +75,15 @@ describe('documentActions', () => { describe('if document is already loaded', () => { it('should not reload the form', () => { - spyOn(formActions, 'load').and.returnValue({type: 'loadAction'}); - let reference = {sourceDocument: '123', sourceRange: {start: 12, end: 23, text: 'Chapter 1'}}; - let chapter1 = {range: {start: 12, end: 23}, label: 'Chapter 1', indentation: 0}; - let chapter2 = {range: {start: 22, end: 43}, label: 'Chapter 2', indentation: 0}; + spyOn(formActions, 'load').and.returnValue({ type: 'loadAction' }); + const reference = { sourceDocument: '123', sourceRange: { start: 12, end: 23, text: 'Chapter 1' } }; + const chapter1 = { range: { start: 12, end: 23 }, label: 'Chapter 1', indentation: 0 }; + const chapter2 = { range: { start: 22, end: 43 }, label: 'Chapter 2', indentation: 0 }; const expectedActions = [ - {type: 'documentViewer/tocBeingEdited/SET', value: true}, - {type: 'loadAction'}, - {type: types.OPEN_PANEL, panel: 'viewMetadataPanel'}, - {type: 'viewer.sidepanel.tab/SET', value: 'toc'} + { type: 'documentViewer/tocBeingEdited/SET', value: true }, + { type: 'loadAction' }, + { type: types.OPEN_PANEL, panel: 'viewMetadataPanel' }, + { type: 'viewer.sidepanel.tab/SET', value: 'toc' } ]; const store = mockStore({ documentViewer: { @@ -101,12 +101,12 @@ describe('documentActions', () => { describe('removeFromToc', () => { it('should remove the toc entry from the form', () => { - spyOn(formActions, 'load').and.returnValue({type: 'loadAction'}); - let chapter1 = {range: {start: 12, end: 23}, label: 'Chapter 1', indentation: 0, _id: 1}; - let chapter2 = {range: {start: 22, end: 43}, label: 'Chapter 2', indentation: 0, _id: 2}; + spyOn(formActions, 'load').and.returnValue({ type: 'loadAction' }); + const chapter1 = { range: { start: 12, end: 23 }, label: 'Chapter 1', indentation: 0, _id: 1 }; + const chapter2 = { range: { start: 22, end: 43 }, label: 'Chapter 2', indentation: 0, _id: 2 }; const expectedActions = [ - {type: 'loadAction'} + { type: 'loadAction' } ]; const store = mockStore({ @@ -127,10 +127,10 @@ describe('documentActions', () => { describe('indentTocElement', () => { it('should change the toc entry indentation', () => { - let chapter1 = {range: {start: 12, end: 23}, label: 'Chapter 1', indentation: 0, _id: 1}; - let chapter2 = {range: {start: 22, end: 43}, label: 'Chapter 2', indentation: 0, _id: 2}; + const chapter1 = { range: { start: 12, end: 23 }, label: 'Chapter 1', indentation: 0, _id: 1 }; + const chapter2 = { range: { start: 22, end: 43 }, label: 'Chapter 2', indentation: 0, _id: 2 }; - let formState = [chapter1, chapter2]; + const formState = [chapter1, chapter2]; const store = mockStore({ documentViewer: { tocForm: formState, @@ -152,33 +152,33 @@ describe('documentActions', () => { mockID(); backend.restore(); backend - .get(APIURL + 'documents/search?searchTerm=term&fields=%5B%22field%22%5D', {body: JSON.stringify('documents')}) - .get(APIURL + 'entities?_id=targetId', {body: JSON.stringify({rows: [{target: 'document', pdfInfo: 'test'}]})}) - .get(APIURL + 'entities?_id=docWithPDFRdy', {body: JSON.stringify({rows: [{pdfInfo: 'processed pdf', _id: 'pdfReady'}]})}) - .get(APIURL + 'entities?_id=docWithPDFNotRdy', {body: JSON.stringify({rows: [{_id: 'pdfNotReady'}]})}) - .get(APIURL + 'references/by_document/targetId', {body: JSON.stringify([{connectedDocument: '1'}])}); + .get(`${APIURL}documents/search?searchTerm=term&fields=%5B%22field%22%5D`, { body: JSON.stringify('documents') }) + .get(`${APIURL}entities?_id=targetId`, { body: JSON.stringify({ rows: [{ target: 'document', pdfInfo: 'test' }] }) }) + .get(`${APIURL}entities?_id=docWithPDFRdy`, { body: JSON.stringify({ rows: [{ pdfInfo: 'processed pdf', _id: 'pdfReady' }] }) }) + .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { body: JSON.stringify({ rows: [{ _id: 'pdfNotReady', sharedId: 'shared', unwantedProp: 'unwanted' }] }) }) + .get(`${APIURL}references/by_document/targetId`, { body: JSON.stringify([{ connectedDocument: '1' }]) }); }); afterEach(() => backend.restore()); describe('saveDocument', () => { it('should save the document (omitting fullText) and dispatch a notification on success', (done) => { - spyOn(documents.api, 'save').and.returnValue(Promise.resolve({sharedId: 'responseId'})); - let doc = {name: 'doc', fullText: 'fullText'}; - spyOn(relationshipActions, 'reloadRelationships').and.returnValue({type: 'reloadRelationships'}); + spyOn(documents.api, 'save').and.returnValue(Promise.resolve({ sharedId: 'responseId' })); + const doc = { name: 'doc', fullText: 'fullText' }; + spyOn(relationshipActions, 'reloadRelationships').and.returnValue({ type: 'reloadRelationships' }); const expectedActions = [ - {type: notificationsTypes.NOTIFY, notification: {message: 'Document updated', type: 'success', id: 'unique_id'}}, - {type: types.VIEWER_UPDATE_DOCUMENT, doc: {name: 'doc', fullText: 'fullText'}}, - {type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata'}, - {type: 'viewer/doc/SET', value: {sharedId: 'responseId'}}, - {type: 'reloadRelationships'} + { type: notificationsTypes.NOTIFY, notification: { message: 'Document updated', type: 'success', id: 'unique_id' } }, + { type: types.VIEWER_UPDATE_DOCUMENT, doc: { name: 'doc', fullText: 'fullText' } }, + { type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata' }, + { type: 'viewer/doc/SET', value: { sharedId: 'responseId' } }, + { type: 'reloadRelationships' } ]; const store = mockStore({}); store.dispatch(actions.saveDocument(doc)) .then(() => { - expect(documents.api.save).toHaveBeenCalledWith({name: 'doc'}); + expect(documents.api.save).toHaveBeenCalledWith({ name: 'doc' }); expect(store.getActions()).toEqual(expectedActions); }) .then(done) @@ -198,8 +198,8 @@ describe('documentActions', () => { describe('when the doc does not have the pdf processed', () => { it('should process it and save it before it gets returned', (done) => { spyOn(PDFUtils, 'extractPDFInfo').and.returnValue(Promise.resolve('test')); - const expected = {_id: 'pdfNotReady', pdfInfo: 'test'}; - spyOn(api, 'post').and.returnValue(Promise.resolve({json: expected})); + const expected = { sharedId: 'shared', _id: 'pdfNotReady', pdfInfo: 'test' }; + spyOn(api, 'post').and.returnValue(Promise.resolve({ json: expected })); actions.getDocument('docWithPDFNotRdy') .then((doc) => { expect(PDFUtils.extractPDFInfo).toHaveBeenCalledWith(`${APIURL}documents/download?_id=${expected._id}`); @@ -214,21 +214,21 @@ describe('documentActions', () => { describe('saveToc', () => { it('should save the document with the new toc and dispatch a notification on success', (done) => { spyOn(documents.api, 'save').and.returnValue(Promise.resolve('response')); - spyOn(relationshipActions, 'reloadRelationships').and.returnValue({type: 'reloadRelationships'}); - let doc = {name: 'doc', _id: 'id', _rev: 'rev', sharedId: 'sharedId', file: {fileName: '123.pdf'}}; - let toc = [ - {range: {start: 12, end: 23}, label: 'Chapter 1', indentation: 0}, - {range: {start: 22, end: 44}, label: 'Chapter 1.1', indentation: 1} + spyOn(relationshipActions, 'reloadRelationships').and.returnValue({ type: 'reloadRelationships' }); + const doc = { name: 'doc', _id: 'id', _rev: 'rev', sharedId: 'sharedId', file: { fileName: '123.pdf' } }; + const toc = [ + { range: { start: 12, end: 23 }, label: 'Chapter 1', indentation: 0 }, + { range: { start: 22, end: 44 }, label: 'Chapter 1.1', indentation: 1 } ]; const expectedActions = [ - {type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata'}, - {type: 'documentViewer/tocBeingEdited/SET', value: false}, - {type: notificationsTypes.NOTIFY, notification: {message: 'Document updated', type: 'success', id: 'unique_id'}}, - {type: types.VIEWER_UPDATE_DOCUMENT, doc: {_id: 'id', _rev: 'rev', sharedId: 'sharedId', toc, file: {fileName: '123.pdf'}}}, - {type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata'}, - {type: 'viewer/doc/SET', value: 'response'}, - {type: 'reloadRelationships'} + { type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata' }, + { type: 'documentViewer/tocBeingEdited/SET', value: false }, + { type: notificationsTypes.NOTIFY, notification: { message: 'Document updated', type: 'success', id: 'unique_id' } }, + { type: types.VIEWER_UPDATE_DOCUMENT, doc: { _id: 'id', _rev: 'rev', sharedId: 'sharedId', toc, file: { fileName: '123.pdf' } } }, + { type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata' }, + { type: 'viewer/doc/SET', value: 'response' }, + { type: 'reloadRelationships' } ]; const store = mockStore({ documentViewer: { @@ -238,7 +238,7 @@ describe('documentActions', () => { store.dispatch(actions.saveToc(toc)) .then(() => { - expect(documents.api.save).toHaveBeenCalledWith({_id: 'id', _rev: 'rev', sharedId: 'sharedId', toc, file: {fileName: '123.pdf'}}); + expect(documents.api.save).toHaveBeenCalledWith({ _id: 'id', _rev: 'rev', sharedId: 'sharedId', toc, file: { fileName: '123.pdf' } }); expect(store.getActions()).toEqual(expectedActions); }) .then(done) @@ -249,13 +249,13 @@ describe('documentActions', () => { describe('deleteDocument', () => { it('should delete the document and dispatch a notification on success', (done) => { spyOn(documents.api, 'delete').and.returnValue(Promise.resolve('response')); - let doc = {name: 'doc'}; + const doc = { name: 'doc' }; const expectedActions = [ - {type: notificationsTypes.NOTIFY, notification: {message: 'Document deleted', type: 'success', id: 'unique_id'}}, - {type: types.RESET_DOCUMENT_VIEWER}, - {type: 'REMOVE_DOCUMENT', doc: {name: 'doc'}}, - {type: 'UNSELECT_ALL_DOCUMENTS'} + { type: notificationsTypes.NOTIFY, notification: { message: 'Document deleted', type: 'success', id: 'unique_id' } }, + { type: types.RESET_DOCUMENT_VIEWER }, + { type: 'REMOVE_DOCUMENT', doc: { name: 'doc' } }, + { type: 'UNSELECT_ALL_DOCUMENTS' } ]; const store = mockStore({}); @@ -271,13 +271,13 @@ describe('documentActions', () => { describe('loadTargetDocument', () => { it('should loadTargetDocument with id passed', (done) => { - let targetId = 'targetId'; + const targetId = 'targetId'; const expectedActions = [ - {type: 'viewer/targetDoc/SET', value: {target: 'document', pdfInfo: 'test'}}, - {type: 'viewer/targetDocReferences/SET', value: [{connectedDocument: '1'}]} + { type: 'viewer/targetDoc/SET', value: { target: 'document', pdfInfo: 'test' } }, + { type: 'viewer/targetDocReferences/SET', value: [{ connectedDocument: '1' }] } ]; - const store = mockStore({locale: 'es'}); + const store = mockStore({ locale: 'es' }); store.dispatch(actions.loadTargetDocument(targetId)) .then(() => { @@ -291,11 +291,11 @@ describe('documentActions', () => { describe('cancelTargetDocument', () => { it('should reset ranged connection defaults', () => { const expectedActions = [ - {type: 'CANCEL_RANGED_CONNECTION'}, - {type: 'viewer/targetDoc/UNSET'}, - {type: 'viewer/targetDocReferences/UNSET'}, - {type: 'UNSET_TARGET_SELECTION'}, - {type: 'OPEN_PANEL', panel: 'viewMetadataPanel'} + { type: 'CANCEL_RANGED_CONNECTION' }, + { type: 'viewer/targetDoc/UNSET' }, + { type: 'viewer/targetDocReferences/UNSET' }, + { type: 'UNSET_TARGET_SELECTION' }, + { type: 'OPEN_PANEL', panel: 'viewMetadataPanel' } ]; const store = mockStore({}); From 2950eb4b6c56d6d66bc6aeca6b94a99ae3f8496c Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 22 Nov 2018 16:44:42 +0100 Subject: [PATCH 4/6] fix lints --- .../actions/specs/documentActions.spec.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/app/react/Viewer/actions/specs/documentActions.spec.js b/app/react/Viewer/actions/specs/documentActions.spec.js index 7caeeaa5ca..6be6fca2b9 100644 --- a/app/react/Viewer/actions/specs/documentActions.spec.js +++ b/app/react/Viewer/actions/specs/documentActions.spec.js @@ -1,19 +1,20 @@ /* eslint-disable max-nested-callbacks */ -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import backend from 'fetch-mock'; +import { actions as formActions } from 'react-redux-form'; import Immutable from 'immutable'; -import api from 'app/utils/api'; +import thunk from 'redux-thunk'; -import { PDFUtils } from '../../../PDF/'; +import { APIURL } from 'app/config.js'; import { mockID } from 'shared/uniqueID.js'; +import { actions as relationshipActions } from 'app/Relationships'; +import api from 'app/utils/api'; +import backend from 'fetch-mock'; +import configureMockStore from 'redux-mock-store'; import documents from 'app/Documents'; -import { APIURL } from 'app/config.js'; import * as notificationsTypes from 'app/Notifications/actions/actionTypes'; + +import { PDFUtils } from '../../../PDF/'; import * as actions from '../documentActions'; import * as types from '../actionTypes'; -import { actions as formActions } from 'react-redux-form'; -import { actions as relationshipActions } from 'app/Relationships'; const middlewares = [thunk]; const mockStore = configureMockStore(middlewares); @@ -155,7 +156,15 @@ describe('documentActions', () => { .get(`${APIURL}documents/search?searchTerm=term&fields=%5B%22field%22%5D`, { body: JSON.stringify('documents') }) .get(`${APIURL}entities?_id=targetId`, { body: JSON.stringify({ rows: [{ target: 'document', pdfInfo: 'test' }] }) }) .get(`${APIURL}entities?_id=docWithPDFRdy`, { body: JSON.stringify({ rows: [{ pdfInfo: 'processed pdf', _id: 'pdfReady' }] }) }) - .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { body: JSON.stringify({ rows: [{ _id: 'pdfNotReady', sharedId: 'shared', unwantedProp: 'unwanted' }] }) }) + .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { + body: JSON.stringify({ + rows: [{ + _id: 'pdfNotReady', + sharedId: 'shared', + unwantedProp: 'unwanted' + }] + }) + }) .get(`${APIURL}references/by_document/targetId`, { body: JSON.stringify([{ connectedDocument: '1' }]) }); }); From f7e3cab571b0b4c8f9d3b9d77e0f4ea0864443c9 Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 22 Nov 2018 17:10:07 +0100 Subject: [PATCH 5/6] fixed circular dependencies on specs --- app/react/Viewer/actions/documentActions.js | 4 ++-- .../actions/specs/documentActions.spec.js | 22 ++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/app/react/Viewer/actions/documentActions.js b/app/react/Viewer/actions/documentActions.js index ebcb2af0dd..723faef83d 100644 --- a/app/react/Viewer/actions/documentActions.js +++ b/app/react/Viewer/actions/documentActions.js @@ -9,11 +9,11 @@ import { removeDocument, unselectAllDocuments } from 'app/Library/actions/librar import api from 'app/utils/api'; import * as connectionsTypes from 'app/Connections/actions/actionTypes'; import documents from 'app/Documents'; -import referencesAPI from 'app/Viewer/referencesAPI'; -import * as types from 'app/Viewer/actions/actionTypes'; import { PDFUtils } from '../../PDF/'; +import referencesAPI from '../referencesAPI'; import * as selectionActions from './selectionActions'; +import * as types from './actionTypes'; import * as uiActions from './uiActions'; export function setDocument(document, html) { diff --git a/app/react/Viewer/actions/specs/documentActions.spec.js b/app/react/Viewer/actions/specs/documentActions.spec.js index 6be6fca2b9..232e182614 100644 --- a/app/react/Viewer/actions/specs/documentActions.spec.js +++ b/app/react/Viewer/actions/specs/documentActions.spec.js @@ -1,16 +1,16 @@ /* eslint-disable max-nested-callbacks */ import { actions as formActions } from 'react-redux-form'; -import Immutable from 'immutable'; +import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import backend from 'fetch-mock'; +import Immutable from 'immutable'; +import api from 'app/utils/api'; -import { APIURL } from 'app/config.js'; import { mockID } from 'shared/uniqueID.js'; -import { actions as relationshipActions } from 'app/Relationships'; -import api from 'app/utils/api'; -import backend from 'fetch-mock'; -import configureMockStore from 'redux-mock-store'; import documents from 'app/Documents'; +import { APIURL } from 'app/config.js'; import * as notificationsTypes from 'app/Notifications/actions/actionTypes'; +import { actions as relationshipActions } from 'app/Relationships'; import { PDFUtils } from '../../../PDF/'; import * as actions from '../documentActions'; @@ -156,15 +156,7 @@ describe('documentActions', () => { .get(`${APIURL}documents/search?searchTerm=term&fields=%5B%22field%22%5D`, { body: JSON.stringify('documents') }) .get(`${APIURL}entities?_id=targetId`, { body: JSON.stringify({ rows: [{ target: 'document', pdfInfo: 'test' }] }) }) .get(`${APIURL}entities?_id=docWithPDFRdy`, { body: JSON.stringify({ rows: [{ pdfInfo: 'processed pdf', _id: 'pdfReady' }] }) }) - .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { - body: JSON.stringify({ - rows: [{ - _id: 'pdfNotReady', - sharedId: 'shared', - unwantedProp: 'unwanted' - }] - }) - }) + .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { body: JSON.stringify({ rows: [{ _id: 'pdfNotReady', sharedId: 'shared', unwantedProp: 'unwanted' }] }) }) .get(`${APIURL}references/by_document/targetId`, { body: JSON.stringify([{ connectedDocument: '1' }]) }); }); From 999557ba11b90f16f56e25f47bf50639a146d4a8 Mon Sep 17 00:00:00 2001 From: Daneryl Date: Thu, 22 Nov 2018 17:25:52 +0100 Subject: [PATCH 6/6] fix circular dependencies --- app/react/Viewer/actions/documentActions.js | 19 +++++++++---------- .../actions/specs/documentActions.spec.js | 11 ++++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/react/Viewer/actions/documentActions.js b/app/react/Viewer/actions/documentActions.js index 723faef83d..a41d408069 100644 --- a/app/react/Viewer/actions/documentActions.js +++ b/app/react/Viewer/actions/documentActions.js @@ -1,20 +1,19 @@ -import { actions as formActions } from 'react-redux-form'; +import api from 'app/utils/api'; +import referencesAPI from 'app/Viewer/referencesAPI'; +import * as types from 'app/Viewer/actions/actionTypes'; +import * as connectionsTypes from 'app/Connections/actions/actionTypes'; import { APIURL } from 'app/config.js'; +import { PDFUtils } from '../../PDF/'; import { actions } from 'app/BasicReducer'; -import { isClient } from 'app/utils'; +import { actions as formActions } from 'react-redux-form'; +import documents from 'app/Documents'; import { notify } from 'app/Notifications'; -import { actions as relationshipActions } from 'app/Relationships'; import { removeDocument, unselectAllDocuments } from 'app/Library/actions/libraryActions'; -import api from 'app/utils/api'; -import * as connectionsTypes from 'app/Connections/actions/actionTypes'; -import documents from 'app/Documents'; - -import { PDFUtils } from '../../PDF/'; -import referencesAPI from '../referencesAPI'; import * as selectionActions from './selectionActions'; -import * as types from './actionTypes'; import * as uiActions from './uiActions'; +import { isClient } from 'app/utils'; +import { actions as relationshipActions } from 'app/Relationships'; export function setDocument(document, html) { return { diff --git a/app/react/Viewer/actions/specs/documentActions.spec.js b/app/react/Viewer/actions/specs/documentActions.spec.js index 232e182614..9f4a4db14d 100644 --- a/app/react/Viewer/actions/specs/documentActions.spec.js +++ b/app/react/Viewer/actions/specs/documentActions.spec.js @@ -1,20 +1,19 @@ /* eslint-disable max-nested-callbacks */ -import { actions as formActions } from 'react-redux-form'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import backend from 'fetch-mock'; import Immutable from 'immutable'; import api from 'app/utils/api'; +import { PDFUtils } from '../../../PDF/'; import { mockID } from 'shared/uniqueID.js'; import documents from 'app/Documents'; import { APIURL } from 'app/config.js'; import * as notificationsTypes from 'app/Notifications/actions/actionTypes'; -import { actions as relationshipActions } from 'app/Relationships'; - -import { PDFUtils } from '../../../PDF/'; import * as actions from '../documentActions'; import * as types from '../actionTypes'; +import { actions as formActions } from 'react-redux-form'; +import { actions as relationshipActions } from 'app/Relationships'; const middlewares = [thunk]; const mockStore = configureMockStore(middlewares); @@ -156,7 +155,9 @@ describe('documentActions', () => { .get(`${APIURL}documents/search?searchTerm=term&fields=%5B%22field%22%5D`, { body: JSON.stringify('documents') }) .get(`${APIURL}entities?_id=targetId`, { body: JSON.stringify({ rows: [{ target: 'document', pdfInfo: 'test' }] }) }) .get(`${APIURL}entities?_id=docWithPDFRdy`, { body: JSON.stringify({ rows: [{ pdfInfo: 'processed pdf', _id: 'pdfReady' }] }) }) - .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { body: JSON.stringify({ rows: [{ _id: 'pdfNotReady', sharedId: 'shared', unwantedProp: 'unwanted' }] }) }) + .get(`${APIURL}entities?_id=docWithPDFNotRdy`, { + body: JSON.stringify({ rows: [{ _id: 'pdfNotReady', sharedId: 'shared', unwantedProp: 'unwanted' }] }) + }) .get(`${APIURL}references/by_document/targetId`, { body: JSON.stringify([{ connectedDocument: '1' }]) }); });