Commit 60246db2 authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Refactor and update tests

Update tests for the changes to props and callbacks in `AnnotationEditor`
and `AnnotationPublishControl`

There were some mis-nested tests for `AnnotationEditor`; fix.
parent 1cfb11c3
......@@ -12,6 +12,8 @@ import AnnotationEditor, { $imports } from '../AnnotationEditor';
describe('AnnotationEditor', () => {
let fakeApplyTheme;
let fakeAnnotationsService;
let fakeGroup;
let fakeMetadata;
let fakeTagsService;
let fakeSettings;
let fakeToastMessenger;
......@@ -22,6 +24,7 @@ describe('AnnotationEditor', () => {
return mount(
<AnnotationEditor
annotation={fixtures.defaultAnnotation()}
draft={fixtures.defaultDraft()}
annotationsService={fakeAnnotationsService}
settings={fakeSettings}
tags={fakeTagsService}
......@@ -36,6 +39,17 @@ describe('AnnotationEditor', () => {
fakeAnnotationsService = {
save: sinon.stub(),
};
fakeGroup = {
name: 'Fake Group',
type: 'private',
};
fakeMetadata = {
isReply: sinon.stub().returns(false),
isSaved: sinon.stub().returns(true),
};
fakeSettings = {};
fakeTagsService = {
store: sinon.stub(),
......@@ -46,30 +60,31 @@ describe('AnnotationEditor', () => {
fakeStore = {
createDraft: sinon.stub(),
getDraft: sinon.stub().returns(fixtures.defaultDraft()),
getGroup: sinon.stub(),
getGroup: sinon.stub().returns(fakeGroup),
setDefault: sinon.stub(),
removeDraft: sinon.stub(),
removeAnnotations: sinon.stub(),
};
$imports.$mock(mockImportedComponents());
$imports.$mock({
'../../store/use-store': { useStoreProxy: () => fakeStore },
'../../helpers/annotation-metadata': fakeMetadata,
'../../helpers/theme': { applyTheme: fakeApplyTheme },
});
// `AnnotationLicense` is a presentation-only component and is only used
// within `AnnotationEditor`. Not mocking it allows it to be exercised and
// meet code coverage.
$imports.$restore({
'./AnnotationLicense': true,
});
});
afterEach(() => {
$imports.$restore();
});
it('is an empty component if there is no draft', () => {
fakeStore.getDraft.returns(null);
const wrapper = createComponent();
assert.equal(wrapper.html(), '');
});
describe('markdown content editor', () => {
describe('editing text content', () => {
it('applies theme', () => {
const textStyle = { fontFamily: 'serif' };
fakeApplyTheme
......@@ -95,190 +110,255 @@ describe('AnnotationEditor', () => {
assert.calledOnce(fakeStore.createDraft);
assert.equal(call.args[1].text, 'updated text');
});
});
describe('tag editing', () => {
it('adds tag when add callback invoked', () => {
const wrapper = createComponent();
wrapper.find('TagEditor').props().onAddTag('newTag');
describe('editing tags', () => {
it('adds tag when add callback invoked', () => {
const wrapper = createComponent();
wrapper.find('TagEditor').props().onAddTag('newTag');
const storeCall = fakeTagsService.store.getCall(0);
const draftCall = fakeStore.createDraft.getCall(0);
const storeCall = fakeTagsService.store.getCall(0);
const draftCall = fakeStore.createDraft.getCall(0);
assert.deepEqual(storeCall.args[0], ['newTag']);
assert.deepEqual(draftCall.args[1].tags, ['newTag']);
});
assert.deepEqual(storeCall.args[0], ['newTag']);
assert.deepEqual(draftCall.args[1].tags, ['newTag']);
});
it('does not add duplicate tags', () => {
const draft = fixtures.defaultDraft();
draft.tags = ['newTag'];
fakeStore.getDraft.returns(draft);
it('does not add duplicate tags', () => {
const draft = fixtures.defaultDraft();
draft.tags = ['newTag'];
const wrapper = createComponent();
wrapper.find('TagEditor').props().onAddTag('newTag');
const wrapper = createComponent({ draft });
wrapper.find('TagEditor').props().onAddTag('newTag');
assert.equal(fakeTagsService.store.callCount, 0);
assert.equal(fakeStore.createDraft.callCount, 0);
});
assert.equal(fakeTagsService.store.callCount, 0);
assert.equal(fakeStore.createDraft.callCount, 0);
});
it('removes tag when remove callback invoked', () => {
const draft = fixtures.defaultDraft();
draft.tags = ['newTag'];
fakeStore.getDraft.returns(draft);
it('removes tag when remove callback invoked', () => {
const draft = fixtures.defaultDraft();
draft.tags = ['newTag'];
const wrapper = createComponent();
wrapper.find('TagEditor').props().onRemoveTag('newTag');
const wrapper = createComponent({ draft });
wrapper.find('TagEditor').props().onRemoveTag('newTag');
const draftCall = fakeStore.createDraft.getCall(0);
const draftCall = fakeStore.createDraft.getCall(0);
assert.equal(fakeTagsService.store.callCount, 0);
assert.deepEqual(draftCall.args[1].tags, []);
});
assert.equal(fakeTagsService.store.callCount, 0);
assert.deepEqual(draftCall.args[1].tags, []);
});
it('does not remove non-existent tags', () => {
const draft = fixtures.defaultDraft();
fakeStore.getDraft.returns(draft);
it('does not remove non-existent tags', () => {
const draft = fixtures.defaultDraft();
const wrapper = createComponent();
wrapper.find('TagEditor').props().onRemoveTag('newTag');
const wrapper = createComponent({ draft });
wrapper.find('TagEditor').props().onRemoveTag('newTag');
assert.equal(fakeTagsService.store.callCount, 0);
assert.equal(fakeStore.createDraft.callCount, 0);
});
assert.equal(fakeTagsService.store.callCount, 0);
assert.equal(fakeStore.createDraft.callCount, 0);
});
});
describe('saving the annotation', () => {
it('saves the annotation when save callback invoked', async () => {
const annotation = fixtures.defaultAnnotation();
const wrapper = createComponent({ annotation });
describe('saving the annotation', () => {
it('saves the annotation when save callback invoked', async () => {
const annotation = fixtures.defaultAnnotation();
const wrapper = createComponent({ annotation });
await wrapper.find('AnnotationPublishControl').props().onSave();
await wrapper.find('AnnotationPublishControl').props().onSave();
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(fakeAnnotationsService.save, annotation);
});
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(fakeAnnotationsService.save, annotation);
});
it('checks for unsaved tags on save', async () => {
const wrapper = createComponent();
// Simulate "typing in" some tag text into the tag editor
wrapper.find('TagEditor').props().onTagInput('foobar');
wrapper.update();
it('checks for unsaved tags on save', async () => {
const wrapper = createComponent();
// Simulate "typing in" some tag text into the tag editor
wrapper.find('TagEditor').props().onTagInput('foobar');
wrapper.update();
await act(
async () =>
await wrapper.find('AnnotationPublishControl').props().onSave()
);
await act(
async () =>
await wrapper.find('AnnotationPublishControl').props().onSave()
);
const draftCall = fakeStore.createDraft.getCall(0);
assert.equal(fakeTagsService.store.callCount, 1);
assert.equal(fakeStore.createDraft.callCount, 1);
assert.deepEqual(draftCall.args[1].tags, ['foobar']);
});
const draftCall = fakeStore.createDraft.getCall(0);
assert.equal(fakeTagsService.store.callCount, 1);
assert.equal(fakeStore.createDraft.callCount, 1);
assert.deepEqual(draftCall.args[1].tags, ['foobar']);
});
it('shows a toast message on error', async () => {
fakeAnnotationsService.save.throws();
it('shows a toast message on error', async () => {
fakeAnnotationsService.save.throws();
const wrapper = createComponent();
const wrapper = createComponent();
fakeAnnotationsService.save.rejects();
fakeAnnotationsService.save.rejects();
wrapper.find('AnnotationPublishControl').props().onSave();
wrapper.find('AnnotationPublishControl').props().onSave();
await waitFor(() => fakeToastMessenger.error.called);
});
await waitFor(() => fakeToastMessenger.error.called);
});
it('should save annotation if `CTRL+Enter` is typed', () => {
const draft = fixtures.defaultDraft();
// Need some content so that it won't evaluate as "empty" and not save
draft.text = 'something is here';
fakeStore.getDraft.returns(draft);
const wrapper = createComponent();
it('should save annotation if `CTRL+Enter` is typed', () => {
const draft = fixtures.defaultDraft();
// Need some content so that it won't evaluate as "empty" and not save
draft.text = 'something is here';
const wrapper = createComponent({ draft });
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', ctrlKey: true });
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', ctrlKey: true });
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(
fakeAnnotationsService.save,
wrapper.props().annotation
);
});
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(
fakeAnnotationsService.save,
wrapper.props().annotation
it('should save annotation if `META+Enter` is typed', () => {
const draft = fixtures.defaultDraft();
// Need some content so that it won't evaluate as "empty" and not save
draft.text = 'something is here';
const wrapper = createComponent({ draft });
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', metaKey: true });
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(
fakeAnnotationsService.save,
wrapper.props().annotation
);
});
it('should not save annotation if `META+Enter` is typed but annotation empty', () => {
const wrapper = createComponent();
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', metaKey: true });
assert.notCalled(fakeAnnotationsService.save);
});
describe('handling publish options', () => {
it('sets the publish control to disabled if the annotation is empty', () => {
// default draft has no tags or text
const wrapper = createComponent();
assert.isTrue(
wrapper.find('AnnotationPublishControl').props().isDisabled
);
});
it('should save annotation if `META+Enter` is typed', () => {
it('enables the publish control when annotation has content', () => {
const draft = fixtures.defaultDraft();
// Need some content so that it won't evaluate as "empty" and not save
draft.text = 'something is here';
fakeStore.getDraft.returns(draft);
const wrapper = createComponent();
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', metaKey: true });
const wrapper = createComponent({ draft });
assert.calledOnce(fakeAnnotationsService.save);
assert.calledWith(
fakeAnnotationsService.save,
wrapper.props().annotation
assert.isFalse(
wrapper.find('AnnotationPublishControl').props().isDisabled
);
});
it('should not save annotation if `META+Enter` is typed but annotation empty', () => {
const wrapper = createComponent();
context('privacy changed in publish control', () => {
it("updates the draft's privacy when set to private", () => {
const draft = fixtures.defaultDraft();
draft.isPrivate = false;
const wrapper = createComponent({ draft });
wrapper.find('AnnotationPublishControl').props().onSetPrivacy(true);
const call = fakeStore.createDraft.getCall(0);
assert.calledOnce(fakeStore.createDraft);
assert.isTrue(call.args[1].isPrivate);
});
it("updates the draft's privacy when set to shared", () => {
const wrapper = createComponent();
wrapper.find('AnnotationPublishControl').props().onSetPrivacy(false);
const call = fakeStore.createDraft.getCall(0);
wrapper
.find('.AnnotationEditor')
.simulate('keydown', { key: 'Enter', metaKey: true });
assert.calledOnce(fakeStore.createDraft);
assert.isFalse(call.args[1].isPrivate);
});
assert.notCalled(fakeAnnotationsService.save);
it('updates privacy default setting', () => {
const wrapper = createComponent();
wrapper.find('AnnotationPublishControl').props().onSetPrivacy(false);
assert.calledOnce(fakeStore.setDefault);
assert.calledWith(
fakeStore.setDefault,
'annotationPrivacy',
'shared'
);
});
it('does not update privacy default if annotation is a reply', () => {
fakeMetadata.isReply.returns(true);
const wrapper = createComponent();
wrapper.find('AnnotationPublishControl').props().onSetPrivacy(false);
assert.notCalled(fakeStore.setDefault);
});
});
});
});
it('sets the publish control to disabled if the annotation is empty', () => {
// default draft has no tags or text
context('exiting edit mode', () => {
it('removes the current draft when canceled', () => {
const wrapper = createComponent();
assert.isTrue(
wrapper.find('AnnotationPublishControl').props().isDisabled
);
wrapper.find('AnnotationPublishControl').props().onCancel();
assert.calledOnce(fakeStore.removeDraft);
assert.calledWith(fakeStore.removeDraft, wrapper.props().annotation);
assert.equal(fakeStore.removeAnnotations.callCount, 0);
});
it('enables the publish control when annotation has content', () => {
const draft = fixtures.defaultDraft();
draft.text = 'something is here';
fakeStore.getDraft.returns(draft);
it('removes annotation from store if it is an unsaved annotation', () => {
fakeMetadata.isSaved.returns(false);
const wrapper = createComponent();
assert.isFalse(
wrapper.find('AnnotationPublishControl').props().isDisabled
);
wrapper.find('AnnotationPublishControl').props().onCancel();
assert.calledOnce(fakeStore.removeDraft);
assert.calledOnce(fakeStore.removeAnnotations);
});
});
it('shows license info if annotation is shared and in a public group', () => {
fakeStore.getGroup.returns({ type: 'open' });
it('shows license info if annotation is shared and in a public group', () => {
fakeStore.getGroup.returns({ type: 'open' });
const wrapper = createComponent();
const wrapper = createComponent();
assert.isTrue(wrapper.find('AnnotationLicense').exists());
});
assert.isTrue(wrapper.find('AnnotationLicense').exists());
});
it('does not show license info if annotation is only-me', () => {
const draft = fixtures.defaultDraft();
draft.isPrivate = true;
fakeStore.getGroup.returns({ type: 'open' });
fakeStore.getDraft.returns(draft);
it('does not show license info if annotation is only-me', () => {
const draft = fixtures.defaultDraft();
draft.isPrivate = true;
fakeStore.getGroup.returns({ type: 'open' });
const wrapper = createComponent();
const wrapper = createComponent({ draft });
assert.isFalse(wrapper.find('AnnotationLicense').exists());
});
assert.isFalse(wrapper.find('AnnotationLicense').exists());
});
it('does not show license if annotation is in a private group', () => {
fakeStore.getGroup.returns({ type: 'private' });
it('does not show license if annotation is in a private group', () => {
fakeStore.getGroup.returns({ type: 'private' });
const wrapper = createComponent();
const wrapper = createComponent();
assert.isFalse(wrapper.find('AnnotationLicense').exists());
});
assert.isFalse(wrapper.find('AnnotationLicense').exists());
});
it(
......
import { mount } from 'enzyme';
import * as fixtures from '../../../test/annotation-fixtures';
import { checkAccessibility } from '../../../../test-util/accessibility';
import { mockImportedComponents } from '../../../../test-util/mock-imported-components';
......@@ -11,17 +9,22 @@ import AnnotationPublishControl, {
describe('AnnotationPublishControl', () => {
let fakeGroup;
let fakeMetadata;
let fakeSettings;
let fakeStore;
let fakeApplyTheme;
let fakeOnSave;
let fakeOnCancel;
let fakeOnSetPrivacy;
const createAnnotationPublishControl = (props = {}) => {
return mount(
<AnnotationPublishControl
annotation={fixtures.defaultAnnotation()}
group={fakeGroup}
isDisabled={false}
onSave={sinon.stub()}
isPrivate={false}
onCancel={fakeOnCancel}
onSave={fakeOnSave}
onSetPrivacy={fakeOnSetPrivacy}
settings={fakeSettings}
{...props}
/>
......@@ -29,16 +32,14 @@ describe('AnnotationPublishControl', () => {
};
beforeEach(() => {
fakeOnCancel = sinon.stub();
fakeOnSave = sinon.stub();
fakeOnSetPrivacy = sinon.stub();
fakeGroup = {
name: 'Fake Group',
type: 'private',
};
fakeMetadata = {
isNew: sinon.stub(),
isReply: sinon.stub().returns(false),
};
fakeSettings = {
branding: {
ctaTextColor: '#0f0',
......@@ -46,21 +47,10 @@ describe('AnnotationPublishControl', () => {
},
};
fakeStore = {
createDraft: sinon.stub(),
getDraft: sinon.stub().returns(fixtures.defaultDraft()),
getGroup: sinon.stub().returns(fakeGroup),
setDefault: sinon.stub(),
removeAnnotations: sinon.stub(),
removeDraft: sinon.stub(),
};
fakeApplyTheme = sinon.stub();
$imports.$mock(mockImportedComponents());
$imports.$mock({
'../../store/use-store': { useStoreProxy: () => fakeStore },
'../../helpers/annotation-metadata': fakeMetadata,
'../../helpers/theme': {
applyTheme: fakeApplyTheme,
},
......@@ -71,12 +61,6 @@ describe('AnnotationPublishControl', () => {
$imports.$restore();
});
it('should not render if group is missing', () => {
fakeStore.getGroup.returns(undefined);
const wrapper = createAnnotationPublishControl();
assert.isFalse(wrapper.find('.AnnotationPublishControl').exists());
});
const getPublishButton = wrapper =>
wrapper.find('LabeledButton[data-testid="publish-control-button"]');
......@@ -108,10 +92,7 @@ describe('AnnotationPublishControl', () => {
context('private annotation', () => {
it('should label the button with "Only Me"', () => {
const draft = fixtures.defaultDraft();
draft.isPrivate = true;
fakeStore.getDraft.returns(draft);
const wrapper = createAnnotationPublishControl();
const wrapper = createAnnotationPublishControl({ isPrivate: true });
const btn = getPublishButton(wrapper);
assert.equal(btn.text(), 'Post to Only Me');
......@@ -144,43 +125,14 @@ describe('AnnotationPublishControl', () => {
describe('menu', () => {
describe('share (to group) menu item', () => {
it('should invoke privacy callback with shared privacy', () => {
it('should set privacy to shared when group name clicked', () => {
const wrapper = createAnnotationPublishControl();
const shareMenuItem = wrapper.find('MenuItem').first();
const shareMenuItem = wrapper.find('MenuItem[label="Fake Group"]');
shareMenuItem.prop('onClick')();
const call = fakeStore.createDraft.getCall(0);
assert.calledOnce(fakeStore.createDraft);
assert.isFalse(call.args[1].isPrivate);
});
it('should update default privacy level to shared', () => {
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').first();
privateMenuItem.prop('onClick')();
assert.calledOnce(fakeStore.setDefault);
assert.calledWith(fakeStore.setDefault, 'annotationPrivacy', 'shared');
});
it('should not update default privacy level if annotation is reply', () => {
fakeMetadata.isReply.returns(true);
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').first();
privateMenuItem.prop('onClick')();
assert.equal(fakeStore.setDefault.callCount, 0);
});
it('should have a label that is the name of the group', () => {
const wrapper = createAnnotationPublishControl();
const shareMenuItem = wrapper.find('MenuItem').first();
assert.equal(shareMenuItem.prop('label'), fakeGroup.name);
assert.calledOnce(fakeOnSetPrivacy);
assert.calledWith(fakeOnSetPrivacy, false);
});
context('private group', () => {
......@@ -191,6 +143,7 @@ describe('AnnotationPublishControl', () => {
assert.equal(shareMenuItem.prop('icon'), 'groups');
});
});
context('open group', () => {
beforeEach(() => {
fakeGroup.type = 'open';
......@@ -206,36 +159,14 @@ describe('AnnotationPublishControl', () => {
});
describe('private (only me) menu item', () => {
it('should invoke callback with private privacy', () => {
it('should set privacy to private when "Only Me" option clicked', () => {
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').at(1);
privateMenuItem.prop('onClick')();
const shareMenuItem = wrapper.find('MenuItem[label="Only Me"]');
const call = fakeStore.createDraft.getCall(0);
assert.calledOnce(fakeStore.createDraft);
assert.isTrue(call.args[1].isPrivate);
});
it('should update default privacy level to private', () => {
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').at(1);
privateMenuItem.prop('onClick')();
assert.calledOnce(fakeStore.setDefault);
assert.calledWith(fakeStore.setDefault, 'annotationPrivacy', 'private');
});
it('should not update default privacy level if annotation is reply', () => {
fakeMetadata.isReply.returns(true);
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').at(1);
privateMenuItem.prop('onClick')();
shareMenuItem.prop('onClick')();
assert.equal(fakeStore.setDefault.callCount, 0);
assert.calledOnce(fakeOnSetPrivacy);
assert.calledWith(fakeOnSetPrivacy, true);
});
it('should use a private/lock icon', () => {
......@@ -244,6 +175,7 @@ describe('AnnotationPublishControl', () => {
assert.equal(privateMenuItem.prop('icon'), 'lock');
});
it('should have an "Only me" label', () => {
const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find('MenuItem').at(1);
......@@ -254,29 +186,15 @@ describe('AnnotationPublishControl', () => {
});
describe('cancel button', () => {
it('should remove the current draft on cancel button click', () => {
const wrapper = createAnnotationPublishControl({});
const cancelBtn = wrapper
.find('LabeledButton')
.filter({ icon: 'cancel' });
cancelBtn.props().onClick();
assert.calledOnce(fakeStore.removeDraft);
assert.calledWith(fakeStore.removeDraft, wrapper.props().annotation);
assert.equal(fakeStore.removeAnnotations.callCount, 0);
});
it('should remove the annotation from the store if it is new/unsaved', () => {
fakeMetadata.isNew.returns(true);
const wrapper = createAnnotationPublishControl({});
it('should invoke the `onCancel` callback when cancel button clicked', () => {
const wrapper = createAnnotationPublishControl();
const cancelBtn = wrapper
.find('LabeledButton')
.filter({ icon: 'cancel' });
cancelBtn.props().onClick();
assert.calledOnce(fakeStore.removeAnnotations);
assert.calledOnce(fakeOnCancel);
});
});
......
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