Unverified Commit 599e8d75 authored by Kyle Keating's avatar Kyle Keating Committed by GitHub

Merge pull request #1350 from hypothesis/namespace-annotations-module

Namespace annotations module
parents e1cb9919 9b46e378
...@@ -45,7 +45,7 @@ function SidebarContentController( ...@@ -45,7 +45,7 @@ function SidebarContentController(
const id = Object.keys( const id = Object.keys(
store.getRootState().selection.selectedAnnotationMap store.getRootState().selection.selectedAnnotationMap
)[0]; )[0];
return store.getState().annotations.find(function(annot) { return store.getRootState().annotations.annotations.find(function(annot) {
return annot.id === id; return annot.id === id;
}); });
} else { } else {
......
...@@ -5,7 +5,7 @@ const angular = require('angular'); ...@@ -5,7 +5,7 @@ const angular = require('angular');
const events = require('../events'); const events = require('../events');
function getExistingAnnotation(store, id) { function getExistingAnnotation(store, id) {
return store.getState().annotations.find(function(annot) { return store.getRootState().annotations.annotations.find(function(annot) {
return annot.id === id; return annot.id === id;
}); });
} }
......
...@@ -56,9 +56,9 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) { ...@@ -56,9 +56,9 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) {
let prevPublicAnns = 0; let prevPublicAnns = 0;
store.subscribe(function() { store.subscribe(function() {
const state = store.getState(); const state = store.getRootState();
if ( if (
state.annotations === prevAnnotations && state.annotations.annotations === prevAnnotations &&
state.frames === prevFrames state.frames === prevFrames
) { ) {
return; return;
...@@ -68,7 +68,7 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) { ...@@ -68,7 +68,7 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) {
const inSidebar = new Set(); const inSidebar = new Set();
const added = []; const added = [];
state.annotations.forEach(function(annot) { state.annotations.annotations.forEach(function(annot) {
if (metadata.isReply(annot)) { if (metadata.isReply(annot)) {
// The frame does not need to know about replies // The frame does not need to know about replies
return; return;
...@@ -86,7 +86,7 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) { ...@@ -86,7 +86,7 @@ function FrameSync($rootScope, $window, Discovery, store, bridge) {
const deleted = prevAnnotations.filter(function(annot) { const deleted = prevAnnotations.filter(function(annot) {
return !inSidebar.has(annot.$tag); return !inSidebar.has(annot.$tag);
}); });
prevAnnotations = state.annotations; prevAnnotations = state.annotations.annotations;
prevFrames = state.frames; prevFrames = state.frames;
// We currently only handle adding and removing annotations from the frame // We currently only handle adding and removing annotations from the frame
......
...@@ -83,7 +83,7 @@ function RootThread($rootScope, store, searchFilter, viewFilter) { ...@@ -83,7 +83,7 @@ function RootThread($rootScope, store, searchFilter, viewFilter) {
// Get the currently loaded annotations and the set of inputs which // Get the currently loaded annotations and the set of inputs which
// determines what is visible and build the visible thread structure // determines what is visible and build the visible thread structure
return buildThread(state.base.annotations, { return buildThread(state.annotations.annotations, {
forceVisible: truthyKeys(state.selection.forceVisible), forceVisible: truthyKeys(state.selection.forceVisible),
expanded: state.selection.expanded, expanded: state.selection.expanded,
highlighted: state.selection.highlighted, highlighted: state.selection.highlighted,
...@@ -127,8 +127,8 @@ function RootThread($rootScope, store, searchFilter, viewFilter) { ...@@ -127,8 +127,8 @@ function RootThread($rootScope, store, searchFilter, viewFilter) {
// logic in this event handler can be moved to the annotations reducer. // logic in this event handler can be moved to the annotations reducer.
$rootScope.$on(events.GROUP_FOCUSED, function(event, focusedGroupId) { $rootScope.$on(events.GROUP_FOCUSED, function(event, focusedGroupId) {
const updatedAnnots = store const updatedAnnots = store
.getState() .getRootState()
.annotations.filter(function(ann) { .annotations.annotations.filter(function(ann) {
return metadata.isNew(ann) && !metadata.isReply(ann); return metadata.isNew(ann) && !metadata.isReply(ann);
}) })
.map(function(ann) { .map(function(ann) {
......
...@@ -110,7 +110,9 @@ describe('sidebar.frame-sync', function() { ...@@ -110,7 +110,9 @@ describe('sidebar.frame-sync', function() {
context('when annotations are loaded into the sidebar', function() { context('when annotations are loaded into the sidebar', function() {
it('sends a "loadAnnotations" message to the frame', function() { it('sends a "loadAnnotations" message to the frame', function() {
fakeStore.setState({ annotations: [fixtures.ann] }); fakeStore.setState({
annotations: { annotations: [fixtures.ann] },
});
assert.calledWithMatch( assert.calledWithMatch(
fakeBridge.call, fakeBridge.call,
'loadAnnotations', 'loadAnnotations',
...@@ -120,10 +122,14 @@ describe('sidebar.frame-sync', function() { ...@@ -120,10 +122,14 @@ describe('sidebar.frame-sync', function() {
it('sends a "loadAnnotations" message only for new annotations', function() { it('sends a "loadAnnotations" message only for new annotations', function() {
const ann2 = Object.assign({}, fixtures.ann, { $tag: 't2', id: 'a2' }); const ann2 = Object.assign({}, fixtures.ann, { $tag: 't2', id: 'a2' });
fakeStore.setState({ annotations: [fixtures.ann] }); fakeStore.setState({
annotations: { annotations: [fixtures.ann] },
});
fakeBridge.call.reset(); fakeBridge.call.reset();
fakeStore.setState({ annotations: [fixtures.ann, ann2] }); fakeStore.setState({
annotations: { annotations: [fixtures.ann, ann2] },
});
assert.calledWithMatch( assert.calledWithMatch(
fakeBridge.call, fakeBridge.call,
...@@ -133,7 +139,9 @@ describe('sidebar.frame-sync', function() { ...@@ -133,7 +139,9 @@ describe('sidebar.frame-sync', function() {
}); });
it('does not send a "loadAnnotations" message for replies', function() { it('does not send a "loadAnnotations" message for replies', function() {
fakeStore.setState({ annotations: [annotationFixtures.newReply()] }); fakeStore.setState({
annotations: { annotations: [annotationFixtures.newReply()] },
});
assert.isFalse(fakeBridge.call.calledWith('loadAnnotations')); assert.isFalse(fakeBridge.call.calledWith('loadAnnotations'));
}); });
}); });
...@@ -141,7 +149,7 @@ describe('sidebar.frame-sync', function() { ...@@ -141,7 +149,7 @@ describe('sidebar.frame-sync', function() {
context('when annotation count has changed', function() { context('when annotation count has changed', function() {
it('sends a "publicAnnotationCountChanged" message to the frame when there are public annotations', function() { it('sends a "publicAnnotationCountChanged" message to the frame when there are public annotations', function() {
fakeStore.setState({ fakeStore.setState({
annotations: [annotationFixtures.publicAnnotation()], annotations: { annotations: [annotationFixtures.publicAnnotation()] },
}); });
assert.calledWithMatch( assert.calledWithMatch(
fakeBridge.call, fakeBridge.call,
...@@ -152,7 +160,7 @@ describe('sidebar.frame-sync', function() { ...@@ -152,7 +160,7 @@ describe('sidebar.frame-sync', function() {
it('sends a "publicAnnotationCountChanged" message to the frame when there are only private annotations', function() { it('sends a "publicAnnotationCountChanged" message to the frame when there are only private annotations', function() {
fakeStore.setState({ fakeStore.setState({
annotations: [annotationFixtures.defaultAnnotation()], annotations: { annotations: [annotationFixtures.defaultAnnotation()] },
}); });
assert.calledWithMatch( assert.calledWithMatch(
fakeBridge.call, fakeBridge.call,
...@@ -164,7 +172,7 @@ describe('sidebar.frame-sync', function() { ...@@ -164,7 +172,7 @@ describe('sidebar.frame-sync', function() {
it('does not send a "publicAnnotationCountChanged" message to the frame if annotation fetch is not complete', function() { it('does not send a "publicAnnotationCountChanged" message to the frame if annotation fetch is not complete', function() {
fakeStore.frames.returns([{ uri: 'http://example.com' }]); fakeStore.frames.returns([{ uri: 'http://example.com' }]);
fakeStore.setState({ fakeStore.setState({
annotations: [annotationFixtures.publicAnnotation()], annotations: { annotations: [annotationFixtures.publicAnnotation()] },
}); });
assert.isFalse( assert.isFalse(
fakeBridge.call.calledWith('publicAnnotationCountChanged') fakeBridge.call.calledWith('publicAnnotationCountChanged')
...@@ -174,7 +182,7 @@ describe('sidebar.frame-sync', function() { ...@@ -174,7 +182,7 @@ describe('sidebar.frame-sync', function() {
it('does not send a "publicAnnotationCountChanged" message if there are no connected frames', function() { it('does not send a "publicAnnotationCountChanged" message if there are no connected frames', function() {
fakeStore.frames.returns([]); fakeStore.frames.returns([]);
fakeStore.setState({ fakeStore.setState({
annotations: [annotationFixtures.publicAnnotation()], annotations: { annotations: [annotationFixtures.publicAnnotation()] },
}); });
assert.isFalse( assert.isFalse(
fakeBridge.call.calledWith('publicAnnotationCountChanged') fakeBridge.call.calledWith('publicAnnotationCountChanged')
...@@ -184,8 +192,12 @@ describe('sidebar.frame-sync', function() { ...@@ -184,8 +192,12 @@ describe('sidebar.frame-sync', function() {
context('when annotations are removed from the sidebar', function() { context('when annotations are removed from the sidebar', function() {
it('sends a "deleteAnnotation" message to the frame', function() { it('sends a "deleteAnnotation" message to the frame', function() {
fakeStore.setState({ annotations: [fixtures.ann] }); fakeStore.setState({
fakeStore.setState({ annotations: [] }); annotations: { annotations: [fixtures.ann] },
});
fakeStore.setState({
annotations: { annotations: [] },
});
assert.calledWithMatch( assert.calledWithMatch(
fakeBridge.call, fakeBridge.call,
'deleteAnnotation', 'deleteAnnotation',
......
...@@ -34,7 +34,7 @@ describe('rootThread', function() { ...@@ -34,7 +34,7 @@ describe('rootThread', function() {
beforeEach(function() { beforeEach(function() {
fakeStore = { fakeStore = {
state: { state: {
base: { annotations: {
annotations: [], annotations: [],
}, },
viewer: { viewer: {
...@@ -53,8 +53,8 @@ describe('rootThread', function() { ...@@ -53,8 +53,8 @@ describe('rootThread', function() {
sortKeysAvailable: ['Location'], sortKeysAvailable: ['Location'],
}, },
}, },
getState: function() { getRootState: function() {
return this.state.base; return this.state;
}, },
subscribe: sinon.stub(), subscribe: sinon.stub(),
...@@ -115,7 +115,7 @@ describe('rootThread', function() { ...@@ -115,7 +115,7 @@ describe('rootThread', function() {
it('passes loaded annotations to buildThread()', function() { it('passes loaded annotations to buildThread()', function() {
const annotation = annotationFixtures.defaultAnnotation(); const annotation = annotationFixtures.defaultAnnotation();
fakeStore.state.base.annotations = [annotation]; fakeStore.state.annotations.annotations = [annotation];
rootThread.thread(fakeStore.state); rootThread.thread(fakeStore.state);
assert.calledWith(fakeBuildThread, sinon.match([annotation])); assert.calledWith(fakeBuildThread, sinon.match([annotation]));
}); });
...@@ -389,7 +389,7 @@ describe('rootThread', function() { ...@@ -389,7 +389,7 @@ describe('rootThread', function() {
$rootScope.$on(events.ANNOTATION_DELETED, onDelete); $rootScope.$on(events.ANNOTATION_DELETED, onDelete);
existingNewAnnot = { $tag: 'a-new-tag' }; existingNewAnnot = { $tag: 'a-new-tag' };
fakeStore.state.base.annotations.push(existingNewAnnot); fakeStore.state.annotations.annotations.push(existingNewAnnot);
}); });
it('does not remove annotations that have non-empty drafts', function() { it('does not remove annotations that have non-empty drafts', function() {
...@@ -405,7 +405,7 @@ describe('rootThread', function() { ...@@ -405,7 +405,7 @@ describe('rootThread', function() {
it('does not remove saved annotations', function() { it('does not remove saved annotations', function() {
const ann = annotationFixtures.defaultAnnotation(); const ann = annotationFixtures.defaultAnnotation();
fakeStore.state.base.annotations = [ann]; fakeStore.state.annotations.annotations = [ann];
$rootScope.$broadcast( $rootScope.$broadcast(
events.BEFORE_ANNOTATION_CREATED, events.BEFORE_ANNOTATION_CREATED,
...@@ -420,7 +420,7 @@ describe('rootThread', function() { ...@@ -420,7 +420,7 @@ describe('rootThread', function() {
describe('when the focused group changes', function() { describe('when the focused group changes', function() {
it('moves new annotations to the focused group', function() { it('moves new annotations to the focused group', function() {
fakeStore.state.base.annotations = [{ $tag: 'a-tag' }]; fakeStore.state.annotations.annotations = [{ $tag: 'a-tag' }];
$rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group'); $rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group');
...@@ -436,7 +436,7 @@ describe('rootThread', function() { ...@@ -436,7 +436,7 @@ describe('rootThread', function() {
}); });
it('does not move replies to the new group', function() { it('does not move replies to the new group', function() {
fakeStore.state.annotations = [annotationFixtures.newReply()]; fakeStore.state.annotations.annotations = [annotationFixtures.newReply()];
$rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group'); $rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group');
...@@ -444,7 +444,9 @@ describe('rootThread', function() { ...@@ -444,7 +444,9 @@ describe('rootThread', function() {
}); });
it('does not move saved annotations to the new group', function() { it('does not move saved annotations to the new group', function() {
fakeStore.state.annotations = [annotationFixtures.defaultAnnotation()]; fakeStore.state.annotations.annotations = [
annotationFixtures.defaultAnnotation(),
];
$rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group'); $rootScope.$broadcast(events.GROUP_FOCUSED, 'private-group');
......
...@@ -243,13 +243,13 @@ function addAnnotations(annotations, now) { ...@@ -243,13 +243,13 @@ function addAnnotations(annotations, now) {
return function(dispatch, getState) { return function(dispatch, getState) {
const added = annotations.filter(function(annot) { const added = annotations.filter(function(annot) {
return !findByID(getState().base.annotations, annot.id); return !findByID(getState().annotations.annotations, annot.id);
}); });
dispatch({ dispatch({
type: actions.ADD_ANNOTATIONS, type: actions.ADD_ANNOTATIONS,
annotations: annotations, annotations: annotations,
currentAnnotationCount: getState().base.annotations.length, currentAnnotationCount: getState().annotations.annotations.length,
}); });
if (!getState().viewer.isSidebar) { if (!getState().viewer.isSidebar) {
...@@ -267,7 +267,7 @@ function addAnnotations(annotations, now) { ...@@ -267,7 +267,7 @@ function addAnnotations(annotations, now) {
if (anchoringIDs.length > 0) { if (anchoringIDs.length > 0) {
setTimeout(() => { setTimeout(() => {
// Find annotations which haven't yet been anchored in the document. // Find annotations which haven't yet been anchored in the document.
const anns = getState().base.annotations; const anns = getState().annotations.annotations;
const annsStillAnchoring = anchoringIDs const annsStillAnchoring = anchoringIDs
.map(id => findByID(anns, id)) .map(id => findByID(anns, id))
.filter(ann => ann && metadata.isWaitingToAnchor(ann)); .filter(ann => ann && metadata.isWaitingToAnchor(ann));
...@@ -290,7 +290,7 @@ function addAnnotations(annotations, now) { ...@@ -290,7 +290,7 @@ function addAnnotations(annotations, now) {
function removeAnnotations(annotations) { function removeAnnotations(annotations) {
return (dispatch, getState) => { return (dispatch, getState) => {
const remainingAnnotations = excludeAnnotations( const remainingAnnotations = excludeAnnotations(
getState().base.annotations, getState().annotations.annotations,
annotations annotations
); );
dispatch({ dispatch({
...@@ -380,14 +380,14 @@ function unhideAnnotation(id) { ...@@ -380,14 +380,14 @@ function unhideAnnotation(id) {
* @param {state} - The global app state * @param {state} - The global app state
*/ */
function savedAnnotations(state) { function savedAnnotations(state) {
return state.annotations.filter(function(ann) { return state.annotations.annotations.filter(function(ann) {
return !metadata.isNew(ann); return !metadata.isNew(ann);
}); });
} }
/** Return true if the annotation with a given ID is currently loaded. */ /** Return true if the annotation with a given ID is currently loaded. */
function annotationExists(state, id) { function annotationExists(state, id) {
return state.annotations.some(function(annot) { return state.annotations.annotations.some(function(annot) {
return annot.id === id; return annot.id === id;
}); });
} }
...@@ -403,7 +403,7 @@ function annotationExists(state, id) { ...@@ -403,7 +403,7 @@ function annotationExists(state, id) {
function findIDsForTags(state, tags) { function findIDsForTags(state, tags) {
const ids = []; const ids = [];
tags.forEach(function(tag) { tags.forEach(function(tag) {
const annot = findByTag(state.annotations, tag); const annot = findByTag(state.annotations.annotations, tag);
if (annot && annot.id) { if (annot && annot.id) {
ids.push(annot.id); ids.push(annot.id);
} }
...@@ -415,14 +415,14 @@ function findIDsForTags(state, tags) { ...@@ -415,14 +415,14 @@ function findIDsForTags(state, tags) {
* Return the annotation with the given ID. * Return the annotation with the given ID.
*/ */
function findAnnotationByID(state, id) { function findAnnotationByID(state, id) {
return findByID(state.annotations, id); return findByID(state.annotations.annotations, id);
} }
/** /**
* Return the number of page notes. * Return the number of page notes.
*/ */
const noteCount = createSelector( const noteCount = createSelector(
state => state.annotations, state => state.annotations.annotations,
annotations => arrayUtil.countIf(annotations, metadata.isPageNote) annotations => arrayUtil.countIf(annotations, metadata.isPageNote)
); );
...@@ -430,7 +430,7 @@ const noteCount = createSelector( ...@@ -430,7 +430,7 @@ const noteCount = createSelector(
* Returns the number of annotations (as opposed to notes or orphans). * Returns the number of annotations (as opposed to notes or orphans).
*/ */
const annotationCount = createSelector( const annotationCount = createSelector(
state => state.annotations, state => state.annotations.annotations,
annotations => arrayUtil.countIf(annotations, metadata.isAnnotation) annotations => arrayUtil.countIf(annotations, metadata.isAnnotation)
); );
...@@ -438,7 +438,7 @@ const annotationCount = createSelector( ...@@ -438,7 +438,7 @@ const annotationCount = createSelector(
* Returns the number of orphaned annotations. * Returns the number of orphaned annotations.
*/ */
const orphanCount = createSelector( const orphanCount = createSelector(
state => state.annotations, state => state.annotations.annotations,
annotations => arrayUtil.countIf(annotations, metadata.isOrphan) annotations => arrayUtil.countIf(annotations, metadata.isOrphan)
); );
...@@ -446,12 +446,13 @@ const orphanCount = createSelector( ...@@ -446,12 +446,13 @@ const orphanCount = createSelector(
* Returns true if some annotations have not been anchored yet. * Returns true if some annotations have not been anchored yet.
*/ */
const isWaitingToAnchorAnnotations = createSelector( const isWaitingToAnchorAnnotations = createSelector(
state => state.annotations, state => state.annotations.annotations,
annotations => annotations.some(metadata.isWaitingToAnchor) annotations => annotations.some(metadata.isWaitingToAnchor)
); );
module.exports = { module.exports = {
init: init, init: init,
namespace: 'annotations',
update: update, update: update,
actions: { actions: {
addAnnotations, addAnnotations,
......
...@@ -113,7 +113,7 @@ function receiveRealTimeUpdates({ ...@@ -113,7 +113,7 @@ function receiveRealTimeUpdates({
// even if the annotation is from the current group, it might be for a // even if the annotation is from the current group, it might be for a
// new annotation (saved in pendingUpdates and removed above), that has // new annotation (saved in pendingUpdates and removed above), that has
// not yet been loaded. // not yet been loaded.
if (annotationSelectors.annotationExists(getState().base, ann.id)) { if (annotationSelectors.annotationExists(getState(), ann.id)) {
pendingDeletions[ann.id] = true; pendingDeletions[ann.id] = true;
} }
}); });
......
...@@ -7,7 +7,6 @@ const fixtures = require('../../../test/annotation-fixtures'); ...@@ -7,7 +7,6 @@ const fixtures = require('../../../test/annotation-fixtures');
const selection = require('../selection'); const selection = require('../selection');
const viewer = require('../viewer'); const viewer = require('../viewer');
const uiConstants = require('../../../ui-constants'); const uiConstants = require('../../../ui-constants');
const unroll = require('../../../../shared/test/util').unroll;
const { actions, selectors } = annotations; const { actions, selectors } = annotations;
...@@ -21,24 +20,28 @@ function createStore() { ...@@ -21,24 +20,28 @@ function createStore() {
// Tests for most of the functionality in reducers/annotations.js are currently // Tests for most of the functionality in reducers/annotations.js are currently
// in the tests for the whole Redux store // in the tests for the whole Redux store
describe('annotations reducer', function() { describe('sidebar/store/modules/annotations', function() {
describe('isWaitingToAnchorAnnotations', () => { describe('isWaitingToAnchorAnnotations', () => {
it('returns true if there are unanchored annotations', () => { it('returns true if there are unanchored annotations', () => {
const unanchored = Object.assign(fixtures.oldAnnotation(), { const unanchored = Object.assign(fixtures.oldAnnotation(), {
$orphan: 'undefined', $orphan: 'undefined',
}); });
const state = { const state = {
annotations: [unanchored, fixtures.defaultAnnotation()], annotations: {
annotations: [unanchored, fixtures.defaultAnnotation()],
},
}; };
assert.isTrue(selectors.isWaitingToAnchorAnnotations(state)); assert.isTrue(selectors.isWaitingToAnchorAnnotations(state));
}); });
it('returns false if all annotations are anchored', () => { it('returns false if all annotations are anchored', () => {
const state = { const state = {
annotations: [ annotations: {
Object.assign(fixtures.oldPageNote(), { $orphan: false }), annotations: [
Object.assign(fixtures.defaultAnnotation(), { $orphan: false }), Object.assign(fixtures.oldPageNote(), { $orphan: false }),
], Object.assign(fixtures.defaultAnnotation(), { $orphan: false }),
],
},
}; };
assert.isFalse(selectors.isWaitingToAnchorAnnotations(state)); assert.isFalse(selectors.isWaitingToAnchorAnnotations(state));
}); });
...@@ -47,11 +50,13 @@ describe('annotations reducer', function() { ...@@ -47,11 +50,13 @@ describe('annotations reducer', function() {
describe('noteCount', () => { describe('noteCount', () => {
it('returns number of page notes', () => { it('returns number of page notes', () => {
const state = { const state = {
annotations: [ annotations: {
fixtures.oldPageNote(), annotations: [
fixtures.oldAnnotation(), fixtures.oldPageNote(),
fixtures.defaultAnnotation(), fixtures.oldAnnotation(),
], fixtures.defaultAnnotation(),
],
},
}; };
assert.deepEqual(selectors.noteCount(state), 1); assert.deepEqual(selectors.noteCount(state), 1);
}); });
...@@ -60,11 +65,13 @@ describe('annotations reducer', function() { ...@@ -60,11 +65,13 @@ describe('annotations reducer', function() {
describe('annotationCount', () => { describe('annotationCount', () => {
it('returns number of annotations', () => { it('returns number of annotations', () => {
const state = { const state = {
annotations: [ annotations: {
fixtures.oldPageNote(), annotations: [
fixtures.oldAnnotation(), fixtures.oldPageNote(),
fixtures.defaultAnnotation(), fixtures.oldAnnotation(),
], fixtures.defaultAnnotation(),
],
},
}; };
assert.deepEqual(selectors.annotationCount(state), 2); assert.deepEqual(selectors.annotationCount(state), 2);
}); });
...@@ -74,11 +81,13 @@ describe('annotations reducer', function() { ...@@ -74,11 +81,13 @@ describe('annotations reducer', function() {
it('returns number of orphaned annotations', () => { it('returns number of orphaned annotations', () => {
const orphan = Object.assign(fixtures.oldAnnotation(), { $orphan: true }); const orphan = Object.assign(fixtures.oldAnnotation(), { $orphan: true });
const state = { const state = {
annotations: [ annotations: {
orphan, annotations: [
fixtures.oldAnnotation(), orphan,
fixtures.defaultAnnotation(), fixtures.oldAnnotation(),
], fixtures.defaultAnnotation(),
],
},
}; };
assert.deepEqual(selectors.orphanCount(state), 1); assert.deepEqual(selectors.orphanCount(state), 1);
}); });
...@@ -89,7 +98,9 @@ describe('annotations reducer', function() { ...@@ -89,7 +98,9 @@ describe('annotations reducer', function() {
it('returns annotations which are saved', function() { it('returns annotations which are saved', function() {
const state = { const state = {
annotations: [fixtures.newAnnotation(), fixtures.defaultAnnotation()], annotations: {
annotations: [fixtures.newAnnotation(), fixtures.defaultAnnotation()],
},
}; };
assert.deepEqual(savedAnnotations(state), [fixtures.defaultAnnotation()]); assert.deepEqual(savedAnnotations(state), [fixtures.defaultAnnotation()]);
}); });
...@@ -101,7 +112,9 @@ describe('annotations reducer', function() { ...@@ -101,7 +112,9 @@ describe('annotations reducer', function() {
it('returns the IDs corresponding to the provided local tags', function() { it('returns the IDs corresponding to the provided local tags', function() {
const ann = fixtures.defaultAnnotation(); const ann = fixtures.defaultAnnotation();
const state = { const state = {
annotations: [Object.assign(ann, { $tag: 't1' })], annotations: {
annotations: [Object.assign(ann, { $tag: 't1' })],
},
}; };
assert.deepEqual(findIDsForTags(state, ['t1']), [ann.id]); assert.deepEqual(findIDsForTags(state, ['t1']), [ann.id]);
}); });
...@@ -109,7 +122,9 @@ describe('annotations reducer', function() { ...@@ -109,7 +122,9 @@ describe('annotations reducer', function() {
it('does not return IDs for annotations that do not have an ID', function() { it('does not return IDs for annotations that do not have an ID', function() {
const ann = fixtures.newAnnotation(); const ann = fixtures.newAnnotation();
const state = { const state = {
annotations: [Object.assign(ann, { $tag: 't1' })], annotations: {
annotations: [Object.assign(ann, { $tag: 't1' })],
},
}; };
assert.deepEqual(findIDsForTags(state, ['t1']), []); assert.deepEqual(findIDsForTags(state, ['t1']), []);
}); });
...@@ -123,7 +138,10 @@ describe('annotations reducer', function() { ...@@ -123,7 +138,10 @@ describe('annotations reducer', function() {
store.dispatch(actions.addAnnotations([ann])); store.dispatch(actions.addAnnotations([ann]));
store.dispatch(actions.hideAnnotation(ann.id)); store.dispatch(actions.hideAnnotation(ann.id));
const storeAnn = selectors.findAnnotationByID(store.getState(), ann.id); const storeAnn = selectors.findAnnotationByID(
store.getRootState(),
ann.id
);
assert.equal(storeAnn.hidden, true); assert.equal(storeAnn.hidden, true);
}); });
}); });
...@@ -136,7 +154,10 @@ describe('annotations reducer', function() { ...@@ -136,7 +154,10 @@ describe('annotations reducer', function() {
store.dispatch(actions.addAnnotations([ann])); store.dispatch(actions.addAnnotations([ann]));
store.dispatch(actions.unhideAnnotation(ann.id)); store.dispatch(actions.unhideAnnotation(ann.id));
const storeAnn = selectors.findAnnotationByID(store.getState(), ann.id); const storeAnn = selectors.findAnnotationByID(
store.getRootState(),
ann.id
);
assert.equal(storeAnn.hidden, false); assert.equal(storeAnn.hidden, false);
}); });
}); });
...@@ -147,14 +168,56 @@ describe('annotations reducer', function() { ...@@ -147,14 +168,56 @@ describe('annotations reducer', function() {
const ann = fixtures.defaultAnnotation(); const ann = fixtures.defaultAnnotation();
store.dispatch(actions.addAnnotations([ann])); store.dispatch(actions.addAnnotations([ann]));
store.dispatch(actions.removeAnnotations([ann])); store.dispatch(actions.removeAnnotations([ann]));
assert.equal(store.getState().annotations.length, 0); assert.equal(store.getRootState().annotations.annotations.length, 0);
}); });
}); });
describe('#updateFlagStatus', function() { describe('#updateFlagStatus', function() {
unroll( [
'updates the flagged status of an annotation', {
function(testCase) { description: 'non-moderator flags annotation',
wasFlagged: false,
nowFlagged: true,
oldModeration: undefined,
newModeration: undefined,
},
{
description: 'non-moderator un-flags an annotation',
wasFlagged: true,
nowFlagged: false,
oldModeration: undefined,
newModeration: undefined,
},
{
description: 'moderator un-flags an already un-flagged annotation',
wasFlagged: false,
nowFlagged: false,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 1 },
},
{
description: 'moderator flags an already flagged annotation',
wasFlagged: true,
nowFlagged: true,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 1 },
},
{
description: 'moderator flags an annotation',
wasFlagged: false,
nowFlagged: true,
oldModeration: { flagCount: 0 },
newModeration: { flagCount: 1 },
},
{
description: 'moderator un-flags an annotation',
wasFlagged: true,
nowFlagged: false,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 0 },
},
].forEach(testCase => {
it(`updates the flagged status of an annotation when a ${testCase.description}`, () => {
const store = createStore(); const store = createStore();
const ann = fixtures.defaultAnnotation(); const ann = fixtures.defaultAnnotation();
ann.flagged = testCase.wasFlagged; ann.flagged = testCase.wasFlagged;
...@@ -163,55 +226,14 @@ describe('annotations reducer', function() { ...@@ -163,55 +226,14 @@ describe('annotations reducer', function() {
store.dispatch(actions.addAnnotations([ann])); store.dispatch(actions.addAnnotations([ann]));
store.dispatch(actions.updateFlagStatus(ann.id, testCase.nowFlagged)); store.dispatch(actions.updateFlagStatus(ann.id, testCase.nowFlagged));
const storeAnn = selectors.findAnnotationByID(store.getState(), ann.id); const storeAnn = selectors.findAnnotationByID(
store.getRootState(),
ann.id
);
assert.equal(storeAnn.flagged, testCase.nowFlagged); assert.equal(storeAnn.flagged, testCase.nowFlagged);
assert.deepEqual(storeAnn.moderation, testCase.newModeration); assert.deepEqual(storeAnn.moderation, testCase.newModeration);
}, });
[ });
{
// Non-moderator flags annotation
wasFlagged: false,
nowFlagged: true,
oldModeration: undefined,
newModeration: undefined,
},
{
// Non-moderator un-flags annotation
wasFlagged: true,
nowFlagged: false,
oldModeration: undefined,
newModeration: undefined,
},
{
// Moderator un-flags an already unflagged annotation
wasFlagged: false,
nowFlagged: false,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 1 },
},
{
// Moderator flags an already flagged annotation
wasFlagged: true,
nowFlagged: true,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 1 },
},
{
// Moderator flags annotation
wasFlagged: false,
nowFlagged: true,
oldModeration: { flagCount: 0 },
newModeration: { flagCount: 1 },
},
{
// Moderator un-flags annotation
wasFlagged: true,
nowFlagged: false,
oldModeration: { flagCount: 1 },
newModeration: { flagCount: 0 },
},
]
);
}); });
describe('#createAnnotation', function() { describe('#createAnnotation', function() {
...@@ -220,7 +242,7 @@ describe('annotations reducer', function() { ...@@ -220,7 +242,7 @@ describe('annotations reducer', function() {
const ann = fixtures.oldAnnotation(); const ann = fixtures.oldAnnotation();
store.dispatch(actions.createAnnotation(ann)); store.dispatch(actions.createAnnotation(ann));
assert.equal( assert.equal(
selectors.findAnnotationByID(store.getState(), ann.id).id, selectors.findAnnotationByID(store.getRootState(), ann.id).id,
ann.id ann.id
); );
}); });
......
...@@ -144,7 +144,9 @@ describe('store', function() { ...@@ -144,7 +144,9 @@ describe('store', function() {
it('adds annotations not in the store', function() { it('adds annotations not in the store', function() {
const annot = defaultAnnotation(); const annot = defaultAnnotation();
store.addAnnotations([annot]); store.addAnnotations([annot]);
assert.match(store.getState().annotations, [sinon.match(annot)]); assert.match(store.getRootState().annotations.annotations, [
sinon.match(annot),
]);
}); });
it('does not change `selectedTab` state if annotations are already loaded', function() { it('does not change `selectedTab` state if annotations are already loaded', function() {
...@@ -183,9 +185,11 @@ describe('store', function() { ...@@ -183,9 +185,11 @@ describe('store', function() {
store.addAnnotations([annotA, annotB]); store.addAnnotations([annotA, annotB]);
const tags = store.getState().annotations.map(function(a) { const tags = store
return a.$tag; .getRootState()
}); .annotations.annotations.map(function(a) {
return a.$tag;
});
assert.deepEqual(tags, ['t1', 't2']); assert.deepEqual(tags, ['t1', 't2']);
}); });
...@@ -196,7 +200,7 @@ describe('store', function() { ...@@ -196,7 +200,7 @@ describe('store', function() {
const update = Object.assign({}, defaultAnnotation(), { text: 'update' }); const update = Object.assign({}, defaultAnnotation(), { text: 'update' });
store.addAnnotations([update]); store.addAnnotations([update]);
const updatedAnnot = store.getState().annotations[0]; const updatedAnnot = store.getRootState().annotations.annotations[0];
assert.equal(updatedAnnot.text, 'update'); assert.equal(updatedAnnot.text, 'update');
}); });
...@@ -208,7 +212,7 @@ describe('store', function() { ...@@ -208,7 +212,7 @@ describe('store', function() {
const saved = Object.assign({}, annot, { id: 'server-id' }); const saved = Object.assign({}, annot, { id: 'server-id' });
store.addAnnotations([saved]); store.addAnnotations([saved]);
const annots = store.getState().annotations; const annots = store.getRootState().annotations.annotations;
assert.equal(annots.length, 1); assert.equal(annots.length, 1);
assert.equal(annots[0].id, 'server-id'); assert.equal(annots[0].id, 'server-id');
}); });
...@@ -221,7 +225,7 @@ describe('store', function() { ...@@ -221,7 +225,7 @@ describe('store', function() {
const nowStr = now.toISOString(); const nowStr = now.toISOString();
store.addAnnotations([newAnnotation()], now); store.addAnnotations([newAnnotation()], now);
const annot = store.getState().annotations[0]; const annot = store.getRootState().annotations.annotations[0];
assert.equal(annot.created, nowStr); assert.equal(annot.created, nowStr);
assert.equal(annot.updated, nowStr); assert.equal(annot.updated, nowStr);
...@@ -234,7 +238,7 @@ describe('store', function() { ...@@ -234,7 +238,7 @@ describe('store', function() {
annot.updated = '2000-01-01T04:05:06Z'; annot.updated = '2000-01-01T04:05:06Z';
store.addAnnotations([annot], now); store.addAnnotations([annot], now);
const result = store.getState().annotations[0]; const result = store.getRootState().annotations.annotations[0];
assert.equal(result.created, annot.created); assert.equal(result.created, annot.created);
assert.equal(result.updated, annot.updated); assert.equal(result.updated, annot.updated);
...@@ -248,7 +252,7 @@ describe('store', function() { ...@@ -248,7 +252,7 @@ describe('store', function() {
const update = Object.assign({}, defaultAnnotation(), { text: 'update' }); const update = Object.assign({}, defaultAnnotation(), { text: 'update' });
store.addAnnotations([update]); store.addAnnotations([update]);
const updatedAnnot = store.getState().annotations[0]; const updatedAnnot = store.getRootState().annotations.annotations[0];
assert.isFalse(updatedAnnot.$orphan); assert.isFalse(updatedAnnot.$orphan);
}); });
...@@ -258,7 +262,9 @@ describe('store', function() { ...@@ -258,7 +262,9 @@ describe('store', function() {
clock.tick(ANCHOR_TIME_LIMIT); clock.tick(ANCHOR_TIME_LIMIT);
assert.isTrue(store.getState().annotations[0].$anchorTimeout); assert.isTrue(
store.getRootState().annotations.annotations[0].$anchorTimeout
);
}); });
it('does not set the timeout flag on annotations that do anchor within a time limit', function() { it('does not set the timeout flag on annotations that do anchor within a time limit', function() {
...@@ -268,7 +274,9 @@ describe('store', function() { ...@@ -268,7 +274,9 @@ describe('store', function() {
clock.tick(ANCHOR_TIME_LIMIT); clock.tick(ANCHOR_TIME_LIMIT);
assert.isFalse(store.getState().annotations[0].$anchorTimeout); assert.isFalse(
store.getRootState().annotations.annotations[0].$anchorTimeout
);
}); });
it('does not attempt to modify orphan status if annotations are removed before anchoring timeout expires', function() { it('does not attempt to modify orphan status if annotations are removed before anchoring timeout expires', function() {
...@@ -284,7 +292,9 @@ describe('store', function() { ...@@ -284,7 +292,9 @@ describe('store', function() {
it('does not expect annotations to anchor on the stream', function() { it('does not expect annotations to anchor on the stream', function() {
const isOrphan = function() { const isOrphan = function() {
return !!metadata.isOrphan(store.getState().annotations[0]); return !!metadata.isOrphan(
store.getRootState().annotations.annotations[0]
);
}; };
const annot = defaultAnnotation(); const annot = defaultAnnotation();
...@@ -298,14 +308,14 @@ describe('store', function() { ...@@ -298,14 +308,14 @@ describe('store', function() {
it('initializes the $orphan field for new annotations', function() { it('initializes the $orphan field for new annotations', function() {
store.addAnnotations([newAnnotation()]); store.addAnnotations([newAnnotation()]);
assert.isFalse(store.getState().annotations[0].$orphan); assert.isFalse(store.getRootState().annotations.annotations[0].$orphan);
}); });
it('adds multiple new annotations', function() { it('adds multiple new annotations', function() {
store.addAnnotations([fixtures.newPair[0]]); store.addAnnotations([fixtures.newPair[0]]);
store.addAnnotations([fixtures.newPair[1]]); store.addAnnotations([fixtures.newPair[1]]);
assert.equal(store.getState().annotations.length, 2); assert.equal(store.getRootState().annotations.annotations.length, 2);
}); });
}); });
...@@ -314,14 +324,14 @@ describe('store', function() { ...@@ -314,14 +324,14 @@ describe('store', function() {
const annot = defaultAnnotation(); const annot = defaultAnnotation();
store.addAnnotations([annot]); store.addAnnotations([annot]);
store.removeAnnotations([annot]); store.removeAnnotations([annot]);
assert.deepEqual(store.getState().annotations, []); assert.deepEqual(store.getRootState().annotations.annotations, []);
}); });
it('matches annotations to remove by ID', function() { it('matches annotations to remove by ID', function() {
store.addAnnotations(fixtures.pair); store.addAnnotations(fixtures.pair);
store.removeAnnotations([{ id: fixtures.pair[0].id }]); store.removeAnnotations([{ id: fixtures.pair[0].id }]);
const ids = store.getState().annotations.map(function(a) { const ids = store.getRootState().annotations.annotations.map(function(a) {
return a.id; return a.id;
}); });
assert.deepEqual(ids, [fixtures.pair[1].id]); assert.deepEqual(ids, [fixtures.pair[1].id]);
...@@ -331,9 +341,11 @@ describe('store', function() { ...@@ -331,9 +341,11 @@ describe('store', function() {
store.addAnnotations(fixtures.pair); store.addAnnotations(fixtures.pair);
store.removeAnnotations([{ $tag: fixtures.pair[0].$tag }]); store.removeAnnotations([{ $tag: fixtures.pair[0].$tag }]);
const tags = store.getState().annotations.map(function(a) { const tags = store
return a.$tag; .getRootState()
}); .annotations.annotations.map(function(a) {
return a.$tag;
});
assert.deepEqual(tags, [fixtures.pair[1].$tag]); assert.deepEqual(tags, [fixtures.pair[1].$tag]);
}); });
...@@ -354,7 +366,7 @@ describe('store', function() { ...@@ -354,7 +366,7 @@ describe('store', function() {
const annot = defaultAnnotation(); const annot = defaultAnnotation();
store.addAnnotations([annot]); store.addAnnotations([annot]);
store.clearAnnotations(); store.clearAnnotations();
assert.deepEqual(store.getState().annotations, []); assert.deepEqual(store.getRootState().annotations.annotations, []);
}); });
}); });
...@@ -377,7 +389,10 @@ describe('store', function() { ...@@ -377,7 +389,10 @@ describe('store', function() {
const annot = defaultAnnotation(); const annot = defaultAnnotation();
store.addAnnotations([annot]); store.addAnnotations([annot]);
store.updateAnchorStatus({ [tagForID(annot.id)]: 'orphan' }); store.updateAnchorStatus({ [tagForID(annot.id)]: 'orphan' });
assert.equal(store.getState().annotations[0].$orphan, true); assert.equal(
store.getRootState().annotations.annotations[0].$orphan,
true
);
}); });
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment