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