Commit 147f561c authored by Robert Knight's avatar Robert Knight

Change mocking approach for imported components in tests

Shallow rendering with Enzyme's `shallow` function conflates two changes in
rendering behavior:

 1. Only rendering "one level deep" so that a test for a component is
    isolated from the details of any child components

 2. In React (but not Preact) rendering to a data structure instead of
    real DOM nodes. Even though Preact still renders real DOM nodes,
    Enzyme's shallow rendering API removes many of the features of
    `mount` rendering

In practice, we only used shallow rendering for (1) and we have found
that there are use cases where we want (1) but still want to be able to
interact with the real DOM nodes (ie. we don't want (2)). The need to
use different mocking approaches and understand the conflated behaviors
mentioned above has been a source of confusion for developers.

This commit changes the mocking approach to always use a pattern that we
have until now only used selectively. Enzyme's `mount` rendering mode is
always used, but a utility function, `mockImportedComponents`, is used
with our existing import mocking (via babel-plugin-mockable-imports) to
semi-automatically mock all imported components.

Each test has been changed as follows:

 1. Replace `shallow` with `mount`

 2. Call `ComponentUnderTest.$imports.$mock(mockImportedComponents())`
    in the test setup to mock all imported components, but not helper
    components defined in the same file.

 3. Call `ComponentUnderTest.$imports.$restore()` in test cleanup, if
    the test was not already doing that.

 4. Replace uses of (`wrapper.find(SomeChildComponent)`) with
    (`wrapper.find('SomeChildComponent')`. This is necessary because the
    mocked component no longer has the same identity as the original, so
    we look it up by name instead.
parent 721bf3f8
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const fixtures = require('../../test/annotation-fixtures'); const fixtures = require('../../test/annotation-fixtures');
const AnnotationDocumentInfo = require('../annotation-document-info'); const AnnotationDocumentInfo = require('../annotation-document-info');
const mockImportedComponents = require('./mock-imported-components');
describe('AnnotationDocumentInfo', () => { describe('AnnotationDocumentInfo', () => {
let fakeDomainAndTitle; let fakeDomainAndTitle;
let fakeMetadata; let fakeMetadata;
const createAnnotationDocumentInfo = props => { const createAnnotationDocumentInfo = props => {
return shallow( return mount(
<AnnotationDocumentInfo <AnnotationDocumentInfo
annotation={fixtures.defaultAnnotation()} annotation={fixtures.defaultAnnotation()}
{...props} {...props}
...@@ -23,6 +24,8 @@ describe('AnnotationDocumentInfo', () => { ...@@ -23,6 +24,8 @@ describe('AnnotationDocumentInfo', () => {
beforeEach(() => { beforeEach(() => {
fakeDomainAndTitle = sinon.stub(); fakeDomainAndTitle = sinon.stub();
fakeMetadata = { domainAndTitle: fakeDomainAndTitle }; fakeMetadata = { domainAndTitle: fakeDomainAndTitle };
AnnotationDocumentInfo.$imports.$mock(mockImportedComponents());
AnnotationDocumentInfo.$imports.$mock({ AnnotationDocumentInfo.$imports.$mock({
'../util/annotation-metadata': fakeMetadata, '../util/annotation-metadata': fakeMetadata,
}); });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const fixtures = require('../../test/annotation-fixtures'); const fixtures = require('../../test/annotation-fixtures');
const AnnotationHeader = require('../annotation-header'); const AnnotationHeader = require('../annotation-header');
const AnnotationDocumentInfo = require('../annotation-document-info'); const mockImportedComponents = require('./mock-imported-components');
const Timestamp = require('../timestamp');
describe('AnnotationHeader', () => { describe('AnnotationHeader', () => {
const createAnnotationHeader = props => { const createAnnotationHeader = props => {
return shallow( return mount(
<AnnotationHeader <AnnotationHeader
annotation={fixtures.defaultAnnotation()} annotation={fixtures.defaultAnnotation()}
isEditing={false} isEditing={false}
...@@ -25,6 +24,14 @@ describe('AnnotationHeader', () => { ...@@ -25,6 +24,14 @@ describe('AnnotationHeader', () => {
); );
}; };
beforeEach(() => {
AnnotationHeader.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
AnnotationHeader.$imports.$restore();
});
describe('collapsed replies', () => { describe('collapsed replies', () => {
it('should have a callback', () => { it('should have a callback', () => {
const fakeCallback = sinon.stub(); const fakeCallback = sinon.stub();
...@@ -84,7 +91,7 @@ describe('AnnotationHeader', () => { ...@@ -84,7 +91,7 @@ describe('AnnotationHeader', () => {
annotation: annotation, annotation: annotation,
}); });
const timestamp = wrapper const timestamp = wrapper
.find(Timestamp) .find('Timestamp')
.filter('.annotation-header__timestamp-edited-link'); .filter('.annotation-header__timestamp-edited-link');
assert.isTrue(timestamp.exists()); assert.isTrue(timestamp.exists());
...@@ -97,7 +104,7 @@ describe('AnnotationHeader', () => { ...@@ -97,7 +104,7 @@ describe('AnnotationHeader', () => {
annotation: fixtures.newAnnotation(), annotation: fixtures.newAnnotation(),
}); });
const timestamp = wrapper const timestamp = wrapper
.find(Timestamp) .find('Timestamp')
.filter('.annotation-header__timestamp-edited-link'); .filter('.annotation-header__timestamp-edited-link');
assert.isFalse(timestamp.exists()); assert.isFalse(timestamp.exists());
...@@ -130,7 +137,7 @@ describe('AnnotationHeader', () => { ...@@ -130,7 +137,7 @@ describe('AnnotationHeader', () => {
it('should render document info if `showDocumentInfo` is enabled', () => { it('should render document info if `showDocumentInfo` is enabled', () => {
const wrapper = createAnnotationHeader({ showDocumentInfo: true }); const wrapper = createAnnotationHeader({ showDocumentInfo: true });
const documentInfo = wrapper.find(AnnotationDocumentInfo); const documentInfo = wrapper.find('AnnotationDocumentInfo');
assert.isTrue(documentInfo.exists()); assert.isTrue(documentInfo.exists());
}); });
...@@ -138,7 +145,7 @@ describe('AnnotationHeader', () => { ...@@ -138,7 +145,7 @@ describe('AnnotationHeader', () => {
it('should not render document info if `showDocumentInfo` is not enabled', () => { it('should not render document info if `showDocumentInfo` is not enabled', () => {
const wrapper = createAnnotationHeader({ showDocumentInfo: false }); const wrapper = createAnnotationHeader({ showDocumentInfo: false });
const documentInfo = wrapper.find(AnnotationDocumentInfo); const documentInfo = wrapper.find('AnnotationDocumentInfo');
assert.isFalse(documentInfo.exists()); assert.isFalse(documentInfo.exists());
}); });
...@@ -151,7 +158,7 @@ describe('AnnotationHeader', () => { ...@@ -151,7 +158,7 @@ describe('AnnotationHeader', () => {
isEditing: true, isEditing: true,
}); });
const timestamp = wrapper.find(Timestamp); const timestamp = wrapper.find('Timestamp');
assert.isFalse(timestamp.exists()); assert.isFalse(timestamp.exists());
}); });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const AnnotationPublishControl = require('../annotation-publish-control'); const AnnotationPublishControl = require('../annotation-publish-control');
const MenuItem = require('../menu-item'); const mockImportedComponents = require('./mock-imported-components');
describe('AnnotationPublishControl', () => { describe('AnnotationPublishControl', () => {
let fakeGroup; let fakeGroup;
...@@ -12,7 +12,7 @@ describe('AnnotationPublishControl', () => { ...@@ -12,7 +12,7 @@ describe('AnnotationPublishControl', () => {
let fakeApplyTheme; let fakeApplyTheme;
const createAnnotationPublishControl = (props = {}) => { const createAnnotationPublishControl = (props = {}) => {
return shallow( return mount(
<AnnotationPublishControl <AnnotationPublishControl
group={fakeGroup} group={fakeGroup}
isDisabled={false} isDisabled={false}
...@@ -23,7 +23,7 @@ describe('AnnotationPublishControl', () => { ...@@ -23,7 +23,7 @@ describe('AnnotationPublishControl', () => {
settings={fakeSettings} settings={fakeSettings}
{...props} {...props}
/> />
).dive(); // Dive needed because this component uses `withServices` );
}; };
beforeEach(() => { beforeEach(() => {
...@@ -40,6 +40,7 @@ describe('AnnotationPublishControl', () => { ...@@ -40,6 +40,7 @@ describe('AnnotationPublishControl', () => {
fakeApplyTheme = sinon.stub(); fakeApplyTheme = sinon.stub();
AnnotationPublishControl.$imports.$mock(mockImportedComponents());
AnnotationPublishControl.$imports.$mock({ AnnotationPublishControl.$imports.$mock({
'../util/theme': { '../util/theme': {
applyTheme: fakeApplyTheme, applyTheme: fakeApplyTheme,
...@@ -121,7 +122,7 @@ describe('AnnotationPublishControl', () => { ...@@ -121,7 +122,7 @@ describe('AnnotationPublishControl', () => {
const wrapper = createAnnotationPublishControl({ const wrapper = createAnnotationPublishControl({
onSetPrivacy: fakeOnSetPrivacy, onSetPrivacy: fakeOnSetPrivacy,
}); });
const shareMenuItem = wrapper.find(MenuItem).first(); const shareMenuItem = wrapper.find('MenuItem').first();
shareMenuItem.prop('onClick')(); shareMenuItem.prop('onClick')();
...@@ -130,7 +131,7 @@ describe('AnnotationPublishControl', () => { ...@@ -130,7 +131,7 @@ describe('AnnotationPublishControl', () => {
it('should have a label that is the name of the group', () => { it('should have a label that is the name of the group', () => {
const wrapper = createAnnotationPublishControl(); const wrapper = createAnnotationPublishControl();
const shareMenuItem = wrapper.find(MenuItem).first(); const shareMenuItem = wrapper.find('MenuItem').first();
assert.equal(shareMenuItem.prop('label'), fakeGroup.name); assert.equal(shareMenuItem.prop('label'), fakeGroup.name);
}); });
...@@ -138,7 +139,7 @@ describe('AnnotationPublishControl', () => { ...@@ -138,7 +139,7 @@ describe('AnnotationPublishControl', () => {
context('private group', () => { context('private group', () => {
it('should have a group icon', () => { it('should have a group icon', () => {
const wrapper = createAnnotationPublishControl(); const wrapper = createAnnotationPublishControl();
const shareMenuItem = wrapper.find(MenuItem).first(); const shareMenuItem = wrapper.find('MenuItem').first();
assert.equal(shareMenuItem.prop('icon'), 'groups'); assert.equal(shareMenuItem.prop('icon'), 'groups');
}); });
...@@ -150,7 +151,7 @@ describe('AnnotationPublishControl', () => { ...@@ -150,7 +151,7 @@ describe('AnnotationPublishControl', () => {
it('should have a public icon', () => { it('should have a public icon', () => {
const wrapper = createAnnotationPublishControl(); const wrapper = createAnnotationPublishControl();
const shareMenuItem = wrapper.find(MenuItem).first(); const shareMenuItem = wrapper.find('MenuItem').first();
assert.equal(shareMenuItem.prop('icon'), 'public'); assert.equal(shareMenuItem.prop('icon'), 'public');
}); });
...@@ -163,7 +164,7 @@ describe('AnnotationPublishControl', () => { ...@@ -163,7 +164,7 @@ describe('AnnotationPublishControl', () => {
const wrapper = createAnnotationPublishControl({ const wrapper = createAnnotationPublishControl({
onSetPrivacy: fakeOnSetPrivacy, onSetPrivacy: fakeOnSetPrivacy,
}); });
const privateMenuItem = wrapper.find(MenuItem).at(1); const privateMenuItem = wrapper.find('MenuItem').at(1);
privateMenuItem.prop('onClick')(); privateMenuItem.prop('onClick')();
...@@ -171,13 +172,13 @@ describe('AnnotationPublishControl', () => { ...@@ -171,13 +172,13 @@ describe('AnnotationPublishControl', () => {
}); });
it('should use a private/lock icon', () => { it('should use a private/lock icon', () => {
const wrapper = createAnnotationPublishControl(); const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find(MenuItem).at(1); const privateMenuItem = wrapper.find('MenuItem').at(1);
assert.equal(privateMenuItem.prop('icon'), 'lock'); assert.equal(privateMenuItem.prop('icon'), 'lock');
}); });
it('should have an "Only me" label', () => { it('should have an "Only me" label', () => {
const wrapper = createAnnotationPublishControl(); const wrapper = createAnnotationPublishControl();
const privateMenuItem = wrapper.find(MenuItem).at(1); const privateMenuItem = wrapper.find('MenuItem').at(1);
assert.equal(privateMenuItem.prop('label'), 'Only Me'); assert.equal(privateMenuItem.prop('label'), 'Only Me');
}); });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const fixtures = require('../../test/annotation-fixtures'); const fixtures = require('../../test/annotation-fixtures');
const AnnotationShareInfo = require('../annotation-share-info'); const AnnotationShareInfo = require('../annotation-share-info');
const mockImportedComponents = require('./mock-imported-components');
describe('AnnotationShareInfo', () => { describe('AnnotationShareInfo', () => {
let fakeGroup; let fakeGroup;
...@@ -13,7 +14,7 @@ describe('AnnotationShareInfo', () => { ...@@ -13,7 +14,7 @@ describe('AnnotationShareInfo', () => {
let fakeGetGroup; let fakeGetGroup;
const createAnnotationShareInfo = props => { const createAnnotationShareInfo = props => {
return shallow( return mount(
<AnnotationShareInfo <AnnotationShareInfo
annotation={fixtures.defaultAnnotation()} annotation={fixtures.defaultAnnotation()}
isPrivate={false} isPrivate={false}
...@@ -33,6 +34,7 @@ describe('AnnotationShareInfo', () => { ...@@ -33,6 +34,7 @@ describe('AnnotationShareInfo', () => {
fakeGetGroup = sinon.stub().returns(fakeGroup); fakeGetGroup = sinon.stub().returns(fakeGroup);
fakeStore = { getGroup: fakeGetGroup }; fakeStore = { getGroup: fakeGetGroup };
AnnotationShareInfo.$imports.$mock(mockImportedComponents());
AnnotationShareInfo.$imports.$mock({ AnnotationShareInfo.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const AnnotationUser = require('../annotation-user'); const AnnotationUser = require('../annotation-user');
const mockImportedComponents = require('./mock-imported-components');
describe('AnnotationUser', () => { describe('AnnotationUser', () => {
let fakeAnnotation; let fakeAnnotation;
...@@ -14,14 +15,14 @@ describe('AnnotationUser', () => { ...@@ -14,14 +15,14 @@ describe('AnnotationUser', () => {
let fakeUsername; let fakeUsername;
const createAnnotationUser = () => { const createAnnotationUser = () => {
return shallow( return mount(
<AnnotationUser <AnnotationUser
annotation={fakeAnnotation} annotation={fakeAnnotation}
features={fakeFeatures} features={fakeFeatures}
serviceUrl={fakeServiceUrl} serviceUrl={fakeServiceUrl}
settings={fakeSettings} settings={fakeSettings}
/> />
).dive(); // Dive needed because of `withServices` usage in component );
}; };
beforeEach(() => { beforeEach(() => {
...@@ -34,6 +35,7 @@ describe('AnnotationUser', () => { ...@@ -34,6 +35,7 @@ describe('AnnotationUser', () => {
fakeSettings = {}; fakeSettings = {};
fakeUsername = sinon.stub(); fakeUsername = sinon.stub();
AnnotationUser.$imports.$mock(mockImportedComponents());
AnnotationUser.$imports.$mock({ AnnotationUser.$imports.$mock({
'../util/account-id': { '../util/account-id': {
isThirdPartyUser: fakeIsThirdPartyUser, isThirdPartyUser: fakeIsThirdPartyUser,
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const FocusedModeHeader = require('../focused-mode-header'); const FocusedModeHeader = require('../focused-mode-header');
const mockImportedComponents = require('./mock-imported-components');
describe('FocusedModeHeader', function() { describe('FocusedModeHeader', function() {
let fakeStore; let fakeStore;
function createComponent() { function createComponent() {
return shallow(<FocusedModeHeader />); return mount(<FocusedModeHeader />);
} }
beforeEach(function() { beforeEach(function() {
...@@ -24,6 +25,8 @@ describe('FocusedModeHeader', function() { ...@@ -24,6 +25,8 @@ describe('FocusedModeHeader', function() {
focusModeHasUser: sinon.stub().returns(true), focusModeHasUser: sinon.stub().returns(true),
setFocusModeFocused: sinon.stub(), setFocusModeFocused: sinon.stub(),
}; };
FocusedModeHeader.$imports.$mock(mockImportedComponents());
FocusedModeHeader.$imports.$mock({ FocusedModeHeader.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const GroupListSection = require('../group-list-section'); const GroupListSection = require('../group-list-section');
const GroupListItem = require('../group-list-item'); const mockImportedComponents = require('./mock-imported-components');
const MenuSection = require('../menu-section');
describe('GroupListSection', () => { describe('GroupListSection', () => {
const testGroups = [ const testGroups = [
...@@ -24,26 +23,34 @@ describe('GroupListSection', () => { ...@@ -24,26 +23,34 @@ describe('GroupListSection', () => {
heading = 'Test section', heading = 'Test section',
...props ...props
} = {}) => { } = {}) => {
return shallow( return mount(
<GroupListSection groups={groups} heading={heading} {...props} /> <GroupListSection groups={groups} heading={heading} {...props} />
); );
}; };
beforeEach(() => {
GroupListSection.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
GroupListSection.$imports.$restore();
});
it('renders heading', () => { it('renders heading', () => {
const wrapper = createGroupListSection(); const wrapper = createGroupListSection();
assert.equal(wrapper.find(MenuSection).prop('heading'), 'Test section'); assert.equal(wrapper.find('MenuSection').prop('heading'), 'Test section');
}); });
it('renders groups', () => { it('renders groups', () => {
const wrapper = createGroupListSection(); const wrapper = createGroupListSection();
assert.equal(wrapper.find(GroupListItem).length, testGroups.length); assert.equal(wrapper.find('GroupListItem').length, testGroups.length);
}); });
it('expands group specified by `expandedGroup` prop', () => { it('expands group specified by `expandedGroup` prop', () => {
const wrapper = createGroupListSection(); const wrapper = createGroupListSection();
for (let i = 0; i < testGroups.length; i++) { for (let i = 0; i < testGroups.length; i++) {
wrapper.setProps({ expandedGroup: testGroups[i] }); wrapper.setProps({ expandedGroup: testGroups[i] });
wrapper.find(GroupListItem).forEach((n, idx) => { wrapper.find('GroupListItem').forEach((n, idx) => {
assert.equal(n.prop('isExpanded'), idx === i); assert.equal(n.prop('isExpanded'), idx === i);
}); });
} }
...@@ -53,7 +60,7 @@ describe('GroupListSection', () => { ...@@ -53,7 +60,7 @@ describe('GroupListSection', () => {
const onExpandGroup = sinon.stub(); const onExpandGroup = sinon.stub();
const wrapper = createGroupListSection({ onExpandGroup }); const wrapper = createGroupListSection({ onExpandGroup });
wrapper wrapper
.find(GroupListItem) .find('GroupListItem')
.first() .first()
.props() .props()
.onExpand(true); .onExpand(true);
...@@ -67,7 +74,7 @@ describe('GroupListSection', () => { ...@@ -67,7 +74,7 @@ describe('GroupListSection', () => {
onExpandGroup, onExpandGroup,
}); });
wrapper wrapper
.find(GroupListItem) .find('GroupListItem')
.first() .first()
.props() .props()
.onExpand(false); .onExpand(false);
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const { act } = require('preact/test-utils'); const { act } = require('preact/test-utils');
const GroupList = require('../group-list'); const GroupList = require('../group-list');
const mockImportedComponents = require('./mock-imported-components');
describe('GroupList', () => { describe('GroupList', () => {
let fakeServiceConfig; let fakeServiceConfig;
...@@ -19,9 +20,9 @@ describe('GroupList', () => { ...@@ -19,9 +20,9 @@ describe('GroupList', () => {
}; };
function createGroupList() { function createGroupList() {
return shallow( return mount(
<GroupList serviceUrl={fakeServiceUrl} settings={fakeSettings} /> <GroupList serviceUrl={fakeServiceUrl} settings={fakeSettings} />
).dive(); );
} }
/** /**
...@@ -59,6 +60,7 @@ describe('GroupList', () => { ...@@ -59,6 +60,7 @@ describe('GroupList', () => {
}; };
fakeServiceConfig = sinon.stub().returns(null); fakeServiceConfig = sinon.stub().returns(null);
GroupList.$imports.$mock(mockImportedComponents());
GroupList.$imports.$mock({ GroupList.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
'../service-config': fakeServiceConfig, '../service-config': fakeServiceConfig,
...@@ -162,14 +164,14 @@ describe('GroupList', () => { ...@@ -162,14 +164,14 @@ describe('GroupList', () => {
fakeStore.focusedGroup.returns(null); fakeStore.focusedGroup.returns(null);
const wrapper = createGroupList(); const wrapper = createGroupList();
const label = wrapper.find('Menu').prop('label'); const label = wrapper.find('Menu').prop('label');
assert.equal(shallow(label).text(), '…'); assert.equal(mount(label).text(), '…');
}); });
it('renders the publisher-provided icon in the toggle button', () => { it('renders the publisher-provided icon in the toggle button', () => {
fakeServiceConfig.returns({ icon: 'test-icon' }); fakeServiceConfig.returns({ icon: 'test-icon' });
const wrapper = createGroupList(); const wrapper = createGroupList();
const label = wrapper.find('Menu').prop('label'); const label = wrapper.find('Menu').prop('label');
const img = shallow(label).find('img'); const img = mount(label).find('img');
assert.equal(img.prop('src'), 'test-icon'); assert.equal(img.prop('src'), 'test-icon');
}); });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const HelpLink = require('../help-link'); const HelpLink = require('../help-link');
const mockImportedComponents = require('./mock-imported-components');
describe('Help (mailto) Link', () => { describe('Help (mailto) Link', () => {
let fakeAuth; let fakeAuth;
...@@ -14,7 +15,7 @@ describe('Help (mailto) Link', () => { ...@@ -14,7 +15,7 @@ describe('Help (mailto) Link', () => {
let fakeVersion; let fakeVersion;
const createHelpLink = () => { const createHelpLink = () => {
return shallow( return mount(
<HelpLink <HelpLink
auth={fakeAuth} auth={fakeAuth}
dateTime={fakeDateTime} dateTime={fakeDateTime}
...@@ -35,6 +36,12 @@ describe('Help (mailto) Link', () => { ...@@ -35,6 +36,12 @@ describe('Help (mailto) Link', () => {
fakeUrl = 'http://www.example.com'; fakeUrl = 'http://www.example.com';
fakeUserAgent = 'Some User Agent'; fakeUserAgent = 'Some User Agent';
fakeVersion = '1.0.0'; fakeVersion = '1.0.0';
HelpLink.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
HelpLink.$imports.$restore();
}); });
it('sets required props as part of formatted email body', () => { it('sets required props as part of formatted email body', () => {
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const LoggedOutMessage = require('../logged-out-message'); const LoggedOutMessage = require('../logged-out-message');
const mockImportedComponents = require('./mock-imported-components');
describe('LoggedOutMessage', () => { describe('LoggedOutMessage', () => {
const createLoggedOutMessage = props => { const createLoggedOutMessage = props => {
return shallow( return mount(
<LoggedOutMessage <LoggedOutMessage
onLogin={sinon.stub()} onLogin={sinon.stub()}
serviceUrl={sinon.stub()} serviceUrl={sinon.stub()}
{...props} {...props}
/> />
).dive(); // Dive needed because this component uses `withServices` );
}; };
beforeEach(() => {
LoggedOutMessage.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
LoggedOutMessage.$imports.$restore();
});
it('should link to signup', () => { it('should link to signup', () => {
const fakeServiceUrl = sinon.stub().returns('signup_link'); const fakeServiceUrl = sinon.stub().returns('signup_link');
const wrapper = createLoggedOutMessage({ serviceUrl: fakeServiceUrl }); const wrapper = createLoggedOutMessage({ serviceUrl: fakeServiceUrl });
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const MenuItem = require('../menu-item'); const MenuItem = require('../menu-item');
const mockImportedComponents = require('./mock-imported-components');
describe('MenuItem', () => { describe('MenuItem', () => {
const createMenuItem = props => const createMenuItem = props =>
shallow(<MenuItem label="Test item" {...props} />); mount(<MenuItem label="Test item" {...props} />);
beforeEach(() => {
MenuItem.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
MenuItem.$imports.$restore();
});
it('invokes `onClick` callback when clicked', () => { it('invokes `onClick` callback when clicked', () => {
const onClick = sinon.stub(); const onClick = sinon.stub();
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const MenuSection = require('../menu-section'); const MenuSection = require('../menu-section');
const mockImportedComponents = require('./mock-imported-components');
describe('MenuSection', () => { describe('MenuSection', () => {
const createMenuSection = props => const createMenuSection = props =>
shallow( mount(
<MenuSection {...props}> <MenuSection {...props}>
<div className="menu-item">Test item</div> <div className="menu-item">Test item</div>
</MenuSection> </MenuSection>
); );
beforeEach(() => {
MenuSection.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
MenuSection.$imports.$restore();
});
it('renders the heading', () => { it('renders the heading', () => {
const wrapper = createMenuSection({ heading: 'A heading' }); const wrapper = createMenuSection({ heading: 'A heading' });
assert.equal(wrapper.find('h2').text(), 'A heading'); assert.equal(wrapper.find('h2').text(), 'A heading');
......
...@@ -8,8 +8,20 @@ function isComponent(value) { ...@@ -8,8 +8,20 @@ function isComponent(value) {
); );
} }
/**
* Return the display name of a component, stripping away any the names of
* any wrapper components which use the `withWrapper(OriginalName)` convention.
*/
function getDisplayName(component) { function getDisplayName(component) {
return component.displayName || component.name || 'UnknownComponent'; let displayName =
component.displayName || component.name || 'UnknownComponent';
const wrappedComponentMatch = displayName.match(/\([A-Z][A-Za-z0-9]+\)/);
if (wrappedComponentMatch) {
displayName = wrappedComponentMatch[0].slice(1, -1);
}
return displayName;
} }
/** /**
...@@ -17,7 +29,9 @@ function getDisplayName(component) { ...@@ -17,7 +29,9 @@ function getDisplayName(component) {
* imported by a file. * imported by a file.
* *
* Mocked components will have the same display name as the original component, * Mocked components will have the same display name as the original component,
* but will just render their children and not call the original implementation. * minus any wrappers (eg. `Widget` and `withServices(Widget)` both become
* `Widget`). They will render only their children, as if they were just a
* `Fragment`.
* *
* @example * @example
* beforeEach(() => { * beforeEach(() => {
...@@ -38,6 +52,7 @@ function mockImportedComponents() { ...@@ -38,6 +52,7 @@ function mockImportedComponents() {
const mock = props => props.children; const mock = props => props.children;
mock.displayName = getDisplayName(value); mock.displayName = getDisplayName(value);
return mock; return mock;
}; };
} }
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const ModerationBanner = require('../moderation-banner'); const ModerationBanner = require('../moderation-banner');
const fixtures = require('../../test/annotation-fixtures'); const fixtures = require('../../test/annotation-fixtures');
const mockImportedComponents = require('./mock-imported-components');
const moderatedAnnotation = fixtures.moderatedAnnotation; const moderatedAnnotation = fixtures.moderatedAnnotation;
...@@ -13,9 +14,9 @@ describe('ModerationBanner', () => { ...@@ -13,9 +14,9 @@ describe('ModerationBanner', () => {
let fakeFlash; let fakeFlash;
function createComponent(props) { function createComponent(props) {
return shallow( return mount(
<ModerationBanner api={fakeApi} flash={fakeFlash} {...props} /> <ModerationBanner api={fakeApi} flash={fakeFlash} {...props} />
).dive(); // dive() needed because this component uses `withServices` );
} }
beforeEach(() => { beforeEach(() => {
...@@ -30,6 +31,7 @@ describe('ModerationBanner', () => { ...@@ -30,6 +31,7 @@ describe('ModerationBanner', () => {
}, },
}; };
ModerationBanner.$imports.$mock(mockImportedComponents());
ModerationBanner.$imports.$mock({ ModerationBanner.$imports.$mock({
'../store/use-store': callback => '../store/use-store': callback =>
callback({ callback({
...@@ -83,7 +85,7 @@ describe('ModerationBanner', () => { ...@@ -83,7 +85,7 @@ describe('ModerationBanner', () => {
if (testCase.expectVisible) { if (testCase.expectVisible) {
assert.notEqual(wrapper.text().trim(), ''); assert.notEqual(wrapper.text().trim(), '');
} else { } else {
assert.isFalse(wrapper.exists()); assert.equal(wrapper.text().trim(), '');
} }
}); });
}); });
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const events = require('../../events'); const events = require('../../events');
const NewNoteButton = require('../new-note-btn'); const NewNoteButton = require('../new-note-btn');
const mockImportedComponents = require('./mock-imported-components');
describe('NewNoteButton', function() { describe('NewNoteButton', function() {
let fakeStore; let fakeStore;
...@@ -12,13 +13,13 @@ describe('NewNoteButton', function() { ...@@ -12,13 +13,13 @@ describe('NewNoteButton', function() {
let fakeRootScope; let fakeRootScope;
function createComponent() { function createComponent() {
return shallow( return mount(
<NewNoteButton <NewNoteButton
$rootScope={fakeRootScope} $rootScope={fakeRootScope}
settings={fakeSettings} settings={fakeSettings}
store={fakeStore} store={fakeStore}
/> />
).dive(); // dive() needed because this component uses `withServices` );
} }
beforeEach(function() { beforeEach(function() {
...@@ -39,6 +40,8 @@ describe('NewNoteButton', function() { ...@@ -39,6 +40,8 @@ describe('NewNoteButton', function() {
{ id: '1', uri: 'www.example.org' }, { id: '1', uri: 'www.example.org' },
]), ]),
}; };
NewNoteButton.$imports.$mock(mockImportedComponents());
NewNoteButton.$imports.$mock({ NewNoteButton.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
...@@ -54,7 +57,7 @@ describe('NewNoteButton', function() { ...@@ -54,7 +57,7 @@ describe('NewNoteButton', function() {
}); });
it("has a backgroundColor equal to the setting's ctaBackgroundColor color", () => { it("has a backgroundColor equal to the setting's ctaBackgroundColor color", () => {
const wrapper = createComponent(); const wrapper = createComponent().find('button');
assert.equal( assert.equal(
wrapper.prop('style').backgroundColor, wrapper.prop('style').backgroundColor,
fakeSettings.branding.ctaBackgroundColor fakeSettings.branding.ctaBackgroundColor
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const SearchStatusBar = require('../search-status-bar'); const SearchStatusBar = require('../search-status-bar');
const mockImportedComponents = require('./mock-imported-components');
describe('SearchStatusBar', () => { describe('SearchStatusBar', () => {
let fakeRootThread; let fakeRootThread;
let fakeStore; let fakeStore;
function createComponent(props) { function createComponent(props) {
return shallow( return mount(<SearchStatusBar rootThread={fakeRootThread} {...props} />);
<SearchStatusBar rootThread={fakeRootThread} {...props} />
).dive(); // dive() needed because this component uses `withServices`
} }
beforeEach(() => { beforeEach(() => {
...@@ -30,6 +29,7 @@ describe('SearchStatusBar', () => { ...@@ -30,6 +29,7 @@ describe('SearchStatusBar', () => {
noteCount: sinon.stub().returns(0), noteCount: sinon.stub().returns(0),
}; };
SearchStatusBar.$imports.$mock(mockImportedComponents());
SearchStatusBar.$imports.$mock({ SearchStatusBar.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
......
'use strict'; 'use strict';
const { shallow, mount } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const NewNoteBtn = require('../new-note-btn');
const uiConstants = require('../../ui-constants'); const uiConstants = require('../../ui-constants');
const SelectionTabs = require('../selection-tabs'); const SelectionTabs = require('../selection-tabs');
const mockImportedComponents = require('./mock-imported-components');
describe('SelectionTabs', function() { describe('SelectionTabs', function() {
// mock services // mock services
...@@ -18,23 +18,7 @@ describe('SelectionTabs', function() { ...@@ -18,23 +18,7 @@ describe('SelectionTabs', function() {
isLoading: false, isLoading: false,
}; };
SelectionTabs.$imports.$mock({
'../store/use-store': callback => callback(fakeStore),
});
function createComponent(props) { function createComponent(props) {
return shallow(
<SelectionTabs
session={fakeSession}
settings={fakeSettings}
{...defaultProps}
{...props}
/>
).dive();
}
// required for <Tab> rendering
function createDeepComponent(props) {
return mount( return mount(
<SelectionTabs <SelectionTabs
session={fakeSession} session={fakeSession}
...@@ -69,6 +53,15 @@ describe('SelectionTabs', function() { ...@@ -69,6 +53,15 @@ describe('SelectionTabs', function() {
}, },
}), }),
}; };
SelectionTabs.$imports.$mock(mockImportedComponents());
SelectionTabs.$imports.$mock({
'../store/use-store': callback => callback(fakeStore),
});
});
afterEach(() => {
SelectionTabs.$imports.$restore();
}); });
const unavailableMessage = wrapper => const unavailableMessage = wrapper =>
...@@ -76,7 +69,7 @@ describe('SelectionTabs', function() { ...@@ -76,7 +69,7 @@ describe('SelectionTabs', function() {
context('displays selection tabs and counts', function() { context('displays selection tabs and counts', function() {
it('should display the tabs and counts of annotations and notes', function() { it('should display the tabs and counts of annotations and notes', function() {
const wrapper = createDeepComponent(); const wrapper = createComponent();
const tabs = wrapper.find('a'); const tabs = wrapper.find('a');
assert.isTrue(tabs.at(0).contains('Annotations')); assert.isTrue(tabs.at(0).contains('Annotations'));
assert.equal( assert.equal(
...@@ -97,7 +90,7 @@ describe('SelectionTabs', function() { ...@@ -97,7 +90,7 @@ describe('SelectionTabs', function() {
}); });
it('should display annotations tab as selected', function() { it('should display annotations tab as selected', function() {
const wrapper = createDeepComponent(); const wrapper = createComponent();
const aTags = wrapper.find('a'); const aTags = wrapper.find('a');
assert.isTrue(aTags.at(0).hasClass('is-selected')); assert.isTrue(aTags.at(0).hasClass('is-selected'));
}); });
...@@ -106,7 +99,7 @@ describe('SelectionTabs', function() { ...@@ -106,7 +99,7 @@ describe('SelectionTabs', function() {
fakeStore.getState.returns({ fakeStore.getState.returns({
selection: { selectedTab: uiConstants.TAB_NOTES }, selection: { selectedTab: uiConstants.TAB_NOTES },
}); });
const wrapper = createDeepComponent({}); const wrapper = createComponent({});
const tabs = wrapper.find('a'); const tabs = wrapper.find('a');
assert.isTrue(tabs.at(1).hasClass('is-selected')); assert.isTrue(tabs.at(1).hasClass('is-selected'));
}); });
...@@ -116,7 +109,7 @@ describe('SelectionTabs', function() { ...@@ -116,7 +109,7 @@ describe('SelectionTabs', function() {
selection: { selectedTab: uiConstants.TAB_ORPHANS }, selection: { selectedTab: uiConstants.TAB_ORPHANS },
}); });
fakeStore.orphanCount.returns(1); fakeStore.orphanCount.returns(1);
const wrapper = createDeepComponent({}); const wrapper = createComponent({});
const tabs = wrapper.find('a'); const tabs = wrapper.find('a');
assert.isTrue(tabs.at(2).hasClass('is-selected')); assert.isTrue(tabs.at(2).hasClass('is-selected'));
}); });
...@@ -125,7 +118,7 @@ describe('SelectionTabs', function() { ...@@ -125,7 +118,7 @@ describe('SelectionTabs', function() {
fakeStore.getState.returns({ fakeStore.getState.returns({
selection: { selectedTab: uiConstants.TAB_ORPHANS }, selection: { selectedTab: uiConstants.TAB_ORPHANS },
}); });
const wrapper = createDeepComponent({}); const wrapper = createComponent({});
const tabs = wrapper.find('a'); const tabs = wrapper.find('a');
assert.equal(tabs.length, 2); assert.equal(tabs.length, 2);
}); });
...@@ -143,7 +136,7 @@ describe('SelectionTabs', function() { ...@@ -143,7 +136,7 @@ describe('SelectionTabs', function() {
it('should not display the new-note-btn when the annotations tab is active', function() { it('should not display the new-note-btn when the annotations tab is active', function() {
const wrapper = createComponent(); const wrapper = createComponent();
assert.equal(wrapper.find(NewNoteBtn).length, 0); assert.equal(wrapper.find('NewNoteButton').length, 0);
}); });
it('should not display the new-note-btn when the notes tab is active and the new-note-btn is disabled', function() { it('should not display the new-note-btn when the notes tab is active and the new-note-btn is disabled', function() {
...@@ -151,7 +144,7 @@ describe('SelectionTabs', function() { ...@@ -151,7 +144,7 @@ describe('SelectionTabs', function() {
selection: { selectedTab: uiConstants.TAB_NOTES }, selection: { selectedTab: uiConstants.TAB_NOTES },
}); });
const wrapper = createComponent({}); const wrapper = createComponent({});
assert.equal(wrapper.find(NewNoteBtn).length, 0); assert.equal(wrapper.find('NewNoteButton').length, 0);
}); });
it('should display the new-note-btn when the notes tab is active and the new-note-btn is enabled', function() { it('should display the new-note-btn when the notes tab is active and the new-note-btn is enabled', function() {
...@@ -160,7 +153,7 @@ describe('SelectionTabs', function() { ...@@ -160,7 +153,7 @@ describe('SelectionTabs', function() {
selection: { selectedTab: uiConstants.TAB_NOTES }, selection: { selectedTab: uiConstants.TAB_NOTES },
}); });
const wrapper = createComponent({}); const wrapper = createComponent({});
assert.equal(wrapper.find(NewNoteBtn).length, 1); assert.equal(wrapper.find('NewNoteButton').length, 1);
}); });
it('should not display a message when its loading annotation count is 0', function() { it('should not display a message when its loading annotation count is 0', function() {
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const ShareAnnotationsPanel = require('../share-annotations-panel'); const ShareAnnotationsPanel = require('../share-annotations-panel');
const SidebarPanel = require('../sidebar-panel'); const mockImportedComponents = require('./mock-imported-components');
describe('ShareAnnotationsPanel', () => { describe('ShareAnnotationsPanel', () => {
let fakeStore; let fakeStore;
...@@ -19,13 +19,13 @@ describe('ShareAnnotationsPanel', () => { ...@@ -19,13 +19,13 @@ describe('ShareAnnotationsPanel', () => {
}; };
const createShareAnnotationsPanel = props => const createShareAnnotationsPanel = props =>
shallow( mount(
<ShareAnnotationsPanel <ShareAnnotationsPanel
analytics={fakeAnalytics} analytics={fakeAnalytics}
flash={fakeFlash} flash={fakeFlash}
{...props} {...props}
/> />
).dive(); // Needed because of `withServices` );
beforeEach(() => { beforeEach(() => {
fakeAnalytics = { fakeAnalytics = {
...@@ -49,6 +49,7 @@ describe('ShareAnnotationsPanel', () => { ...@@ -49,6 +49,7 @@ describe('ShareAnnotationsPanel', () => {
}), }),
}; };
ShareAnnotationsPanel.$imports.$mock(mockImportedComponents());
ShareAnnotationsPanel.$imports.$mock({ ShareAnnotationsPanel.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
'../util/copy-to-clipboard': fakeCopyToClipboard, '../util/copy-to-clipboard': fakeCopyToClipboard,
...@@ -64,7 +65,7 @@ describe('ShareAnnotationsPanel', () => { ...@@ -64,7 +65,7 @@ describe('ShareAnnotationsPanel', () => {
const wrapper = createShareAnnotationsPanel(); const wrapper = createShareAnnotationsPanel();
assert.equal( assert.equal(
wrapper.find(SidebarPanel).prop('title'), wrapper.find('SidebarPanel').prop('title'),
'Share Annotations in Test Private Group' 'Share Annotations in Test Private Group'
); );
}); });
...@@ -74,7 +75,7 @@ describe('ShareAnnotationsPanel', () => { ...@@ -74,7 +75,7 @@ describe('ShareAnnotationsPanel', () => {
const wrapper = createShareAnnotationsPanel(); const wrapper = createShareAnnotationsPanel();
assert.equal( assert.equal(
wrapper.find(SidebarPanel).prop('title'), wrapper.find('SidebarPanel').prop('title'),
'Share Annotations in ...' 'Share Annotations in ...'
); );
}); });
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const SidebarContentError = require('../sidebar-content-error'); const SidebarContentError = require('../sidebar-content-error');
const mockImportedComponents = require('./mock-imported-components');
describe('SidebarContentError', () => { describe('SidebarContentError', () => {
const createSidebarContentError = ( const createSidebarContentError = (
...@@ -11,7 +12,7 @@ describe('SidebarContentError', () => { ...@@ -11,7 +12,7 @@ describe('SidebarContentError', () => {
loggedInErrorMessage, loggedInErrorMessage,
isLoggedIn isLoggedIn
) => { ) => {
return shallow( return mount(
<SidebarContentError <SidebarContentError
loggedOutErrorMessage={loggedOutErrorMessage} loggedOutErrorMessage={loggedOutErrorMessage}
loggedInErrorMessage={loggedInErrorMessage} loggedInErrorMessage={loggedInErrorMessage}
...@@ -21,6 +22,14 @@ describe('SidebarContentError', () => { ...@@ -21,6 +22,14 @@ describe('SidebarContentError', () => {
); );
}; };
beforeEach(() => {
SidebarContentError.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
SidebarContentError.$imports.$restore();
});
it('shows error you may need to login to view message when logged out', () => { it('shows error you may need to login to view message when logged out', () => {
const isLoggedIn = false; const isLoggedIn = false;
const loggedOutErrorMessage = 'This annotation is not available.'; const loggedOutErrorMessage = 'This annotation is not available.';
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const SidebarPanel = require('../sidebar-panel'); const SidebarPanel = require('../sidebar-panel');
const mockImportedComponents = require('./mock-imported-components');
describe('SidebarPanel', () => { describe('SidebarPanel', () => {
let fakeStore; let fakeStore;
let fakeScrollIntoView; let fakeScrollIntoView;
const createSidebarPanel = props => const createSidebarPanel = props =>
shallow( mount(<SidebarPanel panelName="testpanel" title="Test Panel" {...props} />);
<SidebarPanel panelName="testpanel" title="Test Panel" {...props} />
);
beforeEach(() => { beforeEach(() => {
fakeScrollIntoView = sinon.stub(); fakeScrollIntoView = sinon.stub();
...@@ -27,6 +26,7 @@ describe('SidebarPanel', () => { ...@@ -27,6 +26,7 @@ describe('SidebarPanel', () => {
toggleSidebarPanel: sinon.stub(), toggleSidebarPanel: sinon.stub(),
}; };
SidebarPanel.$imports.$mock(mockImportedComponents());
SidebarPanel.$imports.$mock({ SidebarPanel.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
'scroll-into-view': fakeScrollIntoView, 'scroll-into-view': fakeScrollIntoView,
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const SortMenu = require('../sort-menu'); const SortMenu = require('../sort-menu');
const MenuItem = require('../menu-item'); const mockImportedComponents = require('./mock-imported-components');
describe('SortMenu', () => { describe('SortMenu', () => {
let fakeState; let fakeState;
let fakeStore; let fakeStore;
const createSortMenu = () => { const createSortMenu = () => {
return shallow(<SortMenu />); return mount(<SortMenu />);
}; };
beforeEach(() => { beforeEach(() => {
...@@ -26,6 +26,7 @@ describe('SortMenu', () => { ...@@ -26,6 +26,7 @@ describe('SortMenu', () => {
getState: sinon.stub().returns(fakeState), getState: sinon.stub().returns(fakeState),
}; };
SortMenu.$imports.$mock(mockImportedComponents());
SortMenu.$imports.$mock({ SortMenu.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
}); });
...@@ -38,7 +39,7 @@ describe('SortMenu', () => { ...@@ -38,7 +39,7 @@ describe('SortMenu', () => {
it('renders a menu item for each sort option', () => { it('renders a menu item for each sort option', () => {
const wrapper = createSortMenu(); const wrapper = createSortMenu();
const menuItems = wrapper.find(MenuItem); const menuItems = wrapper.find('MenuItem');
assert.lengthOf(menuItems, fakeState.selection.sortKeysAvailable.length); assert.lengthOf(menuItems, fakeState.selection.sortKeysAvailable.length);
fakeState.selection.sortKeysAvailable.forEach(sortKey => { fakeState.selection.sortKeysAvailable.forEach(sortKey => {
...@@ -53,7 +54,7 @@ describe('SortMenu', () => { ...@@ -53,7 +54,7 @@ describe('SortMenu', () => {
const wrapper = createSortMenu(); const wrapper = createSortMenu();
const currentSortKeyMenuItem = wrapper const currentSortKeyMenuItem = wrapper
.find(MenuItem) .find('MenuItem')
.filterWhere( .filterWhere(
menuItem => menuItem.prop('label') === fakeState.selection.sortKey menuItem => menuItem.prop('label') === fakeState.selection.sortKey
); );
...@@ -62,7 +63,7 @@ describe('SortMenu', () => { ...@@ -62,7 +63,7 @@ describe('SortMenu', () => {
it('sets the sort key via action when onClick callback invoked', () => { it('sets the sort key via action when onClick callback invoked', () => {
const wrapper = createSortMenu(); const wrapper = createSortMenu();
const menuItems = wrapper.find(MenuItem); const menuItems = wrapper.find('MenuItem');
menuItems.forEach(menuItem => { menuItems.forEach(menuItem => {
const callback = menuItem.prop('onClick'); const callback = menuItem.prop('onClick');
......
'use strict'; 'use strict';
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const { createElement } = require('preact'); const { createElement } = require('preact');
const { act } = require('preact/test-utils'); const { act } = require('preact/test-utils');
const StreamSearchInput = require('../stream-search-input'); const StreamSearchInput = require('../stream-search-input');
const mockImportedComponents = require('./mock-imported-components');
describe('StreamSearchInput', () => { describe('StreamSearchInput', () => {
let fakeLocation; let fakeLocation;
...@@ -19,16 +20,22 @@ describe('StreamSearchInput', () => { ...@@ -19,16 +20,22 @@ describe('StreamSearchInput', () => {
$apply: callback => callback(), $apply: callback => callback(),
$on: sinon.stub(), $on: sinon.stub(),
}; };
StreamSearchInput.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
StreamSearchInput.$imports.$restore();
}); });
function createSearchInput(props = {}) { function createSearchInput(props = {}) {
return shallow( return mount(
<StreamSearchInput <StreamSearchInput
$location={fakeLocation} $location={fakeLocation}
$rootScope={fakeRootScope} $rootScope={fakeRootScope}
{...props} {...props}
/> />
).dive(); // Dive through `withServices` wrapper. );
} }
it('displays current "q" search param', () => { it('displays current "q" search param', () => {
......
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const uiConstants = require('../../ui-constants'); const uiConstants = require('../../ui-constants');
const GroupList = require('../group-list');
const SearchInput = require('../search-input');
const StreamSearchInput = require('../stream-search-input');
const SortMenu = require('../sort-menu');
const TopBar = require('../top-bar'); const TopBar = require('../top-bar');
const UserMenu = require('../user-menu'); const mockImportedComponents = require('./mock-imported-components');
describe('TopBar', () => { describe('TopBar', () => {
const fakeSettings = {}; const fakeSettings = {};
...@@ -37,6 +33,7 @@ describe('TopBar', () => { ...@@ -37,6 +33,7 @@ describe('TopBar', () => {
applyPendingUpdates: sinon.stub(), applyPendingUpdates: sinon.stub(),
}; };
TopBar.$imports.$mock(mockImportedComponents());
TopBar.$imports.$mock({ TopBar.$imports.$mock({
'../store/use-store': callback => callback(fakeStore), '../store/use-store': callback => callback(fakeStore),
'../util/is-third-party-service': fakeIsThirdPartyService, '../util/is-third-party-service': fakeIsThirdPartyService,
...@@ -57,7 +54,7 @@ describe('TopBar', () => { ...@@ -57,7 +54,7 @@ describe('TopBar', () => {
function createTopBar(props = {}) { function createTopBar(props = {}) {
const auth = { status: 'unknown' }; const auth = { status: 'unknown' };
return shallow( return mount(
<TopBar <TopBar
auth={auth} auth={auth}
isSidebar={true} isSidebar={true}
...@@ -65,7 +62,7 @@ describe('TopBar', () => { ...@@ -65,7 +62,7 @@ describe('TopBar', () => {
streamer={fakeStreamer} streamer={fakeStreamer}
{...props} {...props}
/> />
).dive(); // Dive through `withServices` wrapper. );
} }
it('shows the pending update count', () => { it('shows the pending update count', () => {
...@@ -137,7 +134,7 @@ describe('TopBar', () => { ...@@ -137,7 +134,7 @@ describe('TopBar', () => {
const wrapper = createTopBar({ auth, onLogout }); const wrapper = createTopBar({ auth, onLogout });
assert.isFalse(getLoginText(wrapper).exists()); assert.isFalse(getLoginText(wrapper).exists());
const userMenu = wrapper.find(UserMenu); const userMenu = wrapper.find('UserMenu');
assert.isTrue(userMenu.exists()); assert.isTrue(userMenu.exists());
assert.include(userMenu.props(), { auth, onLogout }); assert.include(userMenu.props(), { auth, onLogout });
}); });
...@@ -192,7 +189,7 @@ describe('TopBar', () => { ...@@ -192,7 +189,7 @@ describe('TopBar', () => {
it('displays search input in the sidebar', () => { it('displays search input in the sidebar', () => {
fakeStore.filterQuery.returns('test-query'); fakeStore.filterQuery.returns('test-query');
const wrapper = createTopBar(); const wrapper = createTopBar();
assert.equal(wrapper.find(SearchInput).prop('query'), 'test-query'); assert.equal(wrapper.find('SearchInput').prop('query'), 'test-query');
}); });
it('updates current filter when changing search query in the sidebar', () => { it('updates current filter when changing search query in the sidebar', () => {
...@@ -203,7 +200,7 @@ describe('TopBar', () => { ...@@ -203,7 +200,7 @@ describe('TopBar', () => {
it('displays search input in the single annotation view / stream', () => { it('displays search input in the single annotation view / stream', () => {
const wrapper = createTopBar({ isSidebar: false }); const wrapper = createTopBar({ isSidebar: false });
const searchInput = wrapper.find(StreamSearchInput); const searchInput = wrapper.find('StreamSearchInput');
assert.ok(searchInput.exists()); assert.ok(searchInput.exists());
}); });
...@@ -216,8 +213,8 @@ describe('TopBar', () => { ...@@ -216,8 +213,8 @@ describe('TopBar', () => {
context('in the stream and single annotation pages', () => { context('in the stream and single annotation pages', () => {
it('does not render the group list, sort menu or share menu', () => { it('does not render the group list, sort menu or share menu', () => {
const wrapper = createTopBar({ isSidebar: false }); const wrapper = createTopBar({ isSidebar: false });
assert.isFalse(wrapper.exists(GroupList)); assert.isFalse(wrapper.exists('GroupList'));
assert.isFalse(wrapper.exists(SortMenu)); assert.isFalse(wrapper.exists('SortMenu'));
assert.isFalse(wrapper.exists('button[title="Share this page"]')); assert.isFalse(wrapper.exists('button[title="Share this page"]'));
}); });
...@@ -227,7 +224,7 @@ describe('TopBar', () => { ...@@ -227,7 +224,7 @@ describe('TopBar', () => {
auth: { status: 'logged-in' }, auth: { status: 'logged-in' },
}); });
assert.isTrue(wrapper.exists('button[title="Help"]')); assert.isTrue(wrapper.exists('button[title="Help"]'));
assert.isTrue(wrapper.exists(UserMenu)); assert.isTrue(wrapper.exists('UserMenu'));
}); });
}); });
}); });
'use strict'; 'use strict';
const { createElement } = require('preact'); const { createElement } = require('preact');
const { shallow } = require('enzyme'); const { mount } = require('enzyme');
const UserMenu = require('../user-menu'); const UserMenu = require('../user-menu');
const MenuItem = require('../menu-item'); const mockImportedComponents = require('./mock-imported-components');
describe('UserMenu', () => { describe('UserMenu', () => {
let fakeAuth; let fakeAuth;
...@@ -17,7 +17,7 @@ describe('UserMenu', () => { ...@@ -17,7 +17,7 @@ describe('UserMenu', () => {
let fakeSettings; let fakeSettings;
const createUserMenu = () => { const createUserMenu = () => {
return shallow( return mount(
<UserMenu <UserMenu
auth={fakeAuth} auth={fakeAuth}
bridge={fakeBridge} bridge={fakeBridge}
...@@ -25,12 +25,12 @@ describe('UserMenu', () => { ...@@ -25,12 +25,12 @@ describe('UserMenu', () => {
serviceUrl={fakeServiceUrl} serviceUrl={fakeServiceUrl}
settings={fakeSettings} settings={fakeSettings}
/> />
).dive(); // Dive needed because this component uses `withServices` );
}; };
const findMenuItem = (wrapper, labelText) => { const findMenuItem = (wrapper, labelText) => {
return wrapper return wrapper
.find(MenuItem) .find('MenuItem')
.filterWhere(n => n.prop('label') === labelText); .filterWhere(n => n.prop('label') === labelText);
}; };
...@@ -51,6 +51,7 @@ describe('UserMenu', () => { ...@@ -51,6 +51,7 @@ describe('UserMenu', () => {
authDomain: 'hypothes.is', authDomain: 'hypothes.is',
}; };
UserMenu.$imports.$mock(mockImportedComponents());
UserMenu.$imports.$mock({ UserMenu.$imports.$mock({
'../util/account-id': { '../util/account-id': {
isThirdPartyUser: fakeIsThirdPartyUser, isThirdPartyUser: fakeIsThirdPartyUser,
......
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